diff --git a/pkg/kubelet/rkt/container_id.go b/pkg/kubelet/rkt/container_id.go index d497c8d501f..aa35361dda9 100644 --- a/pkg/kubelet/rkt/container_id.go +++ b/pkg/kubelet/rkt/container_id.go @@ -25,28 +25,26 @@ import ( // be returned to kubelet, and kubelet will use this for // container level operations. type containerID struct { - uuid string // uuid of the pod. - appName string // name of the app in that pod. - imageID string // id of the image. TODO(yifan): Depreciate this. + uuid string // rkt uuid of the pod. + appName string // Name of the app in that pod. } // buildContainerID constructs the containers's ID using containerID, -// which containers the pod uuid and the container name. -// The result can be used to globally identify a container. +// which consists of the pod uuid and the container name. +// The result can be used to uniquely identify a container. func buildContainerID(c *containerID) string { - return fmt.Sprintf("%s:%s:%s", c.uuid, c.appName, c.imageID) + return fmt.Sprintf("%s:%s", c.uuid, c.appName) } // parseContainerID parses the containerID into pod uuid and the container name. The // results can be used to get more information of the container. func parseContainerID(id string) (*containerID, error) { tuples := strings.Split(id, ":") - if len(tuples) != 3 { + if len(tuples) != 2 { return nil, fmt.Errorf("rkt: cannot parse container ID for: %v", id) } return &containerID{ uuid: tuples[0], appName: tuples[1], - imageID: tuples[2], }, nil } diff --git a/pkg/kubelet/rkt/pod_info.go b/pkg/kubelet/rkt/pod_info.go index 64a38cb5c03..e30a13e327f 100644 --- a/pkg/kubelet/rkt/pod_info.go +++ b/pkg/kubelet/rkt/pod_info.go @@ -38,6 +38,9 @@ const ( Deleting = "deleting" // This covers pod.isExitedDeleting and pod.isDeleting. Exited = "exited" // This covers pod.isExited and pod.isExitedGarbage. Garbage = "garbage" + + // The prefix before the app name for each app's exit code in the output of 'rkt status'. + exitCodePrefix = "app-" ) // podInfo is the internal type that represents the state of @@ -49,12 +52,21 @@ type podInfo struct { ip string // The pid of the init process in the pod. pid int - // A map from image hashes to exit codes. - // TODO(yifan): Should be appName to exit code in the future. + // A map of [app name]:[exit code]. exitCodes map[string]int + // TODO(yifan): Expose [app name]:[image id]. } // parsePodInfo parses the result of 'rkt status' into podInfo. +// +// Example output of 'rkt status': +// +// state=exited +// pid=-1 +// exited=true +// app-etcd=0 # The exit code of the app "etcd" in the pod. +// app-redis=0 # The exit code of the app "redis" in the pod. +// func parsePodInfo(status []string) (*podInfo, error) { p := &podInfo{ pid: -1, @@ -80,12 +92,14 @@ func parsePodInfo(status []string) (*podInfo, error) { } p.pid = pid } - if strings.HasPrefix(tuples[0], "sha512") { + + if strings.HasPrefix(tuples[0], exitCodePrefix) { exitcode, err := strconv.Atoi(tuples[1]) if err != nil { return nil, fmt.Errorf("cannot parse exit code from %s : %v", tuples[1], err) } - p.exitCodes[tuples[0]] = exitcode + appName := strings.TrimPrefix(tuples[0], exitCodePrefix) + p.exitCodes[appName] = exitcode } } return p, nil @@ -94,30 +108,27 @@ func parsePodInfo(status []string) (*podInfo, error) { // getIPFromNetworkInfo returns the IP of a pod by parsing the network info. // The network info looks like this: // -// default:ip4=172.16.28.3, database:ip4=172.16.28.42 +// default:ip4=172.16.28.3 +// database:ip4=172.16.28.42 // func getIPFromNetworkInfo(networkInfo string) string { parts := strings.Split(networkInfo, ",") - for _, part := range parts { - if strings.HasPrefix(part, "default:") { - tuples := strings.Split(part, "=") - if len(tuples) == 2 { - return tuples[1] - } + tuples := strings.Split(part, "=") + if len(tuples) == 2 { + return tuples[1] } } return "" } // getContainerStatus creates the api.containerStatus of a container from the podInfo. -// TODO(yifan): Get more detailed info such as Image, ImageID, etc. func (p *podInfo) getContainerStatus(container *kubecontainer.Container) api.ContainerStatus { var status api.ContainerStatus status.Name = container.Name status.Image = container.Image - containerID, _ := parseContainerID(string(container.ID)) - status.ImageID = containerID.imageID + status.ContainerID = string(container.ID) + // TODO(yifan): Add image ID info. switch p.state { case Running: @@ -130,11 +141,12 @@ func (p *podInfo) getContainerStatus(container *kubecontainer.Container) api.Con case Embryo, Preparing, Prepared: status.State = api.ContainerState{Waiting: &api.ContainerStateWaiting{}} case AbortedPrepare, Deleting, Exited, Garbage: - exitCode, ok := p.exitCodes[status.ImageID] + exitCode, ok := p.exitCodes[status.Name] if !ok { glog.Warningf("rkt: Cannot get exit code for container %v", container) + exitCode = -1 + } - exitCode = -1 status.State = api.ContainerState{ Terminated: &api.ContainerStateTerminated{ ExitCode: exitCode, diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go index 4092f38488e..92d718636b7 100644 --- a/pkg/kubelet/rkt/rkt.go +++ b/pkg/kubelet/rkt/rkt.go @@ -48,17 +48,13 @@ import ( ) const ( - rktBinName = "rkt" + acVersion = "0.6.1" + rktMinimumVersion = "0.7.0" + systemdMinimumVersion = "219" - acversion = "0.5.1" - rktMinimumVersion = "0.5.4" - systemdMinimumVersion = "215" - - systemdServiceDir = "/run/systemd/system" - rktDataDir = "/var/lib/rkt" - rktLocalConfigDir = "/etc/rkt" - rktMetadataServiceFile = "rkt-metadata.service" - rktMetadataSocketFile = "rkt-metadata.socket" + systemdServiceDir = "/run/systemd/system" + rktDataDir = "/var/lib/rkt" + rktLocalConfigDir = "/etc/rkt" kubernetesUnitPrefix = "k8s" unitKubernetesSection = "X-Kubernetes" @@ -133,7 +129,8 @@ func New(config *Config, } // Test if rkt binary is in $PATH. - rktBinAbsPath, err := exec.LookPath(rktBinName) + // TODO(yifan): Use a kubelet flag to read the path. + rktBinAbsPath, err := exec.LookPath("rkt") if err != nil { return nil, fmt.Errorf("cannot find rkt binary: %v", err) } @@ -167,7 +164,7 @@ func New(config *Config, } func (r *runtime) buildCommand(args ...string) *exec.Cmd { - cmd := exec.Command(rktBinName) + cmd := exec.Command(r.rktBinAbsPath) cmd.Args = append(cmd.Args, r.config.buildGlobalOptions()...) cmd.Args = append(cmd.Args, args...) return cmd @@ -187,7 +184,8 @@ func (r *runtime) runCommand(args ...string) ([]string, error) { // makePodServiceFileName constructs the unit file name for a pod using its UID. func makePodServiceFileName(uid types.UID) string { - // TODO(yifan): Revisit this later, decide whether we want to use UID. + // TODO(yifan): Add name for readability? We need to consider the + // limit of the length. return fmt.Sprintf("%s_%s.service", kubernetesUnitPrefix, uid) } @@ -377,7 +375,6 @@ func (r *runtime) getImageManifest(image string) (*appcschema.ImageManifest, err } // 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() @@ -463,8 +460,7 @@ func newUnitOption(section, name, value string) *unit.UnitOption { return &unit.UnitOption{Section: section, Name: name, Value: value} } -// TODO(yifan): Remove the receiver once we can solve the appName->imageID problem. -func (r *runtime) apiPodToruntimePod(uuid string, pod *api.Pod) *kubecontainer.Pod { +func apiPodToruntimePod(uuid string, pod *api.Pod) *kubecontainer.Pod { p := &kubecontainer.Pod{ ID: pod.UID, Name: pod.Name, @@ -472,12 +468,8 @@ func (r *runtime) apiPodToruntimePod(uuid string, pod *api.Pod) *kubecontainer.P } for i := range pod.Spec.Containers { c := &pod.Spec.Containers[i] - img, err := r.getImageByName(c.Image) - if err != nil { - glog.Warningf("rkt: Cannot get image for %q: %v", c.Image, err) - } p.Containers = append(p.Containers, &kubecontainer.Container{ - ID: types.UID(buildContainerID(&containerID{uuid, c.Name, img.id})), + ID: types.UID(buildContainerID(&containerID{uuid, c.Name})), Name: c.Name, Image: c.Image, Hash: kubecontainer.HashContainer(c), @@ -503,7 +495,7 @@ func (r *runtime) preparePod(pod *api.Pod) (string, bool, error) { if err != nil { return "", false, err } - manifestFile, err := ioutil.TempFile("", "manifest") + manifestFile, err := ioutil.TempFile("", fmt.Sprintf("manifest-%s-", pod.Name)) if err != nil { return "", false, err } @@ -533,18 +525,26 @@ func (r *runtime) preparePod(pod *api.Pod) (string, bool, error) { return "", false, fmt.Errorf("cannot get uuid from 'rkt prepare'") } uuid := output[0] - glog.V(4).Infof("'rkt prepare' returns %q.", uuid) + glog.V(4).Infof("'rkt prepare' returns %q", uuid) - p := r.apiPodToruntimePod(uuid, pod) + p := apiPodToruntimePod(uuid, pod) b, err := json.Marshal(p) if err != nil { return "", false, err } - runPrepared := fmt.Sprintf("%s run-prepared --private-net=%v %s", r.rktBinAbsPath, !pod.Spec.HostNetwork, uuid) + var runPrepared string + if pod.Spec.HostNetwork { + runPrepared = fmt.Sprintf("%s run-prepared --mds-register=false %s", r.rktBinAbsPath, uuid) + } else { + runPrepared = fmt.Sprintf("%s run-prepared --mds-register=false --private-net %s", r.rktBinAbsPath, uuid) + } + units := []*unit.UnitOption{ newUnitOption(unitKubernetesSection, unitRktID, uuid), newUnitOption(unitKubernetesSection, unitPodName, string(b)), + // This makes the service show up for 'systemctl list-units' even if it exits successfully. + newUnitOption("Service", "RemainAfterExit", "true"), newUnitOption("Service", "ExecStart", runPrepared), } @@ -934,7 +934,7 @@ func (r *runtime) GarbageCollect() error { return nil } -// Note: In rkt, the container ID is in the form of "UUID:appName:ImageID", where +// Note: In rkt, the container ID is in the form of "UUID:appName", where // appName is the container name. func (r *runtime) RunInContainer(containerID string, cmd []string) ([]byte, error) { glog.V(4).Infof("Rkt running in container.") @@ -943,9 +943,7 @@ func (r *runtime) RunInContainer(containerID string, cmd []string) ([]byte, erro if err != nil { return nil, err } - // TODO(yifan): Use appName instead of imageID. - // see https://github.com/coreos/rkt/pull/640 - args := append([]string{}, "enter", "--imageid", id.imageID, id.uuid) + args := append([]string{}, "enter", fmt.Sprintf("--app=%s", id.appName), id.uuid) args = append(args, cmd...) result, err := r.runCommand(args...) @@ -956,8 +954,8 @@ func (r *runtime) AttachContainer(containerID string, stdin io.Reader, stdout, s return errors.New("unimplemented") } -// Note: In rkt, the container ID is in the form of "UUID:appName:ImageID", where -// appName is the container name. +// Note: In rkt, the container ID is in the form of "UUID:appName", where UUID is +// the rkt UUID, and appName is the container name. func (r *runtime) ExecInContainer(containerID string, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool) error { glog.V(4).Infof("Rkt execing in container.") @@ -965,9 +963,7 @@ func (r *runtime) ExecInContainer(containerID string, cmd []string, stdin io.Rea if err != nil { return err } - // TODO(yifan): Use appName instead of imageID. - // see https://github.com/coreos/rkt/pull/640 - args := append([]string{}, "enter", "--imageid", id.imageID, id.uuid) + args := append([]string{}, "enter", fmt.Sprintf("--app=%s", id.appName), id.uuid) args = append(args, cmd...) command := r.buildCommand(args...) @@ -1116,12 +1112,13 @@ func (r *runtime) getPodInfos() (map[string]*podInfo, error) { return result, nil } - // Example output of current 'rkt list --full' (version == 0.4.2): - // UUID ACI STATE NETWORKS - // 2372bc17-47cb-43fb-8d78-20b31729feda foo running default:ip4=172.16.28.3 - // bar - // 40e2813b-9d5d-4146-a817-0de92646da96 foo exited - // 40e2813b-9d5d-4146-a817-0de92646da96 bar exited + // Example output of 'rkt list --full' (version == 0.7.0): + // + // UUID APP ACI STATE NETWORKS + // 2372bc17-47cb-43fb-8d78-20b31729feda foo coreos.com/etcd running default:ip4=172.16.28.3 + // bar nginx running + // 40e2813b-9d5d-4146-a817-0de92646da96 foo redis exited + // 40e2813b-9d5d-4146-a817-0de92646da96 bar busybox exited // // With '--no-legend', the first line is eliminated. for _, line := range output { @@ -1171,8 +1168,9 @@ func (r *runtime) listImages() ([]image, error) { } // getImageByName tries to find the image info with the given image name. +// TODO(yifan): Replace with 'rkt image cat-manifest'. func (r *runtime) getImageByName(imageName string) (image, error) { - // TODO(yifan): Print hash in rkt image? + // TODO(yifan): Print hash in 'rkt image cat-manifest'? images, err := r.listImages() if err != nil { return image{}, err @@ -1181,9 +1179,7 @@ func (r *runtime) getImageByName(imageName string) (image, error) { 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]) + name, err = appctypes.SanitizeACIdentifier(nameVersion[0]) if err != nil { return image{}, err }