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"`
// Required: This must be a DNS_SUBDOMAIN.
// 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"`
Containers []Container `yaml:"containers" json:"containers"`
}

View File

@ -54,7 +54,11 @@ type ContainerManifest struct {
Version string `yaml:"version" json:"version"`
// Required: This must be a DNS_SUBDOMAIN.
// 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"`
Containers []Container `yaml:"containers" json:"containers"`
}

View File

@ -27,7 +27,7 @@ import (
const defaultHealthyRegex = "^OK$"
type CommandRunner interface {
RunInContainer(podFullName, containerName string, cmd []string) ([]byte, error)
RunInContainer(podFullName, uuid, containerName string, cmd []string) ([]byte, error)
}
type ExecHealthChecker struct {
@ -47,7 +47,7 @@ func (e *ExecHealthChecker) HealthCheck(podFullName string, currentState api.Pod
if container.LivenessProbe.Exec == nil {
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))
if err != nil {
if IsExitError(err) {

View File

@ -31,7 +31,7 @@ type FakeExec struct {
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
return f.out, f.err
}

View File

@ -99,7 +99,9 @@ func eventToPods(ev watch.Event) ([]kubelet.Pod, error) {
if name == "" {
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

View File

@ -109,21 +109,24 @@ func (p dockerPuller) Pull(image string) error {
// DockerContainers is a map of containers
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 {
dockerManifestID, dockerContainerName, hash := parseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName && dockerContainerName == containerName {
dockerManifestID, dockerUUID, dockerContainerName, hash := parseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName &&
(uuid == "" || dockerUUID == uuid) &&
dockerContainerName == containerName {
return dockerContainer, true, hash
}
}
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 {
containers := make(map[string]*docker.APIContainers)
for _, dockerContainer := range c {
dockerManifestID, dockerContainerName, _ := parseDockerName(dockerContainer.Names[0])
dockerManifestID, _, dockerContainerName, _ := parseDockerName(dockerContainer.Names[0])
if dockerManifestID == podFullName {
containers[dockerContainerName] = dockerContainer
}
@ -154,7 +157,7 @@ func getKubeletDockerContainers(client DockerInterface) (DockerContainers, error
var ErrNoContainersInPod = errors.New("no containers exist for this pod")
// 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{}
containers, err := client.ListContainers(docker.ListContainersOptions{})
@ -163,10 +166,13 @@ func getDockerPodInfo(client DockerInterface, podFullName string) (api.PodInfo,
}
for _, value := range containers {
dockerManifestID, dockerContainerName, _ := parseDockerName(value.Names[0])
dockerManifestID, dockerUUID, dockerContainerName, _ := parseDockerName(value.Names[0])
if dockerManifestID != podFullName {
continue
}
if uuid != "" && dockerUUID != uuid {
continue
}
inspectResult, err := client.InspectContainer(value.ID)
if err != nil {
return nil, err
@ -211,12 +217,25 @@ func hashContainer(container *api.Container) uint64 {
func buildDockerName(pod *Pod, container *api.Container) string {
containerName := escapeDash(container.Name) + "." + strconv.FormatUint(hashContainer(container), 16)
// 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
// 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.
// If it's there, strip it.
if name[0] == '/' {
@ -240,11 +259,14 @@ func parseDockerName(name string) (podFullName, containerName string, hash uint6
if len(parts) > 2 {
podFullName = unescapeDash(parts[2])
}
if len(parts) > 4 {
uuid = unescapeDash(parts[3])
}
return
}
// 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
// So this can be deprecated at some point.
func parseImageName(image string) (string, string) {

View File

@ -30,8 +30,8 @@ type execActionHandler struct {
kubelet *Kubelet
}
func (e *execActionHandler) Run(podFullName string, container *api.Container, handler *api.Handler) error {
_, err := e.kubelet.RunInContainer(podFullName, container.Name, handler.Exec.Command)
func (e *execActionHandler) Run(podFullName, uuid string, container *api.Container, handler *api.Handler) error {
_, err := e.kubelet.RunInContainer(podFullName, uuid, container.Name, handler.Exec.Command)
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)
}
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
if len(host) == 0 {
var info api.PodInfo
info, err := h.kubelet.GetPodInfo(podFullName)
info, err := h.kubelet.GetPodInfo(podFullName, uuid)
if err != nil {
glog.Errorf("unable to get pod info, event handlers may be invalid.")
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
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 {
@ -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)
if actionHandler == nil {
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
@ -344,7 +344,7 @@ func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes v
NetworkMode: netMode,
})
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 {
kl.killContainerByID(dockerContainer.ID, "")
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 {
return err
}
podFullName, containerName, _ := parseDockerName(name)
podFullName, uuid, containerName, _ := parseDockerName(name)
kl.LogEvent(&api.Event{
Event: "STOP",
Manifest: &api.ContainerManifest{
//TODO: This should be reported using either the apiserver schema or the kubelet schema
ID: podFullName,
ID: podFullName,
UUID: uuid,
},
Container: &api.Container{
Name: containerName,
@ -408,7 +409,7 @@ func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerConta
errs := make(chan error, len(pod.Manifest.Containers))
wg := sync.WaitGroup{}
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++
wg.Add(1)
go func() {
@ -437,12 +438,13 @@ type empty struct{}
func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
podFullName := GetPodFullName(pod)
uuid := pod.Manifest.UUID
containersToKeep := make(map[DockerID]empty)
killedContainers := make(map[DockerID]empty)
// Make sure we have a network container
var netID DockerID
if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, networkContainerName); found {
if networkDockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, networkContainerName); found {
netID = DockerID(networkDockerContainer.ID)
} else {
glog.Infof("Network container doesn't exist, creating")
@ -473,10 +475,11 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
return err
}
podState := api.PodState{}
info, err := kl.GetPodInfo(podFullName)
podState := api.PodState{Manifest: api.ContainerManifest{UUID: uuid}}
info, err := kl.GetPodInfo(podFullName, uuid)
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]
if found && netInfo.NetworkSettings != nil {
@ -485,9 +488,8 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
for _, container := range pod.Manifest.Containers {
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)
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)
// 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).
for id, container := range dockerContainers {
curPodFullName, _, _ := parseDockerName(container.Names[0])
if curPodFullName == podFullName {
curPodFullName, curUUID, _, _ := parseDockerName(container.Names[0])
if curPodFullName == podFullName && curUUID == uuid {
// Don't kill containers we want to keep or those we already killed.
_, keep := containersToKeep[id]
_, killed := killedContainers[id]
@ -549,6 +551,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers DockerContainers) error {
type podContainer struct {
podFullName string
uuid string
containerName string
}
@ -601,11 +604,12 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
for i := range pods {
pod := &pods[i]
podFullName := GetPodFullName(pod)
uuid := pod.Manifest.UUID
// 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 {
desiredContainers[podContainer{podFullName, cont.Name}] = empty{}
desiredContainers[podContainer{podFullName, uuid, cont.Name}] = empty{}
}
// Run the sync in an async manifest worker.
@ -625,8 +629,8 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
}
for _, container := range existingContainers {
// Don't kill containers that are in the desired pods.
podFullName, containerName, _ := parseDockerName(container.Names[0])
if _, ok := desiredContainers[podContainer{podFullName, containerName}]; !ok {
podFullName, uuid, containerName, _ := parseDockerName(container.Names[0])
if _, ok := desiredContainers[podContainer{podFullName, uuid, containerName}]; !ok {
err = kl.killContainer(container)
if err != nil {
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
func (kl *Kubelet) GetPodInfo(podFullName string) (api.PodInfo, error) {
return getDockerPodInfo(kl.dockerClient, podFullName)
func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) {
return getDockerPodInfo(kl.dockerClient, podFullName, uuid)
}
// 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 {
return nil, nil
}
@ -729,7 +733,7 @@ func (kl *Kubelet) GetContainerInfo(podFullName, containerName string, req *info
if err != nil {
return nil, err
}
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, containerName)
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, containerName)
if !found {
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
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 {
return nil, fmt.Errorf("no runner specified.")
}
@ -774,7 +778,7 @@ func (kl *Kubelet) RunInContainer(podFullName, container string, cmd []string) (
if err != nil {
return nil, err
}
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, container)
dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, uuid, container)
if !found {
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,
)
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
returnedPodFullName, returnedContainerName, hash := parseDockerName(name)
returnedPodFullName, _, returnedContainerName, hash := parseDockerName(name)
if podFullName != returnedPodFullName || containerName != returnedContainerName || computedHash != 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)
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 {
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)
}
verifyCalls(t, fakeDocker, []string{"list"})
dockerContainer, found, _ := dockerContainers.FindPodContainer("qux", "foo")
dockerContainer, found, _ := dockerContainers.FindPodContainer("qux", "", "foo")
if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer)
}
fakeDocker.clearCalls()
dockerContainer, found, _ = dockerContainers.FindPodContainer("foobar", "foo")
dockerContainer, found, _ = dockerContainers.FindPodContainer("foobar", "", "foo")
verifyCalls(t, fakeDocker, []string{})
if dockerContainer != nil || found {
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 {
t.Errorf("unexpected error: %v", err)
}
@ -1009,11 +1009,11 @@ func TestGetContainerInfoWithoutCadvisor(t *testing.T) {
ID: "foobar",
// pod id: qux
// 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
if stats == nil {
return
@ -1047,11 +1047,11 @@ func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
ID: containerID,
// pod id: qux
// 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 {
t.Errorf("non-nil stats on error")
}
@ -1072,7 +1072,7 @@ func TestGetContainerInfoOnNonExistContainer(t *testing.T) {
kubelet.cadvisorClient = mockCadvisor
fakeDocker.containerList = []docker.APIContainers{}
stats, _ := kubelet.GetContainerInfo("qux", "foo", nil)
stats, _ := kubelet.GetContainerInfo("qux", "", "foo", nil)
if stats != nil {
t.Errorf("non-nil stats on non exist container")
}
@ -1102,6 +1102,7 @@ func TestRunInContainerNoSuchPod(t *testing.T) {
containerName := "containerFoo"
output, err := kubelet.RunInContainer(
GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}),
"",
containerName,
[]string{"ls"})
if output != nil {
@ -1132,6 +1133,7 @@ func TestRunInContainer(t *testing.T) {
cmd := []string{"ls"}
_, err := kubelet.RunInContainer(
GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}),
"",
containerName,
cmd)
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 {
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 {
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.
// For testablitiy.
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)
GetMachineInfo() (*info.MachineInfo, error)
GetPodInfo(name string) (api.PodInfo, error)
RunInContainer(name, container string, cmd []string) ([]byte, error)
GetPodInfo(name, uuid string) (api.PodInfo, error)
RunInContainer(name, uuid, container string, cmd []string) ([]byte, error)
ServeLogs(w http.ResponseWriter, req *http.Request)
}
@ -150,6 +150,7 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) {
return
}
podID := u.Query().Get("podID")
podUUID := u.Query().Get("UUID")
if len(podID) == 0 {
w.WriteHeader(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
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
info, err := s.host.GetPodInfo(podFullName)
info, err := s.host.GetPodInfo(podFullName, podUUID)
if err == ErrNoContainersInPod {
http.Error(w, "Pod does not exist", http.StatusNotFound)
return
@ -211,15 +212,21 @@ func (s *Server) handleRun(w http.ResponseWriter, req *http.Request) {
return
}
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)
return
}
podID := parts[2]
container := parts[3]
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
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 {
s.error(w, err)
return
@ -241,7 +248,7 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// serveStats implements stats logic.
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), "/"), "/")
var stats *info.ContainerInfo
var err error
@ -260,7 +267,10 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
// TODO(monnand) Implement this
errors.New("pod level status currently unimplemented")
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:
http.Error(w, "unknown resource.", http.StatusNotFound)
return

View File

@ -40,14 +40,14 @@ type fakeKubelet struct {
rootInfoFunc func(query *info.ContainerInfoRequest) (*info.ContainerInfo, error)
machineInfoFunc func() (*info.MachineInfo, error)
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)
}
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)
}
@ -63,8 +63,8 @@ func (fk *fakeKubelet) ServeLogs(w http.ResponseWriter, req *http.Request) {
fk.logFunc(w, req)
}
func (fk *fakeKubelet) RunInContainer(podFullName, containerName string, cmd []string) ([]byte, error) {
return fk.runFunc(podFullName, containerName, cmd)
func (fk *fakeKubelet) RunInContainer(podFullName, uuid, containerName string, cmd []string) ([]byte, error) {
return fk.runFunc(podFullName, uuid, containerName, cmd)
}
type serverTestFramework struct {
@ -301,7 +301,7 @@ func TestServeRunInContainer(t *testing.T) {
expectedPodName := podName + ".etcd"
expectedContainerName := "baz"
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 {
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)
}
}
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) {
pod := obj.(*api.Pod)
pod.DesiredState.Manifest.UUID = uuid.NewUUID().String()
if len(pod.ID) == 0 {
pod.ID = uuid.NewUUID().String()
pod.ID = pod.DesiredState.Manifest.UUID
}
pod.DesiredState.Manifest.ID = pod.ID
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) {
podRegistry := registrytest.NewPodRegistry(nil)
podRegistry.Err = fmt.Errorf("test error")