// getImageByName tries to find the image info with the given image name. func (r *runtime) getImageByName(imageName string) (image, error) { // TODO(yifan): Print hash in rkt image? images, err := r.listImages() if err != nil { return image{}, err } var name, version string nameVersion := strings.Split(imageName, ":") // TODO(yifan): Currently the name cannot include "_", it is replaced // by "-". See the issue in appc/spec: https://github.com/appc/spec/issues/406. name, err = appctypes.SanitizeACName(nameVersion[0]) if err != nil { return image{}, err } if len(nameVersion) == 2 { version = nameVersion[1] } for _, img := range images { if img.name == name { if version == "" || img.version == version { return img, nil } } } return image{}, fmt.Errorf("cannot find the image %q", imageName) }
func convertVolumesToMPs(dockerVolumes map[string]struct{}) ([]appctypes.MountPoint, error) { mps := []appctypes.MountPoint{} dup := make(map[string]int) for p := range dockerVolumes { n := filepath.Join("volume", p) sn, err := appctypes.SanitizeACName(n) if err != nil { return nil, err } // check for duplicate names if i, ok := dup[sn]; ok { dup[sn] = i + 1 sn = fmt.Sprintf("%s-%d", sn, i) } else { dup[sn] = 1 } mp := appctypes.MountPoint{ Name: *appctypes.MustACName(sn), Path: p, } mps = append(mps, mp) } return mps, nil }
func parseDockerPort(dockerPort string) (*appctypes.Port, error) { var portString string proto := "tcp" sp := strings.Split(dockerPort, "/") if len(sp) < 2 { portString = dockerPort } else { proto = sp[1] portString = sp[0] } port, err := strconv.ParseUint(portString, 10, 0) if err != nil { return nil, fmt.Errorf("error parsing port %q: %v", portString, err) } sn, err := appctypes.SanitizeACName(dockerPort) if err != nil { return nil, err } appcPort := &appctypes.Port{ Name: *appctypes.MustACName(sn), Protocol: proto, Port: uint(port), } return appcPort, nil }
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) }
// convertToACName converts a string into ACName. func convertToACName(name string) appctypes.ACName { // Note that as the 'name' already matches 'DNS_LABEL' // defined in pkg/api/types.go, there shouldn't be error or panic. acname, _ := appctypes.SanitizeACName(name) return *appctypes.MustACName(acname) }
// 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 }