// NewAppFromString takes a command line app parameter and returns a map of labels. // // Example app parameters: // example.com/reduce-worker:1.0.0 // example.com/reduce-worker,channel=alpha,label=value func NewAppFromString(app string) (*App, error) { var ( name string labels map[types.ACIdentifier]string ) app = strings.Replace(app, ":", ",version=", -1) app = "name=" + app v, err := url.ParseQuery(strings.Replace(app, ",", "&", -1)) if err != nil { return nil, err } labels = make(map[types.ACIdentifier]string, 0) for key, val := range v { if len(val) > 1 { return nil, fmt.Errorf("label %s with multiple values %q", key, val) } if key == "name" { name = val[0] continue } labelName, err := types.NewACIdentifier(key) if err != nil { return nil, err } labels[*labelName] = val[0] } a, err := NewApp(name, labels) if err != nil { return nil, err } return a, nil }
func checkSignature(ks *Keystore, prefix string, signed, signature io.ReadSeeker) (*openpgp.Entity, error) { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return nil, err } keyring, err := ks.loadKeyring(acidentifier.String()) if err != nil { return nil, fmt.Errorf("keystore: error loading keyring %v", err) } entities, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature) if err == io.EOF { // When the signature is binary instead of armored, the error is io.EOF. // Let's try with binary signatures as well if _, err := signed.Seek(0, 0); err != nil { return nil, fmt.Errorf("error seeking ACI file: %v", err) } if _, err := signature.Seek(0, 0); err != nil { return nil, fmt.Errorf("error seeking signature file: %v", err) } entities, err = openpgp.CheckDetachedSignature(keyring, signed, signature) } if err == io.EOF { // otherwise, the client failure is just "EOF", which is not helpful return nil, fmt.Errorf("keystore: no valid signatures found in signature file") } return entities, err }
// StoreTrustedKeyPrefix stores the contents of public key r as a prefix trusted key. func (ks *Keystore) StoreTrustedKeyPrefix(prefix string, r io.Reader) (string, error) { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return "", err } return storeTrustedKey(path.Join(ks.LocalPrefixPath, acidentifier.String()), r) }
// DeleteTrustedKeyPrefix deletes the prefix trusted key identified by fingerprint. func (ks *Keystore) DeleteTrustedKeyPrefix(prefix, fingerprint string) error { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return err } return os.Remove(path.Join(ks.LocalPrefixPath, acidentifier.String(), fingerprint)) }
func handleAppAnnotation(w http.ResponseWriter, r *http.Request, pm *schema.PodManifest, im *schema.ImageManifest) { defer r.Body.Close() n := mux.Vars(r)["name"] k, err := types.NewACIdentifier(n) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "App annotation name %q is not a valid AC Identifier", n) return } n = mux.Vars(r)["app"] an, err := types.NewACName(n) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "App name %q is not a valid AC Name", n) return } merged := mergeAppAnnotations(im, pm, an) v, ok := merged.Get(k.String()) if !ok { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "App annotation %q not found", k) return } w.Header().Add("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) w.Write([]byte(v)) }
func isolatorStrFromString(is string) (types.ACIdentifier, string, error) { is = "name=" + is v, err := url.ParseQuery(strings.Replace(is, ",", "&", -1)) if err != nil { return "", "", err } var name string var values []string var acn *types.ACIdentifier for key, val := range v { if len(val) > 1 { return "", "", fmt.Errorf("label %s with multiple values %q", key, val) } switch key { case "name": acn, err = types.NewACIdentifier(val[0]) if err != nil { return "", "", err } name = val[0] default: // (TODO)yifan: Not support the default boolean yet. values = append(values, fmt.Sprintf(`"%s": "%s"`, key, val[0])) } } return *acn, getIsolatorStr(name, strings.Join(values, ", ")), nil }
func validatePodAnnotations(metadataURL string, pm *schema.PodManifest) results { r := results{} var actualAnnots types.Annotations annots, err := metadataGet(metadataURL, "/pod/annotations/") if err != nil { return append(r, err) } for _, key := range strings.Split(string(annots), "\n") { if key == "" { continue } val, err := metadataGet(metadataURL, "/pod/annotations/"+key) if err != nil { r = append(r, err) } name, err := types.NewACIdentifier(key) if err != nil { r = append(r, fmt.Errorf("invalid annotation name: %v", err)) continue } actualAnnots.Set(*name, string(val)) } if !reflect.DeepEqual(actualAnnots, pm.Annotations) { r = append(r, fmt.Errorf("pod annotations mismatch: %v vs %v", actualAnnots, pm.Annotations)) } return r }
// NewAppFromString takes a command line app parameter and returns a map of labels. // // Example app parameters: // example.com/reduce-worker:1.0.0 // example.com/reduce-worker,channel=alpha,label=value // example.com/reduce-worker:1.0.0,label=value // // As can be seen in above examples - colon, comma and equal sign have // special meaning. If any of them has to be a part of a label's value // then consider writing your own string to App parser. func NewAppFromString(app string) (*App, error) { var ( name string labels map[types.ACIdentifier]string ) preparedApp, err := prepareAppString(app) if err != nil { return nil, err } v, err := url.ParseQuery(preparedApp) if err != nil { return nil, err } labels = make(map[types.ACIdentifier]string, 0) for key, val := range v { if len(val) > 1 { return nil, fmt.Errorf("label %s with multiple values %q", key, val) } if key == "name" { name = val[0] continue } labelName, err := types.NewACIdentifier(key) if err != nil { return nil, err } labels[*labelName] = val[0] } a, err := NewApp(name, labels) if err != nil { return nil, err } return a, nil }
// MaskTrustedKeySystemPrefix masks the system prefix trusted key identified by fingerprint. func (ks *Keystore) MaskTrustedKeySystemPrefix(prefix, fingerprint string) (string, error) { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return "", err } dst := path.Join(ks.LocalPrefixPath, acidentifier.String(), fingerprint) return dst, ioutil.WriteFile(dst, []byte(""), 0644) }
func (ks *Keystore) loadKeyring(prefix string) (openpgp.KeyRing, error) { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return nil, err } var keyring openpgp.EntityList trustedKeys := make(map[string]*openpgp.Entity) prefixRoot := strings.Split(acidentifier.String(), "/")[0] paths := []struct { root string fullPath string }{ {ks.SystemRootPath, ks.SystemRootPath}, {ks.LocalRootPath, ks.LocalRootPath}, {path.Join(ks.SystemPrefixPath, prefixRoot), path.Join(ks.SystemPrefixPath, acidentifier.String())}, {path.Join(ks.LocalPrefixPath, prefixRoot), path.Join(ks.LocalPrefixPath, acidentifier.String())}, } for _, p := range paths { err := filepath.Walk(p.root, func(path string, info os.FileInfo, err error) error { if err != nil && !os.IsNotExist(err) { return err } if info == nil { return nil } if info.IsDir() { switch { case strings.HasPrefix(p.fullPath, path): return nil default: return filepath.SkipDir } } // Remove trust for default keys. if info.Size() == 0 { delete(trustedKeys, info.Name()) return nil } entity, err := entityFromFile(path) if err != nil { return err } trustedKeys[fingerprintToFilename(entity.PrimaryKey.Fingerprint)] = entity return nil }) if err != nil { return nil, err } } for _, v := range trustedKeys { keyring = append(keyring, v) } return keyring, nil }
func (custom *GoCustomizations) GetImageName() (*types.ACIdentifier, error) { imageName := custom.Configuration.Project if filepath.Base(imageName) == "..." { imageName = filepath.Dir(imageName) if custom.Configuration.UseBinary != "" { imageName += "-" + custom.Configuration.UseBinary } } return types.NewACIdentifier(strings.ToLower(imageName)) }
func newLabel(name, value string) (*types.Label, error) { acName, err := types.NewACIdentifier(name) if err != nil { return nil, err } return &types.Label{ Name: *acName, Value: value, }, nil }
func NewApp(name string, labels map[types.ACIdentifier]string) (*App, error) { if labels == nil { labels = make(map[types.ACIdentifier]string, 0) } acn, err := types.NewACIdentifier(name) if err != nil { return nil, err } return &App{ Name: *acn, Labels: labels, }, nil }
func checkSignature(ks *Keystore, prefix string, signed, signature io.Reader) (*openpgp.Entity, error) { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return nil, err } keyring, err := ks.loadKeyring(acidentifier.String()) if err != nil { return nil, fmt.Errorf("keystore: error loading keyring %v", err) } entities, err := openpgp.CheckArmoredDetachedSignature(keyring, signed, signature) if err == io.EOF { // otherwise, the client failure is just "EOF", which is not helpful return nil, fmt.Errorf("keystore: no valid signatures found in signature file") } return entities, err }
func validateAppAnnotations(metadataURL string, pm *schema.PodManifest, app *schema.RuntimeApp, img *schema.ImageManifest) results { r := results{} // build a map of expected annotations by merging app.Annotations // with PodManifest overrides expectedAnnots := app.Annotations a := pm.Apps.Get(app.Name) if a == nil { panic("could not find app in manifest!") } for _, annot := range a.Annotations { expectedAnnots.Set(annot.Name, annot.Value) } var actualAnnots types.Annotations annots, err := metadataGet(metadataURL, "/apps/"+string(app.Name)+"/annotations/") if err != nil { return append(r, err) } for _, key := range strings.Split(string(annots), "\n") { if key == "" { continue } val, err := metadataGet(metadataURL, "/apps/"+string(app.Name)+"/annotations/"+key) if err != nil { r = append(r, err) } lbl, err := types.NewACIdentifier(key) if err != nil { r = append(r, fmt.Errorf("invalid annotation name: %v", err)) continue } actualAnnots.Set(*lbl, string(val)) } if !reflect.DeepEqual(actualAnnots, expectedAnnots) { err := fmt.Errorf("%v annotations mismatch: %v vs %v", app.Name, actualAnnots, expectedAnnots) r = append(r, err) } return r }
func (ks *Keystore) TrustedKeyPrefixExists(prefix string, r io.ReadSeeker) (bool, error) { defer r.Seek(0, os.SEEK_SET) entityList, err := openpgp.ReadArmoredKeyRing(r) if err != nil { return false, err } if len(entityList) < 1 { return false, errors.New("missing opengpg entity") } pubKey := entityList[0].PrimaryKey fileName := fingerprintToFilename(pubKey.Fingerprint) pathNamesRoot := []string{ // example: /etc/rkt/trustedkeys/root.d/8b86de38890ddb7291867b025210bd8888182190 path.Join(ks.LocalRootPath, fileName), // example: /usr/lib/rkt/trustedkeys/root.d/8b86de38890ddb7291867b025210bd8888182190 path.Join(ks.SystemRootPath, fileName), } var pathNamesPrefix []string if prefix != "" { acidentifier, err := types.NewACIdentifier(prefix) if err != nil { return false, err } pathNamesPrefix = []string{ // example: /etc/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190 path.Join(ks.LocalPrefixPath, acidentifier.String(), fileName), // example: /usr/lib/rkt/trustedkeys/prefix.d/coreos.com/etcd/8b86de38890ddb7291867b025210bd8888182190 path.Join(ks.SystemPrefixPath, acidentifier.String(), fileName), } } pathNames := append(pathNamesRoot, pathNamesPrefix...) for _, p := range pathNames { _, err := os.Stat(p) if err == nil { return true, nil } else if !os.IsNotExist(err) { return false, fmt.Errorf("cannot check file %q: %v", p, err) } } return false, nil }
func (cmd *Builder) getVCSLabel() (*types.Label, error) { repoPath, err := cmd.custom.GetRepoPath() if err != nil { return nil, err } if repoPath == "" { return nil, nil } name, value, err := GetVCSInfo(repoPath) if err != nil { return nil, fmt.Errorf("Failed to get VCS info: %v", err) } acname, err := types.NewACIdentifier(name) if err != nil { return nil, fmt.Errorf("Invalid VCS label: %v", err) } return &types.Label{ Name: *acname, Value: value, }, nil }
func handlePodAnnotation(w http.ResponseWriter, r *http.Request, pm *schema.PodManifest) { defer r.Body.Close() n := mux.Vars(r)["name"] k, err := types.NewACIdentifier(n) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintf(w, "Pod annotation %q is not a valid AC Identifier", n) return } v, ok := pm.Annotations.Get(k.String()) if !ok { w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "Pod annotation %q not found", k) return } w.Header().Add("Content-Type", "text/plain") w.WriteHeader(http.StatusOK) w.Write([]byte(v)) }
func patchManifest(im *schema.ImageManifest) error { if patchName != "" { name, err := types.NewACIdentifier(patchName) if err != nil { return err } im.Name = *name } var app *types.App = im.App if patchExec != "" { if app == nil { // if the original manifest was missing an app and // patchExec is set let's assume the user is trying to // inject one... im.App = &types.App{} app = im.App } app.Exec = strings.Split(patchExec, " ") } if patchUser != "" || patchGroup != "" || patchSupplementaryGIDs != "" || patchCaps != "" || patchMounts != "" || patchPorts != "" || patchIsolators != "" { // ...but if we still don't have an app and the user is trying // to patch one of its other parameters, it's an error if app == nil { return fmt.Errorf("no app in the supplied manifest and no exec command provided") } } if patchUser != "" { app.User = patchUser } if patchGroup != "" { app.Group = patchGroup } if patchSupplementaryGIDs != "" { app.SupplementaryGIDs = []int{} gids := strings.Split(patchSupplementaryGIDs, ",") for _, g := range gids { gid, err := strconv.Atoi(g) if err != nil { return fmt.Errorf("invalid supplementary group %q: %v", g, err) } app.SupplementaryGIDs = append(app.SupplementaryGIDs, gid) } } if patchCaps != "" { isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName) if isolator != nil { return fmt.Errorf("isolator already exists") } // Instantiate a Isolator with the content specified by the --capability // parameter. // TODO: Instead of creating a JSON and then unmarshalling it, the isolator // should be instantiated directory. But it requires a constructor, see: // https://github.com/appc/spec/issues/268 capsList := strings.Split(patchCaps, ",") caps := fmt.Sprintf(`"set": ["%s"]`, strings.Join(capsList, `", "`)) isolatorStr := getIsolatorStr(types.LinuxCapabilitiesRetainSetName, caps) isolator = &types.Isolator{} err := isolator.UnmarshalJSON([]byte(isolatorStr)) if err != nil { return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err) } app.Isolators = append(app.Isolators, *isolator) } if patchMounts != "" { mounts := strings.Split(patchMounts, ":") for _, m := range mounts { mountPoint, err := types.MountPointFromString(m) if err != nil { return fmt.Errorf("cannot parse mount point %q: %v", m, err) } app.MountPoints = append(app.MountPoints, *mountPoint) } } if patchPorts != "" { ports := strings.Split(patchPorts, ":") for _, p := range ports { port, err := types.PortFromString(p) if err != nil { return fmt.Errorf("cannot parse port %q: %v", p, err) } app.Ports = append(app.Ports, *port) } } if patchIsolators != "" { isolators := strings.Split(patchIsolators, ":") for _, is := range isolators { name, isolatorStr, err := isolatorStrFromString(is) if err != nil { return fmt.Errorf("cannot parse isolator %q: %v", is, err) } if _, ok := types.ResourceIsolatorNames[name]; !ok { return fmt.Errorf("isolator %s is not supported for patching", name) } isolator := &types.Isolator{} if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil { return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err) } app.Isolators = append(app.Isolators, *isolator) } } return nil }
func patchManifest(im *schema.ImageManifest) error { if patchName != "" { name, err := types.NewACIdentifier(patchName) if err != nil { return err } im.Name = *name } if patchExec != "" { im.App.Exec = strings.Split(patchExec, " ") } if patchUser != "" { im.App.User = patchUser } if patchGroup != "" { im.App.Group = patchGroup } var app *types.App if patchCaps != "" || patchMounts != "" || patchPorts != "" || patchIsolators != "" { app = im.App if app == nil { return fmt.Errorf("no app in the manifest") } } if patchCaps != "" { isolator := app.Isolators.GetByName(types.LinuxCapabilitiesRetainSetName) if isolator != nil { return fmt.Errorf("isolator already exists") } // Instantiate a Isolator with the content specified by the --capability // parameter. // TODO: Instead of creating a JSON and then unmarshalling it, the isolator // should be instantiated directory. But it requires a constructor, see: // https://github.com/appc/spec/issues/268 capsList := strings.Split(patchCaps, ",") caps := fmt.Sprintf(`"set": ["%s"]`, strings.Join(capsList, `", "`)) isolatorStr := getIsolatorStr(types.LinuxCapabilitiesRetainSetName, caps) isolator = &types.Isolator{} err := isolator.UnmarshalJSON([]byte(isolatorStr)) if err != nil { return fmt.Errorf("cannot parse capability %q: %v", patchCaps, err) } app.Isolators = append(app.Isolators, *isolator) } if patchMounts != "" { mounts := strings.Split(patchMounts, ":") for _, m := range mounts { mountPoint, err := types.MountPointFromString(m) if err != nil { return fmt.Errorf("cannot parse mount point %q: %v", m, err) } app.MountPoints = append(app.MountPoints, *mountPoint) } } if patchPorts != "" { ports := strings.Split(patchPorts, ":") for _, p := range ports { port, err := types.PortFromString(p) if err != nil { return fmt.Errorf("cannot parse port %q: %v", p, err) } app.Ports = append(app.Ports, *port) } } if patchIsolators != "" { isolators := strings.Split(patchIsolators, ":") for _, is := range isolators { name, isolatorStr, err := isolatorStrFromString(is) if err != nil { return fmt.Errorf("cannot parse isolator %q: %v", is, err) } if _, ok := types.ResourceIsolatorNames[name]; !ok { return fmt.Errorf("isolator %s is not supported for patching", name) } isolator := &types.Isolator{} if err := isolator.UnmarshalJSON([]byte(isolatorStr)); err != nil { return fmt.Errorf("cannot unmarshal isolator %v: %v", isolatorStr, err) } app.Isolators = append(app.Isolators, *isolator) } } return nil }