Passing pod UUID to Kubelet.

This commit is contained in:
Dawn Chen 2014-09-05 02:49:11 -07:00
parent 2221d33de1
commit 7ace5a3e83
13 changed files with 188 additions and 73 deletions

View File

@ -54,7 +54,11 @@ type ContainerManifest struct {
Version string `yaml:"version" json:"version"` Version string `yaml:"version" json:"version"`
// Required: This must be a DNS_SUBDOMAIN. // Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future. // TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `yaml:"id" json:"id"` ID string `yaml:"id" json:"id"`
// TODO: UUID on Manifest is deprecated in the future once we are done
// with the API refactoring. It is required for now to determine the instance
// of a Pod.
UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"`
Volumes []Volume `yaml:"volumes" json:"volumes"` Volumes []Volume `yaml:"volumes" json:"volumes"`
Containers []Container `yaml:"containers" json:"containers"` Containers []Container `yaml:"containers" json:"containers"`
} }

View File

@ -54,7 +54,11 @@ type ContainerManifest struct {
Version string `yaml:"version" json:"version"` Version string `yaml:"version" json:"version"`
// Required: This must be a DNS_SUBDOMAIN. // Required: This must be a DNS_SUBDOMAIN.
// TODO: ID on Manifest is deprecated and will be removed in the future. // TODO: ID on Manifest is deprecated and will be removed in the future.
ID string `yaml:"id" json:"id"` ID string `yaml:"id" json:"id"`
// TODO: UUID on Manifext is deprecated in the future once we are done
// with the API refactory. It is required for now to determine the instance
// of a Pod.
UUID string `yaml:"uuid,omitempty" json:"uuid,omitempty"`
Volumes []Volume `yaml:"volumes" json:"volumes"` Volumes []Volume `yaml:"volumes" json:"volumes"`
Containers []Container `yaml:"containers" json:"containers"` Containers []Container `yaml:"containers" json:"containers"`
} }

View File

