From d164ac0ba0f1f75b8d4d92790b48c752446773e3 Mon Sep 17 00:00:00 2001 From: Yifan Gu Date: Tue, 12 May 2015 14:49:35 -0700 Subject: [PATCH 1/2] kubelet/container: Refactor RunContainerOptions. Make Envs, Mounts, PortMappings more generic. Also add default name for PortMapping if it's not specified. --- pkg/kubelet/container/runtime.go | 41 +++- pkg/kubelet/dockertools/docker_test.go | 50 +++-- pkg/kubelet/dockertools/manager.go | 103 +++++---- pkg/kubelet/kubelet.go | 53 ++++- pkg/kubelet/kubelet_test.go | 288 ++++++++++++++----------- 5 files changed, 326 insertions(+), 209 deletions(-) diff --git a/pkg/kubelet/container/runtime.go b/pkg/kubelet/container/runtime.go index ae2f88d20c5..b96424c9dd2 100644 --- a/pkg/kubelet/container/runtime.go +++ b/pkg/kubelet/container/runtime.go @@ -185,14 +185,43 @@ type Image struct { Size int64 } +type EnvVar struct { + Name string + Value string +} + +type Mount struct { + // Name of the volume mount. + Name string + // Path of the mount within the container. + ContainerPath string + // Path of the mount on the host. + HostPath string + // Whether the mount is read-only. + ReadOnly bool +} + +type PortMapping struct { + // Name of the port mapping + Name string + // Protocol of the port mapping. + Protocol api.Protocol + // The port number within the container. + ContainerPort int + // The port number on the host. + HostPort int + // The host IP. + HostIP string +} + // RunContainerOptions specify the options which are necessary for running containers type RunContainerOptions struct { - // The environment variables, they are in the form of 'key=value'. - Envs []string - // The mounts for the containers, they are in the form of: - // 'hostPath:containerPath', or - // 'hostPath:containerPath:ro', if the path read only. - Binds []string + // The environment variables list. + Envs []EnvVar + // The mounts for the containers. + Mounts []Mount + // The port mappings for the containers. + PortMappings []PortMapping // If the container has specified the TerminationMessagePath, then // this directory will be used to create and mount the log file to // container.TerminationMessagePath diff --git a/pkg/kubelet/dockertools/docker_test.go b/pkg/kubelet/dockertools/docker_test.go index bd469f477c1..8c4e1c04901 100644 --- a/pkg/kubelet/dockertools/docker_test.go +++ b/pkg/kubelet/dockertools/docker_test.go @@ -572,34 +572,32 @@ func TestFindContainersByPod(t *testing.T) { } func TestMakePortsAndBindings(t *testing.T) { - container := api.Container{ - Ports: []api.ContainerPort{ - { - ContainerPort: 80, - HostPort: 8080, - HostIP: "127.0.0.1", - }, - { - ContainerPort: 443, - HostPort: 443, - Protocol: "tcp", - }, - { - ContainerPort: 444, - HostPort: 444, - Protocol: "udp", - }, - { - ContainerPort: 445, - HostPort: 445, - Protocol: "foobar", - }, + ports := []kubecontainer.PortMapping{ + { + ContainerPort: 80, + HostPort: 8080, + HostIP: "127.0.0.1", + }, + { + ContainerPort: 443, + HostPort: 443, + Protocol: "tcp", + }, + { + ContainerPort: 444, + HostPort: 444, + Protocol: "udp", + }, + { + ContainerPort: 445, + HostPort: 445, + Protocol: "foobar", }, } - exposedPorts, bindings := makePortsAndBindings(&container) - if len(container.Ports) != len(exposedPorts) || - len(container.Ports) != len(bindings) { - t.Errorf("Unexpected ports and bindings, %#v %#v %#v", container, exposedPorts, bindings) + exposedPorts, bindings := makePortsAndBindings(ports) + if len(ports) != len(exposedPorts) || + len(ports) != len(bindings) { + t.Errorf("Unexpected ports and bindings, %#v %#v %#v", ports, exposedPorts, bindings) } for key, value := range bindings { switch value[0].HostPort { diff --git a/pkg/kubelet/dockertools/manager.go b/pkg/kubelet/dockertools/manager.go index 9dccaef685a..80fbc506370 100644 --- a/pkg/kubelet/dockertools/manager.go +++ b/pkg/kubelet/dockertools/manager.go @@ -473,6 +473,65 @@ func (dm *DockerManager) GetPodInfraContainer(pod kubecontainer.Pod) (kubecontai return kubecontainer.Container{}, fmt.Errorf("unable to find pod infra container for pod %v", pod.ID) } +// makeEnvList converts EnvVar list to a list of strings, in the form of +// '=', which can be understood by docker. +func makeEnvList(envs []kubecontainer.EnvVar) (result []string) { + for _, env := range envs { + result = append(result, fmt.Sprintf("%s=%s", env.Name, env.Value)) + } + return +} + +// makeMountBindings converts the mount list to a list of strings that +// can be understood by docker. +// Each element in the string is in the form of: +// ':', or +// '::ro', if the path is read only. +func makeMountBindings(mounts []kubecontainer.Mount) (result []string) { + for _, m := range mounts { + bind := fmt.Sprintf("%s:%s", m.HostPath, m.ContainerPath) + if m.ReadOnly { + bind += ":ro" + } + result = append(result, bind) + } + return +} + +func makePortsAndBindings(portMappings []kubecontainer.PortMapping) (map[docker.Port]struct{}, map[docker.Port][]docker.PortBinding) { + exposedPorts := map[docker.Port]struct{}{} + portBindings := map[docker.Port][]docker.PortBinding{} + for _, port := range portMappings { + exteriorPort := port.HostPort + if exteriorPort == 0 { + // No need to do port binding when HostPort is not specified + continue + } + interiorPort := port.ContainerPort + // Some of this port stuff is under-documented voodoo. + // See http://stackoverflow.com/questions/20428302/binding-a-port-to-a-host-interface-using-the-rest-api + var protocol string + switch strings.ToUpper(string(port.Protocol)) { + case "UDP": + protocol = "/udp" + case "TCP": + protocol = "/tcp" + default: + glog.Warningf("Unknown protocol %q: defaulting to TCP", port.Protocol) + protocol = "/tcp" + } + dockerPort := docker.Port(strconv.Itoa(interiorPort) + protocol) + exposedPorts[dockerPort] = struct{}{} + portBindings[dockerPort] = []docker.PortBinding{ + { + HostPort: strconv.Itoa(exteriorPort), + HostIP: port.HostIP, + }, + } + } + return exposedPorts, portBindings +} + func (dm *DockerManager) runContainer( pod *api.Pod, container *api.Container, @@ -486,7 +545,7 @@ func (dm *DockerManager) runContainer( PodUID: pod.UID, ContainerName: container.Name, } - exposedPorts, portBindings := makePortsAndBindings(container) + exposedPorts, portBindings := makePortsAndBindings(opts.PortMappings) // TODO(vmarmol): Handle better. // Cap hostname at 63 chars (specification is 64bytes which is 63 chars and the null terminating char). @@ -502,7 +561,7 @@ func (dm *DockerManager) runContainer( dockerOpts := docker.CreateContainerOptions{ Name: BuildDockerName(dockerName, container), Config: &docker.Config{ - Env: opts.Envs, + Env: makeEnvList(opts.Envs), ExposedPorts: exposedPorts, Hostname: containerHostname, Image: container.Image, @@ -531,6 +590,8 @@ func (dm *DockerManager) runContainer( dm.recorder.Eventf(ref, "created", "Created with docker id %v", dockerContainer.ID) } + binds := makeMountBindings(opts.Mounts) + // The reason we create and mount the log file in here (not in kubelet) is because // the file's location depends on the ID of the container, and we need to create and // mount the file before actually starting the container. @@ -545,13 +606,13 @@ func (dm *DockerManager) runContainer( } else { fs.Close() // Close immediately; we're just doing a `touch` here b := fmt.Sprintf("%s:%s", containerLogPath, container.TerminationMessagePath) - opts.Binds = append(opts.Binds, b) + binds = append(binds, b) } } hc := &docker.HostConfig{ PortBindings: portBindings, - Binds: opts.Binds, + Binds: binds, NetworkMode: netMode, IpcMode: ipcMode, } @@ -588,40 +649,6 @@ func setEntrypointAndCommand(container *api.Container, opts *docker.CreateContai } } -func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, map[docker.Port][]docker.PortBinding) { - exposedPorts := map[docker.Port]struct{}{} - portBindings := map[docker.Port][]docker.PortBinding{} - for _, port := range container.Ports { - exteriorPort := port.HostPort - if exteriorPort == 0 { - // No need to do port binding when HostPort is not specified - continue - } - interiorPort := port.ContainerPort - // Some of this port stuff is under-documented voodoo. - // See http://stackoverflow.com/questions/20428302/binding-a-port-to-a-host-interface-using-the-rest-api - var protocol string - switch strings.ToUpper(string(port.Protocol)) { - case "UDP": - protocol = "/udp" - case "TCP": - protocol = "/tcp" - default: - glog.Warningf("Unknown protocol %q: defaulting to TCP", port.Protocol) - protocol = "/tcp" - } - dockerPort := docker.Port(strconv.Itoa(interiorPort) + protocol) - exposedPorts[dockerPort] = struct{}{} - portBindings[dockerPort] = []docker.PortBinding{ - { - HostPort: strconv.Itoa(exteriorPort), - HostIP: port.HostIP, - }, - } - } - return exposedPorts, portBindings -} - // A helper function to get the KubeletContainerName and hash from a docker // container. func getDockerContainerNameInfo(c *docker.APIContainers) (*KubeletContainerName, uint64, error) { diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index a0aaf59eff6..b2bded69c93 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -687,18 +687,49 @@ func (kl *Kubelet) syncNodeStatus() { } } -func makeBinds(container *api.Container, podVolumes kubecontainer.VolumeMap) (binds []string) { +func makeMounts(container *api.Container, podVolumes kubecontainer.VolumeMap) (mounts []kubecontainer.Mount) { for _, mount := range container.VolumeMounts { vol, ok := podVolumes[mount.Name] if !ok { glog.Warningf("Mount cannot be satisified for container %q, because the volume is missing: %q", container.Name, mount) continue } - b := fmt.Sprintf("%s:%s", vol.GetPath(), mount.MountPath) - if mount.ReadOnly { - b += ":ro" + mounts = append(mounts, kubecontainer.Mount{ + Name: mount.Name, + ContainerPath: mount.MountPath, + HostPath: vol.GetPath(), + ReadOnly: mount.ReadOnly, + }) + } + return +} + +func makePortMappings(container *api.Container) (ports []kubecontainer.PortMapping) { + names := make(map[string]struct{}) + for _, p := range container.Ports { + pm := kubecontainer.PortMapping{ + HostPort: p.HostPort, + ContainerPort: p.ContainerPort, + Protocol: p.Protocol, + HostIP: p.HostIP, } - binds = append(binds, b) + + // We need to create some default port name if it's not specified, since + // this is necessary for rkt. + // https://github.com/GoogleCloudPlatform/kubernetes/issues/7710 + if p.Name == "" { + pm.Name = fmt.Sprintf("%s-%s:%d", container.Name, p.Protocol, p.ContainerPort) + } else { + pm.Name = fmt.Sprintf("%s-%s", container.Name, p.Name) + } + + // Protect against exposing the same protocol-port more than once in a container. + if _, ok := names[pm.Name]; ok { + glog.Warningf("Port name conflicted, %q is defined more than once", pm.Name) + continue + } + ports = append(ports, pm) + names[pm.Name] = struct{}{} } return } @@ -713,7 +744,9 @@ func (kl *Kubelet) GenerateRunContainerOptions(pod *api.Pod, container *api.Cont if !ok { return nil, fmt.Errorf("impossible: cannot find the mounted volumes for pod %q", kubecontainer.GetPodFullName(pod)) } - opts.Binds = makeBinds(container, vol) + + opts.PortMappings = makePortMappings(container) + opts.Mounts = makeMounts(container, vol) opts.Envs, err = kl.makeEnvironmentVariables(pod, container) if err != nil { return nil, err @@ -792,8 +825,8 @@ func (kl *Kubelet) getServiceEnvVarMap(ns string) (map[string]string, error) { } // Make the service environment variables for a pod in the given namespace. -func (kl *Kubelet) makeEnvironmentVariables(pod *api.Pod, container *api.Container) ([]string, error) { - var result []string +func (kl *Kubelet) makeEnvironmentVariables(pod *api.Pod, container *api.Container) ([]kubecontainer.EnvVar, error) { + var result []kubecontainer.EnvVar // Note: These are added to the docker.Config, but are not included in the checksum computed // by dockertools.BuildDockerName(...). That way, we can still determine whether an // api.Container is already running by its hash. (We don't want to restart a container just @@ -821,12 +854,12 @@ func (kl *Kubelet) makeEnvironmentVariables(pod *api.Pod, container *api.Contain return result, err } - result = append(result, fmt.Sprintf("%s=%s", value.Name, runtimeValue)) + result = append(result, kubecontainer.EnvVar{Name: value.Name, Value: runtimeValue}) } // Append remaining service env vars. for k, v := range serviceEnv { - result = append(result, fmt.Sprintf("%s=%s", k, v)) + result = append(result, kubecontainer.EnvVar{Name: k, Value: v}) } return result, nil } diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 0e48a6ddf13..53f6b9ada8c 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -1278,7 +1278,7 @@ func (f *stubVolume) GetPath() string { return f.path } -func TestMakeVolumesAndBinds(t *testing.T) { +func TestMakeVolumeMounts(t *testing.T) { container := api.Container{ VolumeMounts: []api.VolumeMount{ { @@ -1310,19 +1310,37 @@ func TestMakeVolumesAndBinds(t *testing.T) { "disk5": &stubVolume{"/var/lib/kubelet/podID/volumes/empty/disk5"}, } - binds := makeBinds(&container, podVolumes) + mounts := makeMounts(&container, podVolumes) - expectedBinds := []string{ - "/mnt/disk:/mnt/path", - "/mnt/disk:/mnt/path3:ro", - "/mnt/host:/mnt/path4", - "/var/lib/kubelet/podID/volumes/empty/disk5:/mnt/path5", + expectedMounts := []kubecontainer.Mount{ + { + "disk", + "/mnt/path", + "/mnt/disk", + false, + }, + { + "disk", + "/mnt/path3", + "/mnt/disk", + true, + }, + { + "disk4", + "/mnt/path4", + "/mnt/host", + false, + }, + { + "disk5", + "/mnt/path5", + "/var/lib/kubelet/podID/volumes/empty/disk5", + false, + }, } - - if len(binds) != len(expectedBinds) { - t.Errorf("Unexpected binds: Expected %#v got %#v. Container was: %#v", expectedBinds, binds, container) + if !reflect.DeepEqual(mounts, expectedMounts) { + t.Errorf("Unexpected mounts: Expected %#v got %#v. Container was: %#v", expectedMounts, mounts, container) } - verifyStringArrayEquals(t, binds, expectedBinds) } func TestGetContainerInfo(t *testing.T) { @@ -1850,6 +1868,16 @@ func (ls testNodeLister) List() (api.NodeList, error) { }, nil } +type envs []kubecontainer.EnvVar + +func (e envs) Len() int { + return len(e) +} + +func (e envs) Swap(i, j int) { e[i], e[j] = e[j], e[i] } + +func (e envs) Less(i, j int) bool { return e[i].Name < e[j].Name } + func TestMakeEnvironmentVariables(t *testing.T) { services := []api.Service{ { @@ -1994,12 +2022,12 @@ func TestMakeEnvironmentVariables(t *testing.T) { } testCases := []struct { - name string // the name of the test case - ns string // the namespace to generate environment for - container *api.Container // the container to use - masterServiceNs string // the namespace to read master service info from - nilLister bool // whether the lister should be nil - expectedEnvs util.StringSet // a set of expected environment vars + name string // the name of the test case + ns string // the namespace to generate environment for + container *api.Container // the container to use + masterServiceNs string // the namespace to read master service info from + nilLister bool // whether the lister should be nil + expectedEnvs []kubecontainer.EnvVar // a set of expected environment vars }{ { name: "api server = Y, kubelet = Y", @@ -2018,29 +2046,30 @@ func TestMakeEnvironmentVariables(t *testing.T) { }, masterServiceNs: api.NamespaceDefault, nilLister: false, - expectedEnvs: util.NewStringSet( - "FOO=BAR", - "TEST_SERVICE_HOST=1.2.3.3", - "TEST_SERVICE_PORT=8083", - "TEST_PORT=tcp://1.2.3.3:8083", - "TEST_PORT_8083_TCP=tcp://1.2.3.3:8083", - "TEST_PORT_8083_TCP_PROTO=tcp", - "TEST_PORT_8083_TCP_PORT=8083", - "TEST_PORT_8083_TCP_ADDR=1.2.3.3", - "KUBERNETES_SERVICE_HOST=1.2.3.1", - "KUBERNETES_SERVICE_PORT=8081", - "KUBERNETES_PORT=tcp://1.2.3.1:8081", - "KUBERNETES_PORT_8081_TCP=tcp://1.2.3.1:8081", - "KUBERNETES_PORT_8081_TCP_PROTO=tcp", - "KUBERNETES_PORT_8081_TCP_PORT=8081", - "KUBERNETES_PORT_8081_TCP_ADDR=1.2.3.1", - "KUBERNETES_RO_SERVICE_HOST=1.2.3.2", - "KUBERNETES_RO_SERVICE_PORT=8082", - "KUBERNETES_RO_PORT=tcp://1.2.3.2:8082", - "KUBERNETES_RO_PORT_8082_TCP=tcp://1.2.3.2:8082", - "KUBERNETES_RO_PORT_8082_TCP_PROTO=tcp", - "KUBERNETES_RO_PORT_8082_TCP_PORT=8082", - "KUBERNETES_RO_PORT_8082_TCP_ADDR=1.2.3.2"), + expectedEnvs: []kubecontainer.EnvVar{ + {Name: "FOO", Value: "BAR"}, + {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"}, + {Name: "TEST_SERVICE_PORT", Value: "8083"}, + {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"}, + {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"}, + {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"}, + {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"}, + {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"}, + {Name: "KUBERNETES_SERVICE_PORT", Value: "8081"}, + {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.1"}, + {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.1:8081"}, + {Name: "KUBERNETES_PORT_8081_TCP", Value: "tcp://1.2.3.1:8081"}, + {Name: "KUBERNETES_PORT_8081_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_PORT_8081_TCP_PORT", Value: "8081"}, + {Name: "KUBERNETES_PORT_8081_TCP_ADDR", Value: "1.2.3.1"}, + {Name: "KUBERNETES_RO_SERVICE_HOST", Value: "1.2.3.2"}, + {Name: "KUBERNETES_RO_SERVICE_PORT", Value: "8082"}, + {Name: "KUBERNETES_RO_PORT", Value: "tcp://1.2.3.2:8082"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP", Value: "tcp://1.2.3.2:8082"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP_PORT", Value: "8082"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP_ADDR", Value: "1.2.3.2"}, + }, }, { name: "api server = Y, kubelet = N", @@ -2059,15 +2088,16 @@ func TestMakeEnvironmentVariables(t *testing.T) { }, masterServiceNs: api.NamespaceDefault, nilLister: true, - expectedEnvs: util.NewStringSet( - "FOO=BAR", - "TEST_SERVICE_HOST=1.2.3.3", - "TEST_SERVICE_PORT=8083", - "TEST_PORT=tcp://1.2.3.3:8083", - "TEST_PORT_8083_TCP=tcp://1.2.3.3:8083", - "TEST_PORT_8083_TCP_PROTO=tcp", - "TEST_PORT_8083_TCP_PORT=8083", - "TEST_PORT_8083_TCP_ADDR=1.2.3.3"), + expectedEnvs: []kubecontainer.EnvVar{ + {Name: "FOO", Value: "BAR"}, + {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"}, + {Name: "TEST_SERVICE_PORT", Value: "8083"}, + {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"}, + {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"}, + {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"}, + {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"}, + {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"}, + }, }, { name: "api server = N; kubelet = Y", @@ -2079,29 +2109,30 @@ func TestMakeEnvironmentVariables(t *testing.T) { }, masterServiceNs: api.NamespaceDefault, nilLister: false, - expectedEnvs: util.NewStringSet( - "FOO=BAZ", - "TEST_SERVICE_HOST=1.2.3.3", - "TEST_SERVICE_PORT=8083", - "TEST_PORT=tcp://1.2.3.3:8083", - "TEST_PORT_8083_TCP=tcp://1.2.3.3:8083", - "TEST_PORT_8083_TCP_PROTO=tcp", - "TEST_PORT_8083_TCP_PORT=8083", - "TEST_PORT_8083_TCP_ADDR=1.2.3.3", - "KUBERNETES_SERVICE_HOST=1.2.3.1", - "KUBERNETES_SERVICE_PORT=8081", - "KUBERNETES_PORT=tcp://1.2.3.1:8081", - "KUBERNETES_PORT_8081_TCP=tcp://1.2.3.1:8081", - "KUBERNETES_PORT_8081_TCP_PROTO=tcp", - "KUBERNETES_PORT_8081_TCP_PORT=8081", - "KUBERNETES_PORT_8081_TCP_ADDR=1.2.3.1", - "KUBERNETES_RO_SERVICE_HOST=1.2.3.2", - "KUBERNETES_RO_SERVICE_PORT=8082", - "KUBERNETES_RO_PORT=tcp://1.2.3.2:8082", - "KUBERNETES_RO_PORT_8082_TCP=tcp://1.2.3.2:8082", - "KUBERNETES_RO_PORT_8082_TCP_PROTO=tcp", - "KUBERNETES_RO_PORT_8082_TCP_PORT=8082", - "KUBERNETES_RO_PORT_8082_TCP_ADDR=1.2.3.2"), + expectedEnvs: []kubecontainer.EnvVar{ + {Name: "FOO", Value: "BAZ"}, + {Name: "TEST_SERVICE_HOST", Value: "1.2.3.3"}, + {Name: "TEST_SERVICE_PORT", Value: "8083"}, + {Name: "TEST_PORT", Value: "tcp://1.2.3.3:8083"}, + {Name: "TEST_PORT_8083_TCP", Value: "tcp://1.2.3.3:8083"}, + {Name: "TEST_PORT_8083_TCP_PROTO", Value: "tcp"}, + {Name: "TEST_PORT_8083_TCP_PORT", Value: "8083"}, + {Name: "TEST_PORT_8083_TCP_ADDR", Value: "1.2.3.3"}, + {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.1"}, + {Name: "KUBERNETES_SERVICE_PORT", Value: "8081"}, + {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.1:8081"}, + {Name: "KUBERNETES_PORT_8081_TCP", Value: "tcp://1.2.3.1:8081"}, + {Name: "KUBERNETES_PORT_8081_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_PORT_8081_TCP_PORT", Value: "8081"}, + {Name: "KUBERNETES_PORT_8081_TCP_ADDR", Value: "1.2.3.1"}, + {Name: "KUBERNETES_RO_SERVICE_HOST", Value: "1.2.3.2"}, + {Name: "KUBERNETES_RO_SERVICE_PORT", Value: "8082"}, + {Name: "KUBERNETES_RO_PORT", Value: "tcp://1.2.3.2:8082"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP", Value: "tcp://1.2.3.2:8082"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP_PORT", Value: "8082"}, + {Name: "KUBERNETES_RO_PORT_8082_TCP_ADDR", Value: "1.2.3.2"}, + }, }, { name: "master service in pod ns", @@ -2113,29 +2144,30 @@ func TestMakeEnvironmentVariables(t *testing.T) { }, masterServiceNs: "kubernetes", nilLister: false, - expectedEnvs: util.NewStringSet( - "FOO=ZAP", - "TEST_SERVICE_HOST=1.2.3.5", - "TEST_SERVICE_PORT=8085", - "TEST_PORT=tcp://1.2.3.5:8085", - "TEST_PORT_8085_TCP=tcp://1.2.3.5:8085", - "TEST_PORT_8085_TCP_PROTO=tcp", - "TEST_PORT_8085_TCP_PORT=8085", - "TEST_PORT_8085_TCP_ADDR=1.2.3.5", - "KUBERNETES_SERVICE_HOST=1.2.3.4", - "KUBERNETES_SERVICE_PORT=8084", - "KUBERNETES_PORT=tcp://1.2.3.4:8084", - "KUBERNETES_PORT_8084_TCP=tcp://1.2.3.4:8084", - "KUBERNETES_PORT_8084_TCP_PROTO=tcp", - "KUBERNETES_PORT_8084_TCP_PORT=8084", - "KUBERNETES_PORT_8084_TCP_ADDR=1.2.3.4", - "KUBERNETES_RO_SERVICE_HOST=1.2.3.7", - "KUBERNETES_RO_SERVICE_PORT=8087", - "KUBERNETES_RO_PORT=tcp://1.2.3.7:8087", - "KUBERNETES_RO_PORT_8087_TCP=tcp://1.2.3.7:8087", - "KUBERNETES_RO_PORT_8087_TCP_PROTO=tcp", - "KUBERNETES_RO_PORT_8087_TCP_PORT=8087", - "KUBERNETES_RO_PORT_8087_TCP_ADDR=1.2.3.7"), + expectedEnvs: []kubecontainer.EnvVar{ + {Name: "FOO", Value: "ZAP"}, + {Name: "TEST_SERVICE_HOST", Value: "1.2.3.5"}, + {Name: "TEST_SERVICE_PORT", Value: "8085"}, + {Name: "TEST_PORT", Value: "tcp://1.2.3.5:8085"}, + {Name: "TEST_PORT_8085_TCP", Value: "tcp://1.2.3.5:8085"}, + {Name: "TEST_PORT_8085_TCP_PROTO", Value: "tcp"}, + {Name: "TEST_PORT_8085_TCP_PORT", Value: "8085"}, + {Name: "TEST_PORT_8085_TCP_ADDR", Value: "1.2.3.5"}, + {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.4"}, + {Name: "KUBERNETES_SERVICE_PORT", Value: "8084"}, + {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.4:8084"}, + {Name: "KUBERNETES_PORT_8084_TCP", Value: "tcp://1.2.3.4:8084"}, + {Name: "KUBERNETES_PORT_8084_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_PORT_8084_TCP_PORT", Value: "8084"}, + {Name: "KUBERNETES_PORT_8084_TCP_ADDR", Value: "1.2.3.4"}, + {Name: "KUBERNETES_RO_SERVICE_HOST", Value: "1.2.3.7"}, + {Name: "KUBERNETES_RO_SERVICE_PORT", Value: "8087"}, + {Name: "KUBERNETES_RO_PORT", Value: "tcp://1.2.3.7:8087"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP", Value: "tcp://1.2.3.7:8087"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP_PORT", Value: "8087"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP_ADDR", Value: "1.2.3.7"}, + }, }, { name: "pod in master service ns", @@ -2143,28 +2175,29 @@ func TestMakeEnvironmentVariables(t *testing.T) { container: &api.Container{}, masterServiceNs: "kubernetes", nilLister: false, - expectedEnvs: util.NewStringSet( - "NOT_SPECIAL_SERVICE_HOST=1.2.3.8", - "NOT_SPECIAL_SERVICE_PORT=8088", - "NOT_SPECIAL_PORT=tcp://1.2.3.8:8088", - "NOT_SPECIAL_PORT_8088_TCP=tcp://1.2.3.8:8088", - "NOT_SPECIAL_PORT_8088_TCP_PROTO=tcp", - "NOT_SPECIAL_PORT_8088_TCP_PORT=8088", - "NOT_SPECIAL_PORT_8088_TCP_ADDR=1.2.3.8", - "KUBERNETES_SERVICE_HOST=1.2.3.6", - "KUBERNETES_SERVICE_PORT=8086", - "KUBERNETES_PORT=tcp://1.2.3.6:8086", - "KUBERNETES_PORT_8086_TCP=tcp://1.2.3.6:8086", - "KUBERNETES_PORT_8086_TCP_PROTO=tcp", - "KUBERNETES_PORT_8086_TCP_PORT=8086", - "KUBERNETES_PORT_8086_TCP_ADDR=1.2.3.6", - "KUBERNETES_RO_SERVICE_HOST=1.2.3.7", - "KUBERNETES_RO_SERVICE_PORT=8087", - "KUBERNETES_RO_PORT=tcp://1.2.3.7:8087", - "KUBERNETES_RO_PORT_8087_TCP=tcp://1.2.3.7:8087", - "KUBERNETES_RO_PORT_8087_TCP_PROTO=tcp", - "KUBERNETES_RO_PORT_8087_TCP_PORT=8087", - "KUBERNETES_RO_PORT_8087_TCP_ADDR=1.2.3.7"), + expectedEnvs: []kubecontainer.EnvVar{ + {Name: "NOT_SPECIAL_SERVICE_HOST", Value: "1.2.3.8"}, + {Name: "NOT_SPECIAL_SERVICE_PORT", Value: "8088"}, + {Name: "NOT_SPECIAL_PORT", Value: "tcp://1.2.3.8:8088"}, + {Name: "NOT_SPECIAL_PORT_8088_TCP", Value: "tcp://1.2.3.8:8088"}, + {Name: "NOT_SPECIAL_PORT_8088_TCP_PROTO", Value: "tcp"}, + {Name: "NOT_SPECIAL_PORT_8088_TCP_PORT", Value: "8088"}, + {Name: "NOT_SPECIAL_PORT_8088_TCP_ADDR", Value: "1.2.3.8"}, + {Name: "KUBERNETES_SERVICE_HOST", Value: "1.2.3.6"}, + {Name: "KUBERNETES_SERVICE_PORT", Value: "8086"}, + {Name: "KUBERNETES_PORT", Value: "tcp://1.2.3.6:8086"}, + {Name: "KUBERNETES_PORT_8086_TCP", Value: "tcp://1.2.3.6:8086"}, + {Name: "KUBERNETES_PORT_8086_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_PORT_8086_TCP_PORT", Value: "8086"}, + {Name: "KUBERNETES_PORT_8086_TCP_ADDR", Value: "1.2.3.6"}, + {Name: "KUBERNETES_RO_SERVICE_HOST", Value: "1.2.3.7"}, + {Name: "KUBERNETES_RO_SERVICE_PORT", Value: "8087"}, + {Name: "KUBERNETES_RO_PORT", Value: "tcp://1.2.3.7:8087"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP", Value: "tcp://1.2.3.7:8087"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP_PROTO", Value: "tcp"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP_PORT", Value: "8087"}, + {Name: "KUBERNETES_RO_PORT_8087_TCP_ADDR", Value: "1.2.3.7"}, + }, }, { name: "downward api pod", @@ -2193,14 +2226,14 @@ func TestMakeEnvironmentVariables(t *testing.T) { }, masterServiceNs: "nothing", nilLister: true, - expectedEnvs: util.NewStringSet( - "POD_NAME=dapi-test-pod-name", - "POD_NAMESPACE=downward-api", - ), + expectedEnvs: []kubecontainer.EnvVar{ + {Name: "POD_NAME", Value: "dapi-test-pod-name"}, + {Name: "POD_NAMESPACE", Value: "downward-api"}, + }, }, } - for _, tc := range testCases { + for i, tc := range testCases { testKubelet := newTestKubelet(t) kl := testKubelet.kubelet kl.masterServiceNamespace = tc.masterServiceNs @@ -2222,14 +2255,11 @@ func TestMakeEnvironmentVariables(t *testing.T) { t.Errorf("[%v] Unexpected error: %v", tc.name, err) } - resultSet := util.NewStringSet(result...) - if !resultSet.HasAll(tc.expectedEnvs.List()...) { + sort.Sort(envs(result)) + sort.Sort(envs(tc.expectedEnvs)) - t.Errorf("[%v] Unexpected env entries; expected {%v}, got {%v}", tc.name, tc.expectedEnvs, resultSet) - } - - if a, e := len(resultSet), len(tc.expectedEnvs); e != a { - t.Errorf("[%v] Unexpected number of env vars; expected %v, got %v", tc.name, e, a) + if !reflect.DeepEqual(result, tc.expectedEnvs) { + t.Errorf("%d: [%v] Unexpected env entries; expected {%v}, got {%v}", i, tc.name, tc.expectedEnvs, result) } } } From 2617cb26b803092f47b5c615dcd8f1b258df7fd0 Mon Sep 17 00:00:00 2001 From: Yifan Gu Date: Tue, 12 May 2015 16:54:39 -0700 Subject: [PATCH 2/2] kubelet/container: Add tests for kubelet.makePortMappings(). --- pkg/kubelet/kubelet_test.go | 74 +++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pkg/kubelet/kubelet_test.go b/pkg/kubelet/kubelet_test.go index 53f6b9ada8c..7a06988bf71 100644 --- a/pkg/kubelet/kubelet_test.go +++ b/pkg/kubelet/kubelet_test.go @@ -4373,3 +4373,77 @@ func TestFilterOutTerminatedPods(t *testing.T) { t.Errorf("expected %#v, got %#v", expected, actual) } } + +func TestMakePortMappings(t *testing.T) { + tests := []struct { + container *api.Container + expectedPortMappings []kubecontainer.PortMapping + }{ + { + &api.Container{ + Name: "fooContainer", + Ports: []api.ContainerPort{ + { + Protocol: api.ProtocolTCP, + ContainerPort: 80, + HostPort: 8080, + HostIP: "127.0.0.1", + }, + { + Protocol: api.ProtocolTCP, + ContainerPort: 443, + HostPort: 4343, + HostIP: "192.168.0.1", + }, + { + Name: "foo", + Protocol: api.ProtocolUDP, + ContainerPort: 555, + HostPort: 5555, + }, + { + Name: "foo", // Duplicated, should be ignored. + Protocol: api.ProtocolUDP, + ContainerPort: 888, + HostPort: 8888, + }, + { + Protocol: api.ProtocolTCP, // Duplicated, should be ignored. + ContainerPort: 80, + HostPort: 8888, + }, + }, + }, + []kubecontainer.PortMapping{ + { + Name: "fooContainer-TCP:80", + Protocol: api.ProtocolTCP, + ContainerPort: 80, + HostPort: 8080, + HostIP: "127.0.0.1", + }, + { + Name: "fooContainer-TCP:443", + Protocol: api.ProtocolTCP, + ContainerPort: 443, + HostPort: 4343, + HostIP: "192.168.0.1", + }, + { + Name: "fooContainer-foo", + Protocol: api.ProtocolUDP, + ContainerPort: 555, + HostPort: 5555, + HostIP: "", + }, + }, + }, + } + + for i, tt := range tests { + actual := makePortMappings(tt.container) + if !reflect.DeepEqual(tt.expectedPortMappings, actual) { + t.Errorf("%d: Expected: %#v, saw: %#v", i, tt.expectedPortMappings, actual) + } + } +}