// getAppName returns the app name to enter // If one was supplied in the flags then it's simply returned // If the PM contains a single app, that app's name is returned // If the PM has multiple apps, the names are printed and an error is returned func getAppName(p *pkgPod.Pod) (*types.ACName, error) { if flagAppName != "" { return types.NewACName(flagAppName) } // figure out the app name, or show a list if multiple are present _, m, err := p.PodManifest() if err != nil { return nil, errwrap.Wrap(errors.New("error reading pod manifest"), err) } switch len(m.Apps) { case 0: return nil, fmt.Errorf("pod contains zero apps") case 1: return &m.Apps[0].Name, nil default: } stderr.Print("pod contains multiple apps:") for _, ra := range m.Apps { stderr.Printf("\t%v", ra.Name) } return nil, fmt.Errorf("specify app using \"rkt enter --app= ...\"") }
func getPodManifestModTime(p *pkgPod.Pod) (time.Time, error) { podFile := filepath.Join(p.Path(), "pod") fi, err := os.Stat(podFile) if err != nil { return time.Time{}, err } return fi.ModTime(), nil }
func getImageName(p *pkgPod.Pod, appName types.ACName) (string, error) { aim, err := p.AppImageManifest(appName.String()) if err != nil { return "", errwrap.Wrap(errors.New("problem retrieving ImageManifests from pod"), err) } imageName := aim.Name.String() if version, ok := aim.Labels.Get("version"); ok { imageName = fmt.Sprintf("%s:%s", imageName, version) } return imageName, nil }
func (s *v1AlphaAPIServer) getBasicPodFromDisk(p *pkgPod.Pod) (*v1alpha.Pod, error) { pod := &v1alpha.Pod{Id: p.UUID.String()} data, manifest, err := p.PodManifest() if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the pod manifest for pod %q", p.UUID), err) } else { pod.Annotations = convertAnnotationsToKeyValue(manifest.Annotations) pod.Apps = getApplist(manifest) pod.Manifest = data err = fillStaticAppInfo(s.store, p, pod) } return pod, err }
// getExitStatuses returns a map of the statuses of the pod. func getExitStatuses(p *pkgPod.Pod) (map[string]int, error) { _, manifest, err := p.PodManifest() if err != nil { return nil, err } stats := make(map[string]int) for _, app := range manifest.Apps { exitCode, err := p.AppExitCode(app.Name.String()) if err != nil { continue } stats[app.Name.String()] = exitCode } return stats, nil }
// getPodCgroup gets the cgroup path for a pod. This can be done in one of a couple ways: // // 1) stage1-coreos // // This one can be tricky because after machined registration, the cgroup might change. // For these flavors, the cgroup it will end up in after machined moves it is // written to a file named `subcgroup`, so we use that instead. // // 2) All others // // Assume that we can simply get the cgroup of the pod's init PID, and use that. func getPodCgroup(p *pkgPod.Pod, pid int) (string, error) { // Note, this file should always exist if the pid is known since it is // written before the pid file // The contents are of the form: // machined-registration (whether it has happened or not, but if stage1 plans to do so): // 'machine.slice/machine-rkt\x2dbfb67ff1\x2dc745\x2d4aec\x2db1e1\x2d7ce85a1fd42b.scope' // no registration, ran as systemd-unit 'foo.service': // 'system.slice/foo.service' // stage1-fly: file does not exist subCgroupFile := filepath.Join(p.Path(), "subcgroup") data, err := ioutil.ReadFile(subCgroupFile) // normalize cgroup to include a leading '/' if err == nil { strCgroup := string(data) if strings.HasPrefix(strCgroup, "/") { return strCgroup, nil } return "/" + strCgroup, nil } if !os.IsNotExist(err) { return "", err } // if it is an "isNotExist", assume this is a stage1 flavor that won't change // cgroups. Just give the systemd cgroup of its pid // Get cgroup for the "name=systemd" controller; we assume the api-server is // running on a system using systemd for returning cgroups, and will just not // set it otherwise. cgroup, err := v1.GetCgroupPathByPid(pid, "name=systemd") if err != nil { return "", err } // If the stage1 systemd > v226, it will put the PID1 into "init.scope" // implicit scope unit in the root slice. // See https://github.com/coreos/rkt/pull/2331#issuecomment-203540543 // // TODO(yifan): Revisit this when using unified cgroup hierarchy. cgroup = strings.TrimSuffix(cgroup, "/init.scope") return cgroup, nil }
// mountOverlay mounts the app from the overlay-rendered pod to the destination directory. func mountOverlay(pod *pkgPod.Pod, app *schema.RuntimeApp, dest string) error { if _, err := os.Stat(dest); err != nil { return err } s, err := imagestore.NewStore(getDataDir()) if err != nil { return errwrap.Wrap(errors.New("cannot open store"), err) } ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { return errwrap.Wrap(errors.New("cannot open treestore"), err) } treeStoreID, err := pod.GetAppTreeStoreID(app.Name) if err != nil { return err } lower := ts.GetRootFS(treeStoreID) imgDir := filepath.Join(filepath.Join(pod.Path(), "overlay"), treeStoreID) if _, err := os.Stat(imgDir); err != nil { return err } upper := filepath.Join(imgDir, "upper", app.Name.String()) if _, err := os.Stat(upper); err != nil { return err } work := filepath.Join(imgDir, "work", app.Name.String()) if _, err := os.Stat(work); err != nil { return err } if err := overlay.Mount(&overlay.MountCfg{lower, upper, work, dest, ""}); err != nil { return errwrap.Wrap(errors.New("problem mounting overlayfs directory"), err) } return nil }
// fillStaticAppInfo will modify the 'v1pod' in place with the information retrieved with 'pod'. // Today, these information are static and will not change during the pod's lifecycle. func fillStaticAppInfo(store *imagestore.Store, pod *pkgPod.Pod, v1pod *v1alpha.Pod) error { var errlist []error // Fill static app image info. for _, app := range v1pod.Apps { // Fill app's image info. app.Image = &v1alpha.Image{ BaseFormat: &v1alpha.ImageFormat{ // Only support appc image now. If it's a docker image, then it // will be transformed to appc before storing in the disk store. Type: v1alpha.ImageType_IMAGE_TYPE_APPC, Version: schema.AppContainerVersion.String(), }, Id: app.Image.Id, // Other information are not available because they require the image // info from store. Some of it is filled in below if possible. } im, err := pod.AppImageManifest(app.Name) if err != nil { stderr.PrintE(fmt.Sprintf("failed to get image manifests for app %q", app.Name), err) errlist = append(errlist, err) } else { app.Image.Name = im.Name.String() version, ok := im.Labels.Get("version") if !ok { version = "latest" } app.Image.Version = version } } if len(errlist) != 0 { return errs{errlist} } return nil }
// NewPodFromInternalPod converts *pkgPod.Pod to *Pod func NewPodFromInternalPod(p *pkgPod.Pod) (*Pod, error) { pod := &Pod{ UUID: p.UUID.String(), State: p.State(), Networks: p.Nets, } startTime, err := p.StartTime() if err != nil { return nil, err } if !startTime.IsZero() { startedAt := startTime.Unix() pod.StartedAt = &startedAt } if !p.PodManifestAvailable() { return pod, nil } // TODO(vc): we should really hold a shared lock here to prevent gc of the pod _, manifest, err := p.PodManifest() if err != nil { return nil, err } for _, app := range manifest.Apps { pod.AppNames = append(pod.AppNames, app.Name.String()) } if len(manifest.UserAnnotations) > 0 { pod.UserAnnotations = make(map[string]string) for name, value := range manifest.UserAnnotations { pod.UserAnnotations[name] = value } } if len(manifest.UserLabels) > 0 { pod.UserLabels = make(map[string]string) for name, value := range manifest.UserLabels { pod.UserLabels[name] = value } } return pod, nil }
// getApp returns the app to export // If one was supplied in the flags then it's returned if present // If the PM contains a single app, that app is returned // If the PM has multiple apps, the names are printed and an error is returned func getApp(p *pkgPod.Pod) (*schema.RuntimeApp, error) { _, manifest, err := p.PodManifest() if err != nil { return nil, errwrap.Wrap(errors.New("problem getting the pod's manifest"), err) } apps := manifest.Apps if flagExportAppName != "" { exportAppName, err := types.NewACName(flagExportAppName) if err != nil { return nil, err } for _, ra := range apps { if *exportAppName == ra.Name { return &ra, nil } } return nil, fmt.Errorf("app %s is not present in pod", flagExportAppName) } switch len(apps) { case 0: return nil, fmt.Errorf("pod contains zero apps") case 1: return &apps[0], nil default: } stderr.Print("pod contains multiple apps:") for _, ra := range apps { stderr.Printf("\t%v", ra.Name) } return nil, fmt.Errorf("specify app using \"rkt export --app= ...\"") }
func mountPodStage1(ts *treestore.Store, p *pkgPod.Pod) error { if !p.UsesOverlay() { return nil } s1Id, err := p.GetStage1TreeStoreID() if err != nil { return errwrap.Wrap(errors.New("error getting stage1 treeStoreID"), err) } s1rootfs := ts.GetRootFS(s1Id) stage1Dir := common.Stage1RootfsPath(p.Path()) overlayDir := filepath.Join(p.Path(), "overlay") imgDir := filepath.Join(overlayDir, s1Id) upperDir := filepath.Join(imgDir, "upper") workDir := filepath.Join(imgDir, "work") opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", s1rootfs, upperDir, workDir) if err := syscall.Mount("overlay", stage1Dir, "overlay", 0, opts); err != nil { return errwrap.Wrap(errors.New("error mounting stage1"), err) } return nil }
func appState(app *App, pod *pkgPod.Pod) error { app.State = AppStateUnknown defer func() { if pod.IsAfterRun() { // If the pod is hard killed, set the app to 'exited' state. // Other than this case, status file is guaranteed to be written. if app.State != AppStateExited { app.State = AppStateExited t, err := pod.GCMarkedTime() if err != nil { fmt.Fprintf(os.Stderr, "Cannot get GC marked time: %v", err) } if !t.IsZero() { finishedAt := t.UnixNano() app.FinishedAt = &finishedAt } } } }() // Check if the app is created. fi, err := os.Stat(common.AppCreatedPath(pod.Path(), app.Name)) if err != nil { if !os.IsNotExist(err) { return fmt.Errorf("cannot stat app creation file: %v", err) } return nil } app.State = AppStateCreated createdAt := fi.ModTime().UnixNano() app.CreatedAt = &createdAt // Check if the app is started. fi, err = os.Stat(common.AppStartedPath(pod.Path(), app.Name)) if err != nil { if !os.IsNotExist(err) { return fmt.Errorf("cannot stat app started file: %v", err) } return nil } app.State = AppStateRunning startedAt := fi.ModTime().UnixNano() app.StartedAt = &startedAt // Check if the app is exited. appStatusFile := common.AppStatusPath(pod.Path(), app.Name) fi, err = os.Stat(appStatusFile) if err != nil { if !os.IsNotExist(err) { return fmt.Errorf("cannot stat app exited file: %v", err) } return nil } app.State = AppStateExited finishedAt := fi.ModTime().UnixNano() app.FinishedAt = &finishedAt // Read exit code. exitCode, err := readExitCode(appStatusFile) if err != nil { return err } app.ExitCode = &exitCode return nil }
// deletePod cleans up files and resource associated with the pod // pod must be under exclusive lock and be in either ExitedGarbage // or Garbage state func deletePod(p *pkgPod.Pod) { podState := p.State() if podState != pkgPod.ExitedGarbage && podState != pkgPod.Garbage { stderr.Panicf("logic error: deletePod called with non-garbage pod %q (status %q)", p.UUID, p.State()) } if podState == pkgPod.ExitedGarbage { s, err := imagestore.NewStore(storeDir()) if err != nil { stderr.PrintE("cannot open store", err) return } defer s.Close() ts, err := treestore.NewStore(treeStoreDir(), s) if err != nil { stderr.PrintE("cannot open store", err) return } if globalFlags.Debug { stage0.InitDebug() } if err := mountPodStage1(ts, p); err == nil { if err = stage0.GC(p.Path(), p.UUID); err != nil { stderr.PrintE(fmt.Sprintf("problem performing stage1 GC on %q", p.UUID), err) } // an overlay fs can be mounted over itself, let's unmount it here // if we mounted it before to avoid problems when running // stage0.MountGC if p.UsesOverlay() { stage1Mnt := common.Stage1RootfsPath(p.Path()) if err := syscall.Unmount(stage1Mnt, 0); err != nil { stderr.PrintE("error unmounting stage1", err) } } } else { stderr.PrintE("skipping stage1 GC", err) } // unmount all leftover mounts if err := stage0.MountGC(p.Path(), p.UUID.String()); err != nil { stderr.PrintE(fmt.Sprintf("GC of leftover mounts for pod %q failed", p.UUID), err) return } } if err := os.RemoveAll(p.Path()); err != nil { stderr.PrintE(fmt.Sprintf("unable to remove pod %q", p.UUID), err) os.Exit(254) } }
// fillPodDetails fills the v1pod's dynamic info in place, e.g. the pod's state, // the pod's network info, the apps' state, etc. Such information can change // during the lifecycle of the pod, so we need to read it in every request. func fillPodDetails(store *imagestore.Store, p *pkgPod.Pod, v1pod *v1alpha.Pod) { v1pod.Pid = -1 switch p.State() { case pkgPod.Embryo: v1pod.State = v1alpha.PodState_POD_STATE_EMBRYO // When a pod is in embryo state, there is not much // information to return. return case pkgPod.Preparing: v1pod.State = v1alpha.PodState_POD_STATE_PREPARING case pkgPod.AbortedPrepare: v1pod.State = v1alpha.PodState_POD_STATE_ABORTED_PREPARE case pkgPod.Prepared: v1pod.State = v1alpha.PodState_POD_STATE_PREPARED case pkgPod.Running: v1pod.State = v1alpha.PodState_POD_STATE_RUNNING v1pod.Networks = getNetworks(p) case pkgPod.Deleting: v1pod.State = v1alpha.PodState_POD_STATE_DELETING case pkgPod.Exited: v1pod.State = v1alpha.PodState_POD_STATE_EXITED case pkgPod.Garbage, pkgPod.ExitedGarbage: v1pod.State = v1alpha.PodState_POD_STATE_GARBAGE default: v1pod.State = v1alpha.PodState_POD_STATE_UNDEFINED return } createdAt, err := p.CreationTime() if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the creation time for pod %q", p.UUID), err) } else if !createdAt.IsZero() { v1pod.CreatedAt = createdAt.UnixNano() } startedAt, err := p.StartTime() if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the start time for pod %q", p.UUID), err) } else if !startedAt.IsZero() { v1pod.StartedAt = startedAt.UnixNano() } gcMarkedAt, err := p.GCMarkedTime() if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the gc marked time for pod %q", p.UUID), err) } else if !gcMarkedAt.IsZero() { v1pod.GcMarkedAt = gcMarkedAt.UnixNano() } pid, err := p.Pid() if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the PID for pod %q", p.UUID), err) } else { v1pod.Pid = int32(pid) } if v1pod.State == v1alpha.PodState_POD_STATE_RUNNING { pid, err := p.ContainerPid1() if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the container PID1 for pod %q", p.UUID), err) } else { cgroup, err := getPodCgroup(p, pid) if err != nil { stderr.PrintE(fmt.Sprintf("failed to get the cgroup path for pod %q", p.UUID), err) } else { v1pod.Cgroup = cgroup } } } for _, app := range v1pod.Apps { readStatus := false if p.State() == pkgPod.Running { readStatus = true app.State = v1alpha.AppState_APP_STATE_RUNNING } else if p.AfterRun() { readStatus = true app.State = v1alpha.AppState_APP_STATE_EXITED } else { app.State = v1alpha.AppState_APP_STATE_UNDEFINED } if readStatus { exitCode, err := p.AppExitCode(app.Name) if err != nil { stderr.PrintE(fmt.Sprintf("failed to read status for app %q", app.Name), err) } app.ExitCode = int32(exitCode) } } }
// printStatus prints the pod's pid and per-app status codes func printStatus(p *pkgPod.Pod) error { if flagFormat != outputFormatTabbed { pod, err := lib.NewPodFromInternalPod(p) if err != nil { return fmt.Errorf("error converting pod: %v", err) } switch flagFormat { case outputFormatJSON: result, err := json.Marshal(pod) if err != nil { return fmt.Errorf("error marshaling the pod: %v", err) } stdout.Print(string(result)) case outputFormatPrettyJSON: result, err := json.MarshalIndent(pod, "", "\t") if err != nil { return fmt.Errorf("error marshaling the pod: %v", err) } stdout.Print(string(result)) } return nil } state := p.State() stdout.Printf("state=%s", state) created, err := p.CreationTime() if err != nil { return fmt.Errorf("unable to get creation time for pod %q: %v", p.UUID, err) } createdStr := created.Format(defaultTimeLayout) stdout.Printf("created=%s", createdStr) started, err := p.StartTime() if err != nil { return fmt.Errorf("unable to get start time for pod %q: %v", p.UUID, err) } var startedStr string if !started.IsZero() { startedStr = started.Format(defaultTimeLayout) stdout.Printf("started=%s", startedStr) } if state == pkgPod.Running { stdout.Printf("networks=%s", fmtNets(p.Nets)) } if state == pkgPod.Running || state == pkgPod.Deleting || state == pkgPod.ExitedDeleting || state == pkgPod.Exited || state == pkgPod.ExitedGarbage { var pid int pidCh := make(chan int, 1) // Wait slightly because the pid file might not be written yet when the state changes to 'Running'. go func() { for { pid, err := p.Pid() if err == nil { pidCh <- pid return } time.Sleep(time.Millisecond * 100) } }() select { case pid = <-pidCh: case <-time.After(time.Second): return fmt.Errorf("unable to get PID for pod %q: %v", p.UUID, err) } stdout.Printf("pid=%d\nexited=%t", pid, (state == pkgPod.Exited || state == pkgPod.ExitedGarbage)) if state != pkgPod.Running { stats, err := getExitStatuses(p) if err != nil { return fmt.Errorf("unable to get exit statuses for pod %q: %v", p.UUID, err) } for app, stat := range stats { stdout.Printf("app-%s=%d", app, stat) } } } return nil }
// printStatus prints the pod's pid and per-app status codes func printStatus(p *pkgPod.Pod) error { state := p.State() stdout.Printf("state=%s", state) created, err := p.CreationTime() if err != nil { return fmt.Errorf("unable to get creation time for pod %q: %v", p.UUID, err) } createdStr := created.Format(defaultTimeLayout) stdout.Printf("created=%s", createdStr) started, err := p.StartTime() if err != nil { return fmt.Errorf("unable to get start time for pod %q: %v", p.UUID, err) } var startedStr string if !started.IsZero() { startedStr = started.Format(defaultTimeLayout) stdout.Printf("started=%s", startedStr) } if state == pkgPod.Running { stdout.Printf("networks=%s", fmtNets(p.Nets)) } if state == pkgPod.Running || state == pkgPod.Deleting || state == pkgPod.ExitedDeleting || state == pkgPod.Exited || state == pkgPod.ExitedGarbage { var pid int pidCh := make(chan int, 1) // Wait slightly because the pid file might not be written yet when the state changes to 'Running'. go func() { for { pid, err := p.Pid() if err == nil { pidCh <- pid return } time.Sleep(time.Millisecond * 100) } }() select { case pid = <-pidCh: case <-time.After(time.Second): return fmt.Errorf("unable to get PID for pod %q: %v", p.UUID, err) } stdout.Printf("pid=%d\nexited=%t", pid, (state == pkgPod.Exited || state == pkgPod.ExitedGarbage)) if state != pkgPod.Running { stats, err := getExitStatuses(p) if err != nil { return fmt.Errorf("unable to get exit statuses for pod %q: %v", p.UUID, err) } for app, stat := range stats { stdout.Printf("app-%s=%d", app, stat) } } } return nil }