@ -27,7 +27,7 @@ import (
const defaultHealthyRegex = "^OK$" const defaultHealthyRegex = "^OK$"
type CommandRunner interface { type CommandRunner interface {
RunInContainer(podFullName, containerName string, cmd []string) ([]byte, error) RunInContainer(podFullName, uuid, containerName string, cmd []string) ([]byte, error)
} }
type ExecHealthChecker struct { type ExecHealthChecker struct {
@ -47,7 +47,7 @@ func (e *ExecHealthChecker) HealthCheck(podFullName string, currentState api.Pod
if container.LivenessProbe.Exec == nil { if container.LivenessProbe.Exec == nil {
return Unknown, fmt.Errorf("Missing exec parameters") return Unknown, fmt.Errorf("Missing exec parameters")
} }
data, err := e.runner.RunInContainer(podFullName, container.Name, container.LivenessProbe.Exec.Command) data, err := e.runner.RunInContainer(podFullName, currentState.Manifest.UUID, container.Name, container.LivenessProbe.Exec.Command)
glog.V(1).Infof("container %s failed health check: %s", podFullName, string(data)) glog.V(1).Infof("container %s failed health check: %s", podFullName, string(data))
if err != nil { if err != nil {
if IsExitError(err) { if IsExitError(err) {

View File

@ -31,7 +31,7 @@ type FakeExec struct {
err error err error
} }
func (f *FakeExec) RunInContainer(podFullName, container string, cmd []string) ([]byte, error) { func (f *FakeExec) RunInContainer(podFullName, uuid, container string, cmd []string) ([]byte, error) {
f.cmd = cmd f.cmd = cmd
return f.out, f.err return f.out, f.err
} }

View File

@ -99,7 +99,9 @@ func eventToPods(ev watch.Event) ([]kubelet.Pod, error) {
if name == "" { if name == "" {
name = fmt.Sprintf("%d", i+1) name = fmt.Sprintf("%d", i+1)
} }
pods = append(pods, kubelet.Pod{Name: name, Manifest: manifest}) pods = append(pods, kubelet.Pod{
Name: name,
Manifest: manifest})
} }
return pods, nil return pods, nil

View File

@ -109,21 +109,24 @@ func (p dockerPuller) Pull(image string) error {
// DockerContainers is a map of containers // DockerContainers is a map of containers
type DockerContainers map[DockerID]*docker.APIContainers type DockerContainers map[DockerID]*docker.APIContainers
func (c DockerContainers) FindPodContainer(podFullName, containerName string) (*docker.APIContainers, bool, uint64) { func (c DockerContainers) FindPodContainer(podFullName, uuid, containerName string) (*docker.APIContainers, bool, uint64) {
for _, dockerContainer := range c { for _, dockerContainer := range c {
dockerManifestID, dockerContainerName, hash := parseDockerName(dockerContainer.Names[0]) dockerManifestID, dockerUUID, dockerContainerName, hash := parseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName && dockerContainerName == containerName { if dockerManifestID == podFullName &&
(uuid == "" || dockerUUID == uuid) &&
dockerContainerName == containerName {
return dockerContainer, true, hash return dockerContainer, true, hash
} }
} }
return nil, false, 0 return nil, false, 0
} }
// Note, this might return containers belong to a different Pod instance with the same name
func (c DockerContainers) FindContainersByPodFullName(podFullName string) map[string]*docker.APIContainers { func (c DockerContainers) FindContainersByPodFullName(podFullName string) map[string]*docker.APIContainers {
containers := make(map[string]*docker.APIContainers) containers := make(map[string]*docker.APIContainers)
for _, dockerContainer := range c { for _, dockerContainer := range c {
dockerManifestID, dockerContainerName, _ := parseDockerName(dockerContainer.Names[0]) dockerManifestID, _, dockerContainerName, _ := parseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName { if dockerManifestID == podFullName {
containers[dockerContainerName] = dockerContainer containers[dockerContainerName] = dockerContainer
} }
@ -154,7 +157,7 @@ func getKubeletDockerContainers(client DockerInterface) (DockerContainers, error
var ErrNoContainersInPod = errors.New("no containers exist for this pod") var ErrNoContainersInPod = errors.New("no containers exist for this pod")
// GetDockerPodInfo returns docker info for all containers in the pod/manifest. // GetDockerPodInfo returns docker info for all containers in the pod/manifest.
func getDockerPodInfo(client DockerInterface, podFullName string) (api.PodInfo, error) { func getDockerPodInfo(client DockerInterface, podFullName, uuid string) (api.PodInfo, error) {
info := api.PodInfo{} info := api.PodInfo{}
containers, err := client.ListContainers(docker.ListContainersOptions{}) containers, err := client.ListContainers(docker.ListContainersOptions{})
@ -163,10 +166,13 @@ func getDockerPodInfo(client DockerInterface, podFullName string) (api.PodInfo,
} }
for _, value := range containers { for _, value := range containers {
dockerManifestID, dockerContainerName, _ := parseDockerName(value.Names[0]) dockerManifestID, dockerUUID, dockerContainerName, _ := parseDockerName(value.Names[0])
if dockerManifestID != podFullName { if dockerManifestID != podFullName {
continue continue
} }
if uuid != "" && dockerUUID != uuid {
continue
}
inspectResult, err := client.InspectContainer(value.ID) inspectResult, err := client.InspectContainer(value.ID)
if err != nil { if err != nil {
return nil, err return nil, err
@ -211,12 +217,25 @@ func hashContainer(container *api.Container) uint64 {
func buildDockerName(pod *Pod, container *api.Container) string { func buildDockerName(pod *Pod, container *api.Container) string {
containerName := escapeDash(container.Name) + "." + strconv.FormatUint(hashContainer(container), 16) containerName := escapeDash(container.Name) + "." + strconv.FormatUint(hashContainer(container), 16)
// Note, manifest.ID could be blank. // Note, manifest.ID could be blank.
return fmt.Sprintf("%s--%s--%s--%08x", containerNamePrefix, containerName, escapeDash(GetPodFullName(pod)), rand.Uint32()) if len(pod.Manifest.UUID) == 0 {
return fmt.Sprintf("%s--%s--%s--%08x",
containerNamePrefix,
containerName,
escapeDash(GetPodFullName(pod)),
rand.Uint32())
} else {
return fmt.Sprintf("%s--%s--%s--%s--%08x",
containerNamePrefix,
containerName,
escapeDash(GetPodFullName(pod)),
escapeDash(pod.Manifest.UUID),
rand.Uint32())
}
} }
// Upacks a container name, returning the pod full name and container name we would have used to // Upacks a container name, returning the pod full name and container name we would have used to
// construct the docker name. If the docker name isn't one we created, we may return empty strings. // construct the docker name. If the docker name isn't one we created, we may return empty strings.
func parseDockerName(name string) (podFullName, containerName string, hash uint64) { func parseDockerName(name string) (podFullName, uuid, containerName string, hash uint64) {
// For some reason docker appears to be appending '/' to names. // For some reason docker appears to be appending '/' to names.
// If it's there, strip it. // If it's there, strip it.
if name[0] == '/' { if name[0] == '/' {
@ -240,11 +259,14 @@ func parseDockerName(name string) (podFullName, containerName string, hash uint6
if len(parts) > 2 { if len(parts) > 2 {
podFullName = unescapeDash(parts[2]) podFullName = unescapeDash(parts[2])
} }
if len(parts) > 4 {
uuid = unescapeDash(parts[3])
}
return return
} }
// Parses image name including a tag and returns image name and tag. // Parses image name including a tag and returns image name and tag.
// TODO: Future Docker versions can parse the tag on daemon side, see: // TODO: Future Docker versions can parse the tag on daemon side, see
// https://github.com/dotcloud/docker/issues/6876 // https://github.com/dotcloud/docker/issues/6876
// So this can be deprecated at some point. // So this can be deprecated at some point.
func parseImageName(image string) (string, string) { func parseImageName(image string) (string, string) {

View File

@ -30,8 +30,8 @@ type execActionHandler struct {
kubelet *Kubelet kubelet *Kubelet
} }
func (e *execActionHandler) Run(podFullName string, container *api.Container, handler *api.Handler) error { func (e *execActionHandler) Run(podFullName, uuid string, container *api.Container, handler *api.Handler) error {
_, err := e.kubelet.RunInContainer(podFullName, container.Name, handler.Exec.Command) _, err := e.kubelet.RunInContainer(podFullName, uuid, container.Name, handler.Exec.Command)
return err return err
} }
@ -65,11 +65,11 @@ func ResolvePort(portReference util.IntOrString, container *api.Container) (int,
return -1, fmt.Errorf("couldn't find port: %v in %v", portReference, container) return -1, fmt.Errorf("couldn't find port: %v in %v", portReference, container)
} }
func (h *httpActionHandler) Run(podFullName string, container *api.Container, handler *api.Handler) error { func (h *httpActionHandler) Run(podFullName, uuid string, container *api.Container, handler *api.Handler) error {
host := handler.HTTPGet.Host host := handler.HTTPGet.Host
if len(host) == 0 { if len(host) == 0 {
var info api.PodInfo var info api.PodInfo
info, err := h.kubelet.GetPodInfo(podFullName) info, err := h.kubelet.GetPodInfo(podFullName, uuid)
if err != nil { if err != nil {
glog.Errorf("unable to get pod info, event handlers may be invalid.") glog.Errorf("unable to get pod info, event handlers may be invalid.")
return err return err

View File

@ -292,7 +292,7 @@ func (kl *Kubelet) mountExternalVolumes(manifest *api.ContainerManifest) (volume
// A basic interface that knows how to execute handlers // A basic interface that knows how to execute handlers
type actionHandler interface { type actionHandler interface {
Run(podFullName string, container *api.Container, handler *api.Handler) error Run(podFullName, uuid string, container *api.Container, handler *api.Handler) error
} }
func (kl *Kubelet) newActionHandler(handler *api.Handler) actionHandler { func (kl *Kubelet) newActionHandler(handler *api.Handler) actionHandler {
@ -307,12 +307,12 @@ func (kl *Kubelet) newActionHandler(handler *api.Handler) actionHandler {
} }
} }
func (kl *Kubelet) runHandler(podFullName string, container *api.Container, handler *api.Handler) error { func (kl *Kubelet) runHandler(podFullName, uuid string, container *api.Container, handler *api.Handler) error {
actionHandler := kl.newActionHandler(handler) actionHandler := kl.newActionHandler(handler)
if actionHandler == nil { if actionHandler == nil {
return fmt.Errorf("invalid handler") return fmt.Errorf("invalid handler")
} }
return actionHandler.Run(podFullName, container, handler) return actionHandler.Run(podFullName, uuid, container, handler)
} }
// Run a single container from a pod. Returns the docker container ID // Run a single container from a pod. Returns the docker container ID
@ -344,7 +344,7 @@ func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes v
NetworkMode: netMode, NetworkMode: netMode,
}) })
if err == nil && container.Lifecycle != nil && container.Lifecycle.PostStart != nil { if err == nil && container.Lifecycle != nil && container.Lifecycle.PostStart != nil {
handlerErr := kl.runHandler(GetPodFullName(pod), container, container.Lifecycle.PostStart) handlerErr := kl.runHandler(GetPodFullName(pod), pod.Manifest.UUID, container, container.Lifecycle.PostStart)
if handlerErr != nil { if handlerErr != nil {
kl.killContainerByID(dockerContainer.ID, "") kl.killContainerByID(dockerContainer.ID, "")
return DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr) return DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr)
@ -364,12 +364,13 @@ func (kl *Kubelet) killContainerByID(ID, name string) error {
if len(name) == 0 { if len(name) == 0 {
return err return err
} }
podFullName, containerName, _ := parseDockerName(name) podFullName, uuid, containerName, _ := parseDockerName(name)
kl.LogEvent(&api.Event{ kl.LogEvent(&api.Event{
Event: "STOP", Event: "STOP",
Manifest: &api.ContainerManifest{ Manifest: &api.ContainerManifest{
//TODO: This should be reported using either the apiserver schema or the kubelet schema //TODO: This should be reported using either the apiserver schema or the kubelet schema
ID: podFullName, ID: podFullName,
UUID: uuid,
}, },
Container: &api.Container{ Container: &api.Container{
Name: containerName, Name: containerName,
@ -408,7 +409,7 @@ func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerConta
errs := make(chan error, len(pod.Manifest.Containers)) errs := make(chan error, len(pod.Manifest.Containers))
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for _, container := range pod.Manifest.Containers { for _, container := range pod.Manifest.Containers {
if dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, container.Name); found { if dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, pod.Manifest.UUID, container.Name); found {
count++ count++
wg.Add(1) wg.Add(1)
go func() { go func() {
@ -437,12 +438,13 @@ type empty struct{}
func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error { func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
podFullName := GetPodFullName(pod) podFullName := GetPodFullName(pod)
uuid := pod.Manifest.UUID
containersToKeep := make(map[DockerID]empty) containersToKeep := make(map[DockerID]empty)
killedContainers := make(map[DockerID]empty) killedContainers := make(map[DockerID]empty)
// Make sure we have a network container // Make sure we have a network container
var netID DockerID var netID DockerID
if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, networkContainerName); found { if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, networkContainerName); found {
netID = DockerID(networkDockerContainer.ID) netID = DockerID(networkDockerContainer.ID)
} else { } else {
glog.Infof("Network container doesn't exist, creating") glog.Infof("Network container doesn't exist, creating")
@ -473,10 +475,11 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
return err return err
} }
podState := api.PodState{} podState := api.PodState{Manifest: api.ContainerManifest{UUID: uuid}}
info, err := kl.GetPodInfo(podFullName) info, err := kl.GetPodInfo(podFullName, uuid)
if err != nil { if err != nil {
glog.Errorf("Unable to get pod info, health checks may be invalid.") glog.Errorf("Unable to get pod with name %s and uuid %s info, health checks may be invalid.",
podFullName, uuid)
} }
netInfo, found := info[networkContainerName] netInfo, found := info[networkContainerName]
if found && netInfo.NetworkSettings != nil { if found && netInfo.NetworkSettings != nil {
@ -485,9 +488,8 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
for _, container := range pod.Manifest.Containers { for _, container := range pod.Manifest.Containers {
expectedHash := hashContainer(&container) expectedHash := hashContainer(&container)
if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, container.Name); found { if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, uuid, container.Name); found {
containerID := DockerID(dockerContainer.ID) containerID := DockerID(dockerContainer.ID)
glog.Infof("pod %s container %s exists as %v", podFullName, container.Name, containerID)
glog.V(1).Infof("pod %s container %s exists as %v", podFullName, container.Name, containerID) glog.V(1).Infof("pod %s container %s exists as %v", podFullName, container.Name, containerID)
// look for changes in the container. // look for changes in the container.
@ -530,8 +532,8 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
// Kill any containers in this pod which were not identified above (guards against duplicates). // Kill any containers in this pod which were not identified above (guards against duplicates).
for id, container := range dockerContainers { for id, container := range dockerContainers {
curPodFullName, _, _ := parseDockerName(container.Names[0]) curPodFullName, curUUID, _, _ := parseDockerName(container.Names[0])
if curPodFullName == podFullName { if curPodFullName == podFullName && curUUID == uuid {
// Don't kill containers we want to keep or those we already killed. // Don't kill containers we want to keep or those we already killed.
_, keep := containersToKeep[id] _, keep := containersToKeep[id]
_, killed := killedContainers[id] _, killed := killedContainers[id]
@ -549,6 +551,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
type podContainer struct { type podContainer struct {
podFullName string podFullName string
uuid string
containerName string containerName string
} }
@ -601,11 +604,12 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
for i := range pods { for i := range pods {
pod := &pods[i] pod := &pods[i]
podFullName := GetPodFullName(pod) podFullName := GetPodFullName(pod)
uuid := pod.Manifest.UUID
// Add all containers (including net) to the map. // Add all containers (including net) to the map.
desiredContainers[podContainer{podFullName, networkContainerName}] = empty{} desiredContainers[podContainer{podFullName, uuid, networkContainerName}] = empty{}
for _, cont := range pod.Manifest.Containers { for _, cont := range pod.Manifest.Containers {
desiredContainers[podContainer{podFullName, cont.Name}] = empty{} desiredContainers[podContainer{podFullName, uuid, cont.Name}] = empty{}
} }
// Run the sync in an async manifest worker. // Run the sync in an async manifest worker.
@ -625,8 +629,8 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
} }
for _, container := range existingContainers { for _, container := range existingContainers {
// Don't kill containers that are in the desired pods. // Don't kill containers that are in the desired pods.
podFullName, containerName, _ := parseDockerName(container.Names[0]) podFullName, uuid, containerName, _ := parseDockerName(container.Names[0])
if _, ok := desiredContainers[podContainer{podFullName, containerName}]; !ok { if _, ok := desiredContainers[podContainer{podFullName, uuid, containerName}]; !ok {
err = kl.killContainer(container) err = kl.killContainer(container)
if err != nil { if err != nil {
glog.Errorf("Error killing container: %v", err) glog.Errorf("Error killing container: %v", err)
@ -716,12 +720,12 @@ func (kl *Kubelet) statsFromContainerPath(containerPath string, req *info.Contai
} }
// GetPodInfo returns information from Docker about the containers in a pod // GetPodInfo returns information from Docker about the containers in a pod
func (kl *Kubelet) GetPodInfo(podFullName string) (api.PodInfo, error) { func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) {
return getDockerPodInfo(kl.dockerClient, podFullName) return getDockerPodInfo(kl.dockerClient, podFullName, uuid)
} }
// GetContainerInfo returns stats (from Cadvisor) for a container. // GetContainerInfo returns stats (from Cadvisor) for a container.
func (kl *Kubelet) GetContainerInfo(podFullName, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) { func (kl *Kubelet) GetContainerInfo(podFullName, uuid, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
if kl.cadvisorClient == nil { if kl.cadvisorClient == nil {
return nil, nil return nil, nil
} }
@ -729,7 +733,7 @@ func (kl *Kubelet) GetContainerInfo(podFullName, containerName string, req *info
if err != nil { if err != nil {
return nil, err return nil, err
} }
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, containerName) dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, containerName)
if !found { if !found {
return nil, errors.New("couldn't find container") return nil, errors.New("couldn't find container")
} }
@ -766,7 +770,7 @@ func (kl *Kubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
} }
// Run a command in a container, returns the combined stdout, stderr as an array of bytes // Run a command in a container, returns the combined stdout, stderr as an array of bytes
func (kl *Kubelet) RunInContainer(podFullName, container string, cmd []string) ([]byte, error) { func (kl *Kubelet) RunInContainer(podFullName, uuid, container string, cmd []string) ([]byte, error) {
if kl.runner == nil { if kl.runner == nil {
return nil, fmt.Errorf("no runner specified.") return nil, fmt.Errorf("no runner specified.")
} }
@ -774,7 +778,7 @@ func (kl *Kubelet) RunInContainer(podFullName, container string, cmd []string) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, container) dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, container)
if !found { if !found {
return nil, fmt.Errorf("container not found (%s)", container) return nil, fmt.Errorf("container not found (%s)", container)
} }

View File

@ -84,7 +84,7 @@ func verifyPackUnpack(t *testing.T, podNamespace, podName, containerName string)
container, container,
) )
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace) podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
returnedPodFullName, returnedContainerName, hash := parseDockerName(name) returnedPodFullName, _, returnedContainerName, hash := parseDockerName(name)
if podFullName != returnedPodFullName || containerName != returnedContainerName || computedHash != hash { if podFullName != returnedPodFullName || containerName != returnedContainerName || computedHash != hash {
t.Errorf("For (%s, %s, %d), unpacked (%s, %s, %d)", podFullName, containerName, computedHash, returnedPodFullName, returnedContainerName, hash) t.Errorf("For (%s, %s, %d), unpacked (%s, %s, %d)", podFullName, containerName, computedHash, returnedPodFullName, returnedContainerName, hash)
} }
@ -108,7 +108,7 @@ func TestContainerManifestNaming(t *testing.T) {
name := fmt.Sprintf("k8s--%s--%s.%s--12345", container.Name, pod.Name, pod.Namespace) name := fmt.Sprintf("k8s--%s--%s.%s--12345", container.Name, pod.Name, pod.Namespace)
podFullName := fmt.Sprintf("%s.%s", pod.Name, pod.Namespace) podFullName := fmt.Sprintf("%s.%s", pod.Name, pod.Namespace)
returnedPodFullName, returnedContainerName, hash := parseDockerName(name) returnedPodFullName, _, returnedContainerName, hash := parseDockerName(name)
if returnedPodFullName != podFullName || returnedContainerName != container.Name || hash != 0 { if returnedPodFullName != podFullName || returnedContainerName != container.Name || hash != 0 {
t.Errorf("unexpected parse: %s %s %d", returnedPodFullName, returnedContainerName, hash) t.Errorf("unexpected parse: %s %s %d", returnedPodFullName, returnedContainerName, hash)
} }
@ -139,13 +139,13 @@ func TestGetContainerID(t *testing.T) {
t.Errorf("Expected %#v, Got %#v", fakeDocker.containerList, dockerContainers) t.Errorf("Expected %#v, Got %#v", fakeDocker.containerList, dockerContainers)
} }
verifyCalls(t, fakeDocker, []string{"list"}) verifyCalls(t, fakeDocker, []string{"list"})
dockerContainer, found, _ := dockerContainers.FindPodContainer("qux", "foo") dockerContainer, found, _ := dockerContainers.FindPodContainer("qux", "", "foo")
if dockerContainer == nil || !found { if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer) t.Errorf("Failed to find container %#v", dockerContainer)
} }
fakeDocker.clearCalls() fakeDocker.clearCalls()
dockerContainer, found, _ = dockerContainers.FindPodContainer("foobar", "foo") dockerContainer, found, _ = dockerContainers.FindPodContainer("foobar", "", "foo")
verifyCalls(t, fakeDocker, []string{}) verifyCalls(t, fakeDocker, []string{})
if dockerContainer != nil || found { if dockerContainer != nil || found {
t.Errorf("Should not have found container %#v", dockerContainer) t.Errorf("Should not have found container %#v", dockerContainer)
@ -943,7 +943,7 @@ func TestGetContainerInfo(t *testing.T) {
}, },
} }
stats, err := kubelet.GetContainerInfo("qux", "foo", req) stats, err := kubelet.GetContainerInfo("qux", "", "foo", req)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -1009,11 +1009,11 @@ func TestGetContainerInfoWithoutCadvisor(t *testing.T) {
ID: "foobar", ID: "foobar",
// pod id: qux // pod id: qux
// container id: foo // container id: foo
Names: []string{"/k8s--foo--qux--1234"}, Names: []string{"/k8s--foo--qux--uuid--1234"},
}, },
} }
stats, _ := kubelet.GetContainerInfo("qux", "foo", nil) stats, _ := kubelet.GetContainerInfo("qux", "uuid", "foo", nil)
// When there's no cAdvisor, the stats should be either nil or empty // When there's no cAdvisor, the stats should be either nil or empty
if stats == nil { if stats == nil {
return return
@ -1047,11 +1047,11 @@ func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
ID: containerID, ID: containerID,
// pod id: qux // pod id: qux
// container id: foo // container id: foo
Names: []string{"/k8s--foo--qux--1234"}, Names: []string{"/k8s--foo--qux--uuid--1234"},
}, },
} }
stats, err := kubelet.GetContainerInfo("qux", "foo", req) stats, err := kubelet.GetContainerInfo("qux", "uuid", "foo", req)
if stats != nil { if stats != nil {
t.Errorf("non-nil stats on error") t.Errorf("non-nil stats on error")
} }
@ -1072,7 +1072,7 @@ func TestGetContainerInfoOnNonExistContainer(t *testing.T) {
kubelet.cadvisorClient = mockCadvisor kubelet.cadvisorClient = mockCadvisor
fakeDocker.containerList = []docker.APIContainers{} fakeDocker.containerList = []docker.APIContainers{}
stats, _ := kubelet.GetContainerInfo("qux", "foo", nil) stats, _ := kubelet.GetContainerInfo("qux", "", "foo", nil)
if stats != nil { if stats != nil {
t.Errorf("non-nil stats on non exist container") t.Errorf("non-nil stats on non exist container")
} }
@ -1102,6 +1102,7 @@ func TestRunInContainerNoSuchPod(t *testing.T) {
containerName := "containerFoo" containerName := "containerFoo"
output, err := kubelet.RunInContainer( output, err := kubelet.RunInContainer(
GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}), GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}),
"",
containerName, containerName,
[]string{"ls"}) []string{"ls"})
if output != nil { if output != nil {
@ -1132,6 +1133,7 @@ func TestRunInContainer(t *testing.T) {
cmd := []string{"ls"} cmd := []string{"ls"}
_, err := kubelet.RunInContainer( _, err := kubelet.RunInContainer(
GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}), GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}),
"",
containerName, containerName,
cmd) cmd)
if fakeCommandRunner.ID != containerID { if fakeCommandRunner.ID != containerID {
@ -1209,7 +1211,7 @@ func TestRunHandlerExec(t *testing.T) {
}, },
}, },
} }
err := kubelet.runHandler(podName+"."+podNamespace, &container, container.Lifecycle.PostStart) err := kubelet.runHandler(podName+"."+podNamespace, "", &container, container.Lifecycle.PostStart)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }
@ -1251,7 +1253,7 @@ func TestRunHandlerHttp(t *testing.T) {
}, },
}, },
} }
err := kubelet.runHandler(podName+"."+podNamespace, &container, container.Lifecycle.PostStart) err := kubelet.runHandler(podName+"."+podNamespace, "", &container, container.Lifecycle.PostStart)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
} }

View File

@ -62,11 +62,11 @@ func ListenAndServeKubeletServer(host HostInterface, updates chan<- interface{},
// HostInterface contains all the kubelet methods required by the server. // HostInterface contains all the kubelet methods required by the server.
// For testablitiy. // For testablitiy.
type HostInterface interface { type HostInterface interface {
GetContainerInfo(podFullName, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) GetContainerInfo(podFullName, uuid, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error) GetRootInfo(req *info.ContainerInfoRequest) (*info.ContainerInfo, error)
GetMachineInfo() (*info.MachineInfo, error) GetMachineInfo() (*info.MachineInfo, error)
GetPodInfo(name string) (api.PodInfo, error) GetPodInfo(name, uuid string) (api.PodInfo, error)
RunInContainer(name, container string, cmd []string) ([]byte, error) RunInContainer(name, uuid, container string, cmd []string) ([]byte, error)
ServeLogs(w http.ResponseWriter, req *http.Request) ServeLogs(w http.ResponseWriter, req *http.Request)
} }
@ -150,6 +150,7 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) {
return return
} }
podID := u.Query().Get("podID") podID := u.Query().Get("podID")
podUUID := u.Query().Get("UUID")
if len(podID) == 0 { if len(podID) == 0 {
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
http.Error(w, "Missing 'podID=' query entry.", http.StatusBadRequest) http.Error(w, "Missing 'podID=' query entry.", http.StatusBadRequest)
@ -157,7 +158,7 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) {
} }
// TODO: backwards compatibility with existing API, needs API change // TODO: backwards compatibility with existing API, needs API change
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"}) podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
info, err := s.host.GetPodInfo(podFullName) info, err := s.host.GetPodInfo(podFullName, podUUID)
if err == ErrNoContainersInPod { if err == ErrNoContainersInPod {
http.Error(w, "Pod does not exist", http.StatusNotFound) http.Error(w, "Pod does not exist", http.StatusNotFound)
return return
@ -211,15 +212,21 @@ func (s *Server) handleRun(w http.ResponseWriter, req *http.Request) {
return return
} }
parts := strings.Split(u.Path, "/") parts := strings.Split(u.Path, "/")
if len(parts) != 4 { var podID, uuid, container string
if len(parts) == 4 {
podID = parts[2]
container = parts[3]
} else if len(parts) == 5 {
podID = parts[2]
uuid = parts[3]
container = parts[4]
} else {
http.Error(w, "Unexpected path for command running", http.StatusBadRequest) http.Error(w, "Unexpected path for command running", http.StatusBadRequest)
return return
} }
podID := parts[2]
container := parts[3]
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"}) podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
command := strings.Split(u.Query().Get("cmd"), " ") command := strings.Split(u.Query().Get("cmd"), " ")
data, err := s.host.RunInContainer(podFullName, container, command) data, err := s.host.RunInContainer(podFullName, uuid, container, command)
if err != nil { if err != nil {
s.error(w, err) s.error(w, err)
return return
@ -241,7 +248,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// serveStats implements stats logic. // serveStats implements stats logic.
func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) { func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
// /stats/<podfullname>/<containerName> // /stats/<podfullname>/<containerName> or /stats/<podfullname>/<uuid>/<containerName>
components := strings.Split(strings.TrimPrefix(path.Clean(req.URL.Path), "/"), "/") components := strings.Split(strings.TrimPrefix(path.Clean(req.URL.Path), "/"), "/")
var stats *info.ContainerInfo var stats *info.ContainerInfo
var err error var err error
@ -260,7 +267,10 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
// TODO(monnand) Implement this // TODO(monnand) Implement this
errors.New("pod level status currently unimplemented") errors.New("pod level status currently unimplemented")
case 3: case 3:
stats, err = s.host.GetContainerInfo(components[1], components[2], &query) // Backward compatibility without uuid information
stats, err = s.host.GetContainerInfo(components[1], "", components[2], &query)
case 4:
stats, err = s.host.GetContainerInfo(components[1], components[2], components[2], &query)
default: default:
http.Error(w, "unknown resource.", http.StatusNotFound) http.Error(w, "unknown resource.", http.StatusNotFound)
return return

View File

@ -40,14 +40,14 @@ type fakeKubelet struct {
rootInfoFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error) rootInfoFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
machineInfoFunc func() (*info.MachineInfo, error) machineInfoFunc func() (*info.MachineInfo, error)
logFunc func(w http.ResponseWriter, req *http.Request) logFunc func(w http.ResponseWriter, req *http.Request)
runFunc func(podFullName, containerName string, cmd []string) ([]byte, error) runFunc func(podFullName, uuid, containerName string, cmd []string) ([]byte, error)
} }
func (fk *fakeKubelet) GetPodInfo(name string) (api.PodInfo, error) { func (fk *fakeKubelet) GetPodInfo(name, uuid string) (api.PodInfo, error) {
return fk.infoFunc(name) return fk.infoFunc(name)
} }
func (fk *fakeKubelet) GetContainerInfo(podFullName, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) { func (fk *fakeKubelet) GetContainerInfo(podFullName, uuid, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
return fk.containerInfoFunc(podFullName, containerName, req) return fk.containerInfoFunc(podFullName, containerName, req)
} }
@ -63,8 +63,8 @@ func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
fk.logFunc(w, req) fk.logFunc(w, req)
} }
func (fk *fakeKubelet) RunInContainer(podFullName, containerName string, cmd []string) ([]byte, error) { func (fk *fakeKubelet) RunInContainer(podFullName, uuid, containerName string, cmd []string) ([]byte, error) {
return fk.runFunc(podFullName, containerName, cmd) return fk.runFunc(podFullName, uuid, containerName, cmd)
} }
type serverTestFramework struct { type serverTestFramework struct {
@ -301,7 +301,7 @@ func TestServeRunInContainer(t *testing.T) {
expectedPodName := podName + ".etcd" expectedPodName := podName + ".etcd"
expectedContainerName := "baz" expectedContainerName := "baz"
expectedCommand := "ls -a" expectedCommand := "ls -a"
fw.fakeKubelet.runFunc = func(podFullName, containerName string, cmd []string) ([]byte, error) { fw.fakeKubelet.runFunc = func(podFullName, uuid, containerName string, cmd []string) ([]byte, error) {
if podFullName != expectedPodName { if podFullName != expectedPodName {
t.Errorf("expected %s, got %s", expectedPodName, podFullName) t.Errorf("expected %s, got %s", expectedPodName, podFullName)
} }
@ -332,3 +332,46 @@ func TestServeRunInContainer(t *testing.T) {
t.Errorf("expected %s, got %s", output, result) t.Errorf("expected %s, got %s", output, result)
} }
} }
func TestServeRunInContainerWithUUID(t *testing.T) {
fw := newServerTest()
output := "foo bar"
podName := "foo"
expectedPodName := podName + ".etcd"
expectedUuid := "7e00838d_-_3523_-_11e4_-_8421_-_42010af0a720"
expectedContainerName := "baz"
expectedCommand := "ls -a"
fw.fakeKubelet.runFunc = func(podFullName, uuid, containerName string, cmd []string) ([]byte, error) {
if podFullName != expectedPodName {
t.Errorf("expected %s, got %s", expectedPodName, podFullName)
}
if uuid != expectedUuid {
t.Errorf("expected %s, got %s", expectedUuid, uuid)
}
if containerName != expectedContainerName {
t.Errorf("expected %s, got %s", expectedContainerName, containerName)
}
if strings.Join(cmd, " ") != expectedCommand {
t.Errorf("expected: %s, got %v", expectedCommand, cmd)
}
return []byte(output), nil
}
resp, err := http.Get(fw.testHTTPServer.URL + "/run/" + podName + "/" + expectedUuid + "/" + expectedContainerName + "?cmd=ls%20-a")
if err != nil {
t.Fatalf("Got error GETing: %v", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
// copying the response body did not work
t.Errorf("Cannot copy resp: %#v", err)
}
result := string(body)
if result != output {
t.Errorf("expected %s, got %s", output, result)
}
}

View File

@ -67,8 +67,9 @@ func NewRegistryStorage(config *RegistryStorageConfig) apiserver.RESTStorage {
func (rs *RegistryStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) { func (rs *RegistryStorage) Create(obj runtime.Object) (<-chan runtime.Object, error) {
pod := obj.(*api.Pod) pod := obj.(*api.Pod)
pod.DesiredState.Manifest.UUID = uuid.NewUUID().String()
if len(pod.ID) == 0 { if len(pod.ID) == 0 {
pod.ID = uuid.NewUUID().String() pod.ID = pod.DesiredState.Manifest.UUID
} }
pod.DesiredState.Manifest.ID = pod.ID pod.DesiredState.Manifest.ID = pod.ID
if errs := validation.ValidatePod(pod); len(errs) > 0 { if errs := validation.ValidatePod(pod); len(errs) > 0 {

View File

@ -99,6 +99,29 @@ func TestCreatePodSetsIds(t *testing.T) {
} }
} }
func TestCreatePodSetsUUIDs(t *testing.T) {
podRegistry := registrytest.NewPodRegistry(nil)
podRegistry.Err = fmt.Errorf("test error")
storage := RegistryStorage{
registry: podRegistry,
}
desiredState := api.PodState{
Manifest: api.ContainerManifest{
Version: "v1beta1",
},
}
pod := &api.Pod{DesiredState: desiredState}
ch, err := storage.Create(pod)
if err != nil {
t.Errorf("Expected %#v, Got %#v", nil, err)
}
expectApiStatusError(t, ch, podRegistry.Err.Error())
if len(podRegistry.Pod.DesiredState.Manifest.UUID) == 0 {
t.Errorf("Expected pod UUID to be set, Got %#v", pod)
}
}
func TestListPodsError(t *testing.T) { func TestListPodsError(t *testing.T) {
podRegistry := registrytest.NewPodRegistry(nil) podRegistry := registrytest.NewPodRegistry(nil)
podRegistry.Err = fmt.Errorf("test error") podRegistry.Err = fmt.Errorf("test error")