diff --git a/pkg/kubelet/rkt/container_id.go b/pkg/kubelet/rkt/container_id.go new file mode 100644 index 00000000000..81a139335c2 --- /dev/null +++ b/pkg/kubelet/rkt/container_id.go @@ -0,0 +1,52 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rkt + +import ( + "fmt" + "strings" +) + +// containerID defines the ID of rkt containers, it will +// 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. +} + +// 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. +func buildContainerID(c *containerID) string { + return fmt.Sprintf("%s:%s:%s", c.uuid, c.appName, c.imageID) +} + +// 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 { + 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 new file mode 100644 index 00000000000..639b869e200 --- /dev/null +++ b/pkg/kubelet/rkt/pod_info.go @@ -0,0 +1,215 @@ +/* +Copyright 2015 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rkt + +import ( + "fmt" + "strconv" + "strings" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + "github.com/golang/glog" +) + +// rkt pod state. +// TODO(yifan): Use exported definition in rkt. +const ( + Embryo = "embryo" + Preparing = "preparing" + AbortedPrepare = "aborted prepare" + Prepared = "prepared" + Running = "running" + Deleting = "deleting" // This covers pod.isExitedDeleting and pod.isDeleting. + Exited = "exited" // This covers pod.isExited and pod.isExitedGarbage. + Garbage = "garbage" +) + +// podInfo is the internal type that represents the state of +// the rkt pod. +type podInfo struct { + // The state of the pod, e.g. Embryo, Preparing. + state string + // The ip of the pod. IPv4 for now. + 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. + exitCodes map[string]int +} + +// parsePodInfo parses the result of 'rkt status' into podInfo. +func parsePodInfo(status []string) (*podInfo, error) { + p := &podInfo{ + pid: -1, + exitCodes: make(map[string]int), + } + + for _, line := range status { + tuples := strings.SplitN(line, "=", 2) + if len(tuples) != 2 { + return nil, fmt.Errorf("invalid status line: %q", line) + } + switch tuples[0] { + case "state": + // TODO(yifan): Parse the status here. This requires more details in + // the rkt status, (e.g. started time, image name, etc). + p.state = tuples[1] + case "networks": + p.ip = getIPFromNetworkInfo(tuples[1]) + case "pid": + pid, err := strconv.Atoi(tuples[1]) + if err != nil { + return nil, fmt.Errorf("cannot parse pid from %s: %v", tuples[1], err) + } + p.pid = pid + } + if strings.HasPrefix(tuples[0], "sha512") { + 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 + } + } + return p, nil +} + +// 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 +// +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] + } + } + } + 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 + + switch p.state { + case Running: + // TODO(yifan): Get StartedAt. + status.State = api.ContainerState{ + Running: &api.ContainerStateRunning{ + StartedAt: util.Unix(container.Created, 0), + }, + } + case Embryo, Preparing, Prepared: + status.State = api.ContainerState{Waiting: &api.ContainerStateWaiting{}} + case AbortedPrepare, Deleting, Exited, Garbage: + exitCode, ok := p.exitCodes[status.ImageID] + if !ok { + glog.Warningf("rkt: Cannot get exit code for container %v", container) + } + exitCode = -1 + status.State = api.ContainerState{ + Termination: &api.ContainerStateTerminated{ + ExitCode: exitCode, + StartedAt: util.Unix(container.Created, 0), + }, + } + default: + glog.Warningf("rkt: Unknown pod state: %q", p.state) + } + return status +} + +// toPodStatus converts a podInfo type into an api.PodStatus type. +func (p *podInfo) toPodStatus(pod *kubecontainer.Pod) api.PodStatus { + var status api.PodStatus + status.PodIP = p.ip + // For now just make every container's state the same as the pod. + for _, container := range pod.Containers { + status.ContainerStatuses = append(status.ContainerStatuses, p.getContainerStatus(container)) + } + return status +} + +// splitLineByTab breaks a line by tabs, and trims the leading and tailing spaces. +func splitLineByTab(line string) []string { + var result []string + tuples := strings.Split(strings.TrimSpace(line), "\t") + for _, t := range tuples { + if t != "" { + result = append(result, t) + } + } + return result +} + +// getPodInfos returns a map of [pod-uuid]:*podInfo +func (r *Runtime) getPodInfos() (map[string]*podInfo, error) { + result := make(map[string]*podInfo) + + output, err := r.runCommand("list", "--no-legend", "--full") + if err != nil { + return result, err + } + if len(output) == 0 { + // No pods are running. + 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 + // + // With '--no-legend', the first line is eliminated. + for _, line := range output { + tuples := splitLineByTab(line) + if len(tuples) < 3 { // At least it should have 3 entries. + glog.Warningf("rkt: Unrecognized line: %q", line) + continue + } + id := tuples[0] + + status, err := r.runCommand("status", id) + if err != nil { + glog.Errorf("rkt: Cannot get status for pod (uuid=%q): %v", id, err) + continue + } + info, err := parsePodInfo(status) + if err != nil { + glog.Errorf("rkt: Cannot parse status for pod (uuid=%q): %v", id, err) + continue + } + result[id] = info + } + return result, nil +} diff --git a/pkg/kubelet/rkt/rkt.go b/pkg/kubelet/rkt/rkt.go index 13c4a2c0dcb..1cda4a2d6ec 100644 --- a/pkg/kubelet/rkt/rkt.go +++ b/pkg/kubelet/rkt/rkt.go @@ -28,6 +28,8 @@ import ( ) const ( + rktBinName = "rkt" + acversion = "0.5.1" rktMinimumVersion = "0.5.4" systemdMinimumVersion = "215" @@ -46,19 +48,6 @@ const ( dockerPrefix = "docker://" ) -const ( - rktBinName = "rkt" - - Embryo = "embryo" - Preparing = "preparing" - AbortedPrepare = "aborted prepare" - Prepared = "prepared" - Running = "running" - Deleting = "deleting" // This covers pod.isExitedDeleting and pod.isDeleting. - Exited = "exited" // This covers pod.isExited and pod.isExitedGarbage. - Garbage = "garbage" -) - // Runtime implements the ContainerRuntime for rkt. The implementation // uses systemd, so in order to run this runtime, systemd must be installed // on the machine.