mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-13 13:55:41 +00:00
Read BoundPods from etcd instead of ContainerManifestList
There are three values that uniquely identify a pod on a host - the configuration source (etcd, file, http), the pod name, and the pod namespace. This change ensures that configuration properly makes those names unique by changing podFullName to contain both name (currently ID in v1beta1, Name in v1beta3) and namespace. The Kubelet does not properly handle information requests for pods not in the default namespace at this time.
This commit is contained in:
parent
332a03b085
commit
892942af8f
@ -17,9 +17,49 @@ limitations under the License.
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec is the identity codec for this package - it can only convert itself
|
// Codec is the identity codec for this package - it can only convert itself
|
||||||
// to itself.
|
// to itself.
|
||||||
var Codec = runtime.CodecFor(Scheme, "")
|
var Codec = runtime.CodecFor(Scheme, "")
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Scheme.AddConversionFuncs(
|
||||||
|
// Convert ContainerManifest to BoundPod
|
||||||
|
func(in *ContainerManifest, out *BoundPod, s conversion.Scope) error {
|
||||||
|
out.Spec.Containers = in.Containers
|
||||||
|
out.Spec.Volumes = in.Volumes
|
||||||
|
out.Spec.RestartPolicy = in.RestartPolicy
|
||||||
|
out.ID = in.ID
|
||||||
|
out.UID = in.UUID
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(in *BoundPod, out *ContainerManifest, s conversion.Scope) error {
|
||||||
|
out.Containers = in.Spec.Containers
|
||||||
|
out.Volumes = in.Spec.Volumes
|
||||||
|
out.RestartPolicy = in.Spec.RestartPolicy
|
||||||
|
out.Version = "v1beta2"
|
||||||
|
out.ID = in.ID
|
||||||
|
out.UUID = in.UID
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(in *ContainerManifestList, out *BoundPods, s conversion.Scope) error {
|
||||||
|
if err := s.Convert(&in.Items, &out.Items, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for i := range out.Items {
|
||||||
|
item := &out.Items[i]
|
||||||
|
item.ResourceVersion = in.ResourceVersion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(in *BoundPods, out *ContainerManifestList, s conversion.Scope) error {
|
||||||
|
if err := s.Convert(&in.Items, &out.Items, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out.ResourceVersion = in.ResourceVersion
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -40,7 +40,9 @@ func init() {
|
|||||||
&Binding{},
|
&Binding{},
|
||||||
&Event{},
|
&Event{},
|
||||||
&EventList{},
|
&EventList{},
|
||||||
|
&ContainerManifest{},
|
||||||
&ContainerManifestList{},
|
&ContainerManifestList{},
|
||||||
|
&BoundPod{},
|
||||||
&BoundPods{},
|
&BoundPods{},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -61,5 +63,7 @@ func (*ServerOp) IsAnAPIObject() {}
|
|||||||
func (*ServerOpList) IsAnAPIObject() {}
|
func (*ServerOpList) IsAnAPIObject() {}
|
||||||
func (*Event) IsAnAPIObject() {}
|
func (*Event) IsAnAPIObject() {}
|
||||||
func (*EventList) IsAnAPIObject() {}
|
func (*EventList) IsAnAPIObject() {}
|
||||||
|
func (*ContainerManifest) IsAnAPIObject() {}
|
||||||
func (*ContainerManifestList) IsAnAPIObject() {}
|
func (*ContainerManifestList) IsAnAPIObject() {}
|
||||||
|
func (*BoundPod) IsAnAPIObject() {}
|
||||||
func (*BoundPods) IsAnAPIObject() {}
|
func (*BoundPods) IsAnAPIObject() {}
|
||||||
|
@ -170,6 +170,10 @@ func TestTypes(t *testing.T) {
|
|||||||
t.Errorf("Couldn't make a %v? %v", kind, err)
|
t.Errorf("Couldn't make a %v? %v", kind, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if _, err := runtime.FindTypeMeta(item); err != nil {
|
||||||
|
t.Logf("%s is not a TypeMeta and cannot be round tripped: %v", kind, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
runTest(t, v1beta1.Codec, item)
|
runTest(t, v1beta1.Codec, item)
|
||||||
runTest(t, v1beta2.Codec, item)
|
runTest(t, v1beta2.Codec, item)
|
||||||
runTest(t, api.Codec, item)
|
runTest(t, api.Codec, item)
|
||||||
|
@ -42,7 +42,9 @@ func init() {
|
|||||||
&ServerOpList{},
|
&ServerOpList{},
|
||||||
&Event{},
|
&Event{},
|
||||||
&EventList{},
|
&EventList{},
|
||||||
|
&ContainerManifest{},
|
||||||
&ContainerManifestList{},
|
&ContainerManifestList{},
|
||||||
|
&BoundPod{},
|
||||||
&BoundPods{},
|
&BoundPods{},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -63,5 +65,7 @@ func (*ServerOp) IsAnAPIObject() {}
|
|||||||
func (*ServerOpList) IsAnAPIObject() {}
|
func (*ServerOpList) IsAnAPIObject() {}
|
||||||
func (*Event) IsAnAPIObject() {}
|
func (*Event) IsAnAPIObject() {}
|
||||||
func (*EventList) IsAnAPIObject() {}
|
func (*EventList) IsAnAPIObject() {}
|
||||||
|
func (*ContainerManifest) IsAnAPIObject() {}
|
||||||
func (*ContainerManifestList) IsAnAPIObject() {}
|
func (*ContainerManifestList) IsAnAPIObject() {}
|
||||||
|
func (*BoundPod) IsAnAPIObject() {}
|
||||||
func (*BoundPods) IsAnAPIObject() {}
|
func (*BoundPods) IsAnAPIObject() {}
|
||||||
|
@ -42,7 +42,9 @@ func init() {
|
|||||||
&ServerOpList{},
|
&ServerOpList{},
|
||||||
&Event{},
|
&Event{},
|
||||||
&EventList{},
|
&EventList{},
|
||||||
|
&ContainerManifest{},
|
||||||
&ContainerManifestList{},
|
&ContainerManifestList{},
|
||||||
|
&BoundPod{},
|
||||||
&BoundPods{},
|
&BoundPods{},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -63,5 +65,7 @@ func (*ServerOp) IsAnAPIObject() {}
|
|||||||
func (*ServerOpList) IsAnAPIObject() {}
|
func (*ServerOpList) IsAnAPIObject() {}
|
||||||
func (*Event) IsAnAPIObject() {}
|
func (*Event) IsAnAPIObject() {}
|
||||||
func (*EventList) IsAnAPIObject() {}
|
func (*EventList) IsAnAPIObject() {}
|
||||||
|
func (*ContainerManifest) IsAnAPIObject() {}
|
||||||
func (*ContainerManifestList) IsAnAPIObject() {}
|
func (*ContainerManifestList) IsAnAPIObject() {}
|
||||||
|
func (*BoundPod) IsAnAPIObject() {}
|
||||||
func (*BoundPods) IsAnAPIObject() {}
|
func (*BoundPods) IsAnAPIObject() {}
|
||||||
|
@ -437,3 +437,25 @@ func ValidateReadOnlyPersistentDisks(volumes []api.Volume) errs.ErrorList {
|
|||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ValidateBoundPod tests if required fields on a bound pod are set.
|
||||||
|
func ValidateBoundPod(pod *api.BoundPod) (errors []error) {
|
||||||
|
if !util.IsDNSSubdomain(pod.ID) {
|
||||||
|
errors = append(errors, errs.NewFieldInvalid("id", pod.ID))
|
||||||
|
}
|
||||||
|
if !util.IsDNSSubdomain(pod.Namespace) {
|
||||||
|
errors = append(errors, errs.NewFieldInvalid("namespace", pod.Namespace))
|
||||||
|
}
|
||||||
|
containerManifest := &api.ContainerManifest{
|
||||||
|
Version: "v1beta2",
|
||||||
|
ID: pod.ID,
|
||||||
|
UUID: pod.UID,
|
||||||
|
Containers: pod.Spec.Containers,
|
||||||
|
Volumes: pod.Spec.Volumes,
|
||||||
|
RestartPolicy: pod.Spec.RestartPolicy,
|
||||||
|
}
|
||||||
|
if errs := ValidateManifest(containerManifest); len(errs) != 0 {
|
||||||
|
errors = append(errors, errs...)
|
||||||
|
}
|
||||||
|
return errors
|
||||||
|
}
|
||||||
|
@ -823,3 +823,21 @@ func TestValidateReplicationController(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateBoundPodNoName(t *testing.T) {
|
||||||
|
errorCases := map[string]api.BoundPod{
|
||||||
|
// manifest is tested in api/validation_test.go, ensure it is invoked
|
||||||
|
"empty version": {TypeMeta: api.TypeMeta{ID: "test"}, Spec: api.PodSpec{Containers: []api.Container{{Name: ""}}}},
|
||||||
|
|
||||||
|
// Name
|
||||||
|
"zero-length name": {TypeMeta: api.TypeMeta{ID: ""}},
|
||||||
|
"name > 255 characters": {TypeMeta: api.TypeMeta{ID: strings.Repeat("a", 256)}},
|
||||||
|
"name not a DNS subdomain": {TypeMeta: api.TypeMeta{ID: "a.b.c."}},
|
||||||
|
"name with underscore": {TypeMeta: api.TypeMeta{ID: "a_b_c"}},
|
||||||
|
}
|
||||||
|
for k, v := range errorCases {
|
||||||
|
if errs := ValidateBoundPod(&v); len(errs) == 0 {
|
||||||
|
t.Errorf("expected failure for %s", k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -21,7 +21,9 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/config"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/config"
|
||||||
@ -89,7 +91,7 @@ func (c *PodConfig) Sync() {
|
|||||||
type podStorage struct {
|
type podStorage struct {
|
||||||
podLock sync.RWMutex
|
podLock sync.RWMutex
|
||||||
// map of source name to pod name to pod reference
|
// map of source name to pod name to pod reference
|
||||||
pods map[string]map[string]*kubelet.Pod
|
pods map[string]map[string]*api.BoundPod
|
||||||
mode PodConfigNotificationMode
|
mode PodConfigNotificationMode
|
||||||
|
|
||||||
// ensures that updates are delivered in strict order
|
// ensures that updates are delivered in strict order
|
||||||
@ -103,7 +105,7 @@ type podStorage struct {
|
|||||||
// TODO: allow initialization of the current state of the store with snapshotted version.
|
// TODO: allow initialization of the current state of the store with snapshotted version.
|
||||||
func newPodStorage(updates chan<- kubelet.PodUpdate, mode PodConfigNotificationMode) *podStorage {
|
func newPodStorage(updates chan<- kubelet.PodUpdate, mode PodConfigNotificationMode) *podStorage {
|
||||||
return &podStorage{
|
return &podStorage{
|
||||||
pods: make(map[string]map[string]*kubelet.Pod),
|
pods: make(map[string]map[string]*api.BoundPod),
|
||||||
mode: mode,
|
mode: mode,
|
||||||
updates: updates,
|
updates: updates,
|
||||||
}
|
}
|
||||||
@ -136,12 +138,12 @@ func (s *podStorage) Merge(source string, change interface{}) error {
|
|||||||
s.updates <- *updates
|
s.updates <- *updates
|
||||||
}
|
}
|
||||||
if len(deletes.Pods) > 0 || len(adds.Pods) > 0 {
|
if len(deletes.Pods) > 0 || len(adds.Pods) > 0 {
|
||||||
s.updates <- kubelet.PodUpdate{s.MergedState().([]kubelet.Pod), kubelet.SET}
|
s.updates <- kubelet.PodUpdate{s.MergedState().([]api.BoundPod), kubelet.SET}
|
||||||
}
|
}
|
||||||
|
|
||||||
case PodConfigNotificationSnapshot:
|
case PodConfigNotificationSnapshot:
|
||||||
if len(updates.Pods) > 0 || len(deletes.Pods) > 0 || len(adds.Pods) > 0 {
|
if len(updates.Pods) > 0 || len(deletes.Pods) > 0 || len(adds.Pods) > 0 {
|
||||||
s.updates <- kubelet.PodUpdate{s.MergedState().([]kubelet.Pod), kubelet.SET}
|
s.updates <- kubelet.PodUpdate{s.MergedState().([]api.BoundPod), kubelet.SET}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -161,7 +163,7 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
|
|
||||||
pods := s.pods[source]
|
pods := s.pods[source]
|
||||||
if pods == nil {
|
if pods == nil {
|
||||||
pods = make(map[string]*kubelet.Pod)
|
pods = make(map[string]*api.BoundPod)
|
||||||
}
|
}
|
||||||
|
|
||||||
update := change.(kubelet.PodUpdate)
|
update := change.(kubelet.PodUpdate)
|
||||||
@ -175,11 +177,11 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
|
|
||||||
filtered := filterInvalidPods(update.Pods, source)
|
filtered := filterInvalidPods(update.Pods, source)
|
||||||
for _, ref := range filtered {
|
for _, ref := range filtered {
|
||||||
name := ref.Name
|
name := podUniqueName(ref)
|
||||||
if existing, found := pods[name]; found {
|
if existing, found := pods[name]; found {
|
||||||
if !reflect.DeepEqual(existing.Manifest, ref.Manifest) {
|
if !reflect.DeepEqual(existing.Spec, ref.Spec) {
|
||||||
// this is an update
|
// this is an update
|
||||||
existing.Manifest = ref.Manifest
|
existing.Spec = ref.Spec
|
||||||
updates.Pods = append(updates.Pods, *existing)
|
updates.Pods = append(updates.Pods, *existing)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -187,7 +189,10 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// this is an add
|
// this is an add
|
||||||
ref.Namespace = source
|
if ref.Annotations == nil {
|
||||||
|
ref.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
ref.Annotations[kubelet.ConfigSourceAnnotationKey] = source
|
||||||
pods[name] = ref
|
pods[name] = ref
|
||||||
adds.Pods = append(adds.Pods, *ref)
|
adds.Pods = append(adds.Pods, *ref)
|
||||||
}
|
}
|
||||||
@ -195,7 +200,7 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
case kubelet.REMOVE:
|
case kubelet.REMOVE:
|
||||||
glog.V(4).Infof("Removing a pod %v", update)
|
glog.V(4).Infof("Removing a pod %v", update)
|
||||||
for _, value := range update.Pods {
|
for _, value := range update.Pods {
|
||||||
name := value.Name
|
name := podUniqueName(&value)
|
||||||
if existing, found := pods[name]; found {
|
if existing, found := pods[name]; found {
|
||||||
// this is a delete
|
// this is a delete
|
||||||
delete(pods, name)
|
delete(pods, name)
|
||||||
@ -209,23 +214,26 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
glog.V(4).Infof("Setting pods for source %s : %v", source, update)
|
glog.V(4).Infof("Setting pods for source %s : %v", source, update)
|
||||||
// Clear the old map entries by just creating a new map
|
// Clear the old map entries by just creating a new map
|
||||||
oldPods := pods
|
oldPods := pods
|
||||||
pods = make(map[string]*kubelet.Pod)
|
pods = make(map[string]*api.BoundPod)
|
||||||
|
|
||||||
filtered := filterInvalidPods(update.Pods, source)
|
filtered := filterInvalidPods(update.Pods, source)
|
||||||
for _, ref := range filtered {
|
for _, ref := range filtered {
|
||||||
name := ref.Name
|
name := podUniqueName(ref)
|
||||||
if existing, found := oldPods[name]; found {
|
if existing, found := oldPods[name]; found {
|
||||||
pods[name] = existing
|
pods[name] = existing
|
||||||
if !reflect.DeepEqual(existing.Manifest, ref.Manifest) {
|
if !reflect.DeepEqual(existing.Spec, ref.Spec) {
|
||||||
// this is an update
|
// this is an update
|
||||||
existing.Manifest = ref.Manifest
|
existing.Spec = ref.Spec
|
||||||
updates.Pods = append(updates.Pods, *existing)
|
updates.Pods = append(updates.Pods, *existing)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// this is a no-op
|
// this is a no-op
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ref.Namespace = source
|
if ref.Annotations == nil {
|
||||||
|
ref.Annotations = make(map[string]string)
|
||||||
|
}
|
||||||
|
ref.Annotations[kubelet.ConfigSourceAnnotationKey] = source
|
||||||
pods[name] = ref
|
pods[name] = ref
|
||||||
adds.Pods = append(adds.Pods, *ref)
|
adds.Pods = append(adds.Pods, *ref)
|
||||||
}
|
}
|
||||||
@ -246,20 +254,21 @@ func (s *podStorage) merge(source string, change interface{}) (adds, updates, de
|
|||||||
return adds, updates, deletes
|
return adds, updates, deletes
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterInvalidPods(pods []kubelet.Pod, source string) (filtered []*kubelet.Pod) {
|
func filterInvalidPods(pods []api.BoundPod, source string) (filtered []*api.BoundPod) {
|
||||||
names := util.StringSet{}
|
names := util.StringSet{}
|
||||||
for i := range pods {
|
for i := range pods {
|
||||||
var errors []error
|
var errors []error
|
||||||
if names.Has(pods[i].Name) {
|
name := podUniqueName(&pods[i])
|
||||||
errors = append(errors, apierrs.NewFieldDuplicate("name", pods[i].Name))
|
if names.Has(name) {
|
||||||
|
errors = append(errors, apierrs.NewFieldDuplicate("name", pods[i].ID))
|
||||||
} else {
|
} else {
|
||||||
names.Insert(pods[i].Name)
|
names.Insert(name)
|
||||||
}
|
}
|
||||||
if errs := kubelet.ValidatePod(&pods[i]); len(errs) != 0 {
|
if errs := validation.ValidateBoundPod(&pods[i]); len(errs) != 0 {
|
||||||
errors = append(errors, errs...)
|
errors = append(errors, errs...)
|
||||||
}
|
}
|
||||||
if len(errors) > 0 {
|
if len(errors) > 0 {
|
||||||
glog.Warningf("Pod %d (%s) from %s failed validation, ignoring: %v", i+1, pods[i].Name, source, errors)
|
glog.Warningf("Pod %d (%s) from %s failed validation, ignoring: %v", i+1, pods[i].ID, source, errors)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
filtered = append(filtered, &pods[i])
|
filtered = append(filtered, &pods[i])
|
||||||
@ -271,20 +280,32 @@ func filterInvalidPods(pods []kubelet.Pod, source string) (filtered []*kubelet.P
|
|||||||
func (s *podStorage) Sync() {
|
func (s *podStorage) Sync() {
|
||||||
s.updateLock.Lock()
|
s.updateLock.Lock()
|
||||||
defer s.updateLock.Unlock()
|
defer s.updateLock.Unlock()
|
||||||
s.updates <- kubelet.PodUpdate{s.MergedState().([]kubelet.Pod), kubelet.SET}
|
s.updates <- kubelet.PodUpdate{s.MergedState().([]api.BoundPod), kubelet.SET}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Object implements config.Accessor
|
// Object implements config.Accessor
|
||||||
func (s *podStorage) MergedState() interface{} {
|
func (s *podStorage) MergedState() interface{} {
|
||||||
s.podLock.RLock()
|
s.podLock.RLock()
|
||||||
defer s.podLock.RUnlock()
|
defer s.podLock.RUnlock()
|
||||||
pods := make([]kubelet.Pod, 0)
|
var pods []api.BoundPod
|
||||||
for source, sourcePods := range s.pods {
|
for _, sourcePods := range s.pods {
|
||||||
for _, podRef := range sourcePods {
|
for _, podRef := range sourcePods {
|
||||||
pod := *podRef
|
pod, err := api.Scheme.Copy(podRef)
|
||||||
pod.Namespace = source
|
if err != nil {
|
||||||
pods = append(pods, pod)
|
glog.Errorf("unable to copy pod: %v", err)
|
||||||
|
}
|
||||||
|
pods = append(pods, *pod.(*api.BoundPod))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pods
|
return pods
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// podUniqueName returns a value for a given pod that is unique across a source,
|
||||||
|
// which is the combination of namespace and ID.
|
||||||
|
func podUniqueName(pod *api.BoundPod) string {
|
||||||
|
namespace := pod.Namespace
|
||||||
|
if len(namespace) == 0 {
|
||||||
|
namespace = api.NamespaceDefault
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s.%s", pod.ID, namespace)
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sort"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
@ -32,7 +33,7 @@ func expectEmptyChannel(t *testing.T, ch <-chan interface{}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type sortedPods []kubelet.Pod
|
type sortedPods []api.BoundPod
|
||||||
|
|
||||||
func (s sortedPods) Len() int {
|
func (s sortedPods) Len() int {
|
||||||
return len(s)
|
return len(s)
|
||||||
@ -41,25 +42,27 @@ func (s sortedPods) Swap(i, j int) {
|
|||||||
s[i], s[j] = s[j], s[i]
|
s[i], s[j] = s[j], s[i]
|
||||||
}
|
}
|
||||||
func (s sortedPods) Less(i, j int) bool {
|
func (s sortedPods) Less(i, j int) bool {
|
||||||
if s[i].Namespace < s[j].Namespace {
|
return s[i].ID < s[j].ID
|
||||||
return true
|
|
||||||
}
|
|
||||||
return s[i].Name < s[j].Name
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateValidPod(name, namespace string) kubelet.Pod {
|
func CreateValidPod(name, namespace, source string) api.BoundPod {
|
||||||
return kubelet.Pod{
|
return api.BoundPod{
|
||||||
Name: name,
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: namespace,
|
ID: name,
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: namespace,
|
||||||
Version: "v1beta1",
|
Annotations: map[string]string{kubelet.ConfigSourceAnnotationKey: source},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreatePodUpdate(op kubelet.PodOperation, pods ...kubelet.Pod) kubelet.PodUpdate {
|
func CreatePodUpdate(op kubelet.PodOperation, pods ...api.BoundPod) kubelet.PodUpdate {
|
||||||
newPods := make([]kubelet.Pod, len(pods))
|
if len(pods) == 0 {
|
||||||
|
return kubelet.PodUpdate{Op: op}
|
||||||
|
}
|
||||||
|
newPods := make([]api.BoundPod, len(pods))
|
||||||
for i := range pods {
|
for i := range pods {
|
||||||
newPods[i] = pods[i]
|
newPods[i] = pods[i]
|
||||||
}
|
}
|
||||||
@ -76,6 +79,7 @@ func createPodConfigTester(mode PodConfigNotificationMode) (chan<- interface{},
|
|||||||
func expectPodUpdate(t *testing.T, ch <-chan kubelet.PodUpdate, expected ...kubelet.PodUpdate) {
|
func expectPodUpdate(t *testing.T, ch <-chan kubelet.PodUpdate, expected ...kubelet.PodUpdate) {
|
||||||
for i := range expected {
|
for i := range expected {
|
||||||
update := <-ch
|
update := <-ch
|
||||||
|
sort.Sort(sortedPods(update.Pods))
|
||||||
if !reflect.DeepEqual(expected[i], update) {
|
if !reflect.DeepEqual(expected[i], update) {
|
||||||
t.Fatalf("Expected %#v, Got %#v", expected[i], update)
|
t.Fatalf("Expected %#v, Got %#v", expected[i], update)
|
||||||
}
|
}
|
||||||
@ -95,24 +99,63 @@ func TestNewPodAdded(t *testing.T) {
|
|||||||
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
// see an update
|
// see an update
|
||||||
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", ""))
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
config.Sync()
|
config.Sync()
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "new", "test")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPodAddedInvalidNamespace(t *testing.T) {
|
||||||
|
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
|
// see an update
|
||||||
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "", ""))
|
||||||
|
channel <- podUpdate
|
||||||
|
config.Sync()
|
||||||
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPodAddedDefaultNamespace(t *testing.T) {
|
||||||
|
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
|
// see an update
|
||||||
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "default", ""))
|
||||||
|
channel <- podUpdate
|
||||||
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "default", "test")))
|
||||||
|
|
||||||
|
config.Sync()
|
||||||
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "default", "test")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPodAddedDifferentNamespaces(t *testing.T) {
|
||||||
|
channel, ch, config := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
|
// see an update
|
||||||
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "default", ""))
|
||||||
|
channel <- podUpdate
|
||||||
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "default", "test")))
|
||||||
|
|
||||||
|
// see an update in another namespace
|
||||||
|
podUpdate = CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""))
|
||||||
|
channel <- podUpdate
|
||||||
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
|
config.Sync()
|
||||||
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "default", "test"), CreateValidPod("foo", "new", "test")))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestInvalidPodFiltered(t *testing.T) {
|
func TestInvalidPodFiltered(t *testing.T) {
|
||||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
// see an update
|
// see an update
|
||||||
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", ""))
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
// add an invalid update
|
// add an invalid update
|
||||||
podUpdate = CreatePodUpdate(kubelet.UPDATE, kubelet.Pod{Name: "foo"})
|
podUpdate = CreatePodUpdate(kubelet.UPDATE, api.BoundPod{TypeMeta: api.TypeMeta{ID: "foo"}})
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectNoPodUpdate(t, ch)
|
expectNoPodUpdate(t, ch)
|
||||||
}
|
}
|
||||||
@ -121,16 +164,16 @@ func TestNewPodAddedSnapshotAndUpdates(t *testing.T) {
|
|||||||
channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshotAndUpdates)
|
channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshotAndUpdates)
|
||||||
|
|
||||||
// see an set
|
// see an set
|
||||||
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", ""))
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
config.Sync()
|
config.Sync()
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
// container updates are separated as UPDATE
|
// container updates are separated as UPDATE
|
||||||
pod := podUpdate.Pods[0]
|
pod := podUpdate.Pods[0]
|
||||||
pod.Manifest.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||||
channel <- CreatePodUpdate(kubelet.ADD, pod)
|
channel <- CreatePodUpdate(kubelet.ADD, pod)
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.UPDATE, pod))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.UPDATE, pod))
|
||||||
}
|
}
|
||||||
@ -139,16 +182,16 @@ func TestNewPodAddedSnapshot(t *testing.T) {
|
|||||||
channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshot)
|
channel, ch, config := createPodConfigTester(PodConfigNotificationSnapshot)
|
||||||
|
|
||||||
// see an set
|
// see an set
|
||||||
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", ""))
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
config.Sync()
|
config.Sync()
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
// container updates are separated as UPDATE
|
// container updates are separated as UPDATE
|
||||||
pod := podUpdate.Pods[0]
|
pod := podUpdate.Pods[0]
|
||||||
pod.Manifest.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||||
channel <- CreatePodUpdate(kubelet.ADD, pod)
|
channel <- CreatePodUpdate(kubelet.ADD, pod)
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, pod))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, pod))
|
||||||
}
|
}
|
||||||
@ -157,21 +200,21 @@ func TestNewPodAddedUpdatedRemoved(t *testing.T) {
|
|||||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
// should register an add
|
// should register an add
|
||||||
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", ""))
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", "test")))
|
||||||
|
|
||||||
// should ignore ADDs that are identical
|
// should ignore ADDs that are identical
|
||||||
expectNoPodUpdate(t, ch)
|
expectNoPodUpdate(t, ch)
|
||||||
|
|
||||||
// an kubelet.ADD should be converted to kubelet.UPDATE
|
// an kubelet.ADD should be converted to kubelet.UPDATE
|
||||||
pod := CreateValidPod("foo", "test")
|
pod := CreateValidPod("foo", "new", "test")
|
||||||
pod.Manifest.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||||
podUpdate = CreatePodUpdate(kubelet.ADD, pod)
|
podUpdate = CreatePodUpdate(kubelet.ADD, pod)
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.UPDATE, pod))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.UPDATE, pod))
|
||||||
|
|
||||||
podUpdate = CreatePodUpdate(kubelet.REMOVE, kubelet.Pod{Name: "foo"})
|
podUpdate = CreatePodUpdate(kubelet.REMOVE, api.BoundPod{TypeMeta: api.TypeMeta{ID: "foo", Namespace: "new"}})
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.REMOVE, pod))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.REMOVE, pod))
|
||||||
}
|
}
|
||||||
@ -180,20 +223,20 @@ func TestNewPodAddedUpdatedSet(t *testing.T) {
|
|||||||
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
channel, ch, _ := createPodConfigTester(PodConfigNotificationIncremental)
|
||||||
|
|
||||||
// should register an add
|
// should register an add
|
||||||
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", ""), CreateValidPod("foo2", ""), CreateValidPod("foo3", ""))
|
podUpdate := CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", ""), CreateValidPod("foo2", "new", ""), CreateValidPod("foo3", "new", ""))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "test"), CreateValidPod("foo2", "test"), CreateValidPod("foo3", "test")))
|
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.ADD, CreateValidPod("foo", "new", "test"), CreateValidPod("foo2", "new", "test"), CreateValidPod("foo3", "new", "test")))
|
||||||
|
|
||||||
// should ignore ADDs that are identical
|
// should ignore ADDs that are identical
|
||||||
expectNoPodUpdate(t, ch)
|
expectNoPodUpdate(t, ch)
|
||||||
|
|
||||||
// should be converted to an kubelet.ADD, kubelet.REMOVE, and kubelet.UPDATE
|
// should be converted to an kubelet.ADD, kubelet.REMOVE, and kubelet.UPDATE
|
||||||
pod := CreateValidPod("foo2", "test")
|
pod := CreateValidPod("foo2", "new", "test")
|
||||||
pod.Manifest.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||||
podUpdate = CreatePodUpdate(kubelet.SET, pod, CreateValidPod("foo3", ""), CreateValidPod("foo4", "test"))
|
podUpdate = CreatePodUpdate(kubelet.SET, pod, CreateValidPod("foo3", "new", ""), CreateValidPod("foo4", "new", "test"))
|
||||||
channel <- podUpdate
|
channel <- podUpdate
|
||||||
expectPodUpdate(t, ch,
|
expectPodUpdate(t, ch,
|
||||||
CreatePodUpdate(kubelet.REMOVE, CreateValidPod("foo", "test")),
|
CreatePodUpdate(kubelet.REMOVE, CreateValidPod("foo", "new", "test")),
|
||||||
CreatePodUpdate(kubelet.ADD, CreateValidPod("foo4", "test")),
|
CreatePodUpdate(kubelet.ADD, CreateValidPod("foo4", "new", "test")),
|
||||||
CreatePodUpdate(kubelet.UPDATE, pod))
|
CreatePodUpdate(kubelet.UPDATE, pod))
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
"path"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
@ -86,21 +87,26 @@ func (s *SourceEtcd) run() {
|
|||||||
|
|
||||||
// eventToPods takes a watch.Event object, and turns it into a structured list of pods.
|
// eventToPods takes a watch.Event object, and turns it into a structured list of pods.
|
||||||
// It returns a list of containers, or an error if one occurs.
|
// It returns a list of containers, or an error if one occurs.
|
||||||
func eventToPods(ev watch.Event) ([]kubelet.Pod, error) {
|
func eventToPods(ev watch.Event) ([]api.BoundPod, error) {
|
||||||
pods := []kubelet.Pod{}
|
pods := []api.BoundPod{}
|
||||||
manifests, ok := ev.Object.(*api.ContainerManifestList)
|
boundPods, ok := ev.Object.(*api.BoundPods)
|
||||||
if !ok {
|
if !ok {
|
||||||
return pods, errors.New("unable to parse response as ContainerManifestList")
|
return pods, errors.New("unable to parse response as BoundPods")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, manifest := range manifests.Items {
|
for i, pod := range boundPods.Items {
|
||||||
name := manifest.ID
|
if len(pod.ID) == 0 {
|
||||||
if name == "" {
|
pod.ID = fmt.Sprintf("%d", i+1)
|
||||||
name = fmt.Sprintf("%d", i+1)
|
|
||||||
}
|
}
|
||||||
pods = append(pods, kubelet.Pod{
|
// TODO: generate random UID if not present
|
||||||
Name: name,
|
if pod.UID == "" && !pod.CreationTimestamp.IsZero() {
|
||||||
Manifest: manifest})
|
pod.UID = strconv.FormatInt(pod.CreationTimestamp.Unix(), 10)
|
||||||
|
}
|
||||||
|
// Backwards compatibility with old api servers
|
||||||
|
if len(pod.Namespace) == 0 {
|
||||||
|
pod.Namespace = api.NamespaceDefault
|
||||||
|
}
|
||||||
|
pods = append(pods, pod)
|
||||||
}
|
}
|
||||||
|
|
||||||
return pods, nil
|
return pods, nil
|
||||||
|
@ -21,53 +21,52 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEventToPods(t *testing.T) {
|
func TestEventToPods(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input watch.Event
|
input watch.Event
|
||||||
pods []kubelet.Pod
|
pods []api.BoundPod
|
||||||
fail bool
|
fail bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
input: watch.Event{Object: nil},
|
input: watch.Event{Object: nil},
|
||||||
pods: []kubelet.Pod{},
|
pods: []api.BoundPod{},
|
||||||
fail: true,
|
fail: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: watch.Event{Object: &api.ContainerManifestList{}},
|
input: watch.Event{Object: &api.BoundPods{}},
|
||||||
pods: []kubelet.Pod{},
|
pods: []api.BoundPod{},
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: watch.Event{
|
input: watch.Event{
|
||||||
Object: &api.ContainerManifestList{
|
Object: &api.BoundPods{
|
||||||
Items: []api.ContainerManifest{
|
Items: []api.BoundPod{
|
||||||
{ID: "foo"},
|
{TypeMeta: api.TypeMeta{ID: "foo"}},
|
||||||
{ID: "bar"},
|
{TypeMeta: api.TypeMeta{ID: "bar"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pods: []kubelet.Pod{
|
pods: []api.BoundPod{
|
||||||
{Name: "foo", Manifest: api.ContainerManifest{ID: "foo"}},
|
{TypeMeta: api.TypeMeta{ID: "foo", Namespace: "default"}, Spec: api.PodSpec{}},
|
||||||
{Name: "bar", Manifest: api.ContainerManifest{ID: "bar"}},
|
{TypeMeta: api.TypeMeta{ID: "bar", Namespace: "default"}, Spec: api.PodSpec{}},
|
||||||
},
|
},
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: watch.Event{
|
input: watch.Event{
|
||||||
Object: &api.ContainerManifestList{
|
Object: &api.BoundPods{
|
||||||
Items: []api.ContainerManifest{
|
Items: []api.BoundPod{
|
||||||
{ID: ""},
|
{TypeMeta: api.TypeMeta{ID: "1"}},
|
||||||
{ID: ""},
|
{TypeMeta: api.TypeMeta{ID: "2", Namespace: "foo"}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
pods: []kubelet.Pod{
|
pods: []api.BoundPod{
|
||||||
{Name: "1", Manifest: api.ContainerManifest{ID: ""}},
|
{TypeMeta: api.TypeMeta{ID: "1", Namespace: "default"}, Spec: api.PodSpec{}},
|
||||||
{Name: "2", Manifest: api.ContainerManifest{ID: ""}},
|
{TypeMeta: api.TypeMeta{ID: "2", Namespace: "foo"}, Spec: api.PodSpec{}},
|
||||||
},
|
},
|
||||||
fail: false,
|
fail: false,
|
||||||
},
|
},
|
||||||
|
@ -29,8 +29,10 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"gopkg.in/v1/yaml"
|
"gopkg.in/v1/yaml"
|
||||||
)
|
)
|
||||||
@ -79,7 +81,7 @@ func (s *SourceFile) extractFromPath() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.updates <- kubelet.PodUpdate{[]kubelet.Pod{pod}, kubelet.SET}
|
s.updates <- kubelet.PodUpdate{[]api.BoundPod{pod}, kubelet.SET}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("path is not a directory or file")
|
return fmt.Errorf("path is not a directory or file")
|
||||||
@ -88,28 +90,29 @@ func (s *SourceFile) extractFromPath() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractFromDir(name string) ([]kubelet.Pod, error) {
|
func extractFromDir(name string) ([]api.BoundPod, error) {
|
||||||
pods := []kubelet.Pod{}
|
|
||||||
|
|
||||||
files, err := filepath.Glob(filepath.Join(name, "[^.]*"))
|
files, err := filepath.Glob(filepath.Join(name, "[^.]*"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pods, err
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(files) == 0 {
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Strings(files)
|
sort.Strings(files)
|
||||||
|
pods := []api.BoundPod{}
|
||||||
for _, file := range files {
|
for _, file := range files {
|
||||||
pod, err := extractFromFile(file)
|
pod, err := extractFromFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []kubelet.Pod{}, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pods = append(pods, pod)
|
pods = append(pods, pod)
|
||||||
}
|
}
|
||||||
return pods, nil
|
return pods, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractFromFile(name string) (kubelet.Pod, error) {
|
func extractFromFile(name string) (api.BoundPod, error) {
|
||||||
var pod kubelet.Pod
|
var pod api.BoundPod
|
||||||
|
|
||||||
file, err := os.Open(name)
|
file, err := os.Open(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -123,15 +126,23 @@ func extractFromFile(name string) (kubelet.Pod, error) {
|
|||||||
return pod, err
|
return pod, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := yaml.Unmarshal(data, &pod.Manifest); err != nil {
|
manifest := &api.ContainerManifest{}
|
||||||
return pod, fmt.Errorf("could not unmarshal manifest: %v", err)
|
// TODO: use api.Scheme.DecodeInto
|
||||||
|
if err := yaml.Unmarshal(data, manifest); err != nil {
|
||||||
|
return pod, err
|
||||||
}
|
}
|
||||||
|
|
||||||
podName := pod.Manifest.ID
|
if err := api.Scheme.Convert(manifest, &pod); err != nil {
|
||||||
if podName == "" {
|
return pod, err
|
||||||
podName = simpleSubdomainSafeHash(name)
|
}
|
||||||
|
|
||||||
|
pod.ID = simpleSubdomainSafeHash(name)
|
||||||
|
if len(pod.UID) == 0 {
|
||||||
|
pod.UID = simpleSubdomainSafeHash(name)
|
||||||
|
}
|
||||||
|
if len(pod.Namespace) == 0 {
|
||||||
|
pod.Namespace = api.NamespaceDefault
|
||||||
}
|
}
|
||||||
pod.Name = podName
|
|
||||||
|
|
||||||
return pod, nil
|
return pod, nil
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,57 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
|
|
||||||
"gopkg.in/v1/yaml"
|
"gopkg.in/v1/yaml"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ExampleManifestAndPod(id string) (api.ContainerManifest, api.BoundPod) {
|
||||||
|
manifest := api.ContainerManifest{
|
||||||
|
ID: id,
|
||||||
|
UUID: "uid",
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "c" + id,
|
||||||
|
Image: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "host-dir",
|
||||||
|
Source: &api.VolumeSource{
|
||||||
|
HostDir: &api.HostDir{"/dir/path"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
expectedPod := api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: id,
|
||||||
|
UID: "uid",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "c" + id,
|
||||||
|
Image: "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "host-dir",
|
||||||
|
Source: &api.VolumeSource{
|
||||||
|
HostDir: &api.HostDir{"/dir/path"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return manifest, expectedPod
|
||||||
|
}
|
||||||
|
|
||||||
func TestExtractFromNonExistentFile(t *testing.T) {
|
func TestExtractFromNonExistentFile(t *testing.T) {
|
||||||
ch := make(chan interface{}, 1)
|
ch := make(chan interface{}, 1)
|
||||||
c := SourceFile{"/some/fake/file", ch}
|
c := SourceFile{"/some/fake/file", ch}
|
||||||
@ -70,16 +117,18 @@ func TestReadFromFile(t *testing.T) {
|
|||||||
select {
|
select {
|
||||||
case got := <-ch:
|
case got := <-ch:
|
||||||
update := got.(kubelet.PodUpdate)
|
update := got.(kubelet.PodUpdate)
|
||||||
expected := CreatePodUpdate(kubelet.SET, kubelet.Pod{
|
expected := CreatePodUpdate(kubelet.SET, api.BoundPod{
|
||||||
Name: "test",
|
TypeMeta: api.TypeMeta{
|
||||||
Manifest: api.ContainerManifest{
|
ID: simpleSubdomainSafeHash(file.Name()),
|
||||||
ID: "test",
|
UID: simpleSubdomainSafeHash(file.Name()),
|
||||||
Version: "v1beta1",
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{{Image: "test/image"}},
|
Containers: []api.Container{{Image: "test/image"}},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if !reflect.DeepEqual(expected, update) {
|
if !reflect.DeepEqual(expected, update) {
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, update)
|
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-time.After(2 * time.Millisecond):
|
case <-time.After(2 * time.Millisecond):
|
||||||
@ -95,29 +144,31 @@ func TestExtractFromBadDataFile(t *testing.T) {
|
|||||||
c := SourceFile{file.Name(), ch}
|
c := SourceFile{file.Name(), ch}
|
||||||
err := c.extractFromPath()
|
err := c.extractFromPath()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Errorf("Expected error")
|
t.Fatalf("Expected error")
|
||||||
}
|
}
|
||||||
expectEmptyChannel(t, ch)
|
expectEmptyChannel(t, ch)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractFromValidDataFile(t *testing.T) {
|
func TestExtractFromValidDataFile(t *testing.T) {
|
||||||
manifest := api.ContainerManifest{ID: ""}
|
manifest, expectedPod := ExampleManifestAndPod("id")
|
||||||
|
|
||||||
text, err := json.Marshal(manifest)
|
text, err := json.Marshal(manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
file := writeTestFile(t, os.TempDir(), "test_pod_config", string(text))
|
file := writeTestFile(t, os.TempDir(), "test_pod_config", string(text))
|
||||||
defer os.Remove(file.Name())
|
defer os.Remove(file.Name())
|
||||||
|
|
||||||
|
expectedPod.ID = simpleSubdomainSafeHash(file.Name())
|
||||||
|
|
||||||
ch := make(chan interface{}, 1)
|
ch := make(chan interface{}, 1)
|
||||||
c := SourceFile{file.Name(), ch}
|
c := SourceFile{file.Name(), ch}
|
||||||
err = c.extractFromPath()
|
err = c.extractFromPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
update := (<-ch).(kubelet.PodUpdate)
|
update := (<-ch).(kubelet.PodUpdate)
|
||||||
expected := CreatePodUpdate(kubelet.SET, kubelet.Pod{Name: simpleSubdomainSafeHash(file.Name()), Manifest: manifest})
|
expected := CreatePodUpdate(kubelet.SET, expectedPod)
|
||||||
if !reflect.DeepEqual(expected, update) {
|
if !reflect.DeepEqual(expected, update) {
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, update)
|
t.Errorf("Expected %#v, Got %#v", expected, update)
|
||||||
}
|
}
|
||||||
@ -126,7 +177,7 @@ func TestExtractFromValidDataFile(t *testing.T) {
|
|||||||
func TestExtractFromEmptyDir(t *testing.T) {
|
func TestExtractFromEmptyDir(t *testing.T) {
|
||||||
dirName, err := ioutil.TempDir("", "foo")
|
dirName, err := ioutil.TempDir("", "foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dirName)
|
defer os.RemoveAll(dirName)
|
||||||
|
|
||||||
@ -134,7 +185,7 @@ func TestExtractFromEmptyDir(t *testing.T) {
|
|||||||
c := SourceFile{dirName, ch}
|
c := SourceFile{dirName, ch}
|
||||||
err = c.extractFromPath()
|
err = c.extractFromPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
update := (<-ch).(kubelet.PodUpdate)
|
update := (<-ch).(kubelet.PodUpdate)
|
||||||
@ -145,54 +196,55 @@ func TestExtractFromEmptyDir(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractFromDir(t *testing.T) {
|
func TestExtractFromDir(t *testing.T) {
|
||||||
manifests := []api.ContainerManifest{
|
manifest, expectedPod := ExampleManifestAndPod("1")
|
||||||
{Version: "v1beta1", Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
manifest2, expectedPod2 := ExampleManifestAndPod("2")
|
||||||
{Version: "v1beta1", Containers: []api.Container{{Name: "2", Image: "bar"}}},
|
|
||||||
}
|
manifests := []api.ContainerManifest{manifest, manifest2}
|
||||||
|
pods := []api.BoundPod{expectedPod, expectedPod2}
|
||||||
files := make([]*os.File, len(manifests))
|
files := make([]*os.File, len(manifests))
|
||||||
|
|
||||||
dirName, err := ioutil.TempDir("", "foo")
|
dirName, err := ioutil.TempDir("", "foo")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, manifest := range manifests {
|
for i, manifest := range manifests {
|
||||||
data, err := json.Marshal(manifest)
|
data, err := json.Marshal(manifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
file, err := ioutil.TempFile(dirName, manifest.ID)
|
file, err := ioutil.TempFile(dirName, manifest.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
name := file.Name()
|
name := file.Name()
|
||||||
if err := file.Close(); err != nil {
|
if err := file.Close(); err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Errorf("Unexpected error: %v", err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
ioutil.WriteFile(name, data, 0755)
|
ioutil.WriteFile(name, data, 0755)
|
||||||
files[i] = file
|
files[i] = file
|
||||||
|
pods[i].ID = simpleSubdomainSafeHash(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan interface{}, 1)
|
ch := make(chan interface{}, 1)
|
||||||
c := SourceFile{dirName, ch}
|
c := SourceFile{dirName, ch}
|
||||||
err = c.extractFromPath()
|
err = c.extractFromPath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error: %v", err)
|
t.Fatalf("Unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
update := (<-ch).(kubelet.PodUpdate)
|
update := (<-ch).(kubelet.PodUpdate)
|
||||||
expected := CreatePodUpdate(
|
expected := CreatePodUpdate(kubelet.SET, pods...)
|
||||||
kubelet.SET,
|
|
||||||
kubelet.Pod{Name: simpleSubdomainSafeHash(files[0].Name()), Manifest: manifests[0]},
|
|
||||||
kubelet.Pod{Name: simpleSubdomainSafeHash(files[1].Name()), Manifest: manifests[1]},
|
|
||||||
)
|
|
||||||
sort.Sort(sortedPods(update.Pods))
|
sort.Sort(sortedPods(update.Pods))
|
||||||
sort.Sort(sortedPods(expected.Pods))
|
sort.Sort(sortedPods(expected.Pods))
|
||||||
if !reflect.DeepEqual(expected, update) {
|
if !reflect.DeepEqual(expected, update) {
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, update)
|
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||||
}
|
}
|
||||||
for i := range update.Pods {
|
for i := range update.Pods {
|
||||||
if errs := kubelet.ValidatePod(&update.Pods[i]); len(errs) != 0 {
|
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 %#v", update.Pods[i], errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"gopkg.in/v1/yaml"
|
"gopkg.in/v1/yaml"
|
||||||
)
|
)
|
||||||
@ -79,6 +80,7 @@ func (s *SourceURL) extractFromURL() error {
|
|||||||
|
|
||||||
// First try as if it's a single manifest
|
// First try as if it's a single manifest
|
||||||
var manifest api.ContainerManifest
|
var manifest api.ContainerManifest
|
||||||
|
// TODO: should be api.Scheme.Decode
|
||||||
singleErr := yaml.Unmarshal(data, &manifest)
|
singleErr := yaml.Unmarshal(data, &manifest)
|
||||||
if singleErr == nil {
|
if singleErr == nil {
|
||||||
if errs := validation.ValidateManifest(&manifest); len(errs) > 0 {
|
if errs := validation.ValidateManifest(&manifest); len(errs) > 0 {
|
||||||
@ -86,16 +88,23 @@ func (s *SourceURL) extractFromURL() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if singleErr == nil {
|
if singleErr == nil {
|
||||||
pod := kubelet.Pod{Name: manifest.ID, Manifest: manifest}
|
pod := api.BoundPod{}
|
||||||
if pod.Name == "" {
|
if err := api.Scheme.Convert(&manifest, &pod); err != nil {
|
||||||
pod.Name = "1"
|
return err
|
||||||
}
|
}
|
||||||
s.updates <- kubelet.PodUpdate{[]kubelet.Pod{pod}, kubelet.SET}
|
if len(pod.ID) == 0 {
|
||||||
|
pod.ID = "1"
|
||||||
|
}
|
||||||
|
if len(pod.Namespace) == 0 {
|
||||||
|
pod.Namespace = api.NamespaceDefault
|
||||||
|
}
|
||||||
|
s.updates <- kubelet.PodUpdate{[]api.BoundPod{pod}, kubelet.SET}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// That didn't work, so try an array of manifests.
|
// That didn't work, so try an array of manifests.
|
||||||
var manifests []api.ContainerManifest
|
var manifests []api.ContainerManifest
|
||||||
|
// TODO: should be api.Scheme.Decode
|
||||||
multiErr := yaml.Unmarshal(data, &manifests)
|
multiErr := yaml.Unmarshal(data, &manifests)
|
||||||
// We're not sure if the person reading the logs is going to care about the single or
|
// We're not sure if the person reading the logs is going to care about the single or
|
||||||
// multiple manifest unmarshalling attempt, so we need to put both in the logs, as is
|
// multiple manifest unmarshalling attempt, so we need to put both in the logs, as is
|
||||||
@ -113,18 +122,24 @@ func (s *SourceURL) extractFromURL() error {
|
|||||||
// array of manifests (and no error) when unmarshaled as such. In that case,
|
// array of manifests (and no error) when unmarshaled as such. In that case,
|
||||||
// if the single manifest at least had a Version, we return the single-manifest
|
// if the single manifest at least had a Version, we return the single-manifest
|
||||||
// error (if any).
|
// error (if any).
|
||||||
if len(manifests) == 0 && manifest.Version != "" {
|
if len(manifests) == 0 && len(manifest.Version) != 0 {
|
||||||
return singleErr
|
return singleErr
|
||||||
}
|
}
|
||||||
pods := []kubelet.Pod{}
|
list := api.ContainerManifestList{Items: manifests}
|
||||||
for i, manifest := range manifests {
|
boundPods := &api.BoundPods{}
|
||||||
pod := kubelet.Pod{Name: manifest.ID, Manifest: manifest}
|
if err := api.Scheme.Convert(&list, boundPods); err != nil {
|
||||||
if pod.Name == "" {
|
return err
|
||||||
pod.Name = fmt.Sprintf("%d", i+1)
|
|
||||||
}
|
|
||||||
pods = append(pods, pod)
|
|
||||||
}
|
}
|
||||||
s.updates <- kubelet.PodUpdate{pods, kubelet.SET}
|
for i := range boundPods.Items {
|
||||||
|
pod := &boundPods.Items[i]
|
||||||
|
if len(pod.ID) == 0 {
|
||||||
|
pod.ID = fmt.Sprintf("%d", i+1)
|
||||||
|
}
|
||||||
|
if len(pod.Namespace) == 0 {
|
||||||
|
pod.Namespace = api.NamespaceDefault
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.updates <- kubelet.PodUpdate{boundPods.Items, kubelet.SET}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
@ -122,11 +123,12 @@ func TestExtractFromHTTP(t *testing.T) {
|
|||||||
desc: "Single manifest",
|
desc: "Single manifest",
|
||||||
manifests: api.ContainerManifest{Version: "v1beta1", ID: "foo"},
|
manifests: api.ContainerManifest{Version: "v1beta1", ID: "foo"},
|
||||||
expected: CreatePodUpdate(kubelet.SET,
|
expected: CreatePodUpdate(kubelet.SET,
|
||||||
kubelet.Pod{
|
api.BoundPod{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Manifest: api.ContainerManifest{
|
ID: "foo",
|
||||||
Version: "v1beta1",
|
Namespace: "default",
|
||||||
ID: "foo",
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
@ -138,13 +140,19 @@ func TestExtractFromHTTP(t *testing.T) {
|
|||||||
{Version: "v1beta1", ID: "bar", Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
{Version: "v1beta1", ID: "bar", Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
||||||
},
|
},
|
||||||
expected: CreatePodUpdate(kubelet.SET,
|
expected: CreatePodUpdate(kubelet.SET,
|
||||||
kubelet.Pod{
|
api.BoundPod{
|
||||||
Name: "1",
|
TypeMeta: api.TypeMeta{
|
||||||
Manifest: api.ContainerManifest{Version: "v1beta1", ID: "", Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
ID: "1",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
||||||
},
|
},
|
||||||
kubelet.Pod{
|
api.BoundPod{
|
||||||
Name: "bar",
|
TypeMeta: api.TypeMeta{
|
||||||
Manifest: api.ContainerManifest{Version: "v1beta1", ID: "bar", Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
ID: "bar",
|
||||||
|
Namespace: "default",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{Containers: []api.Container{{Name: "1", Image: "foo"}}},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -167,13 +175,14 @@ func TestExtractFromHTTP(t *testing.T) {
|
|||||||
c := SourceURL{testServer.URL, ch, nil}
|
c := SourceURL{testServer.URL, ch, nil}
|
||||||
if err := c.extractFromURL(); err != nil {
|
if err := c.extractFromURL(); err != nil {
|
||||||
t.Errorf("%s: Unexpected error: %v", testCase.desc, err)
|
t.Errorf("%s: Unexpected error: %v", testCase.desc, err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
update := (<-ch).(kubelet.PodUpdate)
|
update := (<-ch).(kubelet.PodUpdate)
|
||||||
if !reflect.DeepEqual(testCase.expected, update) {
|
if !reflect.DeepEqual(testCase.expected, update) {
|
||||||
t.Errorf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update)
|
t.Errorf("%s: Expected: %#v, Got: %#v", testCase.desc, testCase.expected, update)
|
||||||
}
|
}
|
||||||
for i := range update.Pods {
|
for i := range update.Pods {
|
||||||
if errs := kubelet.ValidatePod(&update.Pods[i]); len(errs) != 0 {
|
if errs := validation.ValidateBoundPod(&update.Pods[i]); len(errs) != 0 {
|
||||||
t.Errorf("%s: Expected no validation errors on %#v, Got %#v", testCase.desc, update.Pods[i], errs)
|
t.Errorf("%s: Expected no validation errors on %#v, Got %#v", testCase.desc, update.Pods[i], errs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,7 +212,7 @@ func GetKubeletDockerContainers(client DockerInterface, allContainers bool) (Doc
|
|||||||
// TODO(dchen1107): Remove the old separator "--" by end of Oct
|
// TODO(dchen1107): Remove the old separator "--" by end of Oct
|
||||||
if !strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"_") &&
|
if !strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"_") &&
|
||||||
!strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"--") {
|
!strings.HasPrefix(container.Names[0], "/"+containerNamePrefix+"--") {
|
||||||
glog.Infof("Docker Container:%s is not managed by kubelet.", container.Names[0])
|
glog.Infof("Docker Container: %s is not managed by kubelet.", container.Names[0])
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result[DockerID(container.ID)] = container
|
result[DockerID(container.ID)] = container
|
||||||
@ -330,7 +330,7 @@ func inspectContainer(client DockerInterface, dockerID, containerName string) (*
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDockerPodInfo returns docker info for all containers in the pod/manifest.
|
// GetDockerPodInfo returns docker info for all containers in the pod/manifest.
|
||||||
func GetDockerPodInfo(client DockerInterface, manifest api.ContainerManifest, podFullName, uuid string) (api.PodInfo, error) {
|
func GetDockerPodInfo(client DockerInterface, manifest api.PodSpec, podFullName, uuid string) (api.PodInfo, error) {
|
||||||
info := api.PodInfo{}
|
info := api.PodInfo{}
|
||||||
|
|
||||||
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
|
containers, err := client.ListContainers(docker.ListContainersOptions{All: true})
|
||||||
|
@ -55,7 +55,7 @@ type CadvisorInterface interface {
|
|||||||
|
|
||||||
// SyncHandler is an interface implemented by Kubelet, for testability
|
// SyncHandler is an interface implemented by Kubelet, for testability
|
||||||
type SyncHandler interface {
|
type SyncHandler interface {
|
||||||
SyncPods([]Pod) error
|
SyncPods([]api.BoundPod) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type volumeMap map[string]volume.Interface
|
type volumeMap map[string]volume.Interface
|
||||||
@ -111,7 +111,7 @@ type Kubelet struct {
|
|||||||
networkContainerImage string
|
networkContainerImage string
|
||||||
podWorkers *podWorkers
|
podWorkers *podWorkers
|
||||||
resyncInterval time.Duration
|
resyncInterval time.Duration
|
||||||
pods []Pod
|
pods []api.BoundPod
|
||||||
|
|
||||||
// Optional, no events will be sent without it
|
// Optional, no events will be sent without it
|
||||||
etcdClient tools.EtcdClient
|
etcdClient tools.EtcdClient
|
||||||
@ -213,7 +213,7 @@ func makeEnvironmentVariables(container *api.Container) []string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeBinds(pod *Pod, container *api.Container, podVolumes volumeMap) []string {
|
func makeBinds(pod *api.BoundPod, container *api.Container, podVolumes volumeMap) []string {
|
||||||
binds := []string{}
|
binds := []string{}
|
||||||
for _, mount := range container.VolumeMounts {
|
for _, mount := range container.VolumeMounts {
|
||||||
vol, ok := podVolumes[mount.Name]
|
vol, ok := podVolumes[mount.Name]
|
||||||
@ -276,10 +276,10 @@ func milliCPUToShares(milliCPU int) int {
|
|||||||
return shares
|
return shares
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kl *Kubelet) mountExternalVolumes(manifest *api.ContainerManifest) (volumeMap, error) {
|
func (kl *Kubelet) mountExternalVolumes(pod *api.BoundPod) (volumeMap, error) {
|
||||||
podVolumes := make(volumeMap)
|
podVolumes := make(volumeMap)
|
||||||
for _, vol := range manifest.Volumes {
|
for _, vol := range pod.Spec.Volumes {
|
||||||
extVolume, err := volume.CreateVolumeBuilder(&vol, manifest.ID, kl.rootDirectory)
|
extVolume, err := volume.CreateVolumeBuilder(&vol, pod.ID, kl.rootDirectory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -323,18 +323,18 @@ func (kl *Kubelet) runHandler(podFullName, uuid string, container *api.Container
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run a single container from a pod. Returns the docker container ID
|
// Run a single container from a pod. Returns the docker container ID
|
||||||
func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes volumeMap, netMode string) (id dockertools.DockerID, err error) {
|
func (kl *Kubelet) runContainer(pod *api.BoundPod, container *api.Container, podVolumes volumeMap, netMode string) (id dockertools.DockerID, err error) {
|
||||||
envVariables := makeEnvironmentVariables(container)
|
envVariables := makeEnvironmentVariables(container)
|
||||||
binds := makeBinds(pod, container, podVolumes)
|
binds := makeBinds(pod, container, podVolumes)
|
||||||
exposedPorts, portBindings := makePortsAndBindings(container)
|
exposedPorts, portBindings := makePortsAndBindings(container)
|
||||||
|
|
||||||
opts := docker.CreateContainerOptions{
|
opts := docker.CreateContainerOptions{
|
||||||
Name: dockertools.BuildDockerName(pod.Manifest.UUID, GetPodFullName(pod), container),
|
Name: dockertools.BuildDockerName(pod.UID, GetPodFullName(pod), container),
|
||||||
Config: &docker.Config{
|
Config: &docker.Config{
|
||||||
Cmd: container.Command,
|
Cmd: container.Command,
|
||||||
Env: envVariables,
|
Env: envVariables,
|
||||||
ExposedPorts: exposedPorts,
|
ExposedPorts: exposedPorts,
|
||||||
Hostname: pod.Name,
|
Hostname: pod.ID,
|
||||||
Image: container.Image,
|
Image: container.Image,
|
||||||
Memory: int64(container.Memory),
|
Memory: int64(container.Memory),
|
||||||
CpuShares: int64(milliCPUToShares(container.CPU)),
|
CpuShares: int64(milliCPUToShares(container.CPU)),
|
||||||
@ -358,7 +358,7 @@ func (kl *Kubelet) runContainer(pod *Pod, container *api.Container, podVolumes v
|
|||||||
Privileged: privileged,
|
Privileged: privileged,
|
||||||
})
|
})
|
||||||
if err == nil && container.Lifecycle != nil && container.Lifecycle.PostStart != nil {
|
if err == nil && container.Lifecycle != nil && container.Lifecycle.PostStart != nil {
|
||||||
handlerErr := kl.runHandler(GetPodFullName(pod), pod.Manifest.UUID, container, container.Lifecycle.PostStart)
|
handlerErr := kl.runHandler(GetPodFullName(pod), pod.UID, container, container.Lifecycle.PostStart)
|
||||||
if handlerErr != nil {
|
if handlerErr != nil {
|
||||||
kl.killContainerByID(dockerContainer.ID, "")
|
kl.killContainerByID(dockerContainer.ID, "")
|
||||||
return dockertools.DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr)
|
return dockertools.DockerID(""), fmt.Errorf("failed to call event handler: %v", handlerErr)
|
||||||
@ -392,11 +392,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// createNetworkContainer starts the network container for a pod. Returns the docker container ID of the newly created container.
|
// createNetworkContainer starts the network container for a pod. Returns the docker container ID of the newly created container.
|
||||||
func (kl *Kubelet) createNetworkContainer(pod *Pod) (dockertools.DockerID, error) {
|
func (kl *Kubelet) createNetworkContainer(pod *api.BoundPod) (dockertools.DockerID, error) {
|
||||||
var ports []api.Port
|
var ports []api.Port
|
||||||
// Docker only exports ports from the network container. Let's
|
// Docker only exports ports from the network container. Let's
|
||||||
// collect all of the relevant ports and export them.
|
// collect all of the relevant ports and export them.
|
||||||
for _, container := range pod.Manifest.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
ports = append(ports, container.Ports...)
|
ports = append(ports, container.Ports...)
|
||||||
}
|
}
|
||||||
container := &api.Container{
|
container := &api.Container{
|
||||||
@ -419,12 +419,12 @@ func (kl *Kubelet) createNetworkContainer(pod *Pod) (dockertools.DockerID, error
|
|||||||
|
|
||||||
// Delete all containers in a pod (except the network container) returns the number of containers deleted
|
// Delete all containers in a pod (except the network container) returns the number of containers deleted
|
||||||
// and an error if one occurs.
|
// and an error if one occurs.
|
||||||
func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerContainers dockertools.DockerContainers) (int, error) {
|
func (kl *Kubelet) deleteAllContainers(pod *api.BoundPod, podFullName string, dockerContainers dockertools.DockerContainers) (int, error) {
|
||||||
count := 0
|
count := 0
|
||||||
errs := make(chan error, len(pod.Manifest.Containers))
|
errs := make(chan error, len(pod.Spec.Containers))
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
for _, container := range pod.Manifest.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
if dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, pod.Manifest.UUID, container.Name); found {
|
if dockerContainer, found, _ := dockerContainers.FindPodContainer(podFullName, pod.UID, container.Name); found {
|
||||||
count++
|
count++
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
@ -451,9 +451,9 @@ func (kl *Kubelet) deleteAllContainers(pod *Pod, podFullName string, dockerConta
|
|||||||
|
|
||||||
type empty struct{}
|
type empty struct{}
|
||||||
|
|
||||||
func (kl *Kubelet) syncPod(pod *Pod, dockerContainers dockertools.DockerContainers) error {
|
func (kl *Kubelet) syncPod(pod *api.BoundPod, dockerContainers dockertools.DockerContainers) error {
|
||||||
podFullName := GetPodFullName(pod)
|
podFullName := GetPodFullName(pod)
|
||||||
uuid := pod.Manifest.UUID
|
uuid := pod.UID
|
||||||
containersToKeep := make(map[dockertools.DockerID]empty)
|
containersToKeep := make(map[dockertools.DockerID]empty)
|
||||||
killedContainers := make(map[dockertools.DockerID]empty)
|
killedContainers := make(map[dockertools.DockerID]empty)
|
||||||
|
|
||||||
@ -484,7 +484,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers dockertools.DockerContaine
|
|||||||
}
|
}
|
||||||
containersToKeep[netID] = empty{}
|
containersToKeep[netID] = empty{}
|
||||||
|
|
||||||
podVolumes, err := kl.mountExternalVolumes(&pod.Manifest)
|
podVolumes, err := kl.mountExternalVolumes(pod)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Unable to mount volumes for pod %s: (%v) Skipping pod.", podFullName, err)
|
glog.Errorf("Unable to mount volumes for pod %s: (%v) Skipping pod.", podFullName, err)
|
||||||
return err
|
return err
|
||||||
@ -501,7 +501,7 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers dockertools.DockerContaine
|
|||||||
podState.PodIP = netInfo.PodIP
|
podState.PodIP = netInfo.PodIP
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, container := range pod.Manifest.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
expectedHash := dockertools.HashContainer(&container)
|
expectedHash := dockertools.HashContainer(&container)
|
||||||
if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, uuid, container.Name); found {
|
if dockerContainer, found, hash := dockerContainers.FindPodContainer(podFullName, uuid, container.Name); found {
|
||||||
containerID := dockertools.DockerID(dockerContainer.ID)
|
containerID := dockertools.DockerID(dockerContainer.ID)
|
||||||
@ -538,13 +538,13 @@ func (kl *Kubelet) syncPod(pod *Pod, dockerContainers dockertools.DockerContaine
|
|||||||
// TODO(dawnchen): error handling here?
|
// TODO(dawnchen): error handling here?
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(recentContainers) > 0 && pod.Manifest.RestartPolicy.Always == nil {
|
if len(recentContainers) > 0 && pod.Spec.RestartPolicy.Always == nil {
|
||||||
if pod.Manifest.RestartPolicy.Never != nil {
|
if pod.Spec.RestartPolicy.Never != nil {
|
||||||
glog.V(3).Infof("Already ran container with name %s--%s--%s, do nothing",
|
glog.V(3).Infof("Already ran container with name %s--%s--%s, do nothing",
|
||||||
podFullName, uuid, container.Name)
|
podFullName, uuid, container.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if pod.Manifest.RestartPolicy.OnFailure != nil {
|
if pod.Spec.RestartPolicy.OnFailure != nil {
|
||||||
// Check the exit code of last run
|
// Check the exit code of last run
|
||||||
if recentContainers[0].State.ExitCode == 0 {
|
if recentContainers[0].State.ExitCode == 0 {
|
||||||
glog.V(3).Infof("Already successfully ran container with name %s--%s--%s, do nothing",
|
glog.V(3).Infof("Already successfully ran container with name %s--%s--%s, do nothing",
|
||||||
@ -605,11 +605,11 @@ type podContainer struct {
|
|||||||
|
|
||||||
// Stores all volumes defined by the set of pods into a map.
|
// Stores all volumes defined by the set of pods into a map.
|
||||||
// Keys for each entry are in the format (POD_ID)/(VOLUME_NAME)
|
// Keys for each entry are in the format (POD_ID)/(VOLUME_NAME)
|
||||||
func getDesiredVolumes(pods []Pod) map[string]api.Volume {
|
func getDesiredVolumes(pods []api.BoundPod) map[string]api.Volume {
|
||||||
desiredVolumes := make(map[string]api.Volume)
|
desiredVolumes := make(map[string]api.Volume)
|
||||||
for _, pod := range pods {
|
for _, pod := range pods {
|
||||||
for _, volume := range pod.Manifest.Volumes {
|
for _, volume := range pod.Spec.Volumes {
|
||||||
identifier := path.Join(pod.Manifest.ID, volume.Name)
|
identifier := path.Join(pod.ID, volume.Name)
|
||||||
desiredVolumes[identifier] = volume
|
desiredVolumes[identifier] = volume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -618,7 +618,7 @@ func getDesiredVolumes(pods []Pod) map[string]api.Volume {
|
|||||||
|
|
||||||
// Compares the map of current volumes to the map of desired volumes.
|
// Compares the map of current volumes to the map of desired volumes.
|
||||||
// If an active volume does not have a respective desired volume, clean it up.
|
// If an active volume does not have a respective desired volume, clean it up.
|
||||||
func (kl *Kubelet) reconcileVolumes(pods []Pod) error {
|
func (kl *Kubelet) reconcileVolumes(pods []api.BoundPod) error {
|
||||||
desiredVolumes := getDesiredVolumes(pods)
|
desiredVolumes := getDesiredVolumes(pods)
|
||||||
currentVolumes := volume.GetCurrentVolumes(kl.rootDirectory)
|
currentVolumes := volume.GetCurrentVolumes(kl.rootDirectory)
|
||||||
for name, vol := range currentVolumes {
|
for name, vol := range currentVolumes {
|
||||||
@ -637,7 +637,7 @@ func (kl *Kubelet) reconcileVolumes(pods []Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SyncPods synchronizes the configured list of pods (desired state) with the host current state.
|
// SyncPods synchronizes the configured list of pods (desired state) with the host current state.
|
||||||
func (kl *Kubelet) SyncPods(pods []Pod) error {
|
func (kl *Kubelet) SyncPods(pods []api.BoundPod) error {
|
||||||
glog.V(4).Infof("Desired [%s]: %+v", kl.hostname, pods)
|
glog.V(4).Infof("Desired [%s]: %+v", kl.hostname, pods)
|
||||||
var err error
|
var err error
|
||||||
desiredContainers := make(map[podContainer]empty)
|
desiredContainers := make(map[podContainer]empty)
|
||||||
@ -652,11 +652,11 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
|
|||||||
for ix := range pods {
|
for ix := range pods {
|
||||||
pod := &pods[ix]
|
pod := &pods[ix]
|
||||||
podFullName := GetPodFullName(pod)
|
podFullName := GetPodFullName(pod)
|
||||||
uuid := pod.Manifest.UUID
|
uuid := pod.UID
|
||||||
|
|
||||||
// Add all containers (including net) to the map.
|
// Add all containers (including net) to the map.
|
||||||
desiredContainers[podContainer{podFullName, uuid, networkContainerName}] = empty{}
|
desiredContainers[podContainer{podFullName, uuid, networkContainerName}] = empty{}
|
||||||
for _, cont := range pod.Manifest.Containers {
|
for _, cont := range pod.Spec.Containers {
|
||||||
desiredContainers[podContainer{podFullName, uuid, cont.Name}] = empty{}
|
desiredContainers[podContainer{podFullName, uuid, cont.Name}] = empty{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -693,13 +693,13 @@ func (kl *Kubelet) SyncPods(pods []Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// filterHostPortConflicts removes pods that conflict on Port.HostPort values
|
// filterHostPortConflicts removes pods that conflict on Port.HostPort values
|
||||||
func filterHostPortConflicts(pods []Pod) []Pod {
|
func filterHostPortConflicts(pods []api.BoundPod) []api.BoundPod {
|
||||||
filtered := []Pod{}
|
filtered := []api.BoundPod{}
|
||||||
ports := map[int]bool{}
|
ports := map[int]bool{}
|
||||||
extract := func(p *api.Port) int { return p.HostPort }
|
extract := func(p *api.Port) int { return p.HostPort }
|
||||||
for i := range pods {
|
for i := range pods {
|
||||||
pod := &pods[i]
|
pod := &pods[i]
|
||||||
if errs := validation.AccumulateUniquePorts(pod.Manifest.Containers, ports, extract); len(errs) != 0 {
|
if errs := validation.AccumulateUniquePorts(pod.Spec.Containers, ports, extract); len(errs) != 0 {
|
||||||
glog.Warningf("Pod %s has conflicting ports, ignoring: %v", GetPodFullName(pod), errs)
|
glog.Warningf("Pod %s has conflicting ports, ignoring: %v", GetPodFullName(pod), errs)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -784,10 +784,10 @@ func (kl *Kubelet) GetKubeletContainerLogs(podFullName, containerName, tail stri
|
|||||||
|
|
||||||
// GetPodInfo returns information from Docker about the containers in a pod
|
// GetPodInfo returns information from Docker about the containers in a pod
|
||||||
func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) {
|
func (kl *Kubelet) GetPodInfo(podFullName, uuid string) (api.PodInfo, error) {
|
||||||
var manifest api.ContainerManifest
|
var manifest api.PodSpec
|
||||||
for _, pod := range kl.pods {
|
for _, pod := range kl.pods {
|
||||||
if GetPodFullName(&pod) == podFullName {
|
if GetPodFullName(&pod) == podFullName {
|
||||||
manifest = pod.Manifest
|
manifest = pod.Spec
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,7 +125,7 @@ func TestKillContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type channelReader struct {
|
type channelReader struct {
|
||||||
list [][]Pod
|
list [][]api.BoundPod
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ func startReading(channel <-chan interface{}) *channelReader {
|
|||||||
return cr
|
return cr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cr *channelReader) GetList() [][]Pod {
|
func (cr *channelReader) GetList() [][]api.BoundPod {
|
||||||
cr.wg.Wait()
|
cr.wg.Wait()
|
||||||
return cr.list
|
return cr.list
|
||||||
}
|
}
|
||||||
@ -156,21 +156,23 @@ func TestSyncPodsDoesNothing(t *testing.T) {
|
|||||||
fakeDocker.ContainerList = []docker.APIContainers{
|
fakeDocker.ContainerList = []docker.APIContainers{
|
||||||
{
|
{
|
||||||
// format is k8s_<container-id>_<pod-fullname>
|
// format is k8s_<container-id>_<pod-fullname>
|
||||||
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&container), 16) + "_foo.test"},
|
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&container), 16) + "_foo.new.test"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.SyncPods([]Pod{
|
err := kubelet.SyncPods([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
container,
|
container,
|
||||||
},
|
},
|
||||||
@ -209,12 +211,14 @@ func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
|
|||||||
kubelet, _, fakeDocker := newTestKubelet(t)
|
kubelet, _, fakeDocker := newTestKubelet(t)
|
||||||
kubelet.networkContainerImage = "custom_image_name"
|
kubelet.networkContainerImage = "custom_image_name"
|
||||||
fakeDocker.ContainerList = []docker.APIContainers{}
|
fakeDocker.ContainerList = []docker.APIContainers{}
|
||||||
err := kubelet.SyncPods([]Pod{
|
err := kubelet.SyncPods([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar"},
|
{Name: "bar"},
|
||||||
},
|
},
|
||||||
@ -242,8 +246,8 @@ func TestSyncPodsCreatesNetAndContainer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(fakeDocker.Created) != 2 ||
|
if len(fakeDocker.Created) != 2 ||
|
||||||
!matchString(t, "k8s_net\\.[a-f0-9]+_foo.test_", fakeDocker.Created[0]) ||
|
!matchString(t, "k8s_net\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) ||
|
||||||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.test_", fakeDocker.Created[1]) {
|
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[1]) {
|
||||||
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
||||||
}
|
}
|
||||||
fakeDocker.Unlock()
|
fakeDocker.Unlock()
|
||||||
@ -255,12 +259,14 @@ func TestSyncPodsCreatesNetAndContainerPullsImage(t *testing.T) {
|
|||||||
puller.HasImages = []string{}
|
puller.HasImages = []string{}
|
||||||
kubelet.networkContainerImage = "custom_image_name"
|
kubelet.networkContainerImage = "custom_image_name"
|
||||||
fakeDocker.ContainerList = []docker.APIContainers{}
|
fakeDocker.ContainerList = []docker.APIContainers{}
|
||||||
err := kubelet.SyncPods([]Pod{
|
err := kubelet.SyncPods([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar"},
|
{Name: "bar"},
|
||||||
},
|
},
|
||||||
@ -282,8 +288,8 @@ func TestSyncPodsCreatesNetAndContainerPullsImage(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(fakeDocker.Created) != 2 ||
|
if len(fakeDocker.Created) != 2 ||
|
||||||
!matchString(t, "k8s_net\\.[a-f0-9]+_foo.test_", fakeDocker.Created[0]) ||
|
!matchString(t, "k8s_net\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) ||
|
||||||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.test_", fakeDocker.Created[1]) {
|
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[1]) {
|
||||||
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
||||||
}
|
}
|
||||||
fakeDocker.Unlock()
|
fakeDocker.Unlock()
|
||||||
@ -294,16 +300,18 @@ func TestSyncPodsWithNetCreatesContainer(t *testing.T) {
|
|||||||
fakeDocker.ContainerList = []docker.APIContainers{
|
fakeDocker.ContainerList = []docker.APIContainers{
|
||||||
{
|
{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.SyncPods([]Pod{
|
err := kubelet.SyncPods([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar"},
|
{Name: "bar"},
|
||||||
},
|
},
|
||||||
@ -320,7 +328,7 @@ func TestSyncPodsWithNetCreatesContainer(t *testing.T) {
|
|||||||
|
|
||||||
fakeDocker.Lock()
|
fakeDocker.Lock()
|
||||||
if len(fakeDocker.Created) != 1 ||
|
if len(fakeDocker.Created) != 1 ||
|
||||||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.test_", fakeDocker.Created[0]) {
|
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) {
|
||||||
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
||||||
}
|
}
|
||||||
fakeDocker.Unlock()
|
fakeDocker.Unlock()
|
||||||
@ -333,16 +341,18 @@ func TestSyncPodsWithNetCreatesContainerCallsHandler(t *testing.T) {
|
|||||||
fakeDocker.ContainerList = []docker.APIContainers{
|
fakeDocker.ContainerList = []docker.APIContainers{
|
||||||
{
|
{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.SyncPods([]Pod{
|
err := kubelet.SyncPods([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
@ -370,7 +380,7 @@ func TestSyncPodsWithNetCreatesContainerCallsHandler(t *testing.T) {
|
|||||||
|
|
||||||
fakeDocker.Lock()
|
fakeDocker.Lock()
|
||||||
if len(fakeDocker.Created) != 1 ||
|
if len(fakeDocker.Created) != 1 ||
|
||||||
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.test_", fakeDocker.Created[0]) {
|
!matchString(t, "k8s_bar\\.[a-f0-9]+_foo.new.test_", fakeDocker.Created[0]) {
|
||||||
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
t.Errorf("Unexpected containers created %v", fakeDocker.Created)
|
||||||
}
|
}
|
||||||
fakeDocker.Unlock()
|
fakeDocker.Unlock()
|
||||||
@ -384,16 +394,18 @@ func TestSyncPodsDeletesWithNoNetContainer(t *testing.T) {
|
|||||||
fakeDocker.ContainerList = []docker.APIContainers{
|
fakeDocker.ContainerList = []docker.APIContainers{
|
||||||
{
|
{
|
||||||
// format is k8s_<container-id>_<pod-fullname>
|
// format is k8s_<container-id>_<pod-fullname>
|
||||||
Names: []string{"/k8s_bar_foo.test"},
|
Names: []string{"/k8s_bar_foo.new.test"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.SyncPods([]Pod{
|
err := kubelet.SyncPods([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar"},
|
{Name: "bar"},
|
||||||
},
|
},
|
||||||
@ -425,12 +437,12 @@ func TestSyncPodsDeletes(t *testing.T) {
|
|||||||
fakeDocker.ContainerList = []docker.APIContainers{
|
fakeDocker.ContainerList = []docker.APIContainers{
|
||||||
{
|
{
|
||||||
// the k8s prefix is required for the kubelet to manage the container
|
// the k8s prefix is required for the kubelet to manage the container
|
||||||
Names: []string{"/k8s_foo_bar.test"},
|
Names: []string{"/k8s_foo_bar.new.test"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -438,7 +450,7 @@ func TestSyncPodsDeletes(t *testing.T) {
|
|||||||
ID: "4567",
|
ID: "4567",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.SyncPods([]Pod{})
|
err := kubelet.SyncPods([]api.BoundPod{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("unexpected error: %v", err)
|
t.Errorf("unexpected error: %v", err)
|
||||||
}
|
}
|
||||||
@ -463,30 +475,32 @@ func TestSyncPodDeletesDuplicate(t *testing.T) {
|
|||||||
dockerContainers := dockertools.DockerContainers{
|
dockerContainers := dockertools.DockerContainers{
|
||||||
"1234": &docker.APIContainers{
|
"1234": &docker.APIContainers{
|
||||||
// the k8s prefix is required for the kubelet to manage the container
|
// the k8s prefix is required for the kubelet to manage the container
|
||||||
Names: []string{"/k8s_foo_bar.test_1"},
|
Names: []string{"/k8s_foo_bar.new.test_1"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
"9876": &docker.APIContainers{
|
"9876": &docker.APIContainers{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_bar.test_"},
|
Names: []string{"/k8s_net_bar.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
"4567": &docker.APIContainers{
|
"4567": &docker.APIContainers{
|
||||||
// Duplicate for the same container.
|
// Duplicate for the same container.
|
||||||
Names: []string{"/k8s_foo_bar.test_2"},
|
Names: []string{"/k8s_foo_bar.new.test_2"},
|
||||||
ID: "4567",
|
ID: "4567",
|
||||||
},
|
},
|
||||||
"2304": &docker.APIContainers{
|
"2304": &docker.APIContainers{
|
||||||
// Container for another pod, untouched.
|
// Container for another pod, untouched.
|
||||||
Names: []string{"/k8s_baz_fiz.test_6"},
|
Names: []string{"/k8s_baz_fiz.new.test_6"},
|
||||||
ID: "2304",
|
ID: "2304",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.syncPod(&Pod{
|
err := kubelet.syncPod(&api.BoundPod{
|
||||||
Name: "bar",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "bar",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "bar",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "foo"},
|
{Name: "foo"},
|
||||||
},
|
},
|
||||||
@ -520,20 +534,22 @@ func TestSyncPodBadHash(t *testing.T) {
|
|||||||
dockerContainers := dockertools.DockerContainers{
|
dockerContainers := dockertools.DockerContainers{
|
||||||
"1234": &docker.APIContainers{
|
"1234": &docker.APIContainers{
|
||||||
// the k8s prefix is required for the kubelet to manage the container
|
// the k8s prefix is required for the kubelet to manage the container
|
||||||
Names: []string{"/k8s_bar.1234_foo.test"},
|
Names: []string{"/k8s_bar.1234_foo.new.test"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
"9876": &docker.APIContainers{
|
"9876": &docker.APIContainers{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.syncPod(&Pod{
|
err := kubelet.syncPod(&api.BoundPod{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar"},
|
{Name: "bar"},
|
||||||
},
|
},
|
||||||
@ -562,20 +578,22 @@ func TestSyncPodUnhealthy(t *testing.T) {
|
|||||||
dockerContainers := dockertools.DockerContainers{
|
dockerContainers := dockertools.DockerContainers{
|
||||||
"1234": &docker.APIContainers{
|
"1234": &docker.APIContainers{
|
||||||
// the k8s prefix is required for the kubelet to manage the container
|
// the k8s prefix is required for the kubelet to manage the container
|
||||||
Names: []string{"/k8s_bar_foo.test"},
|
Names: []string{"/k8s_bar_foo.new.test"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
},
|
},
|
||||||
"9876": &docker.APIContainers{
|
"9876": &docker.APIContainers{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.syncPod(&Pod{
|
err := kubelet.syncPod(&api.BoundPod{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar",
|
{Name: "bar",
|
||||||
LivenessProbe: &api.LivenessProbe{
|
LivenessProbe: &api.LivenessProbe{
|
||||||
@ -629,25 +647,31 @@ func TestMakeEnvVariables(t *testing.T) {
|
|||||||
|
|
||||||
func TestMountExternalVolumes(t *testing.T) {
|
func TestMountExternalVolumes(t *testing.T) {
|
||||||
kubelet, _, _ := newTestKubelet(t)
|
kubelet, _, _ := newTestKubelet(t)
|
||||||
manifest := api.ContainerManifest{
|
pod := api.BoundPod{
|
||||||
Volumes: []api.Volume{
|
TypeMeta: api.TypeMeta{
|
||||||
{
|
ID: "foo",
|
||||||
Name: "host-dir",
|
Namespace: "test",
|
||||||
Source: &api.VolumeSource{
|
},
|
||||||
HostDir: &api.HostDir{"/dir/path"},
|
Spec: api.PodSpec{
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "host-dir",
|
||||||
|
Source: &api.VolumeSource{
|
||||||
|
HostDir: &api.HostDir{"/dir/path"},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
podVolumes, _ := kubelet.mountExternalVolumes(&manifest)
|
podVolumes, _ := kubelet.mountExternalVolumes(&pod)
|
||||||
expectedPodVolumes := make(volumeMap)
|
expectedPodVolumes := make(volumeMap)
|
||||||
expectedPodVolumes["host-dir"] = &volume.HostDir{"/dir/path"}
|
expectedPodVolumes["host-dir"] = &volume.HostDir{"/dir/path"}
|
||||||
if len(expectedPodVolumes) != len(podVolumes) {
|
if len(expectedPodVolumes) != len(podVolumes) {
|
||||||
t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, manifest)
|
t.Errorf("Unexpected volumes. Expected %#v got %#v. Manifest was: %#v", expectedPodVolumes, podVolumes, pod)
|
||||||
}
|
}
|
||||||
for name, expectedVolume := range expectedPodVolumes {
|
for name, expectedVolume := range expectedPodVolumes {
|
||||||
if _, ok := podVolumes[name]; !ok {
|
if _, ok := podVolumes[name]; !ok {
|
||||||
t.Errorf("Pod volumes map is missing key: %s. %#v", expectedVolume, podVolumes)
|
t.Errorf("api.BoundPod volumes map is missing key: %s. %#v", expectedVolume, podVolumes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -678,9 +702,11 @@ func TestMakeVolumesAndBinds(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pod := Pod{
|
pod := api.BoundPod{
|
||||||
Name: "pod",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "pod",
|
||||||
|
Namespace: "test",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
podVolumes := volumeMap{
|
podVolumes := volumeMap{
|
||||||
@ -769,26 +795,26 @@ func TestMakePortsAndBindings(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestCheckHostPortConflicts(t *testing.T) {
|
func TestCheckHostPortConflicts(t *testing.T) {
|
||||||
successCaseAll := []Pod{
|
successCaseAll := []api.BoundPod{
|
||||||
{Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 80}}}}}},
|
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 80}}}}}},
|
||||||
{Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}}},
|
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}}},
|
||||||
{Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 82}}}}}},
|
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 82}}}}}},
|
||||||
}
|
}
|
||||||
successCaseNew := Pod{
|
successCaseNew := api.BoundPod{
|
||||||
Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 83}}}}},
|
Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 83}}}}},
|
||||||
}
|
}
|
||||||
expected := append(successCaseAll, successCaseNew)
|
expected := append(successCaseAll, successCaseNew)
|
||||||
if actual := filterHostPortConflicts(expected); !reflect.DeepEqual(actual, expected) {
|
if actual := filterHostPortConflicts(expected); !reflect.DeepEqual(actual, expected) {
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, actual)
|
t.Errorf("Expected %#v, Got %#v", expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
failureCaseAll := []Pod{
|
failureCaseAll := []api.BoundPod{
|
||||||
{Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 80}}}}}},
|
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 80}}}}}},
|
||||||
{Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}}},
|
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}}},
|
||||||
{Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 82}}}}}},
|
{Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 82}}}}}},
|
||||||
}
|
}
|
||||||
failureCaseNew := Pod{
|
failureCaseNew := api.BoundPod{
|
||||||
Manifest: api.ContainerManifest{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}},
|
Spec: api.PodSpec{Containers: []api.Container{{Ports: []api.Port{{HostPort: 81}}}}},
|
||||||
}
|
}
|
||||||
if actual := filterHostPortConflicts(append(failureCaseAll, failureCaseNew)); !reflect.DeepEqual(failureCaseAll, actual) {
|
if actual := filterHostPortConflicts(append(failureCaseAll, failureCaseNew)); !reflect.DeepEqual(failureCaseAll, actual) {
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, actual)
|
t.Errorf("Expected %#v, Got %#v", expected, actual)
|
||||||
@ -965,7 +991,7 @@ func TestRunInContainerNoSuchPod(t *testing.T) {
|
|||||||
podNamespace := "etcd"
|
podNamespace := "etcd"
|
||||||
containerName := "containerFoo"
|
containerName := "containerFoo"
|
||||||
output, err := kubelet.RunInContainer(
|
output, err := kubelet.RunInContainer(
|
||||||
GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}),
|
GetPodFullName(&api.BoundPod{TypeMeta: api.TypeMeta{ID: podName, Namespace: podNamespace}}),
|
||||||
"",
|
"",
|
||||||
containerName,
|
containerName,
|
||||||
[]string{"ls"})
|
[]string{"ls"})
|
||||||
@ -990,13 +1016,19 @@ func TestRunInContainer(t *testing.T) {
|
|||||||
fakeDocker.ContainerList = []docker.APIContainers{
|
fakeDocker.ContainerList = []docker.APIContainers{
|
||||||
{
|
{
|
||||||
ID: containerID,
|
ID: containerID,
|
||||||
Names: []string{"/k8s_" + containerName + "_" + podName + "." + podNamespace + "_1234"},
|
Names: []string{"/k8s_" + containerName + "_" + podName + "." + podNamespace + ".test_1234"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := []string{"ls"}
|
cmd := []string{"ls"}
|
||||||
_, err := kubelet.RunInContainer(
|
_, err := kubelet.RunInContainer(
|
||||||
GetPodFullName(&Pod{Name: podName, Namespace: podNamespace}),
|
GetPodFullName(&api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: podName,
|
||||||
|
Namespace: podNamespace,
|
||||||
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
}),
|
||||||
"",
|
"",
|
||||||
containerName,
|
containerName,
|
||||||
cmd)
|
cmd)
|
||||||
@ -1128,15 +1160,17 @@ func TestSyncPodEventHandlerFails(t *testing.T) {
|
|||||||
dockerContainers := dockertools.DockerContainers{
|
dockerContainers := dockertools.DockerContainers{
|
||||||
"9876": &docker.APIContainers{
|
"9876": &docker.APIContainers{
|
||||||
// network container
|
// network container
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
err := kubelet.syncPod(&Pod{
|
err := kubelet.syncPod(&api.BoundPod{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar",
|
{Name: "bar",
|
||||||
Lifecycle: &api.Lifecycle{
|
Lifecycle: &api.Lifecycle{
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/dockertools"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
)
|
)
|
||||||
@ -32,7 +33,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type RunPodResult struct {
|
type RunPodResult struct {
|
||||||
Pod *Pod
|
Pod *api.BoundPod
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ func (kl *Kubelet) RunOnce(updates <-chan PodUpdate) ([]RunPodResult, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runOnce runs a given set of pods and returns their status.
|
// runOnce runs a given set of pods and returns their status.
|
||||||
func (kl *Kubelet) runOnce(pods []Pod) (results []RunPodResult, err error) {
|
func (kl *Kubelet) runOnce(pods []api.BoundPod) (results []RunPodResult, err error) {
|
||||||
if kl.dockerPuller == nil {
|
if kl.dockerPuller == nil {
|
||||||
kl.dockerPuller = dockertools.NewDockerPuller(kl.dockerClient, kl.pullQPS, kl.pullBurst)
|
kl.dockerPuller = dockertools.NewDockerPuller(kl.dockerClient, kl.pullQPS, kl.pullBurst)
|
||||||
}
|
}
|
||||||
@ -72,10 +73,10 @@ func (kl *Kubelet) runOnce(pods []Pod) (results []RunPodResult, err error) {
|
|||||||
results = append(results, res)
|
results = append(results, res)
|
||||||
if res.Err != nil {
|
if res.Err != nil {
|
||||||
// TODO(proppy): report which containers failed the pod.
|
// TODO(proppy): report which containers failed the pod.
|
||||||
glog.Infof("failed to start pod %q: %v", res.Pod.Name, res.Err)
|
glog.Infof("failed to start pod %q: %v", res.Pod.ID, res.Err)
|
||||||
failedPods = append(failedPods, res.Pod.Name)
|
failedPods = append(failedPods, res.Pod.ID)
|
||||||
} else {
|
} else {
|
||||||
glog.Infof("started pod %q", res.Pod.Name)
|
glog.Infof("started pod %q", res.Pod.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(failedPods) > 0 {
|
if len(failedPods) > 0 {
|
||||||
@ -86,7 +87,7 @@ func (kl *Kubelet) runOnce(pods []Pod) (results []RunPodResult, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// runPod runs a single pod and wait until all containers are running.
|
// runPod runs a single pod and wait until all containers are running.
|
||||||
func (kl *Kubelet) runPod(pod Pod) error {
|
func (kl *Kubelet) runPod(pod api.BoundPod) error {
|
||||||
delay := RunOnceRetryDelay
|
delay := RunOnceRetryDelay
|
||||||
retry := 0
|
retry := 0
|
||||||
for {
|
for {
|
||||||
@ -95,18 +96,18 @@ func (kl *Kubelet) runPod(pod Pod) error {
|
|||||||
return fmt.Errorf("failed to get kubelet docker containers: %v", err)
|
return fmt.Errorf("failed to get kubelet docker containers: %v", err)
|
||||||
}
|
}
|
||||||
if running := kl.isPodRunning(pod, dockerContainers); running {
|
if running := kl.isPodRunning(pod, dockerContainers); running {
|
||||||
glog.Infof("pod %q containers running", pod.Name)
|
glog.Infof("pod %q containers running", pod.ID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
glog.Infof("pod %q containers not running: syncing", pod.Name)
|
glog.Infof("pod %q containers not running: syncing", pod.ID)
|
||||||
if err = kl.syncPod(&pod, dockerContainers); err != nil {
|
if err = kl.syncPod(&pod, dockerContainers); err != nil {
|
||||||
return fmt.Errorf("error syncing pod: %v", err)
|
return fmt.Errorf("error syncing pod: %v", err)
|
||||||
}
|
}
|
||||||
if retry >= RunOnceMaxRetries {
|
if retry >= RunOnceMaxRetries {
|
||||||
return fmt.Errorf("timeout error: pod %q containers not running after %d retries", pod.Name, RunOnceMaxRetries)
|
return fmt.Errorf("timeout error: pod %q containers not running after %d retries", pod.ID, RunOnceMaxRetries)
|
||||||
}
|
}
|
||||||
// TODO(proppy): health checking would be better than waiting + checking the state at the next iteration.
|
// TODO(proppy): health checking would be better than waiting + checking the state at the next iteration.
|
||||||
glog.Infof("pod %q containers synced, waiting for %v", pod.Name, delay)
|
glog.Infof("pod %q containers synced, waiting for %v", pod.ID, delay)
|
||||||
<-time.After(delay)
|
<-time.After(delay)
|
||||||
retry++
|
retry++
|
||||||
delay *= RunOnceRetryDelayBackoff
|
delay *= RunOnceRetryDelayBackoff
|
||||||
@ -114,9 +115,9 @@ func (kl *Kubelet) runPod(pod Pod) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// isPodRunning returns true if all containers of a manifest are running.
|
// isPodRunning returns true if all containers of a manifest are running.
|
||||||
func (kl *Kubelet) isPodRunning(pod Pod, dockerContainers dockertools.DockerContainers) bool {
|
func (kl *Kubelet) isPodRunning(pod api.BoundPod, dockerContainers dockertools.DockerContainers) bool {
|
||||||
for _, container := range pod.Manifest.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
if dockerContainer, found, _ := dockerContainers.FindPodContainer(GetPodFullName(&pod), pod.Manifest.UUID, container.Name); !found || dockerContainer.Status != "running" {
|
if dockerContainer, found, _ := dockerContainers.FindPodContainer(GetPodFullName(&pod), pod.UID, container.Name); !found || dockerContainer.Status != "running" {
|
||||||
glog.Infof("container %q not found (%v) or not running: %#v", container.Name, found, dockerContainer)
|
glog.Infof("container %q not found (%v) or not running: %#v", container.Name, found, dockerContainer)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -69,12 +69,12 @@ func TestRunOnce(t *testing.T) {
|
|||||||
kb := &Kubelet{}
|
kb := &Kubelet{}
|
||||||
podContainers := []docker.APIContainers{
|
podContainers := []docker.APIContainers{
|
||||||
{
|
{
|
||||||
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&api.Container{Name: "bar"}), 16) + "_foo.test"},
|
Names: []string{"/k8s_bar." + strconv.FormatUint(dockertools.HashContainer(&api.Container{Name: "bar"}), 16) + "_foo.new.test"},
|
||||||
ID: "1234",
|
ID: "1234",
|
||||||
Status: "running",
|
Status: "running",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Names: []string{"/k8s_net_foo.test_"},
|
Names: []string{"/k8s_net_foo.new.test_"},
|
||||||
ID: "9876",
|
ID: "9876",
|
||||||
Status: "running",
|
Status: "running",
|
||||||
},
|
},
|
||||||
@ -106,12 +106,14 @@ func TestRunOnce(t *testing.T) {
|
|||||||
t: t,
|
t: t,
|
||||||
}
|
}
|
||||||
kb.dockerPuller = &dockertools.FakeDockerPuller{}
|
kb.dockerPuller = &dockertools.FakeDockerPuller{}
|
||||||
results, err := kb.runOnce([]Pod{
|
results, err := kb.runOnce([]api.BoundPod{
|
||||||
{
|
{
|
||||||
Name: "foo",
|
TypeMeta: api.TypeMeta{
|
||||||
Namespace: "test",
|
ID: "foo",
|
||||||
Manifest: api.ContainerManifest{
|
Namespace: "new",
|
||||||
ID: "foo",
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "test"},
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{Name: "bar"},
|
{Name: "bar"},
|
||||||
},
|
},
|
||||||
@ -124,7 +126,7 @@ func TestRunOnce(t *testing.T) {
|
|||||||
if results[0].Err != nil {
|
if results[0].Err != nil {
|
||||||
t.Errorf("unexpected run pod error: %v", results[0].Err)
|
t.Errorf("unexpected run pod error: %v", results[0].Err)
|
||||||
}
|
}
|
||||||
if results[0].Pod.Name != "foo" {
|
if results[0].Pod.ID != "foo" {
|
||||||
t.Errorf("unexpected pod: %q", results[0].Pod.Name)
|
t.Errorf("unexpected pod: %q", results[0].Pod.ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,15 +119,26 @@ func (s *Server) handleContainer(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// This is to provide backward compatibility. It only supports a single manifest
|
// This is to provide backward compatibility. It only supports a single manifest
|
||||||
var pod Pod
|
var pod api.BoundPod
|
||||||
err = yaml.Unmarshal(data, &pod.Manifest)
|
var containerManifest api.ContainerManifest
|
||||||
|
err = yaml.Unmarshal(data, &containerManifest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.error(w, err)
|
s.error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
pod.ID = containerManifest.ID
|
||||||
|
pod.UID = containerManifest.UUID
|
||||||
|
pod.Spec.Containers = containerManifest.Containers
|
||||||
|
pod.Spec.Volumes = containerManifest.Volumes
|
||||||
|
pod.Spec.RestartPolicy = containerManifest.RestartPolicy
|
||||||
//TODO: sha1 of manifest?
|
//TODO: sha1 of manifest?
|
||||||
pod.Name = "1"
|
if pod.ID == "" {
|
||||||
s.updates <- PodUpdate{[]Pod{pod}, SET}
|
pod.ID = "1"
|
||||||
|
}
|
||||||
|
if pod.UID == "" {
|
||||||
|
pod.UID = "1"
|
||||||
|
}
|
||||||
|
s.updates <- PodUpdate{[]api.BoundPod{pod}, SET}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,16 +150,16 @@ func (s *Server) handleContainers(w http.ResponseWriter, req *http.Request) {
|
|||||||
s.error(w, err)
|
s.error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var manifests []api.ContainerManifest
|
var specs []api.PodSpec
|
||||||
err = yaml.Unmarshal(data, &manifests)
|
err = yaml.Unmarshal(data, &specs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.error(w, err)
|
s.error(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pods := make([]Pod, len(manifests))
|
pods := make([]api.BoundPod, len(specs))
|
||||||
for i := range manifests {
|
for i := range specs {
|
||||||
pods[i].Name = fmt.Sprintf("%d", i+1)
|
pods[i].ID = fmt.Sprintf("%d", i+1)
|
||||||
pods[i].Manifest = manifests[i]
|
pods[i].Spec = specs[i]
|
||||||
}
|
}
|
||||||
s.updates <- PodUpdate{pods, SET}
|
s.updates <- PodUpdate{pods, SET}
|
||||||
|
|
||||||
@ -186,7 +197,14 @@ func (s *Server) handleContainerLogs(w http.ResponseWriter, req *http.Request) {
|
|||||||
follow, _ := strconv.ParseBool(uriValues.Get("follow"))
|
follow, _ := strconv.ParseBool(uriValues.Get("follow"))
|
||||||
tail := uriValues.Get("tail")
|
tail := uriValues.Get("tail")
|
||||||
|
|
||||||
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
|
podFullName := GetPodFullName(&api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: podID,
|
||||||
|
// TODO: I am broken
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "etcd"},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
fw := FlushWriter{writer: w}
|
fw := FlushWriter{writer: w}
|
||||||
if flusher, ok := w.(http.Flusher); ok {
|
if flusher, ok := w.(http.Flusher); ok {
|
||||||
@ -216,10 +234,17 @@ func (s *Server) handlePodInfo(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: backwards compatibility with existing API, needs API change
|
// TODO: backwards compatibility with existing API, needs API change
|
||||||
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
|
podFullName := GetPodFullName(&api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: podID,
|
||||||
|
// TODO: I am broken
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "etcd"},
|
||||||
|
},
|
||||||
|
})
|
||||||
info, err := s.host.GetPodInfo(podFullName, podUUID)
|
info, err := s.host.GetPodInfo(podFullName, podUUID)
|
||||||
if err == dockertools.ErrNoContainersInPod {
|
if err == dockertools.ErrNoContainersInPod {
|
||||||
http.Error(w, "Pod does not exist", http.StatusNotFound)
|
http.Error(w, "api.BoundPod does not exist", http.StatusNotFound)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -283,7 +308,14 @@ func (s *Server) handleRun(w http.ResponseWriter, req *http.Request) {
|
|||||||
http.Error(w, "Unexpected path for command running", http.StatusBadRequest)
|
http.Error(w, "Unexpected path for command running", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
podFullName := GetPodFullName(&Pod{Name: podID, Namespace: "etcd"})
|
podFullName := GetPodFullName(&api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: podID,
|
||||||
|
// TODO: I am broken
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "etcd"},
|
||||||
|
},
|
||||||
|
})
|
||||||
command := strings.Split(u.Query().Get("cmd"), " ")
|
command := strings.Split(u.Query().Get("cmd"), " ")
|
||||||
data, err := s.host.RunInContainer(podFullName, uuid, container, command)
|
data, err := s.host.RunInContainer(podFullName, uuid, container, command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -327,10 +359,24 @@ func (s *Server) serveStats(w http.ResponseWriter, req *http.Request) {
|
|||||||
errors.New("pod level status currently unimplemented")
|
errors.New("pod level status currently unimplemented")
|
||||||
case 3:
|
case 3:
|
||||||
// Backward compatibility without uuid information
|
// Backward compatibility without uuid information
|
||||||
podFullName := GetPodFullName(&Pod{Name: components[1], Namespace: "etcd"})
|
podFullName := GetPodFullName(&api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: components[1],
|
||||||
|
// TODO: I am broken
|
||||||
|
Namespace: api.NamespaceDefault,
|
||||||
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "etcd"},
|
||||||
|
},
|
||||||
|
})
|
||||||
stats, err = s.host.GetContainerInfo(podFullName, "", components[2], &query)
|
stats, err = s.host.GetContainerInfo(podFullName, "", components[2], &query)
|
||||||
case 4:
|
case 4:
|
||||||
podFullName := GetPodFullName(&Pod{Name: components[1], Namespace: "etcd"})
|
podFullName := GetPodFullName(&api.BoundPod{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: components[1],
|
||||||
|
// TODO: I am broken
|
||||||
|
Namespace: "",
|
||||||
|
Annotations: map[string]string{ConfigSourceAnnotationKey: "etcd"},
|
||||||
|
},
|
||||||
|
})
|
||||||
stats, err = s.host.GetContainerInfo(podFullName, components[2], components[2], &query)
|
stats, err = s.host.GetContainerInfo(podFullName, components[2], components[2], &query)
|
||||||
default:
|
default:
|
||||||
http.Error(w, "unknown resource.", http.StatusNotFound)
|
http.Error(w, "unknown resource.", http.StatusNotFound)
|
||||||
|
@ -101,7 +101,23 @@ func readResp(resp *http.Response) (string, error) {
|
|||||||
func TestContainer(t *testing.T) {
|
func TestContainer(t *testing.T) {
|
||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
expected := []api.ContainerManifest{
|
expected := []api.ContainerManifest{
|
||||||
{ID: "test_manifest"},
|
{
|
||||||
|
ID: "test_manifest",
|
||||||
|
UUID: "value",
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicy{
|
||||||
|
Never: &api.RestartPolicyNever{},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
body := bytes.NewBuffer([]byte(util.EncodeJSON(expected[0]))) // Only send a single ContainerManifest
|
body := bytes.NewBuffer([]byte(util.EncodeJSON(expected[0]))) // Only send a single ContainerManifest
|
||||||
resp, err := http.Post(fw.testHTTPServer.URL+"/container", "application/json", body)
|
resp, err := http.Post(fw.testHTTPServer.URL+"/container", "application/json", body)
|
||||||
@ -114,7 +130,29 @@ func TestContainer(t *testing.T) {
|
|||||||
if len(received) != 1 {
|
if len(received) != 1 {
|
||||||
t.Errorf("Expected 1 manifest, but got %v", len(received))
|
t.Errorf("Expected 1 manifest, but got %v", len(received))
|
||||||
}
|
}
|
||||||
expectedPods := []Pod{{Name: "1", Manifest: expected[0]}}
|
expectedPods := []api.BoundPod{
|
||||||
|
{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: "test_manifest",
|
||||||
|
UID: "value",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicy{
|
||||||
|
Never: &api.RestartPolicyNever{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
if !reflect.DeepEqual(expectedPods, received[0]) {
|
if !reflect.DeepEqual(expectedPods, received[0]) {
|
||||||
t.Errorf("Expected %#v, but got %#v", expectedPods, received[0])
|
t.Errorf("Expected %#v, but got %#v", expectedPods, received[0])
|
||||||
}
|
}
|
||||||
@ -123,8 +161,38 @@ func TestContainer(t *testing.T) {
|
|||||||
func TestContainers(t *testing.T) {
|
func TestContainers(t *testing.T) {
|
||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
expected := []api.ContainerManifest{
|
expected := []api.ContainerManifest{
|
||||||
{ID: "test_manifest_1"},
|
{
|
||||||
{ID: "test_manifest_2"},
|
ID: "test_manifest_1",
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicy{
|
||||||
|
Never: &api.RestartPolicyNever{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "test_manifest_2",
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "container2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicy{
|
||||||
|
Never: &api.RestartPolicyNever{},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
body := bytes.NewBuffer([]byte(util.EncodeJSON(expected)))
|
body := bytes.NewBuffer([]byte(util.EncodeJSON(expected)))
|
||||||
resp, err := http.Post(fw.testHTTPServer.URL+"/containers", "application/json", body)
|
resp, err := http.Post(fw.testHTTPServer.URL+"/containers", "application/json", body)
|
||||||
@ -137,7 +205,48 @@ func TestContainers(t *testing.T) {
|
|||||||
if len(received) != 1 {
|
if len(received) != 1 {
|
||||||
t.Errorf("Expected 1 update, but got %v", len(received))
|
t.Errorf("Expected 1 update, but got %v", len(received))
|
||||||
}
|
}
|
||||||
expectedPods := []Pod{{Name: "1", Manifest: expected[0]}, {Name: "2", Manifest: expected[1]}}
|
expectedPods := []api.BoundPod{
|
||||||
|
{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: "1",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicy{
|
||||||
|
Never: &api.RestartPolicyNever{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
TypeMeta: api.TypeMeta{
|
||||||
|
ID: "2",
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
Containers: []api.Container{
|
||||||
|
{
|
||||||
|
Name: "container2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: []api.Volume{
|
||||||
|
{
|
||||||
|
Name: "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicy{
|
||||||
|
Never: &api.RestartPolicyNever{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
if !reflect.DeepEqual(expectedPods, received[0]) {
|
if !reflect.DeepEqual(expectedPods, received[0]) {
|
||||||
t.Errorf("Expected %#v, but got %#v", expectedPods, received[0])
|
t.Errorf("Expected %#v, but got %#v", expectedPods, received[0])
|
||||||
}
|
}
|
||||||
@ -149,7 +258,7 @@ func TestPodInfo(t *testing.T) {
|
|||||||
"goodpod": api.ContainerStatus{},
|
"goodpod": api.ContainerStatus{},
|
||||||
}
|
}
|
||||||
fw.fakeKubelet.infoFunc = func(name string) (api.PodInfo, error) {
|
fw.fakeKubelet.infoFunc = func(name string) (api.PodInfo, error) {
|
||||||
if name == "goodpod.etcd" {
|
if name == "goodpod.default.etcd" {
|
||||||
return expected, nil
|
return expected, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("bad pod %s", name)
|
return nil, fmt.Errorf("bad pod %s", name)
|
||||||
@ -175,7 +284,7 @@ func TestContainerInfo(t *testing.T) {
|
|||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
expectedInfo := &info.ContainerInfo{}
|
expectedInfo := &info.ContainerInfo{}
|
||||||
podID := "somepod"
|
podID := "somepod"
|
||||||
expectedPodID := "somepod" + ".etcd"
|
expectedPodID := "somepod" + ".default.etcd"
|
||||||
expectedContainerName := "goodcontainer"
|
expectedContainerName := "goodcontainer"
|
||||||
fw.fakeKubelet.containerInfoFunc = func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
fw.fakeKubelet.containerInfoFunc = func(podID, containerName string, req *info.ContainerInfoRequest) (*info.ContainerInfo, error) {
|
||||||
if podID != expectedPodID || containerName != expectedContainerName {
|
if podID != expectedPodID || containerName != expectedContainerName {
|
||||||
@ -278,7 +387,7 @@ func TestServeRunInContainer(t *testing.T) {
|
|||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
output := "foo bar"
|
output := "foo bar"
|
||||||
podName := "foo"
|
podName := "foo"
|
||||||
expectedPodName := podName + ".etcd"
|
expectedPodName := podName + ".default.etcd"
|
||||||
expectedContainerName := "baz"
|
expectedContainerName := "baz"
|
||||||
expectedCommand := "ls -a"
|
expectedCommand := "ls -a"
|
||||||
fw.fakeKubelet.runFunc = func(podFullName, uuid, containerName string, cmd []string) ([]byte, error) {
|
fw.fakeKubelet.runFunc = func(podFullName, uuid, containerName string, cmd []string) ([]byte, error) {
|
||||||
@ -317,7 +426,7 @@ func TestServeRunInContainerWithUUID(t *testing.T) {
|
|||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
output := "foo bar"
|
output := "foo bar"
|
||||||
podName := "foo"
|
podName := "foo"
|
||||||
expectedPodName := podName + ".etcd"
|
expectedPodName := podName + ".default.etcd"
|
||||||
expectedUuid := "7e00838d_-_3523_-_11e4_-_8421_-_42010af0a720"
|
expectedUuid := "7e00838d_-_3523_-_11e4_-_8421_-_42010af0a720"
|
||||||
expectedContainerName := "baz"
|
expectedContainerName := "baz"
|
||||||
expectedCommand := "ls -a"
|
expectedCommand := "ls -a"
|
||||||
@ -360,7 +469,7 @@ func TestContainerLogs(t *testing.T) {
|
|||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
output := "foo bar"
|
output := "foo bar"
|
||||||
podName := "foo"
|
podName := "foo"
|
||||||
expectedPodName := podName + ".etcd"
|
expectedPodName := podName + ".default.etcd"
|
||||||
expectedContainerName := "baz"
|
expectedContainerName := "baz"
|
||||||
expectedTail := ""
|
expectedTail := ""
|
||||||
expectedFollow := false
|
expectedFollow := false
|
||||||
@ -399,7 +508,7 @@ func TestContainerLogsWithTail(t *testing.T) {
|
|||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
output := "foo bar"
|
output := "foo bar"
|
||||||
podName := "foo"
|
podName := "foo"
|
||||||
expectedPodName := podName + ".etcd"
|
expectedPodName := podName + ".default.etcd"
|
||||||
expectedContainerName := "baz"
|
expectedContainerName := "baz"
|
||||||
expectedTail := "5"
|
expectedTail := "5"
|
||||||
expectedFollow := false
|
expectedFollow := false
|
||||||
@ -438,7 +547,7 @@ func TestContainerLogsWithFollow(t *testing.T) {
|
|||||||
fw := newServerTest()
|
fw := newServerTest()
|
||||||
output := "foo bar"
|
output := "foo bar"
|
||||||
podName := "foo"
|
podName := "foo"
|
||||||
expectedPodName := podName + ".etcd"
|
expectedPodName := podName + ".default.etcd"
|
||||||
expectedContainerName := "baz"
|
expectedContainerName := "baz"
|
||||||
expectedTail := ""
|
expectedTail := ""
|
||||||
expectedFollow := true
|
expectedFollow := true
|
||||||
|
@ -22,13 +22,7 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Pod represents the structure of a pod on the Kubelet, distinct from the apiserver
|
const ConfigSourceAnnotationKey = "kubernetes/config.source"
|
||||||
// representation of a Pod.
|
|
||||||
type Pod struct {
|
|
||||||
Namespace string
|
|
||||||
Name string
|
|
||||||
Manifest api.ContainerManifest
|
|
||||||
}
|
|
||||||
|
|
||||||
// PodOperation defines what changes will be made on a pod configuration.
|
// PodOperation defines what changes will be made on a pod configuration.
|
||||||
type PodOperation int
|
type PodOperation int
|
||||||
@ -48,13 +42,13 @@ const (
|
|||||||
// sending an array of size one and Op == ADD|REMOVE (with REMOVE, only the ID is required).
|
// sending an array of size one and Op == ADD|REMOVE (with REMOVE, only the ID is required).
|
||||||
// For setting the state of the system to a given state for this source configuration, set
|
// For setting the state of the system to a given state for this source configuration, set
|
||||||
// Pods as desired and Op to SET, which will reset the system state to that specified in this
|
// Pods as desired and Op to SET, which will reset the system state to that specified in this
|
||||||
// operation for this source channel. To remove all pods, set Pods to empty array and Op to SET.
|
// operation for this source channel. To remove all pods, set Pods to empty object and Op to SET.
|
||||||
type PodUpdate struct {
|
type PodUpdate struct {
|
||||||
Pods []Pod
|
Pods []api.BoundPod
|
||||||
Op PodOperation
|
Op PodOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPodFullName returns a name that full identifies a pod across all config sources.
|
// GetPodFullName returns a name that uniquely identifies a pod across all config sources.
|
||||||
func GetPodFullName(pod *Pod) string {
|
func GetPodFullName(pod *api.BoundPod) string {
|
||||||
return fmt.Sprintf("%s.%s", pod.Name, pod.Namespace)
|
return fmt.Sprintf("%s.%s.%s", pod.ID, pod.Namespace, pod.Annotations[ConfigSourceAnnotationKey])
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2014 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 (
|
|
||||||
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ValidatePod(pod *Pod) (errors []error) {
|
|
||||||
if !util.IsDNSSubdomain(pod.Name) {
|
|
||||||
errors = append(errors, apierrs.NewFieldInvalid("name", pod.Name))
|
|
||||||
}
|
|
||||||
if errs := validation.ValidateManifest(&pod.Manifest); len(errs) != 0 {
|
|
||||||
errors = append(errors, errs...)
|
|
||||||
}
|
|
||||||
return errors
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2014 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_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
|
||||||
. "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValidatePodNoName(t *testing.T) {
|
|
||||||
errorCases := map[string]Pod{
|
|
||||||
// manifest is tested in api/validation_test.go, ensure it is invoked
|
|
||||||
"empty version": {Name: "test", Manifest: api.ContainerManifest{Version: "", ID: "abc"}},
|
|
||||||
|
|
||||||
// Name
|
|
||||||
"zero-length name": {Name: "", Manifest: api.ContainerManifest{Version: "v1beta1"}},
|
|
||||||
"name > 255 characters": {Name: strings.Repeat("a", 256), Manifest: api.ContainerManifest{Version: "v1beta1"}},
|
|
||||||
"name not a DNS subdomain": {Name: "a.b.c.", Manifest: api.ContainerManifest{Version: "v1beta1"}},
|
|
||||||
"name with underscore": {Name: "a_b_c", Manifest: api.ContainerManifest{Version: "v1beta1"}},
|
|
||||||
}
|
|
||||||
for k, v := range errorCases {
|
|
||||||
if errs := ValidatePod(&v); len(errs) == 0 {
|
|
||||||
t.Errorf("expected failure for %s", k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user