From ec46e94dc285e4fdc13bf6bbb30839ed95b0de13 Mon Sep 17 00:00:00 2001 From: Deyuan Deng Date: Wed, 8 Oct 2014 19:14:37 -0400 Subject: [PATCH] Create MinionController to sync minions from cloudprovider (pkg cloudprovider/controller). --- pkg/cloudprovider/controller/doc.go | 19 ++ .../controller/minioncontroller.go | 109 +++++++++++ .../controller/minioncontroller_test.go | 183 ++++++++++++++++++ pkg/master/master.go | 58 +++--- pkg/registry/minion/caching_registry.go | 118 ----------- pkg/registry/minion/caching_registry_test.go | 137 ------------- pkg/registry/minion/cloud_registry.go | 88 --------- pkg/registry/minion/cloud_registry_test.go | 99 ---------- pkg/registry/registrytest/minion.go | 1 - pkg/util/util.go | 13 ++ 10 files changed, 350 insertions(+), 475 deletions(-) create mode 100644 pkg/cloudprovider/controller/doc.go create mode 100644 pkg/cloudprovider/controller/minioncontroller.go create mode 100644 pkg/cloudprovider/controller/minioncontroller_test.go delete mode 100644 pkg/registry/minion/caching_registry.go delete mode 100644 pkg/registry/minion/caching_registry_test.go delete mode 100644 pkg/registry/minion/cloud_registry.go delete mode 100644 pkg/registry/minion/cloud_registry_test.go diff --git a/pkg/cloudprovider/controller/doc.go b/pkg/cloudprovider/controller/doc.go new file mode 100644 index 00000000000..64b73b3b216 --- /dev/null +++ b/pkg/cloudprovider/controller/doc.go @@ -0,0 +1,19 @@ +/* +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 controller contains code for syncing cloud instances with +// minion registry +package controller diff --git a/pkg/cloudprovider/controller/minioncontroller.go b/pkg/cloudprovider/controller/minioncontroller.go new file mode 100644 index 00000000000..51dbbf175de --- /dev/null +++ b/pkg/cloudprovider/controller/minioncontroller.go @@ -0,0 +1,109 @@ +/* +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 controller + +import ( + "fmt" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/minion" + "github.com/golang/glog" +) + +type MinionController struct { + cloud cloudprovider.Interface + matchRE string + staticResources *api.NodeResources + registry minion.Registry +} + +// NewMinionController returns a new minion controller to sync instances from cloudprovider. +func NewMinionController(cloud cloudprovider.Interface, matchRE string, staticResources *api.NodeResources, registry minion.Registry) (*MinionController, error) { + return &MinionController{ + cloud: cloud, + matchRE: matchRE, + staticResources: staticResources, + registry: registry, + }, nil +} + +// Sync syncs list of instances from cloudprovider to master etcd registry. +func (s *MinionController) Sync() error { + matches, err := s.cloudMinions() + if err != nil { + return err + } + minions, err := s.registry.ListMinions(nil) + if err != nil { + return err + } + minionMap := make(map[string]*api.Minion) + for _, minion := range minions.Items { + minionMap[minion.ID] = &minion + } + + // Create or delete minions from registry. + for _, match := range matches.Items { + if _, ok := minionMap[match.ID]; !ok { + glog.Infof("Create minion in registry: %s", match.ID) + err = s.registry.CreateMinion(nil, &match) + if err != nil { + return err + } + } + delete(minionMap, match.ID) + } + + for minionID := range minionMap { + glog.Infof("Delete minion from registry: %s", minionID) + err = s.registry.DeleteMinion(nil, minionID) + if err != nil { + return err + } + } + return nil +} + +// cloudMinions constructs and returns api.MinionList from cloudprovider. +func (s *MinionController) cloudMinions() (*api.MinionList, error) { + instances, ok := s.cloud.Instances() + if !ok { + return nil, fmt.Errorf("cloud doesn't support instances") + } + matches, err := instances.List(s.matchRE) + if err != nil { + return nil, err + } + result := &api.MinionList{ + Items: make([]api.Minion, len(matches)), + } + for i := range matches { + result.Items[i].ID = matches[i] + resources, err := instances.GetNodeResources(matches[i]) + if err != nil { + return nil, err + } + if resources == nil { + resources = s.staticResources + } + if resources != nil { + result.Items[i].NodeResources = *resources + } + } + return result, nil +} diff --git a/pkg/cloudprovider/controller/minioncontroller_test.go b/pkg/cloudprovider/controller/minioncontroller_test.go new file mode 100644 index 00000000000..f2b5f96ad29 --- /dev/null +++ b/pkg/cloudprovider/controller/minioncontroller_test.go @@ -0,0 +1,183 @@ +/* +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 controller + +import ( + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" + fake_cloud "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/fake" + etcdregistry "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/etcd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" + "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" + "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" + + etcd "github.com/coreos/go-etcd/etcd" +) + +func NewTestEtcdRegistry(client tools.EtcdClient) *etcdregistry.Registry { + registry := etcdregistry.NewRegistry(tools.EtcdHelper{client, latest.Codec, tools.RuntimeVersionAdapter{latest.ResourceVersioner}}, + &pod.BasicManifestFactory{ + ServiceRegistry: ®istrytest.ServiceRegistry{}, + }) + return registry +} + +func TestSyncCreateMinion(t *testing.T) { + ctx := api.NewContext() + fakeClient := tools.NewFakeEtcdClient(t) + m1 := runtime.EncodeOrDie(latest.Codec, &api.Minion{TypeMeta: api.TypeMeta{ID: "m1"}}) + m2 := runtime.EncodeOrDie(latest.Codec, &api.Minion{TypeMeta: api.TypeMeta{ID: "m2"}}) + fakeClient.Set("/registry/minions/m1", m1, 0) + fakeClient.Set("/registry/minions/m2", m2, 0) + fakeClient.ExpectNotFoundGet("/registry/minions/m3") + fakeClient.Data["/registry/minions"] = tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: &etcd.Node{ + Nodes: []*etcd.Node{ + {Value: m1}, + {Value: m2}, + }, + }, + }, + E: nil, + } + + registry := NewTestEtcdRegistry(fakeClient) + instances := []string{"m1", "m2", "m3"} + fakeCloud := fake_cloud.FakeCloud{ + Machines: instances, + } + minionController, err := NewMinionController(&fakeCloud, ".*", nil, registry) + if err != nil { + t.Errorf("Unexpected error") + } + + minion, err := registry.GetMinion(ctx, "m3") + if minion != nil { + t.Errorf("Unexpected contains") + } + + err = minionController.Sync() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + minion, err = registry.GetMinion(ctx, "m3") + if minion == nil { + t.Errorf("Unexpected !contains") + } +} + +func TestSyncDeleteMinion(t *testing.T) { + ctx := api.NewContext() + fakeClient := tools.NewFakeEtcdClient(t) + m1 := runtime.EncodeOrDie(latest.Codec, &api.Minion{TypeMeta: api.TypeMeta{ID: "m1"}}) + m2 := runtime.EncodeOrDie(latest.Codec, &api.Minion{TypeMeta: api.TypeMeta{ID: "m2"}}) + m3 := runtime.EncodeOrDie(latest.Codec, &api.Minion{TypeMeta: api.TypeMeta{ID: "m3"}}) + fakeClient.Set("/registry/minions/m1", m1, 0) + fakeClient.Set("/registry/minions/m2", m2, 0) + fakeClient.Set("/registry/minions/m3", m3, 0) + fakeClient.Data["/registry/minions"] = tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: &etcd.Node{ + Nodes: []*etcd.Node{ + {Value: m1}, + {Value: m2}, + {Value: m3}, + }, + }, + }, + E: nil, + } + + registry := NewTestEtcdRegistry(fakeClient) + instances := []string{"m1", "m2"} + fakeCloud := fake_cloud.FakeCloud{ + Machines: instances, + } + minionController, err := NewMinionController(&fakeCloud, ".*", nil, registry) + if err != nil { + t.Errorf("Unexpected error") + } + + minion, err := registry.GetMinion(ctx, "m3") + if minion == nil { + t.Errorf("Unexpected !contains") + } + + err = minionController.Sync() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + minion, err = registry.GetMinion(ctx, "m3") + if minion != nil { + t.Errorf("Unexpected contains") + } +} + +func TestSyncMinionRegexp(t *testing.T) { + ctx := api.NewContext() + fakeClient := tools.NewFakeEtcdClient(t) + fakeClient.Data["/registry/minions"] = tools.EtcdResponseWithError{ + R: &etcd.Response{ + Node: &etcd.Node{ + Nodes: []*etcd.Node{}, + }, + }, + E: nil, + } + + registry := NewTestEtcdRegistry(fakeClient) + instances := []string{"m1", "m2", "n1", "n2"} + fakeCloud := fake_cloud.FakeCloud{ + Machines: instances, + } + minionController, err := NewMinionController(&fakeCloud, "m[0-9]+", nil, registry) + if err != nil { + t.Errorf("Unexpected error") + } + + err = minionController.Sync() + if err != nil { + t.Errorf("unexpected error: %v", err) + } + + var minion *api.Minion + fakeClient.ExpectNotFoundGet("/registry/minions/n1") + fakeClient.ExpectNotFoundGet("/registry/minions/n2") + + minion, err = registry.GetMinion(ctx, "m1") + if minion == nil { + t.Errorf("Unexpected !contains") + } + minion, err = registry.GetMinion(ctx, "m2") + if minion == nil { + t.Errorf("Unexpected !contains") + } + minion, err = registry.GetMinion(ctx, "n1") + if minion != nil { + t.Errorf("Unexpected !contains") + } + minion, err = registry.GetMinion(ctx, "n2") + if minion != nil { + t.Errorf("Unexpected !contains") + } +} diff --git a/pkg/master/master.go b/pkg/master/master.go index 56370c759e5..fc97aa4727c 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -27,6 +27,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver" "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" + cloudcontroller "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/binding" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/endpoint" @@ -39,7 +40,6 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" - "github.com/golang/glog" ) @@ -100,56 +100,50 @@ func New(c *Config) *Master { minionRegistry: minionRegistry, client: c.Client, } - m.init(c.Cloud, c.PodInfoGetter) + m.init(c) return m } func makeMinionRegistry(c *Config) minion.Registry { - var minionRegistry minion.Registry - if c.Cloud != nil && len(c.MinionRegexp) > 0 { - var err error - minionRegistry, err = minion.NewCloudRegistry(c.Cloud, c.MinionRegexp, &c.NodeResources) - if err != nil { - glog.Errorf("Failed to initalize cloud minion registry reverting to static registry (%#v)", err) - } + var minionRegistry minion.Registry = etcd.NewRegistry(c.EtcdHelper, nil) + if c.HealthCheckMinions { + minionRegistry = minion.NewHealthyRegistry(minionRegistry, &http.Client{}) } - if minionRegistry == nil { - minionRegistry = etcd.NewRegistry(c.EtcdHelper, nil) + return minionRegistry +} + +// init initializes master. +func (m *Master) init(c *Config) { + podCache := NewPodCache(c.PodInfoGetter, m.podRegistry) + go util.Forever(func() { podCache.UpdateAllContainers() }, time.Second*30) + + if c.Cloud != nil && len(c.MinionRegexp) > 0 { + // TODO: Move minion controller to its own code. + minionController, err := cloudcontroller.NewMinionController(c.Cloud, c.MinionRegexp, &c.NodeResources, m.minionRegistry) + if err != nil { + glog.Errorf("Failed to initalize minion controller (%#v)", err) + } + // TODO: Create a Run() method on controller to invoke Sync(). + go util.OnceAndForever(func() { minionController.Sync() }, c.MinionCacheTTL) + } else { for _, minionID := range c.Minions { - minionRegistry.CreateMinion(nil, &api.Minion{ + m.minionRegistry.CreateMinion(nil, &api.Minion{ TypeMeta: api.TypeMeta{ID: minionID}, NodeResources: c.NodeResources, }) } } - if c.HealthCheckMinions { - minionRegistry = minion.NewHealthyRegistry(minionRegistry, &http.Client{}) - } - if c.MinionCacheTTL > 0 { - cachingMinionRegistry, err := minion.NewCachingRegistry(minionRegistry, c.MinionCacheTTL) - if err != nil { - glog.Errorf("Failed to initialize caching layer, ignoring cache.") - } else { - minionRegistry = cachingMinionRegistry - } - } - return minionRegistry -} - -func (m *Master) init(cloud cloudprovider.Interface, podInfoGetter client.PodInfoGetter) { - podCache := NewPodCache(podInfoGetter, m.podRegistry) - go util.Forever(func() { podCache.UpdateAllContainers() }, time.Second*30) m.storage = map[string]apiserver.RESTStorage{ "pods": pod.NewREST(&pod.RESTConfig{ - CloudProvider: cloud, + CloudProvider: c.Cloud, PodCache: podCache, - PodInfoGetter: podInfoGetter, + PodInfoGetter: c.PodInfoGetter, Registry: m.podRegistry, Minions: m.client, }), "replicationControllers": controller.NewREST(m.controllerRegistry, m.podRegistry), - "services": service.NewREST(m.serviceRegistry, cloud, m.minionRegistry), + "services": service.NewREST(m.serviceRegistry, c.Cloud, m.minionRegistry), "endpoints": endpoint.NewREST(m.endpointRegistry), "minions": minion.NewREST(m.minionRegistry), "events": event.NewREST(m.eventRegistry), diff --git a/pkg/registry/minion/caching_registry.go b/pkg/registry/minion/caching_registry.go deleted file mode 100644 index 382c43092b5..00000000000 --- a/pkg/registry/minion/caching_registry.go +++ /dev/null @@ -1,118 +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 minion - -import ( - "sync" - "sync/atomic" - "time" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" -) - -type Clock interface { - Now() time.Time -} - -type SystemClock struct{} - -func (SystemClock) Now() time.Time { - return time.Now() -} - -type CachingRegistry struct { - delegate Registry - ttl time.Duration - nodes *api.MinionList - lastUpdate int64 - lock sync.RWMutex - clock Clock -} - -func NewCachingRegistry(delegate Registry, ttl time.Duration) (Registry, error) { - list, err := delegate.ListMinions(nil) - if err != nil { - return nil, err - } - return &CachingRegistry{ - delegate: delegate, - ttl: ttl, - nodes: list, - lastUpdate: time.Now().Unix(), - clock: SystemClock{}, - }, nil -} - -func (r *CachingRegistry) GetMinion(ctx api.Context, nodeID string) (*api.Minion, error) { - if r.expired() { - if err := r.refresh(ctx, false); err != nil { - return nil, err - } - } - r.lock.RLock() - defer r.lock.RUnlock() - for _, node := range r.nodes.Items { - if node.ID == nodeID { - return &node, nil - } - } - return nil, ErrDoesNotExist -} - -func (r *CachingRegistry) DeleteMinion(ctx api.Context, nodeID string) error { - if err := r.delegate.DeleteMinion(ctx, nodeID); err != nil { - return err - } - return r.refresh(ctx, true) -} - -func (r *CachingRegistry) CreateMinion(ctx api.Context, minion *api.Minion) error { - if err := r.delegate.CreateMinion(ctx, minion); err != nil { - return err - } - return r.refresh(ctx, true) -} - -func (r *CachingRegistry) ListMinions(ctx api.Context) (*api.MinionList, error) { - if r.expired() { - if err := r.refresh(ctx, false); err != nil { - return r.nodes, err - } - } - return r.nodes, nil -} - -func (r *CachingRegistry) expired() bool { - var unix int64 - atomic.SwapInt64(&unix, r.lastUpdate) - return r.clock.Now().Sub(time.Unix(r.lastUpdate, 0)) > r.ttl -} - -// refresh updates the current store. It double checks expired under lock with the assumption -// of optimistic concurrency with the other functions. -func (r *CachingRegistry) refresh(ctx api.Context, force bool) error { - r.lock.Lock() - defer r.lock.Unlock() - if force || r.expired() { - var err error - r.nodes, err = r.delegate.ListMinions(ctx) - time := r.clock.Now() - atomic.SwapInt64(&r.lastUpdate, time.Unix()) - return err - } - return nil -} diff --git a/pkg/registry/minion/caching_registry_test.go b/pkg/registry/minion/caching_registry_test.go deleted file mode 100644 index f2d16c607c3..00000000000 --- a/pkg/registry/minion/caching_registry_test.go +++ /dev/null @@ -1,137 +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 minion - -import ( - "reflect" - "testing" - "time" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" -) - -type fakeClock struct { - now time.Time -} - -func (f *fakeClock) Now() time.Time { - return f.now -} - -func TestCachingHit(t *testing.T) { - ctx := api.NewContext() - fakeClock := fakeClock{ - now: time.Unix(0, 0), - } - fakeRegistry := registrytest.NewMinionRegistry([]string{"m1", "m2"}, api.NodeResources{}) - expected := registrytest.MakeMinionList([]string{"m1", "m2", "m3"}, api.NodeResources{}) - cache := CachingRegistry{ - delegate: fakeRegistry, - ttl: 1 * time.Second, - clock: &fakeClock, - lastUpdate: fakeClock.Now().Unix(), - nodes: expected, - } - list, err := cache.ListMinions(ctx) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(list, expected) { - t.Errorf("expected: %v, got %v", expected, list) - } -} - -func TestCachingMiss(t *testing.T) { - ctx := api.NewContext() - fakeClock := fakeClock{ - now: time.Unix(0, 0), - } - fakeRegistry := registrytest.NewMinionRegistry([]string{"m1", "m2"}, api.NodeResources{}) - expected := registrytest.MakeMinionList([]string{"m1", "m2", "m3"}, api.NodeResources{}) - cache := CachingRegistry{ - delegate: fakeRegistry, - ttl: 1 * time.Second, - clock: &fakeClock, - lastUpdate: fakeClock.Now().Unix(), - nodes: expected, - } - fakeClock.now = time.Unix(3, 0) - list, err := cache.ListMinions(ctx) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(list, &fakeRegistry.Minions) { - t.Errorf("expected: %v, got %v", fakeRegistry.Minions, list) - } -} - -func TestCachingInsert(t *testing.T) { - ctx := api.NewContext() - fakeClock := fakeClock{ - now: time.Unix(0, 0), - } - fakeRegistry := registrytest.NewMinionRegistry([]string{"m1", "m2"}, api.NodeResources{}) - expected := registrytest.MakeMinionList([]string{"m1", "m2", "m3"}, api.NodeResources{}) - cache := CachingRegistry{ - delegate: fakeRegistry, - ttl: 1 * time.Second, - clock: &fakeClock, - lastUpdate: fakeClock.Now().Unix(), - nodes: expected, - } - err := cache.CreateMinion(ctx, &api.Minion{ - TypeMeta: api.TypeMeta{ID: "foo"}, - }) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - list, err := cache.ListMinions(ctx) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(list, &fakeRegistry.Minions) { - t.Errorf("expected: %v, got %v", fakeRegistry.Minions, list) - } -} - -func TestCachingDelete(t *testing.T) { - ctx := api.NewContext() - fakeClock := fakeClock{ - now: time.Unix(0, 0), - } - fakeRegistry := registrytest.NewMinionRegistry([]string{"m1", "m2"}, api.NodeResources{}) - expected := registrytest.MakeMinionList([]string{"m1", "m2", "m3"}, api.NodeResources{}) - cache := CachingRegistry{ - delegate: fakeRegistry, - ttl: 1 * time.Second, - clock: &fakeClock, - lastUpdate: fakeClock.Now().Unix(), - nodes: expected, - } - err := cache.DeleteMinion(ctx, "m2") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - list, err := cache.ListMinions(ctx) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - if !reflect.DeepEqual(list, &fakeRegistry.Minions) { - t.Errorf("expected: %v, got %v", fakeRegistry.Minions, list) - } -} diff --git a/pkg/registry/minion/cloud_registry.go b/pkg/registry/minion/cloud_registry.go deleted file mode 100644 index d3b8b2f290b..00000000000 --- a/pkg/registry/minion/cloud_registry.go +++ /dev/null @@ -1,88 +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 minion - -import ( - "fmt" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" -) - -type CloudRegistry struct { - cloud cloudprovider.Interface - matchRE string - staticResources *api.NodeResources -} - -func NewCloudRegistry(cloud cloudprovider.Interface, matchRE string, staticResources *api.NodeResources) (*CloudRegistry, error) { - return &CloudRegistry{ - cloud: cloud, - matchRE: matchRE, - staticResources: staticResources, - }, nil -} - -func (r *CloudRegistry) GetMinion(ctx api.Context, nodeID string) (*api.Minion, error) { - instances, err := r.ListMinions(ctx) - - if err != nil { - return nil, err - } - for _, node := range instances.Items { - if node.ID == nodeID { - return &node, nil - } - } - return nil, ErrDoesNotExist -} - -func (r CloudRegistry) DeleteMinion(ctx api.Context, nodeID string) error { - return fmt.Errorf("unsupported") -} - -func (r CloudRegistry) CreateMinion(ctx api.Context, minion *api.Minion) error { - return fmt.Errorf("unsupported") -} - -func (r *CloudRegistry) ListMinions(ctx api.Context) (*api.MinionList, error) { - instances, ok := r.cloud.Instances() - if !ok { - return nil, fmt.Errorf("cloud doesn't support instances") - } - matches, err := instances.List(r.matchRE) - if err != nil { - return nil, err - } - result := &api.MinionList{ - Items: make([]api.Minion, len(matches)), - } - for ix := range matches { - result.Items[ix].ID = matches[ix] - resources, err := instances.GetNodeResources(matches[ix]) - if err != nil { - return nil, err - } - if resources == nil { - resources = r.staticResources - } - if resources != nil { - result.Items[ix].NodeResources = *resources - } - } - return result, err -} diff --git a/pkg/registry/minion/cloud_registry_test.go b/pkg/registry/minion/cloud_registry_test.go deleted file mode 100644 index 5dce9240f85..00000000000 --- a/pkg/registry/minion/cloud_registry_test.go +++ /dev/null @@ -1,99 +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 minion - -import ( - "reflect" - "testing" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" - fake_cloud "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider/fake" - "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest" -) - -func TestCloudList(t *testing.T) { - ctx := api.NewContext() - instances := []string{"m1", "m2"} - fakeCloud := fake_cloud.FakeCloud{ - Machines: instances, - } - registry, err := NewCloudRegistry(&fakeCloud, ".*", nil) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - list, err := registry.ListMinions(ctx) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if !reflect.DeepEqual(list, registrytest.MakeMinionList(instances, api.NodeResources{})) { - t.Errorf("Unexpected inequality: %#v, %#v", list, instances) - } -} - -func TestCloudGet(t *testing.T) { - ctx := api.NewContext() - instances := []string{"m1", "m2"} - fakeCloud := fake_cloud.FakeCloud{ - Machines: instances, - } - registry, err := NewCloudRegistry(&fakeCloud, ".*", nil) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - minion, err := registry.GetMinion(ctx, "m1") - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - if minion == nil { - t.Errorf("Unexpected !contains") - } - - minion, err = registry.GetMinion(ctx, "m100") - if err == nil { - t.Errorf("unexpected non error") - } - - if minion != nil { - t.Errorf("Unexpected contains") - } -} - -func TestCloudListRegexp(t *testing.T) { - ctx := api.NewContext() - instances := []string{"m1", "m2", "n1", "n2"} - fakeCloud := fake_cloud.FakeCloud{ - Machines: instances, - } - registry, err := NewCloudRegistry(&fakeCloud, "m[0-9]+", nil) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - list, err := registry.ListMinions(ctx) - if err != nil { - t.Errorf("unexpected error: %v", err) - } - - expectedList := registrytest.MakeMinionList([]string{"m1", "m2"}, api.NodeResources{}) - if !reflect.DeepEqual(list, expectedList) { - t.Errorf("Unexpected inequality: %#v, %#v", list, expectedList) - } -} diff --git a/pkg/registry/registrytest/minion.go b/pkg/registry/registrytest/minion.go index db1200c0380..158e785b608 100644 --- a/pkg/registry/registrytest/minion.go +++ b/pkg/registry/registrytest/minion.go @@ -76,7 +76,6 @@ func (r *MinionRegistry) DeleteMinion(ctx api.Context, minionID string) error { defer r.Unlock() var newList []api.Minion for _, node := range r.Minions.Items { - if node.ID != minionID { newList = append(newList, api.Minion{TypeMeta: api.TypeMeta{ID: node.ID}}) } diff --git a/pkg/util/util.go b/pkg/util/util.go index 515d5754c30..6edcf4b9474 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -60,6 +60,19 @@ func Forever(f func(), period time.Duration) { } } +// OnceAndForever runs f first then loops forever running f every d. Catches any panics, and keeps going. +func OnceAndForever(f func(), period time.Duration) { + defer HandleCrash() + f() + for { + func() { + defer HandleCrash() + f() + }() + time.Sleep(period) + } +} + // EncodeJSON returns obj marshalled as a JSON string, ignoring any errors. func EncodeJSON(obj interface{}) string { data, _ := json.Marshal(obj)