func (a *ACBuild) writeEmptyManifest() error { acid, err := types.NewACIdentifier("acbuild-unnamed") if err != nil { return err } archlabel, err := types.NewACIdentifier("arch") if err != nil { return err } oslabel, err := types.NewACIdentifier("os") if err != nil { return err } manifest := &schema.ImageManifest{ ACKind: schema.ImageManifestKind, ACVersion: schema.AppContainerVersion, Name: *acid, Labels: types.Labels{ types.Label{ *archlabel, runtime.GOARCH, }, types.Label{ *oslabel, runtime.GOOS, }, }, } manblob, err := manifest.MarshalJSON() if err != nil { return err } manfile, err := os.Create(path.Join(a.CurrentACIPath, aci.ManifestFile)) if err != nil { return err } _, err = manfile.Write(manblob) if err != nil { return err } err = manfile.Close() if err != nil { return err } return nil }
func WriteImageManifest(m *spec.AciManifest, targetFile string, projectName string, cntVersion string) { name, err := types.NewACIdentifier(m.NameAndVersion.Name()) if err != nil { panic(err) } version := m.NameAndVersion.Version() if version == "" { version = GenerateVersion() } labels := types.Labels{} labels = append(labels, types.Label{Name: "version", Value: version}) labels = append(labels, types.Label{Name: "os", Value: "linux"}) labels = append(labels, types.Label{Name: "arch", Value: "amd64"}) if m.Aci.App.User == "" { m.Aci.App.User = "******" } if m.Aci.App.Group == "" { m.Aci.App.Group = "0" } im := schema.BlankImageManifest() im.Annotations = m.Aci.Annotations cntVersionIdentifier, _ := types.NewACIdentifier("cnt-version") im.Annotations.Set(*cntVersionIdentifier, cntVersion) im.Dependencies = toAppcDependencies(m.Aci.Dependencies) im.Name = *name im.Labels = labels im.App = &types.App{ Exec: m.Aci.App.Exec, EventHandlers: []types.EventHandler{{Name: "pre-start", Exec: []string{"/cnt/bin/prestart"}}}, User: m.Aci.App.User, Group: m.Aci.App.Group, WorkingDirectory: m.Aci.App.WorkingDirectory, Environment: m.Aci.App.Environment, MountPoints: m.Aci.App.MountPoints, Ports: m.Aci.App.Ports, Isolators: m.Aci.App.Isolators, } buff, err := im.MarshalJSON() if err != nil { panic(err) } err = ioutil.WriteFile(targetFile, buff, 0644) if err != nil { panic(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) }
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)) }
// 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 }
// 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 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 }
// 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)) }
// AddLabel will add a label with the given name and value to the untarred ACI // stored at a.CurrentACIPath. If the label already exists its value will be updated to // the new value. func (a *ACBuild) AddLabel(name, value string) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() acid, err := types.NewACIdentifier(name) if err != nil { return err } fn := func(s *schema.ImageManifest) error { removeLabelFromMan(*acid)(s) s.Labels = append(s.Labels, types.Label{ Name: *acid, Value: value, }) return nil } return util.ModifyManifest(fn, a.CurrentACIPath) }
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 }
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, errwrap.Wrap(errors.New("keystore: error loading keyring"), 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, errwrap.Wrap(errors.New("error seeking ACI file"), err) } if _, err := signature.Seek(0, 0); err != nil { return nil, errwrap.Wrap(errors.New("error seeking signature file"), 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 }
// 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 pathToACIdentifier(path string) (types.ACIdentifier, error) { if dirname := filepath.Base(filepath.Dir(path)); dirname == "@" { return Root, nil } else if prefix, err := types.NewACIdentifier(strings.Replace(dirname, ",", "/", -1)); err != nil { return "", err } else { return *prefix, nil } }
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 *CmakeCustomizations) 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 (p *Pod) processAci(e common.RuntimeApp) (*schema.RuntimeApp, error) { aci, err := p.buildAci(e) if err != nil { return nil, err } name, err := types.NewACName(e.Name) if err != nil { return nil, errs.WithEF(err, p.fields.WithField("name", e.Name), "Invalid name format") } sum, err := Sha512sum(aci.target + pathImageAci) if err != nil { return nil, errs.WithEF(err, p.fields.WithField("file", aci.target+pathImageAci), "Failed to calculate sha512 of aci") } tmp, _ := types.NewHash("sha512-" + sum) labels := types.Labels{} labels = append(labels, types.Label{Name: "version", Value: aci.manifest.NameAndVersion.Version()}) identifier, _ := types.NewACIdentifier(aci.manifest.NameAndVersion.Name()) ttmp := schema.RuntimeImage{Name: identifier, ID: *tmp, Labels: labels} e.App.Group = aci.manifest.Aci.App.Group e.App.User = aci.manifest.Aci.App.User if e.App.User == "" { e.App.User = "******" } if e.App.Group == "" { e.App.Group = "0" } isolators, err := common.ToAppcIsolators(e.App.Isolators) if err != nil { return nil, errs.WithEF(err, p.fields, "Failed to prepare isolators") } return &schema.RuntimeApp{ Name: *name, Image: ttmp, App: &types.App{ Exec: e.App.Exec, User: e.App.User, Group: e.App.Group, WorkingDirectory: e.App.WorkingDirectory, SupplementaryGIDs: e.App.SupplementaryGIDs, Environment: e.App.Environment, MountPoints: e.App.MountPoints, Ports: e.App.Ports, Isolators: isolators, }, Mounts: e.Mounts, Annotations: e.Annotations}, nil }
func (afl *AnnotationsFlag) Set(val string) error { pieces := strings.SplitN(val, "=", 2) if len(pieces) != 2 { return errors.New("Annotations must be provided in NAME=VALUE format") } else if name, err := types.NewACIdentifier(pieces[0]); err != nil { return err } else { (*types.Annotations)(afl).Set(*name, pieces[1]) return nil } }
func (p *Pod) processAci() []schema.RuntimeApp { apps := []schema.RuntimeApp{} for _, e := range p.manifest.Pod.Apps { aciName := p.buildAciIfNeeded(e) // TODO: support not FS override by only storing info pod manifest // if aciName == nil { // aciName = &e.Image // } name, _ := types.NewACName(e.Name) sum, err := utils.Sha512sum(p.path + "/" + e.Name + "/target/image.aci") if err != nil { log.Get().Panic(err) } tmp, _ := types.NewHash("sha512-" + sum) labels := types.Labels{} labels = append(labels, types.Label{Name: "version", Value: aciName.Version()}) identifier, _ := types.NewACIdentifier(aciName.Name()) ttmp := schema.RuntimeImage{Name: identifier, ID: *tmp, Labels: labels} if e.App.User == "" { e.App.User = "******" } if e.App.Group == "" { e.App.Group = "0" } apps = append(apps, schema.RuntimeApp{ Name: *name, Image: ttmp, App: &types.App{ Exec: e.App.Exec, EventHandlers: e.App.EventHandlers, User: e.App.User, Group: e.App.Group, WorkingDirectory: e.App.WorkingDirectory, Environment: e.App.Environment, MountPoints: e.App.MountPoints, Ports: e.App.Ports, Isolators: e.App.Isolators, }, Mounts: e.Mounts, Annotations: e.Annotations}) } return apps }
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 (ls *labellist) Set(input string) error { parts := strings.SplitN(input, "=", 2) if len(parts) != 2 { return fmt.Errorf("no '=' character in %q", input) } acid, err := types.NewACIdentifier(parts[0]) if err != nil { return err } *ls = append(*ls, types.Label{ Name: *acid, Value: parts[1], }) return nil }
func trustKeys(args []string) error { for _, loc := range args { if trustPrefix.Empty() { if acnLoc, err := types.NewACIdentifier(loc); err != nil { return errors.Trace(err) } else if err := Host.TrustKey(*acnLoc, "", trustFingerprint); err != nil { return errors.Trace(err) } } else if err := Host.TrustKey(trustPrefix, loc, trustFingerprint); err != nil { return errors.Trace(err) } } return nil }
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 (p *Pod) processAci() []schema.RuntimeApp { apps := []schema.RuntimeApp{} for _, e := range p.manifest.Pod.Apps { aci := p.buildAci(e) name, _ := types.NewACName(e.Name) sum, err := utils.Sha512sum(aci.target + "/image.aci") if err != nil { panic(err) } tmp, _ := types.NewHash("sha512-" + sum) labels := types.Labels{} labels = append(labels, types.Label{Name: "version", Value: aci.manifest.NameAndVersion.Version()}) identifier, _ := types.NewACIdentifier(aci.manifest.NameAndVersion.Name()) ttmp := schema.RuntimeImage{Name: identifier, ID: *tmp, Labels: labels} if e.App.User == "" { e.App.User = "******" } if e.App.Group == "" { e.App.Group = "0" } apps = append(apps, schema.RuntimeApp{ Name: *name, Image: ttmp, App: &types.App{ Exec: e.App.Exec, User: e.App.User, Group: e.App.Group, WorkingDirectory: e.App.WorkingDirectory, Environment: e.App.Environment, MountPoints: e.App.MountPoints, Ports: e.App.Ports, Isolators: e.App.Isolators, }, Mounts: e.Mounts, Annotations: e.Annotations}) } return apps }
func toAppcDependencies(dependencies []spec.ACFullname) types.Dependencies { appcDependencies := types.Dependencies{} for _, dep := range dependencies { id, err := types.NewACIdentifier(dep.Name()) if err != nil { panic(err) } t := types.Dependency{ImageName: *id} if dep.Version() != "" { t.Labels = types.Labels{} t.Labels = append(t.Labels, types.Label{Name: "version", Value: dep.Version()}) } appcDependencies = append(appcDependencies, t) } return appcDependencies }
func ToAppcDependencies(dependencies []ACFullname) (types.Dependencies, error) { appcDependencies := types.Dependencies{} for _, dep := range dependencies { id, err := types.NewACIdentifier(dep.Name()) if err != nil { return nil, errs.WithEF(err, data.WithField("name", dep.Name()), "invalid identifer name for rkt") } t := types.Dependency{ImageName: *id} if dep.Version() != "" { t.Labels = types.Labels{} t.Labels = append(t.Labels, types.Label{Name: "version", Value: dep.Version()}) } appcDependencies = append(appcDependencies, t) } return appcDependencies, nil }
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, errwrap.Wrap(fmt.Errorf("cannot check file %q", p), err) } } return false, nil }
// RemoveAnnotation will remove the annotation with the given name from the // untarred ACI stored at a.CurrentACIPath func (a *ACBuild) RemoveAnnotation(name string) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() acid, err := types.NewACIdentifier(name) if err != nil { return err } return util.ModifyManifest(removeAnnotation(*acid), a.CurrentACIPath) }
func (a *ACBuild) AddIsolator(name string, value []byte) (err error) { if err = a.lock(); err != nil { return err } defer func() { if err1 := a.unlock(); err == nil { err = err1 } }() acid, err := types.NewACIdentifier(name) if err != nil { return err } rawMsg := json.RawMessage(value) fn := func(s *schema.ImageManifest) error { if s.App == nil { s.App = newManifestApp() } _, ok := types.ResourceIsolatorNames[*acid] if !ok { _, ok = types.LinuxIsolatorNames[*acid] if !ok { return fmt.Errorf("unknown isolator name: %s", name) } } i := &types.Isolator{ Name: *acid, ValueRaw: &rawMsg, } blob, err := json.Marshal(i) if err != nil { return err } err = i.UnmarshalJSON(blob) if err != nil { return err } removeIsolatorFromMan(*acid)(s) s.App.Isolators = append(s.App.Isolators, *i) return nil } return util.ModifyManifest(fn, a.CurrentACIPath) }
func validateACIVars(vars map[string]string, extension bool) (*aciVars, error) { if _, err := types.NewACIdentifier(vars["name"]); err != nil { return nil, fmt.Errorf("invalid ACIdentifier %q, must match the regexp %q", vars["name"], types.ValidACIdentifier) } if _, err := semver.NewVersion(vars["version"]); err != nil { return nil, fmt.Errorf("invalid version %q, must be a valid semver string", vars["version"]) } if extension && (vars["ext"] != ".aci" && vars["ext"] != ".aci.asc") { return nil, fmt.Errorf("invalid extension %q, must be '.aci' or '.aci.asc'", vars["ext"]) } if v, ok := vars["ext"]; (ok && v != ".") && !extension { return nil, fmt.Errorf("extension was specified when not expected") } return &aciVars{name: vars["name"], version: vars["version"], os: vars["os"], arch: vars["arch"], ext: vars["ext"]}, nil }