func checkPodApps(t *testing.T, rawPod *podInfo, apiApps []*v1alpha.App, hasAppState bool) { rawApps := rawPod.apps if len(rawApps) != len(apiApps) { t.Errorf("Expected %d apps, saw %d apps returned by api service %v", len(rawApps), len(apiApps), apiApps) } for _, app := range apiApps { appInfo, ok := rawApps[app.Name] if !ok { t.Errorf("Expected app (name: %q) in the app list", app.Name) continue } appACName := types.MustACName(app.Name) runtimeApp := rawPod.manifest.Apps.Get(*appACName) if runtimeApp == nil { t.Errorf("Expected app (name: %q) in the pod manifest", app.Name) } if hasAppState && appInfo.exitCode != int(app.ExitCode) { t.Errorf("Expected %v, saw %v", appInfo.exitCode, app.ExitCode) } // Image hash in the pod manifest can be partial hash. if !strings.HasPrefix(app.Image.Id, appInfo.image.id) { t.Errorf("Expected partial hash of %q, saw %q", appInfo.image.id, app.Image.Id) } // Check app annotations. checkAnnotations(t, runtimeApp.Annotations, app.Annotations) } }
func imageNameToAppName(name types.ACIdentifier) (*types.ACName, error) { parts := strings.Split(name.String(), "/") last := parts[len(parts)-1] sn, err := types.SanitizeACName(last) if err != nil { return nil, err } return types.MustACName(sn), nil }
func (r *Runtime) newAppcRuntimeApp(pod *api.Pod, c api.Container, pullSecrets []api.Secret) (*appcschema.RuntimeApp, []kubecontainer.PortMapping, error) { if err, _ := r.imagePuller.PullImage(pod, &c, pullSecrets); err != nil { return nil, nil, err } imgManifest, err := r.getImageManifest(c.Image) if err != nil { return nil, nil, err } if imgManifest.App == nil { imgManifest.App = new(appctypes.App) } imageID, err := r.getImageID(c.Image) if err != nil { return nil, nil, err } hash, err := appctypes.NewHash(imageID) if err != nil { return nil, nil, err } opts, err := r.generator.GenerateRunContainerOptions(pod, &c) if err != nil { return nil, nil, err } if err := setApp(imgManifest.App, &c, opts); err != nil { return nil, nil, err } name, err := appctypes.SanitizeACName(c.Name) if err != nil { return nil, nil, err } appName := appctypes.MustACName(name) kubehash := kubecontainer.HashContainer(&c) return &appcschema.RuntimeApp{ Name: *appName, Image: appcschema.RuntimeImage{ID: *hash}, App: imgManifest.App, Annotations: []appctypes.Annotation{ { Name: *appctypes.MustACIdentifier(k8sRktContainerHashAnno), Value: strconv.FormatUint(kubehash, 10), }, }, }, opts.PortMappings, nil }
func (img *Image) RuntimeApp() schema.RuntimeApp { nameStr, err := types.SanitizeACName(path.Base(img.Manifest.Name.String())) if err != nil { panic(err) } name := types.MustACName(nameStr) app := schema.RuntimeApp{ Name: *name, Image: schema.RuntimeImage{Name: &img.Manifest.Name}, } app.Annotations.Set("jetpack/image-uuid", img.UUID.String()) if img.Hash != nil { app.Image.ID = *img.Hash } else { // TODO: do we really need to store ACI tarballs to have an image ID on built images? app.Image.ID.Set(fmt.Sprintf( "sha512-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000%032x", img.UUID, )) } return app }
// makePodManifest transforms a kubelet pod spec to the rkt pod manifest. // TODO(yifan): Use the RunContainerOptions generated by GenerateRunContainerOptions(). func (r *runtime) makePodManifest(pod *api.Pod) (*appcschema.PodManifest, error) { var globalPortMappings []kubecontainer.PortMapping manifest := appcschema.BlankPodManifest() for _, c := range pod.Spec.Containers { imgManifest, err := r.getImageManifest(c.Image) if err != nil { return nil, err } if imgManifest.App == nil { return nil, fmt.Errorf("no app section in image manifest for image: %q", c.Image) } img, err := r.getImageByName(c.Image) if err != nil { return nil, err } hash, err := appctypes.NewHash(img.id) if err != nil { return nil, err } opts, err := r.generator.GenerateRunContainerOptions(pod, &c) if err != nil { return nil, err } globalPortMappings = append(globalPortMappings, opts.PortMappings...) if err := setApp(imgManifest.App, &c, opts); err != nil { return nil, err } manifest.Apps = append(manifest.Apps, appcschema.RuntimeApp{ // TODO(yifan): We should allow app name to be different with // image name. See https://github.com/coreos/rkt/pull/640. Name: imgManifest.Name, Image: appcschema.RuntimeImage{ID: *hash}, App: imgManifest.App, }) } volumeMap, ok := r.volumeGetter.GetVolumes(pod.UID) if !ok { return nil, fmt.Errorf("cannot get the volumes for pod %q", kubecontainer.GetPodFullName(pod)) } // Set global volumes. for name, volume := range volumeMap { volName, err := appctypes.NewACName(name) if err != nil { return nil, fmt.Errorf("cannot use the volume's name %q as ACName: %v", name, err) } manifest.Volumes = append(manifest.Volumes, appctypes.Volume{ Name: *volName, Kind: "host", Source: volume.GetPath(), }) } // Set global ports. for _, port := range globalPortMappings { name, err := appctypes.SanitizeACName(port.Name) if err != nil { return nil, fmt.Errorf("cannot use the port's name %q as ACName: %v", port.Name, err) } portName := appctypes.MustACName(name) manifest.Ports = append(manifest.Ports, appctypes.ExposedPort{ Name: *portName, HostPort: uint(port.HostPort), }) } // TODO(yifan): Set pod-level isolators once it's supported in kubernetes. return manifest, nil }
// setApp overrides the app's fields if any of them are specified in the // container's spec. func setApp(app *appctypes.App, c *api.Container, opts *kubecontainer.RunContainerOptions) error { // Override the exec. // TOOD(yifan): Revisit this for the overriding rule. if len(c.Command) > 0 || len(c.Args) > 0 { app.Exec = append(c.Command, c.Args...) } // TODO(yifan): Use non-root user in the future, see: // https://github.com/coreos/rkt/issues/820 app.User, app.Group = "0", "0" // Override the working directory. if len(c.WorkingDir) > 0 { app.WorkingDirectory = c.WorkingDir } // Override the environment. if len(opts.Envs) > 0 { app.Environment = []appctypes.EnvironmentVariable{} } for _, env := range c.Env { app.Environment = append(app.Environment, appctypes.EnvironmentVariable{ Name: env.Name, Value: env.Value, }) } // Override the mount points. if len(opts.Mounts) > 0 { app.MountPoints = []appctypes.MountPoint{} } for _, m := range opts.Mounts { mountPointName, err := appctypes.NewACName(m.Name) if err != nil { return err } app.MountPoints = append(app.MountPoints, appctypes.MountPoint{ Name: *mountPointName, Path: m.ContainerPath, ReadOnly: m.ReadOnly, }) } // Override the ports. if len(opts.PortMappings) > 0 { app.Ports = []appctypes.Port{} } for _, p := range opts.PortMappings { name, err := appctypes.SanitizeACName(p.Name) if err != nil { return err } portName := appctypes.MustACName(name) app.Ports = append(app.Ports, appctypes.Port{ Name: *portName, Protocol: string(p.Protocol), Port: uint(p.ContainerPort), }) } // Override isolators. return setIsolators(app, c) }
// makePodManifest transforms a kubelet pod spec to the rkt pod manifest. func (r *runtime) makePodManifest(pod *api.Pod, pullSecrets []api.Secret) (*appcschema.PodManifest, error) { var globalPortMappings []kubecontainer.PortMapping manifest := appcschema.BlankPodManifest() for _, c := range pod.Spec.Containers { if err := r.imagePuller.PullImage(pod, &c, pullSecrets); err != nil { return nil, err } imgManifest, err := r.getImageManifest(c.Image) if err != nil { return nil, err } if imgManifest.App == nil { imgManifest.App = new(appctypes.App) } img, err := r.getImageByName(c.Image) if err != nil { return nil, err } hash, err := appctypes.NewHash(img.ID) if err != nil { return nil, err } opts, err := r.generator.GenerateRunContainerOptions(pod, &c) if err != nil { return nil, err } globalPortMappings = append(globalPortMappings, opts.PortMappings...) if err := setApp(imgManifest.App, &c, opts); err != nil { return nil, err } name, err := appctypes.SanitizeACName(c.Name) if err != nil { return nil, err } appName := appctypes.MustACName(name) manifest.Apps = append(manifest.Apps, appcschema.RuntimeApp{ Name: *appName, Image: appcschema.RuntimeImage{ID: *hash}, App: imgManifest.App, }) } volumeMap, ok := r.volumeGetter.GetVolumes(pod.UID) if !ok { return nil, fmt.Errorf("cannot get the volumes for pod %q", kubeletUtil.FormatPodName(pod)) } // Set global volumes. for name, volume := range volumeMap { volName, err := appctypes.NewACName(name) if err != nil { return nil, fmt.Errorf("cannot use the volume's name %q as ACName: %v", name, err) } manifest.Volumes = append(manifest.Volumes, appctypes.Volume{ Name: *volName, Kind: "host", Source: volume.GetPath(), }) } // Set global ports. for _, port := range globalPortMappings { name, err := appctypes.SanitizeACName(port.Name) if err != nil { return nil, fmt.Errorf("cannot use the port's name %q as ACName: %v", port.Name, err) } portName := appctypes.MustACName(name) manifest.Ports = append(manifest.Ports, appctypes.ExposedPort{ Name: *portName, HostPort: uint(port.HostPort), }) } // TODO(yifan): Set pod-level isolators once it's supported in kubernetes. return manifest, nil }
// setApp overrides the app's fields if any of them are specified in the // container's spec. func setApp(app *appctypes.App, c *api.Container, opts *kubecontainer.RunContainerOptions) error { // Override the exec. if len(c.Command) > 0 { app.Exec = c.Command } if len(c.Args) > 0 { app.Exec = append(app.Exec, c.Args...) } // TODO(yifan): Use non-root user in the future, see: // https://github.com/coreos/rkt/issues/820 app.User, app.Group = "0", "0" // Override the working directory. if len(c.WorkingDir) > 0 { app.WorkingDirectory = c.WorkingDir } // Merge the environment. Override the image with the ones defined in the spec if necessary. for _, env := range opts.Envs { if ix := findEnvInList(app.Environment, env); ix >= 0 { app.Environment[ix].Value = env.Value continue } app.Environment = append(app.Environment, appctypes.EnvironmentVariable{ Name: env.Name, Value: env.Value, }) } // Override the mount points. if len(opts.Mounts) > 0 { app.MountPoints = []appctypes.MountPoint{} } for _, m := range opts.Mounts { mountPointName, err := appctypes.NewACName(m.Name) if err != nil { return err } app.MountPoints = append(app.MountPoints, appctypes.MountPoint{ Name: *mountPointName, Path: m.ContainerPath, ReadOnly: m.ReadOnly, }) } // Override the ports. if len(opts.PortMappings) > 0 { app.Ports = []appctypes.Port{} } for _, p := range opts.PortMappings { name, err := appctypes.SanitizeACName(p.Name) if err != nil { return err } portName := appctypes.MustACName(name) app.Ports = append(app.Ports, appctypes.Port{ Name: *portName, Protocol: string(p.Protocol), Port: uint(p.ContainerPort), }) } // Override isolators. return setIsolators(app, c) }
// makePodManifest transforms a kubelet pod spec to the rkt pod manifest. func (r *Runtime) makePodManifest(pod *api.Pod, pullSecrets []api.Secret) (*appcschema.PodManifest, error) { var globalPortMappings []kubecontainer.PortMapping manifest := appcschema.BlankPodManifest() listResp, err := r.apisvc.ListPods(context.Background(), &rktapi.ListPodsRequest{ Detail: true, Filters: kubernetesPodFilters(pod.UID), }) if err != nil { return nil, fmt.Errorf("couldn't list pods: %v", err) } restartCount := 0 for _, pod := range listResp.Pods { manifest := &appcschema.PodManifest{} err = json.Unmarshal(pod.Manifest, manifest) if err != nil { glog.Warningf("rkt: error unmatshaling pod manifest: %v", err) continue } if countString, ok := manifest.Annotations.Get(k8sRktRestartCountAnno); ok { num, err := strconv.Atoi(countString) if err != nil { glog.Warningf("rkt: error reading restart count on pod: %v", err) continue } if num+1 > restartCount { restartCount = num + 1 } } } manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktKubeletAnno), k8sRktKubeletAnnoValue) manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktUIDAnno), string(pod.UID)) manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktNameAnno), pod.Name) manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktNamespaceAnno), pod.Namespace) manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktCreationTimeAnno), strconv.FormatInt(time.Now().Unix(), 10)) manifest.Annotations.Set(*appctypes.MustACIdentifier(k8sRktRestartCountAnno), strconv.Itoa(restartCount)) for _, c := range pod.Spec.Containers { app, portMappings, err := r.newAppcRuntimeApp(pod, c, pullSecrets) if err != nil { return nil, err } manifest.Apps = append(manifest.Apps, *app) globalPortMappings = append(globalPortMappings, portMappings...) } volumeMap, ok := r.volumeGetter.GetVolumes(pod.UID) if !ok { return nil, fmt.Errorf("cannot get the volumes for pod %q", format.Pod(pod)) } // Set global volumes. for name, volume := range volumeMap { volName, err := appctypes.NewACName(name) if err != nil { return nil, fmt.Errorf("cannot use the volume's name %q as ACName: %v", name, err) } manifest.Volumes = append(manifest.Volumes, appctypes.Volume{ Name: *volName, Kind: "host", Source: volume.Builder.GetPath(), }) } // Set global ports. for _, port := range globalPortMappings { name, err := appctypes.SanitizeACName(port.Name) if err != nil { return nil, fmt.Errorf("cannot use the port's name %q as ACName: %v", port.Name, err) } portName := appctypes.MustACName(name) manifest.Ports = append(manifest.Ports, appctypes.ExposedPort{ Name: *portName, HostPort: uint(port.HostPort), }) } // TODO(yifan): Set pod-level isolators once it's supported in kubernetes. return manifest, nil }