Merge pull request #5103 from yujuhong/naming

kubelet: revamp the pod/container naming scheme
pull/6/head
Dawn Chen 2015-03-06 15:58:58 -08:00
commit 9439c0f3bd
16 changed files with 289 additions and 390 deletions

View File

@ -410,7 +410,7 @@ func createAndInitKubelet(kc *KubeletConfig, pc *config.PodConfig) (*kubelet.Kub
kc.RegistryBurst,
kc.MinimumGCAge,
kc.MaxContainerCount,
pc.IsSourceSeen,
pc.SeenAllSources,
kc.ClusterDomain,
net.IP(kc.ClusterDNS),
kc.MasterServiceNamespace,

View File

@ -18,6 +18,7 @@ package config
import (
"fmt"
"os"
"reflect"
"sync"
@ -55,6 +56,10 @@ type PodConfig struct {
// the channel of denormalized changes passed to listeners
updates chan kubelet.PodUpdate
// contains the list of all configured sources
sourcesLock sync.Mutex
sources util.StringSet
}
// NewPodConfig creates an object that can merge many configuration sources into a stream
@ -66,6 +71,7 @@ func NewPodConfig(mode PodConfigNotificationMode, recorder record.EventRecorder)
pods: storage,
mux: config.NewMux(storage),
updates: updates,
sources: util.StringSet{},
}
return podConfig
}
@ -73,17 +79,20 @@ func NewPodConfig(mode PodConfigNotificationMode, recorder record.EventRecorder)
// Channel creates or returns a config source channel. The channel
// only accepts PodUpdates
func (c *PodConfig) Channel(source string) chan<- interface{} {
c.sourcesLock.Lock()
defer c.sourcesLock.Unlock()
c.sources.Insert(source)
return c.mux.Channel(source)
}
// IsSourceSeen returns true if the specified source string has previously
// been marked as seen.
func (c *PodConfig) IsSourceSeen(source string) bool {
// SeenAllSources returns true if this config has received a SET
// message from all configured sources, false otherwise.
func (c *PodConfig) SeenAllSources() bool {
if c.pods == nil {
return false
}
glog.V(6).Infof("Looking for %v, have seen %v", source, c.pods.sourcesSeen)
return c.pods.seenSources(source)
glog.V(6).Infof("Looking for %v, have seen %v", c.sources.List(), c.pods.sourcesSeen)
return c.pods.seenSources(c.sources.List()...)
}
// Updates returns a channel of updates to the configuration, properly denormalized.
@ -198,7 +207,7 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
filtered := filterInvalidPods(update.Pods, source, s.recorder)
for _, ref := range filtered {
name := podUniqueName(ref)
name := kubelet.GetPodFullName(ref)
if existing, found := pods[name]; found {
if !reflect.DeepEqual(existing.Spec, ref.Spec) {
// this is an update
@ -221,7 +230,7 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
case kubelet.REMOVE:
glog.V(4).Infof("Removing a pod %v", update)
for _, value := range update.Pods {
name := podUniqueName(&value)
name := kubelet.GetPodFullName(&value)
if existing, found := pods[name]; found {
// this is a delete
delete(pods, name)
@ -240,7 +249,7 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
filtered := filterInvalidPods(update.Pods, source, s.recorder)
for _, ref := range filtered {
name := podUniqueName(ref)
name := kubelet.GetPodFullName(ref)
if existing, found := oldPods[name]; found {
pods[name] = existing
if !reflect.DeepEqual(existing.Spec, ref.Spec) {
@ -298,7 +307,7 @@ func filterInvalidPods(pods []api.BoundPod, source string, recorder record.Event
// If validation fails, don't trust it any further -
// even Name could be bad.
} else {
name := podUniqueName(pod)
name := kubelet.GetPodFullName(pod)
if names.Has(name) {
errlist = append(errlist, apierrs.NewFieldDuplicate("name", pod.Name))
} else {
@ -341,12 +350,6 @@ func (s *podStorage) MergedState() interface{} {
return pods
}
// podUniqueName returns a value for a given pod that is unique across a source,
// which is the combination of namespace and name.
func podUniqueName(pod *api.BoundPod) string {
return fmt.Sprintf("%s.%s", pod.Name, pod.Namespace)
}
func bestPodIdentString(pod *api.BoundPod) string {
namespace := pod.Namespace
if namespace == "" {
@ -358,3 +361,11 @@ func bestPodIdentString(pod *api.BoundPod) string {
}
return fmt.Sprintf("%s.%s", name, namespace)
}
func GeneratePodName(name string) (string, error) {
hostname, err := os.Hostname() //TODO: kubelet name would be better
if err != nil {
return "", err
}
return fmt.Sprintf("%s-%s", name, hostname), nil
}

View File

@ -108,11 +108,8 @@ func eventToPods(ev watch.Event) ([]api.BoundPod, error) {
}
for _, pod := range boundPods.Items {
// Backwards compatibility with old api servers
// TODO: Remove this after 1.0 release.
if len(pod.Namespace) == 0 {
pod.Namespace = api.NamespaceDefault
}
// Always overrides the namespace provided by the etcd event.
pod.Namespace = kubelet.NamespaceDefault
pods = append(pods, pod)
}

View File

@ -102,8 +102,14 @@ func TestEventToPods(t *testing.T) {
},
},
pods: []api.BoundPod{
{ObjectMeta: api.ObjectMeta{UID: "111", Name: "foo", Namespace: "foo"}, Spec: api.PodSpec{}},
{ObjectMeta: api.ObjectMeta{UID: "222", Name: "bar", Namespace: "bar"}, Spec: api.PodSpec{}},
{
ObjectMeta: api.ObjectMeta{UID: "111", Name: "foo", Namespace: kubelet.NamespaceDefault},
Spec: api.PodSpec{},
},
{
ObjectMeta: api.ObjectMeta{UID: "222", Name: "bar", Namespace: kubelet.NamespaceDefault},
Spec: api.PodSpec{},
},
},
fail: false,
},
@ -116,7 +122,9 @@ func TestEventToPods(t *testing.T) {
},
},
pods: []api.BoundPod{
{ObjectMeta: api.ObjectMeta{UID: "111", Name: "foo", Namespace: "default"}, Spec: api.PodSpec{}},
{
ObjectMeta: api.ObjectMeta{UID: "111", Name: "foo", Namespace: kubelet.NamespaceDefault},
Spec: api.PodSpec{}},
},
fail: false,
},

View File

@ -21,7 +21,6 @@ import (
"crypto/md5"
"encoding/hex"
"fmt"
"hash/adler32"
"io/ioutil"
"os"
"path/filepath"
@ -186,17 +185,16 @@ func extractFromFile(filename string) (api.BoundPod, error) {
// completely deprecate ContainerManifest.
if len(pod.Name) == 0 {
pod.Name = string(pod.UID)
glog.V(5).Infof("Generated Name %q for UID %q from file %s", pod.Name, pod.UID, filename)
}
if len(pod.Namespace) == 0 {
hasher := adler32.New()
fmt.Fprint(hasher, filename)
// TODO: file-<sum>.hostname would be better, if DNS subdomains
// are allowed for namespace (some places only allow DNS
// labels).
pod.Namespace = fmt.Sprintf("file-%08x-%s", hasher.Sum32(), hostname)
glog.V(5).Infof("Generated namespace %q for pod %q from file %s", pod.Namespace, pod.Name, filename)
if pod.Name, err = GeneratePodName(pod.Name); err != nil {
return pod, err
}
glog.V(5).Infof("Generated Name %q for UID %q from file %s", pod.Name, pod.UID, filename)
// Always overrides the namespace provided by the file.
pod.Namespace = kubelet.NamespaceDefault
glog.V(5).Infof("Using namespace %q for pod %q from file %s", pod.Namespace, pod.Name, filename)
// TODO(dchen1107): BoundPod is not type of runtime.Object. Once we allow kubelet talks
// about Pod directly, we can use SelfLinker defined in package: latest
// Currently just simply follow the same format in resthandler.go

View File

@ -54,8 +54,9 @@ func ExampleManifestAndPod(id string) (v1beta1.ContainerManifest, api.BoundPod)
}
expectedPod := api.BoundPod{
ObjectMeta: api.ObjectMeta{
Name: id,
UID: types.UID(id),
Name: id,
UID: types.UID(id),
Namespace: kubelet.NamespaceDefault,
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -131,27 +132,30 @@ func TestReadFromFile(t *testing.T) {
update := got.(kubelet.PodUpdate)
expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, api.BoundPod{
ObjectMeta: api.ObjectMeta{
Name: "test",
Name: "",
UID: "12345",
Namespace: "",
Namespace: kubelet.NamespaceDefault,
SelfLink: "",
},
Spec: api.PodSpec{Containers: []api.Container{{Image: "test/image"}}},
})
if !strings.HasPrefix(update.Pods[0].Name, "test-") {
t.Errorf("Unexpected name: %s", update.Pods[0].Name)
}
// There's no way to provide namespace in ContainerManifest, so
// it will be defaulted.
if !strings.HasPrefix(update.Pods[0].ObjectMeta.Namespace, "file-") {
t.Errorf("Unexpected namespace: %s", update.Pods[0].ObjectMeta.Namespace)
if update.Pods[0].Namespace != kubelet.NamespaceDefault {
t.Errorf("Unexpected namespace: %s", update.Pods[0].Namespace)
}
update.Pods[0].ObjectMeta.Namespace = ""
// SelfLink depends on namespace.
if !strings.HasPrefix(update.Pods[0].ObjectMeta.SelfLink, "/api/") {
t.Errorf("Unexpected selflink: %s", update.Pods[0].ObjectMeta.SelfLink)
if !strings.HasPrefix(update.Pods[0].SelfLink, "/api/") {
t.Errorf("Unexpected selflink: %s", update.Pods[0].SelfLink)
}
update.Pods[0].ObjectMeta.SelfLink = ""
// Reset the fileds that we don't want to compare.
update.Pods[0].Name = ""
update.Pods[0].SelfLink = ""
if !api.Semantic.DeepDerivative(expected, update) {
t.Fatalf("Expected %#v, Got %#v", expected, update)
}
@ -179,7 +183,7 @@ func TestReadFromFileWithoutID(t *testing.T) {
ObjectMeta: api.ObjectMeta{
Name: "",
UID: "12345",
Namespace: "",
Namespace: kubelet.NamespaceDefault,
SelfLink: "",
},
Spec: api.PodSpec{Containers: []api.Container{{Image: "test/image"}}},
@ -188,10 +192,9 @@ func TestReadFromFileWithoutID(t *testing.T) {
if len(update.Pods[0].ObjectMeta.Name) == 0 {
t.Errorf("Name did not get defaulted")
}
update.Pods[0].ObjectMeta.Name = ""
update.Pods[0].ObjectMeta.Namespace = ""
update.Pods[0].ObjectMeta.SelfLink = ""
// Reset the fileds that we don't want to compare.
update.Pods[0].Name = ""
update.Pods[0].SelfLink = ""
if !api.Semantic.DeepDerivative(expected, update) {
t.Fatalf("Expected %#v, Got %#v", expected, update)
}
@ -218,17 +221,17 @@ func TestReadV1Beta2FromFile(t *testing.T) {
update := got.(kubelet.PodUpdate)
expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, api.BoundPod{
ObjectMeta: api.ObjectMeta{
Name: "test",
Name: "",
UID: "12345",
Namespace: "",
Namespace: kubelet.NamespaceDefault,
SelfLink: "",
},
Spec: api.PodSpec{Containers: []api.Container{{Image: "test/image"}}},
})
update.Pods[0].ObjectMeta.Namespace = ""
update.Pods[0].ObjectMeta.SelfLink = ""
// Reset the fileds that we don't want to compare.
update.Pods[0].Name = ""
update.Pods[0].SelfLink = ""
if !api.Semantic.DeepDerivative(expected, update) {
t.Fatalf("Expected %#v, Got %#v", expected, update)
}
@ -252,8 +255,8 @@ func TestReadFromFileWithDefaults(t *testing.T) {
select {
case got := <-ch:
update := got.(kubelet.PodUpdate)
if update.Pods[0].ObjectMeta.UID == "" {
t.Errorf("Unexpected UID: %s", update.Pods[0].ObjectMeta.UID)
if update.Pods[0].UID == "" {
t.Errorf("Unexpected UID: %s", update.Pods[0].UID)
}
case <-time.After(time.Second):
@ -337,12 +340,16 @@ func TestExtractFromDir(t *testing.T) {
update := (<-ch).(kubelet.PodUpdate)
for i := range update.Pods {
update.Pods[i].Namespace = "foobar"
// Pod name is generated with hash and is unique. Skip the comparision
// here by setting it to a simple value.
update.Pods[i].Name = manifests[i].ID
update.Pods[i].SelfLink = ""
}
expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource, pods...)
for i := range expected.Pods {
expected.Pods[i].Namespace = "foobar"
// Pod name is generated with hash and is unique. Skip the comparision
// here by setting it to a simple value.
expected.Pods[i].Name = manifests[i].ID
}
sort.Sort(sortedPods(update.Pods))
sort.Sort(sortedPods(expected.Pods))
@ -351,7 +358,7 @@ func TestExtractFromDir(t *testing.T) {
}
for i := range update.Pods {
if errs := validation.ValidateBoundPod(&update.Pods[i]); len(errs) != 0 {
t.Errorf("Expected no validation errors on %#v, Got %#v", update.Pods[i], errs)
t.Errorf("Expected no validation errors on %#v, Got %q", update.Pods[i], errs)
}
}
}

View File

@ -22,7 +22,6 @@ import (
"crypto/md5"
"encoding/hex"
"fmt"
"hash/adler32"
"io/ioutil"
"net/http"
"time"
@ -92,7 +91,9 @@ func (s *sourceURL) extractFromURL() error {
return singleErr
}
// It parsed!
applyDefaults(&pod, s.url)
if err = applyDefaults(&pod, s.url); err != nil {
return err
}
s.updates <- kubelet.PodUpdate{[]api.BoundPod{pod}, kubelet.SET, kubelet.HTTPSource}
return nil
}
@ -114,7 +115,9 @@ func (s *sourceURL) extractFromURL() error {
// Assume it parsed.
for i := range pods.Items {
pod := &pods.Items[i]
applyDefaults(pod, s.url)
if err = applyDefaults(pod, s.url); err != nil {
return err
}
}
s.updates <- kubelet.PodUpdate{pods.Items, kubelet.SET, kubelet.HTTPSource}
return nil
@ -180,7 +183,7 @@ func tryDecodeList(data []byte) (parsed bool, manifests []v1beta1.ContainerManif
return true, manifests, pods, nil
}
func applyDefaults(pod *api.BoundPod, url string) {
func applyDefaults(pod *api.BoundPod, url string) error {
if len(pod.UID) == 0 {
hasher := md5.New()
fmt.Fprintf(hasher, "url:%s", url)
@ -190,14 +193,18 @@ func applyDefaults(pod *api.BoundPod, url string) {
}
// This is required for backward compatibility, and should be removed once we
// completely deprecate ContainerManifest.
var err error
if len(pod.Name) == 0 {
pod.Name = string(pod.UID)
glog.V(5).Infof("Generate Name %q from UID %q from URL %s", pod.Name, pod.UID, url)
}
if len(pod.Namespace) == 0 {
hasher := adler32.New()
fmt.Fprint(hasher, url)
pod.Namespace = fmt.Sprintf("url-%08x", hasher.Sum32())
glog.V(5).Infof("Generated namespace %q for pod %q from URL %s", pod.Namespace, pod.Name, url)
pod.Name, err = GeneratePodName(pod.Name)
if err != nil {
return err
}
glog.V(5).Infof("Generated Name %q for UID %q from URL %s", pod.Name, pod.UID, url)
// Always overrides the namespace.
pod.Namespace = kubelet.NamespaceDefault
glog.V(5).Infof("Using namespace %q for pod %q from URL %s", pod.Namespace, pod.Name, url)
return nil
}

View File

@ -19,7 +19,7 @@ package config
import (
"encoding/json"
"net/http/httptest"
"strings"
"os"
"testing"
"time"
@ -117,6 +117,8 @@ func TestExtractInvalidManifest(t *testing.T) {
}
func TestExtractFromHTTP(t *testing.T) {
hostname, _ := os.Hostname()
var testCases = []struct {
desc string
manifests interface{}
@ -131,7 +133,7 @@ func TestExtractFromHTTP(t *testing.T) {
api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "111",
Name: "foo",
Name: "foo" + "-" + hostname,
Namespace: "foobar",
},
Spec: api.PodSpec{
@ -153,7 +155,7 @@ func TestExtractFromHTTP(t *testing.T) {
api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "111",
Name: "111",
Name: "111" + "-" + hostname,
Namespace: "foobar",
},
Spec: api.PodSpec{
@ -171,7 +173,7 @@ func TestExtractFromHTTP(t *testing.T) {
api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "111",
Name: "foo",
Name: "foo" + "-" + hostname,
Namespace: "foobar",
},
Spec: api.PodSpec{
@ -198,7 +200,7 @@ func TestExtractFromHTTP(t *testing.T) {
api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "111",
Name: "foo",
Name: "foo" + "-" + hostname,
Namespace: "foobar",
},
Spec: api.PodSpec{
@ -214,7 +216,7 @@ func TestExtractFromHTTP(t *testing.T) {
api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "222",
Name: "bar",
Name: "bar" + "-" + hostname,
Namespace: "foobar",
},
Spec: api.PodSpec{
@ -234,6 +236,7 @@ func TestExtractFromHTTP(t *testing.T) {
expected: CreatePodUpdate(kubelet.SET, kubelet.HTTPSource),
},
}
for _, testCase := range testCases {
data, err := json.Marshal(testCase.manifests)
if err != nil {
@ -256,8 +259,8 @@ func TestExtractFromHTTP(t *testing.T) {
for i := range update.Pods {
// There's no way to provide namespace in ContainerManifest, so
// it will be defaulted.
if !strings.HasPrefix(update.Pods[i].ObjectMeta.Namespace, "url-") {
t.Errorf("Unexpected namespace: %s", update.Pods[0].ObjectMeta.Namespace)
if update.Pods[i].Namespace != kubelet.NamespaceDefault {
t.Errorf("Unexpected namespace: %s", update.Pods[0].Namespace)
}
update.Pods[i].ObjectMeta.Namespace = "foobar"
}

View File

@ -719,11 +719,11 @@ func ParseDockerName(name string) (podFullName string, podUID types.UID, contain
if len(parts) == 0 || parts[0] != containerNamePrefix {
return
}
if len(parts) < 5 {
if len(parts) < 6 {
// We have at least 5 fields. We may have more in the future.
// Anything with less fields than this is not something we can
// manage.
glog.Warningf("found a container with the %q prefix, but too few fields (%d): ", containerNamePrefix, len(parts), name)
glog.Warningf("found a container with the %q prefix, but too few fields (%d): %q", containerNamePrefix, len(parts), name)
return
}
@ -739,11 +739,10 @@ func ParseDockerName(name string) (podFullName string, podUID types.UID, contain
}
// Pod fullname.
podFullName = parts[2]
podFullName = parts[2] + "_" + parts[3]
// Pod UID.
podUID = types.UID(parts[3])
podUID = types.UID(parts[4])
return
}

View File

@ -54,11 +54,11 @@ func TestGetContainerID(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
{
ID: "barbar",
Names: []string{"/k8s_bar_qux_2565_42"},
Names: []string{"/k8s_bar_qux_ns_2565_42"},
},
}
fakeDocker.Container = &docker.Container{
@ -73,7 +73,7 @@ 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_ns", "", "foo")
if dockerContainer == nil || !found {
t.Errorf("Failed to find container %#v", dockerContainer)
}
@ -91,7 +91,7 @@ func verifyPackUnpack(t *testing.T, podNamespace, podUID, podName, containerName
hasher := adler32.New()
util.DeepHashObject(hasher, *container)
computedHash := uint64(hasher.Sum32())
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
podFullName := fmt.Sprintf("%s_%s", podName, podNamespace)
name := BuildDockerName(types.UID(podUID), podFullName, container)
returnedPodFullName, returnedUID, returnedContainerName, hash := ParseDockerName(name)
if podFullName != returnedPodFullName || podUID != string(returnedUID) || containerName != returnedContainerName || computedHash != hash {
@ -111,8 +111,8 @@ func TestContainerManifestNaming(t *testing.T) {
container := &api.Container{Name: "container"}
podName := "foo"
podNamespace := "test"
name := fmt.Sprintf("k8s_%s_%s.%s_%s_42", container.Name, podName, podNamespace, podUID)
podFullName := fmt.Sprintf("%s.%s", podName, podNamespace)
name := fmt.Sprintf("k8s_%s_%s_%s_%s_42", container.Name, podName, podNamespace, podUID)
podFullName := fmt.Sprintf("%s_%s", podName, podNamespace)
returnedPodFullName, returnedPodUID, returnedContainerName, hash := ParseDockerName(name)
if returnedPodFullName != podFullName || string(returnedPodUID) != podUID || returnedContainerName != container.Name || hash != 0 {
@ -463,15 +463,15 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"barbar": &docker.APIContainers{
ID: "barbar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
},
types.UID("1234"),
@ -479,15 +479,15 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"barbar": &docker.APIContainers{
ID: "barbar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
},
},
@ -495,15 +495,15 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"barbar": &docker.APIContainers{
ID: "barbar",
Names: []string{"/k8s_foo_qux_2343_42"},
Names: []string{"/k8s_foo_qux_ns_2343_42"},
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
},
types.UID("1234"),
@ -511,11 +511,11 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
},
},
@ -523,15 +523,15 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"barbar": &docker.APIContainers{
ID: "barbar",
Names: []string{"/k8s_foo_qux_2343_42"},
Names: []string{"/k8s_foo_qux_ns_2343_42"},
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
},
types.UID("5678"),
@ -542,7 +542,7 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"barbar": &docker.APIContainers{
ID: "barbar",
@ -550,7 +550,7 @@ func TestFindContainersByPod(t *testing.T) {
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_5678_42"},
Names: []string{"/k8s_foo_qux_ns_5678_42"},
},
},
types.UID("5678"),
@ -558,7 +558,7 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_5678_42"},
Names: []string{"/k8s_foo_qux_ns_5678_42"},
},
},
},
@ -566,23 +566,23 @@ func TestFindContainersByPod(t *testing.T) {
DockerContainers{
"foobar": &docker.APIContainers{
ID: "foobar",
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
"barbar": &docker.APIContainers{
ID: "barbar",
Names: []string{"/k8s_foo_abc_5678_42"},
Names: []string{"/k8s_foo_abc_ns_5678_42"},
},
"baz": &docker.APIContainers{
ID: "baz",
Names: []string{"/k8s_foo_qux_5678_42"},
Names: []string{"/k8s_foo_qux_ns_5678_42"},
},
},
"",
"abc",
"abc_ns",
DockerContainers{
"barbar": &docker.APIContainers{
ID: "barbar",
Names: []string{"/k8s_foo_abc_5678_42"},
Names: []string{"/k8s_foo_abc_ns_5678_42"},
},
},
},

View File

@ -69,7 +69,7 @@ type SyncHandler interface {
SyncPods(pods []api.BoundPod, podSyncTypes map[types.UID]metrics.SyncPodType, startTime time.Time) error
}
type SourceReadyFn func(source string) bool
type SourcesReadyFn func() bool
type volumeMap map[string]volume.Interface
@ -86,7 +86,7 @@ func NewMainKubelet(
pullBurst int,
minimumGCAge time.Duration,
maxContainerCount int,
sourceReady SourceReadyFn,
sourcesReady SourcesReadyFn,
clusterDomain string,
clusterDNS net.IP,
masterServiceNamespace string,
@ -135,7 +135,7 @@ func NewMainKubelet(
pullBurst: pullBurst,
minimumGCAge: minimumGCAge,
maxContainerCount: maxContainerCount,
sourceReady: sourceReady,
sourcesReady: sourcesReady,
clusterDomain: clusterDomain,
clusterDNS: clusterDNS,
serviceLister: serviceLister,
@ -185,7 +185,7 @@ type Kubelet struct {
podInfraContainerImage string
podWorkers *podWorkers
resyncInterval time.Duration
sourceReady SourceReadyFn
sourcesReady SourcesReadyFn
// Protects the pods array
// We make complete array copies out of this while locked, which is OK because once added to this array,
@ -1402,10 +1402,16 @@ func (kl *Kubelet) SyncPods(allPods []api.BoundPod, podSyncTypes map[types.UID]m
metrics.ContainersPerPodCount.Observe(float64(len(pod.Spec.Containers)))
}
}
// Stop the workers for no-longer existing pods.
kl.podWorkers.ForgetNonExistingPodWorkers(desiredPods)
if !kl.sourcesReady() {
// If the sources aren't ready, skip deletion, as we may accidentally delete pods
// for sources that haven't reported yet.
glog.V(4).Infof("Skipping deletes, sources aren't ready yet.")
return nil
}
// Kill any containers we don't need.
killed := []string{}
for ix := range dockerContainers {
@ -1415,13 +1421,7 @@ func (kl *Kubelet) SyncPods(allPods []api.BoundPod, podSyncTypes map[types.UID]m
// syncPod() will handle this one.
continue
}
_, _, podAnnotations := ParsePodFullName(podFullName)
if source := podAnnotations[ConfigSourceAnnotationKey]; !kl.sourceReady(source) {
// If the source for this container is not ready, skip deletion, so that we don't accidentally
// delete containers for sources that haven't reported yet.
glog.V(4).Infof("Skipping delete of container (%q), source (%s) aren't ready yet.", podFullName, source)
continue
}
pc := podContainer{podFullName, uid, containerName}
if _, ok := desiredContainers[pc]; !ok {
glog.V(1).Infof("Killing unwanted container %+v", pc)

View File

@ -78,7 +78,7 @@ func newTestKubelet(t *testing.T) (*Kubelet, *dockertools.FakeDockerClient, *syn
return err
},
recorder)
kubelet.sourceReady = func(source string) bool { return true }
kubelet.sourcesReady = func() bool { return true }
kubelet.masterServiceNamespace = api.NamespaceDefault
kubelet.serviceLister = testServiceLister{}
kubelet.readiness = newReadinessStates()
@ -396,22 +396,21 @@ func TestSyncPodsDoesNothing(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>_<random>
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&container), 16) + "_foo.new.test_12345678_0"},
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&container), 16) + "_foo_new_12345678_0"},
ID: "1234",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_0"},
Names: []string{"/k8s_POD_foo_new_12345678_0"},
ID: "9876",
},
}
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -439,10 +438,9 @@ func TestSyncPodsWithTerminationLog(t *testing.T) {
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -486,10 +484,9 @@ func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -521,8 +518,8 @@ func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
}
if len(fakeDocker.Created) != 2 ||
!matchString(t, "k8s_POD\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[1]) {
!matchString(t, "k8s_POD\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[1]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
@ -537,10 +534,9 @@ func TestSyncPodsCreatesNetAndContainerPullsImage(t *testing.T) {
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -566,8 +562,8 @@ func TestSyncPodsCreatesNetAndContainerPullsImage(t *testing.T) {
}
if len(fakeDocker.Created) != 2 ||
!matchString(t, "k8s_POD\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[1]) {
!matchString(t, "k8s_POD\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[1]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
@ -578,17 +574,16 @@ func TestSyncPodsWithPodInfraCreatesContainer(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_0"},
Names: []string{"/k8s_POD_foo_new_12345678_0"},
ID: "9876",
},
}
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -609,7 +604,7 @@ func TestSyncPodsWithPodInfraCreatesContainer(t *testing.T) {
fakeDocker.Lock()
if len(fakeDocker.Created) != 1 ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) {
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
@ -622,17 +617,16 @@ func TestSyncPodsWithPodInfraCreatesContainerCallsHandler(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_0"},
Names: []string{"/k8s_POD_foo_new_12345678_0"},
ID: "9876",
},
}
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -664,7 +658,7 @@ func TestSyncPodsWithPodInfraCreatesContainerCallsHandler(t *testing.T) {
fakeDocker.Lock()
if len(fakeDocker.Created) != 1 ||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) {
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo_new_", fakeDocker.Created[0]) {
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
}
fakeDocker.Unlock()
@ -678,17 +672,16 @@ func TestSyncPodsDeletesWithNoPodInfraContainer(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
// format is // k8s_<container-id>_<pod-fullname>_<pod-uid>
Names: []string{"/k8s_bar_foo.new.test_12345678_0"},
Names: []string{"/k8s_bar_foo_new_12345678_0"},
ID: "1234",
},
}
kubelet.pods = []api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -722,17 +715,17 @@ func TestSyncPodsDeletesWithNoPodInfraContainer(t *testing.T) {
func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) {
ready := false
kubelet, fakeDocker, _ := newTestKubelet(t)
kubelet.sourceReady = func(source string) bool { return ready }
kubelet.sourcesReady = func() bool { return ready }
fakeDocker.ContainerList = []docker.APIContainers{
{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar.new.test_12345678_42"},
Names: []string{"/k8s_foo_bar_new_12345678_42"},
ID: "1234",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_42"},
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
@ -762,78 +755,17 @@ func TestSyncPodsDeletesWhenSourcesAreReady(t *testing.T) {
}
}
func TestSyncPodsDeletesWhenContainerSourceReady(t *testing.T) {
ready := false
kubelet, fakeDocker, _ := newTestKubelet(t)
kubelet.sourceReady = func(source string) bool {
if source == "testSource" {
return ready
}
return false
}
fakeDocker.ContainerList = []docker.APIContainers{
{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_boo_bar.default.testSource_12345678_42"},
ID: "7492",
},
{
// pod infra container
Names: []string{"/k8s_POD_boo.default.testSource_12345678_42"},
ID: "3542",
},
{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar.new.otherSource_12345678_42"},
ID: "1234",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.otherSource_12345678_42"},
ID: "9876",
},
}
if err := kubelet.SyncPods([]api.BoundPod{}, emptyPodUIDs, time.Now()); err != nil {
t.Errorf("unexpected error: %v", err)
}
// Validate nothing happened.
verifyCalls(t, fakeDocker, []string{"list"})
fakeDocker.ClearCalls()
ready = true
if err := kubelet.SyncPods([]api.BoundPod{}, emptyPodUIDs, time.Now()); err != nil {
t.Errorf("unexpected error: %v", err)
}
verifyCalls(t, fakeDocker, []string{"list", "stop", "stop", "inspect_container", "inspect_container"})
// Validate container for testSource are killed because testSource is reported as seen, but
// containers for otherSource are not killed because otherSource has not.
expectedToStop := map[string]bool{
"7492": true,
"3542": true,
"1234": false,
"9876": false,
}
if len(fakeDocker.Stopped) != 2 ||
!expectedToStop[fakeDocker.Stopped[0]] ||
!expectedToStop[fakeDocker.Stopped[1]] {
t.Errorf("Wrong containers were stopped: %v", fakeDocker.Stopped)
}
}
func TestSyncPodsDeletes(t *testing.T) {
kubelet, fakeDocker, _ := newTestKubelet(t)
fakeDocker.ContainerList = []docker.APIContainers{
{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar.new.test_12345678_42"},
Names: []string{"/k8s_foo_bar_new_12345678_42"},
ID: "1234",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_42"},
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
{
@ -866,26 +798,25 @@ func TestSyncPodDeletesDuplicate(t *testing.T) {
dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_foo_bar.new.test_12345678_1111"},
Names: []string{"/k8s_foo_bar_new_12345678_1111"},
ID: "1234",
},
"9876": &docker.APIContainers{
// pod infra container
Names: []string{"/k8s_POD_bar.new.test_12345678_2222"},
Names: []string{"/k8s_POD_bar_new_12345678_2222"},
ID: "9876",
},
"4567": &docker.APIContainers{
// Duplicate for the same container.
Names: []string{"/k8s_foo_bar.new.test_12345678_3333"},
Names: []string{"/k8s_foo_bar_new_12345678_3333"},
ID: "4567",
},
}
bound := api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "bar",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "bar",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -911,21 +842,20 @@ func TestSyncPodBadHash(t *testing.T) {
dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_bar.1234_foo.new.test_12345678_42"},
Names: []string{"/k8s_bar.1234_foo_new_12345678_42"},
ID: "1234",
},
"9876": &docker.APIContainers{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_42"},
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
bound := api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -960,21 +890,20 @@ func TestSyncPodUnhealthy(t *testing.T) {
dockerContainers := dockertools.DockerContainers{
"1234": &docker.APIContainers{
// the k8s prefix is required for the kubelet to manage the container
Names: []string{"/k8s_bar_foo.new.test_12345678_42"},
Names: []string{"/k8s_bar_foo_new_12345678_42"},
ID: "1234",
},
"9876": &docker.APIContainers{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_42"},
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
bound := api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -1295,11 +1224,11 @@ func TestGetContainerInfo(t *testing.T) {
ID: containerID,
// pod id: qux
// container id: foo
Names: []string{"/k8s_foo_qux_1234_42"},
Names: []string{"/k8s_foo_qux_ns_1234_42"},
},
}
stats, err := kubelet.GetContainerInfo("qux", "", "foo", cadvisorReq)
stats, err := kubelet.GetContainerInfo("qux_ns", "", "foo", cadvisorReq)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@ -1369,11 +1298,11 @@ func TestGetContainerInfoWhenCadvisorFailed(t *testing.T) {
ID: containerID,
// pod id: qux
// container id: foo
Names: []string{"/k8s_foo_qux_uuid_1234"},
Names: []string{"/k8s_foo_qux_ns_uuid_1234"},
},
}
stats, err := kubelet.GetContainerInfo("qux", "uuid", "foo", cadvisorReq)
stats, err := kubelet.GetContainerInfo("qux_ns", "uuid", "foo", cadvisorReq)
if stats != nil {
t.Errorf("non-nil stats on error")
}
@ -1428,7 +1357,7 @@ func TestGetContainerInfoWithNoContainers(t *testing.T) {
kubelet.cadvisorClient = mockCadvisor
kubelet.dockerClient = &errorTestingDockerClient{listContainersError: nil}
stats, err := kubelet.GetContainerInfo("qux", "", "foo", nil)
stats, err := kubelet.GetContainerInfo("qux_ns", "", "foo", nil)
if err == nil {
t.Errorf("Expected error from cadvisor client, got none")
}
@ -1449,12 +1378,12 @@ func TestGetContainerInfoWithNoMatchingContainers(t *testing.T) {
containerList := []docker.APIContainers{
{
ID: "fakeId",
Names: []string{"/k8s_bar_qux_1234_42"},
Names: []string{"/k8s_bar_qux_ns_1234_42"},
},
}
kubelet.dockerClient = &errorTestingDockerClient{listContainersError: nil, containerList: containerList}
stats, err := kubelet.GetContainerInfo("qux", "", "foo", nil)
stats, err := kubelet.GetContainerInfo("qux_ns", "", "foo", nil)
if err == nil {
t.Errorf("Expected error from cadvisor client, got none")
}
@ -1540,7 +1469,7 @@ func TestRunInContainer(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s_" + containerName + "_" + podName + "." + podNamespace + ".test_12345678_42"},
Names: []string{"/k8s_" + containerName + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
@ -1548,10 +1477,9 @@ func TestRunInContainer(t *testing.T) {
_, err := kubelet.RunInContainer(
GetPodFullName(&api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: podName,
Namespace: podNamespace,
},
}),
"",
@ -1581,7 +1509,7 @@ func TestRunHandlerExec(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s_" + containerName + "_" + podName + "." + podNamespace + "_12345678_42"},
Names: []string{"/k8s_" + containerName + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
@ -1595,7 +1523,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)
}
@ -1637,7 +1565,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)
}
@ -1685,16 +1613,15 @@ func TestSyncPodEventHandlerFails(t *testing.T) {
dockerContainers := dockertools.DockerContainers{
"9876": &docker.APIContainers{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_12345678_42"},
Names: []string{"/k8s_POD_foo_new_12345678_42"},
ID: "9876",
},
}
bound := api.BoundPod{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -1736,17 +1663,17 @@ func TestKubeletGarbageCollection(t *testing.T) {
containers: []docker.APIContainers{
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "1876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "2876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "3876",
},
},
@ -1766,22 +1693,22 @@ func TestKubeletGarbageCollection(t *testing.T) {
containers: []docker.APIContainers{
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "1876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "2876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "3876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "4876",
},
},
@ -1808,7 +1735,7 @@ func TestKubeletGarbageCollection(t *testing.T) {
containers: []docker.APIContainers{
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "1876",
},
},
@ -1818,32 +1745,32 @@ func TestKubeletGarbageCollection(t *testing.T) {
containers: []docker.APIContainers{
{
// pod infra container
Names: []string{"/k8s_POD_foo2.new.test_.beefbeef_40"},
Names: []string{"/k8s_POD_foo2_new_.beefbeef_40"},
ID: "1706",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo2.new.test_.beefbeef_40"},
Names: []string{"/k8s_POD_foo2_new_.beefbeef_40"},
ID: "2706",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo2.new.test_.beefbeef_40"},
Names: []string{"/k8s_POD_foo2_new_.beefbeef_40"},
ID: "3706",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "1876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "2876",
},
{
// pod infra container
Names: []string{"/k8s_POD_foo.new.test_.deadbeef_42"},
Names: []string{"/k8s_POD_foo_new_.deadbeef_42"},
ID: "3876",
},
},
@ -2052,10 +1979,9 @@ func TestSyncPodsWithPullPolicy(t *testing.T) {
err := kubelet.SyncPods([]api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -2851,16 +2777,15 @@ func TestExecInContainerNoSuchContainer(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: "notfound",
Names: []string{"/k8s_notfound_" + podName + "." + podNamespace + ".test_12345678_42"},
Names: []string{"/k8s_notfound_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.ExecInContainer(
GetPodFullName(&api.BoundPod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
containerID,
@ -2909,16 +2834,15 @@ func TestExecInContainer(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: containerID,
Names: []string{"/k8s_" + containerID + "_" + podName + "." + podNamespace + ".test_12345678_42"},
Names: []string{"/k8s_" + containerID + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.ExecInContainer(
GetPodFullName(&api.BoundPod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
containerID,
@ -2987,16 +2911,15 @@ func TestPortForwardNoSuchContainer(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: "notfound",
Names: []string{"/k8s_notfound_" + podName + "." + podNamespace + ".test_12345678_42"},
Names: []string{"/k8s_notfound_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.PortForward(
GetPodFullName(&api.BoundPod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
port,
@ -3027,20 +2950,19 @@ func TestPortForward(t *testing.T) {
fakeDocker.ContainerList = []docker.APIContainers{
{
ID: infraContainerID,
Names: []string{"/k8s_" + kubelet.podInfraContainerImage + "_" + podName + "." + podNamespace + ".test_12345678_42"},
Names: []string{"/k8s_" + kubelet.podInfraContainerImage + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
{
ID: containerID,
Names: []string{"/k8s_" + containerID + "_" + podName + "." + podNamespace + ".test_12345678_42"},
Names: []string{"/k8s_" + containerID + "_" + podName + "_" + podNamespace + "_12345678_42"},
},
}
err := kubelet.PortForward(
GetPodFullName(&api.BoundPod{ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: podName,
Namespace: podNamespace,
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: podName,
Namespace: podNamespace,
}}),
"",
port,

View File

@ -76,7 +76,7 @@ func TestRunOnce(t *testing.T) {
}
podContainers := []docker.APIContainers{
{
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&api.Container{Name: "bar"}), 16) + "_foo.new.test_12345678_42"},
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&api.Container{Name: "bar"}), 16) + "_foo_new_12345678_42"},
ID: "1234",
Status: "running",
},
@ -130,10 +130,9 @@ func TestRunOnce(t *testing.T) {
results, err := kb.runOnce([]api.BoundPod{
{
ObjectMeta: api.ObjectMeta{
UID: "12345678",
Name: "foo",
Namespace: "new",
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
UID: "12345678",
Name: "foo",
Namespace: "new",
},
Spec: api.PodSpec{
Containers: []api.Container{

View File

@ -129,9 +129,6 @@ func newServerTest() *serverTestFramework {
ObjectMeta: api.ObjectMeta{
Namespace: namespace,
Name: name,
Annotations: map[string]string{
ConfigSourceAnnotationKey: "etcd",
},
},
}, true
},
@ -157,6 +154,14 @@ func readResp(resp *http.Response) (string, error) {
return string(body), err
}
// A helper function to return the correct pod name.
func getPodName(name, namespace string) string {
if namespace == "" {
namespace = NamespaceDefault
}
return name + "_" + namespace
}
func TestPodStatus(t *testing.T) {
fw := newServerTest()
expected := api.PodStatus{
@ -165,7 +170,7 @@ func TestPodStatus(t *testing.T) {
},
}
fw.fakeKubelet.statusFunc = func(name string) (api.PodStatus, error) {
if name == "goodpod.default.etcd" {
if name == "goodpod_default" {
return expected, nil
}
return api.PodStatus{}, fmt.Errorf("bad pod %s", name)
@ -191,7 +196,7 @@ func TestContainerInfo(t *testing.T) {
fw := newServerTest()
expectedInfo := &info.ContainerInfo{}
podID := "somepod"
expectedPodID := "somepod" + ".default.etcd"
expectedPodID := getPodName(podID, "")
expectedContainerName := "goodcontainer"
fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
if podID != expectedPodID || containerName != expectedContainerName {
@ -220,7 +225,7 @@ func TestContainerInfoWithUidNamespace(t *testing.T) {
expectedInfo := &info.ContainerInfo{}
podID := "somepod"
expectedNamespace := "custom"
expectedPodID := "somepod" + "." + expectedNamespace + ".etcd"
expectedPodID := getPodName(podID, expectedNamespace)
expectedContainerName := "goodcontainer"
expectedUid := "9b01b80f-8fb4-11e4-95ab-4200af06647"
fw.fakeKubelet.containerInfoFunc = func(podID string, uid types.UID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
@ -344,7 +349,7 @@ func TestServeRunInContainer(t *testing.T) {
output := "foo bar"
podNamespace := "other"
podName := "foo"
expectedPodName := podName + "." + podNamespace + ".etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedContainerName := "baz"
expectedCommand := "ls -a"
fw.fakeKubelet.runFunc = func(podFullName string, uid types.UID, containerName string, cmd []string) ([]byte, error) {
@ -384,7 +389,7 @@ func TestServeRunInContainerWithUID(t *testing.T) {
output := "foo bar"
podNamespace := "other"
podName := "foo"
expectedPodName := podName + "." + podNamespace + ".etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedUID := "7e00838d_-_3523_-_11e4_-_8421_-_42010af0a720"
expectedContainerName := "baz"
expectedCommand := "ls -a"
@ -509,9 +514,6 @@ func setPodByNameFunc(fw *serverTestFramework, namespace, pod, container string)
ObjectMeta: api.ObjectMeta{
Namespace: namespace,
Name: pod,
Annotations: map[string]string{
ConfigSourceAnnotationKey: "etcd",
},
},
Spec: api.PodSpec{
Containers: []api.Container{
@ -548,7 +550,7 @@ func TestContainerLogs(t *testing.T) {
output := "foo bar"
podNamespace := "other"
podName := "foo"
expectedPodName := podName + ".other.etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedContainerName := "baz"
expectedTail := ""
expectedFollow := false
@ -575,7 +577,7 @@ func TestContainerLogsWithTail(t *testing.T) {
output := "foo bar"
podNamespace := "other"
podName := "foo"
expectedPodName := podName + ".other.etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedContainerName := "baz"
expectedTail := "5"
expectedFollow := false
@ -602,7 +604,7 @@ func TestContainerLogsWithFollow(t *testing.T) {
output := "foo bar"
podNamespace := "other"
podName := "foo"
expectedPodName := podName + ".other.etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedContainerName := "baz"
expectedTail := ""
expectedFollow := true
@ -693,7 +695,7 @@ func TestServeExecInContainer(t *testing.T) {
podNamespace := "other"
podName := "foo"
expectedPodName := podName + "." + podNamespace + ".etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedUid := "9b01b80f-8fb4-11e4-95ab-4200af06647"
expectedContainerName := "baz"
expectedCommand := "ls -a"
@ -955,7 +957,7 @@ func TestServePortForward(t *testing.T) {
podNamespace := "other"
podName := "foo"
expectedPodName := podName + "." + podNamespace + ".etcd"
expectedPodName := getPodName(podName, podNamespace)
expectedUid := "9b01b80f-8fb4-11e4-95ab-4200af06647"
for i, test := range tests {

View File

@ -18,10 +18,8 @@ package kubelet
import (
"fmt"
"strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/golang/glog"
)
const ConfigSourceAnnotationKey = "kubernetes.io/config.source"
@ -52,6 +50,8 @@ const (
ApiserverSource = "api"
// Updates from all sources
AllSource = "*"
NamespaceDefault = api.NamespaceDefault
)
// PodUpdate defines an operation sent on the channel. You can add or remove single services by
@ -71,22 +71,12 @@ type PodUpdate struct {
// GetPodFullName returns a name that uniquely identifies a pod across all config sources.
func GetPodFullName(pod *api.BoundPod) string {
return fmt.Sprintf("%s.%s.%s", pod.Name, pod.Namespace, pod.Annotations[ConfigSourceAnnotationKey])
// Use underscore as the delimiter because it is not allowed in pod name
// (DNS subdomain format), while allowed in the container name format.
return fmt.Sprintf("%s_%s", pod.Name, pod.Namespace)
}
// ParsePodFullName unpacks a pod full name and returns the pod name, namespace, and annotations.
// If the pod full name is invalid, empty strings are returend.
func ParsePodFullName(podFullName string) (podName, podNamespace string, podAnnotations map[string]string) {
parts := strings.Split(podFullName, ".")
expectedNumFields := 3
actualNumFields := len(parts)
if actualNumFields != expectedNumFields {
glog.Errorf("found a podFullName (%q) with too few fields: expected %d, actual %d.", podFullName, expectedNumFields, actualNumFields)
return
}
podName = parts[0]
podNamespace = parts[1]
podAnnotations = make(map[string]string)
podAnnotations[ConfigSourceAnnotationKey] = parts[2]
return
// Build the pod full name from pod name and namespace.
func BuildPodFullName(name, namespace string) string {
return name + "_" + namespace
}

View File

@ -1,44 +0,0 @@
/*
Copyright 2015 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubelet
import (
"testing"
)
func TestParsePodFullName(t *testing.T) {
// Arrange
podFullName := "ca4e7148-9ab9-11e4-924c-f0921cde18c1.default.etcd"
// Act
podName, podNamespace, podAnnotations := ParsePodFullName(podFullName)
// Assert
expectedPodName := "ca4e7148-9ab9-11e4-924c-f0921cde18c1"
expectedPodNamespace := "default"
expectedSource := "etcd"
if podName != expectedPodName {
t.Errorf("Unexpected PodName. Expected: %q Actual: %q", expectedPodName, podName)
}
if podNamespace != expectedPodNamespace {
t.Errorf("Unexpected PodNamespace. Expected: %q Actual: %q", expectedPodNamespace, podNamespace)
}
if podAnnotations[ConfigSourceAnnotationKey] != expectedSource {
t.Errorf("Unexpected PodSource. Expected: %q Actual: %q", expectedPodNamespace, podNamespace)
}
}