func (ts *TestStore) GetACI(name types.ACName, labels types.Labels) (string, error) { for _, aci := range ts.acis { if aci.ImageManifest.Name.String() == name.String() { return aci.key, nil } } return "", fmt.Errorf("aci not found") }
// Get retrieves an app by the specified name from the AppList; if there is // no such app, nil is returned. The returned *RuntimeApp MUST be considered // read-only. func (al AppList) Get(name types.ACName) *RuntimeApp { for _, a := range al { if name.Equals(a.Name) { aa := a return &aa } } return nil }
func (ms *ConversionStore) GetACI(name types.ACName, labels types.Labels) (string, error) { for _, aci := range ms.acis { // we implement this function to comply with the interface so don't // bother implementing a proper label check if aci.ImageManifest.Name.String() == name.String() { return aci.key, nil } } return "", fmt.Errorf("aci not found") }
// getAppImageManifest returns an ImageManifest for the corresponding AppName. func (p *pod) getAppImageManifest(appName types.ACName) (*schema.ImageManifest, error) { imb, err := ioutil.ReadFile(common.AppInfoImageManifestPath(p.path(), appName)) if err != nil { return nil, err } aim := &schema.ImageManifest{} if err := aim.UnmarshalJSON(imb); err != nil { return nil, fmt.Errorf("invalid image manifest for app %q: %v", appName.String(), err) } return aim, nil }
// AppToSystemdMountUnits prepare bind mount unit for empty or host kind mounting // between stage1 rootfs and chrooted filesystem for application func AppToSystemdMountUnits(root string, appName types.ACName, mountPoints []types.MountPoint, unitsDir string) error { for _, mountPoint := range mountPoints { name := mountPoint.Name.String() // source relative to stage1 rootfs to relative pod root whatPath := filepath.Join(stage1MntDir, name) whatFullPath := filepath.Join(root, whatPath) // destination relative to stage1 rootfs and relative to pod root wherePath := filepath.Join(common.RelAppRootfsPath(appName), mountPoint.Path) whereFullPath := filepath.Join(root, wherePath) // readOnly mountOptions := "bind" if mountPoint.ReadOnly { mountOptions += ",ro" } // assertion to make sure that "what" exists (created earlier by podToSystemdHostMountUnits) log.Printf("checking required source path: %q", whatFullPath) if _, err := os.Stat(whatFullPath); os.IsNotExist(err) { return fmt.Errorf("app requires a volume that is not defined in Pod (try adding --volume=%s,kind=empty)!", name) } // optionally prepare app directory log.Printf("optionally preparing destination path: %q", whereFullPath) err := os.MkdirAll(whereFullPath, 0700) if err != nil { return fmt.Errorf("failed to prepare dir for mountPoint %v: %v", mountPoint.Name, err) } // install new mount unit for bind mount /mnt/volumeName -> /opt/stage2/{app-id}/rootfs/{{mountPoint.Path}} err = installNewMountUnit( root, // where put a mount unit whatPath, // what - stage1 rootfs /mnt/VolumeName wherePath, // where - inside chroot app filesystem "bind", // fstype mountOptions, serviceUnitName(appName), unitsDir, ) if err != nil { return fmt.Errorf("cannot install new mount unit for app %q: %v", appName.String(), err) } } return nil }
// setupAppImage mounts the overlay filesystem for the app image that // corresponds to the given hash. Then, it creates the tmp directory. // When useOverlay is false it just creates the tmp directory for this app. func setupAppImage(cfg RunConfig, appName types.ACName, img types.Hash, cdir string, useOverlay bool) error { ad := common.AppPath(cdir, appName) if useOverlay { err := os.MkdirAll(ad, 0776) if err != nil { return fmt.Errorf("error creating image directory: %v", err) } if err := overlayRender(cfg, img, cdir, ad, appName.String()); err != nil { return fmt.Errorf("error rendering overlay filesystem: %v", err) } } return nil }
// setupAppImage mounts the overlay filesystem for the app image that // corresponds to the given hash. Then, it creates the tmp directory. // When useOverlay is false it just creates the tmp directory for this app. func setupAppImage(cfg RunConfig, appName types.ACName, img types.Hash, cdir string, useOverlay bool) error { ad := common.AppPath(cdir, appName) if useOverlay { err := os.MkdirAll(ad, defaultRegularDirPerm) if err != nil { return fmt.Errorf("error creating image directory: %v", err) } treeStoreID, err := ioutil.ReadFile(common.AppTreeStoreIDPath(cdir, appName)) if err != nil { return err } if err := copyAppManifest(cdir, appName, ad); err != nil { return err } if err := overlayRender(cfg, string(treeStoreID), cdir, ad, appName.String()); err != nil { return fmt.Errorf("error rendering overlay filesystem: %v", err) } } return nil }
// Enter enters the pod/app by exec()ing the stage1's /enter similar to /init // /enter can expect to have its CWD set to the app root. // appName and command are supplied to /enter on argv followed by any arguments. // stage1Path is the path of the stage1 rootfs func Enter(cdir string, podPID int, appName types.ACName, stage1Path string, cmdline []string) error { if err := os.Chdir(cdir); err != nil { return fmt.Errorf("error changing to dir: %v", err) } ep, err := getStage1Entrypoint(cdir, enterEntrypoint) if err != nil { return fmt.Errorf("error determining entrypoint: %v", err) } argv := []string{filepath.Join(stage1Path, ep)} argv = append(argv, strconv.Itoa(podPID)) argv = append(argv, appName.String()) argv = append(argv, cmdline...) if err := syscall.Exec(argv[0], argv, os.Environ()); err != nil { return fmt.Errorf("error execing enter: %v", err) } // never reached return nil }
// RelAppPath returns the path of an app relative to the stage1 chroot. func RelAppPath(appName types.ACName) string { return filepath.Join(stage2Dir, appName.String()) }
// AppPath returns the path to an app's rootfs. func AppPath(root string, appName types.ACName) string { return filepath.Join(AppsPath(root), appName.String()) }
// serviceUnitName returns a systemd service unit name for the given app name. // note: it was shamefully copy-pasted from stage1/init/path.go // TODO: extract common functions from path.go func serviceUnitName(appName types.ACName) string { return appName.String() + ".service" }
// AppToSystemdMountUnits prepare bind mount unit for empty or host kind mounting // between stage1 rootfs and chrooted filesystem for application func AppToSystemdMountUnits(root string, appName types.ACName, volumes []types.Volume, ra *schema.RuntimeApp, unitsDir string) error { app := ra.App vols := make(map[types.ACName]types.Volume) for _, v := range volumes { vols[v.Name] = v } mounts, err := initcommon.GenerateMounts(ra, vols) if err != nil { return err } for _, m := range mounts { vol := vols[m.Volume] // source relative to stage1 rootfs to relative pod root whatPath := filepath.Join(stage1MntDir, vol.Name.String()) whatFullPath := filepath.Join(root, whatPath) // destination relative to stage1 rootfs and relative to pod root wherePath := filepath.Join(common.RelAppRootfsPath(appName), m.Path) whereFullPath := filepath.Join(root, wherePath) // assertion to make sure that "what" exists (created earlier by PodToSystemdHostMountUnits) log.Printf("checking required source path: %q", whatFullPath) if _, err := os.Stat(whatFullPath); os.IsNotExist(err) { return fmt.Errorf("bug: missing source for volume %v", vol.Name) } // optionally prepare app directory log.Printf("optionally preparing destination path: %q", whereFullPath) err := os.MkdirAll(whereFullPath, 0700) if err != nil { return fmt.Errorf("failed to prepare dir for mount %v: %v", m.Volume, err) } // install new mount unit for bind mount /mnt/volumeName -> /opt/stage2/{app-id}/rootfs/{{mountPoint.Path}} mu, err := installNewMountUnit( root, // where put a mount unit whatPath, // what - stage1 rootfs /mnt/VolumeName wherePath, // where - inside chroot app filesystem "bind", // fstype "bind", // options serviceUnitName(appName), unitsDir, ) if err != nil { return fmt.Errorf("cannot install new mount unit for app %q: %v", appName.String(), err) } // TODO(iaguis) when we update util-linux to 2.27, this code can go // away and we can bind-mount RO with one unit file. // http://ftp.kernel.org/pub/linux/utils/util-linux/v2.27/v2.27-ReleaseNotes if initcommon.IsMountReadOnly(vol, app.MountPoints) { opts := []*unit.UnitOption{ unit.NewUnitOption("Unit", "Description", fmt.Sprintf("Remount read-only unit for %s", wherePath)), unit.NewUnitOption("Unit", "DefaultDependencies", "false"), unit.NewUnitOption("Unit", "After", mu), unit.NewUnitOption("Unit", "Wants", mu), unit.NewUnitOption("Service", "ExecStart", fmt.Sprintf("/usr/bin/mount -o remount,ro %s", wherePath)), unit.NewUnitOption("Install", "RequiredBy", mu), } remountUnitPath := filepath.Join(root, unitsDir, unit.UnitNamePathEscape(wherePath+"-remount.service")) if err := writeUnit(opts, remountUnitPath); err != nil { return err } } } return nil }
// SocketUnitName returns a systemd socket unit name for the given app name. func SocketUnitName(appName types.ACName) string { return appName.String() + ".socket" }
// RelEnvFilePath returns the path to the environment file for the given app name // relative to the pod's root. func RelEnvFilePath(appName types.ACName) string { return filepath.Join(envDir, appName.String()) }
// GetACI retrieves the ACI that best matches the provided app name and labels. // The returned value is the blob store key of the retrieved ACI. // If there are multiple matching ACIs choose the latest one (defined as the // last one imported in the store). // If no version label is requested, ACIs marked as latest in the ACIInfo are // preferred. func (s Store) GetACI(name types.ACName, labels types.Labels) (string, error) { var curaciinfo *ACIInfo versionRequested := false if _, ok := labels.Get("version"); ok { versionRequested = true } var aciinfos []*ACIInfo err := s.db.Do(func(tx *sql.Tx) error { var err error aciinfos, _, err = GetACIInfosWithAppName(tx, name.String()) return err }) if err != nil { return "", err } nextKey: for _, aciinfo := range aciinfos { im, err := s.GetImageManifest(aciinfo.BlobKey) if err != nil { return "", fmt.Errorf("error getting image manifest: %v", err) } // The image manifest must have all the requested labels for _, l := range labels { ok := false for _, rl := range im.Labels { if l.Name == rl.Name && l.Value == rl.Value { ok = true break } } if !ok { continue nextKey } } if curaciinfo != nil { // If no version is requested prefer the acis marked as latest if !versionRequested { if !curaciinfo.Latest && aciinfo.Latest { curaciinfo = aciinfo continue nextKey } if curaciinfo.Latest && !aciinfo.Latest { continue nextKey } } // If multiple matching image manifests are found, choose the latest imported in the cas. if aciinfo.ImportTime.After(curaciinfo.ImportTime) { curaciinfo = aciinfo } } else { curaciinfo = aciinfo } } if curaciinfo != nil { return curaciinfo.BlobKey, nil } return "", fmt.Errorf("aci not found") }