From a77dd5dfd03345b77a2706fc17f928e90b7b0e73 Mon Sep 17 00:00:00 2001 From: chentao1596 Date: Fri, 20 Jan 2017 16:38:30 +0800 Subject: [PATCH 1/4] remove redudant alias --- core/pkg/ingress/status/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pkg/ingress/status/status.go b/core/pkg/ingress/status/status.go index 9d8bc6b5b..b1618bf58 100644 --- a/core/pkg/ingress/status/status.go +++ b/core/pkg/ingress/status/status.go @@ -33,7 +33,7 @@ import ( cache_store "k8s.io/ingress/core/pkg/cache" "k8s.io/ingress/core/pkg/k8s" - strings "k8s.io/ingress/core/pkg/strings" + "k8s.io/ingress/core/pkg/strings" "k8s.io/ingress/core/pkg/task" ) From a72e94f29707fcdffdab940038e904f5c67ef23a Mon Sep 17 00:00:00 2001 From: chentao1596 Date: Fri, 10 Feb 2017 10:51:11 +0800 Subject: [PATCH 2/4] modify to get the right content when updating ingress --- core/pkg/ingress/status/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pkg/ingress/status/status.go b/core/pkg/ingress/status/status.go index b1618bf58..09ddb702d 100644 --- a/core/pkg/ingress/status/status.go +++ b/core/pkg/ingress/status/status.go @@ -251,7 +251,7 @@ func (s *statusSync) updateStatus(newIPs []api.LoadBalancerIngress) { return } - curIPs := ing.Status.LoadBalancer.Ingress + curIPs := currIng.Status.LoadBalancer.Ingress sort.Sort(loadBalancerIngressByIP(curIPs)) if ingressSliceEqual(newIPs, curIPs) { glog.V(3).Infof("skipping update of Ingress %v/%v (there is no change)", currIng.Namespace, currIng.Name) From f33a925e81edc2b2873f5b976bb6e7fe17964b96 Mon Sep 17 00:00:00 2001 From: chentao1596 Date: Fri, 10 Feb 2017 16:03:12 +0800 Subject: [PATCH 3/4] add unit test cases for core.pkg.ingress.status.status --- core/pkg/ingress/status/status_test.go | 487 +++++++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 core/pkg/ingress/status/status_test.go diff --git a/core/pkg/ingress/status/status_test.go b/core/pkg/ingress/status/status_test.go new file mode 100644 index 000000000..5f41623c9 --- /dev/null +++ b/core/pkg/ingress/status/status_test.go @@ -0,0 +1,487 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 status + +import ( + "os" + "sort" + "sync" + "testing" + "time" + + cache_store "k8s.io/ingress/core/pkg/cache" + "k8s.io/ingress/core/pkg/k8s" + "k8s.io/ingress/core/pkg/task" + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/extensions" + "k8s.io/kubernetes/pkg/client/cache" + testclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + "k8s.io/kubernetes/pkg/util/sets" +) + +func buildLoadBalancerIngressByIP() loadBalancerIngressByIP { + return []api.LoadBalancerIngress{ + { + IP: "10.0.0.1", + Hostname: "foo1", + }, + { + IP: "10.0.0.2", + Hostname: "foo2", + }, + { + IP: "10.0.0.3", + Hostname: "", + }, + { + IP: "", + Hostname: "foo4", + }, + } +} + +func buildSimpleClientSet() *testclient.Clientset { + return testclient.NewSimpleClientset( + &api.PodList{Items: []api.Pod{ + { + ObjectMeta: api.ObjectMeta{ + Name: "foo1", + Namespace: api.NamespaceDefault, + Labels: map[string]string{ + "lable_sig": "foo_pod", + }, + }, + Spec: api.PodSpec{ + NodeName: "foo_node_2", + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "foo2", + Namespace: api.NamespaceDefault, + Labels: map[string]string{ + "lable_sig": "foo_no", + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "foo3", + Namespace: api.NamespaceSystem, + Labels: map[string]string{ + "lable_sig": "foo_pod", + }, + }, + Spec: api.PodSpec{ + NodeName: "foo_node_2", + }, + }, + }}, + &api.ServiceList{Items: []api.Service{ + { + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Namespace: api.NamespaceDefault, + }, + Status: api.ServiceStatus{ + LoadBalancer: api.LoadBalancerStatus{ + Ingress: buildLoadBalancerIngressByIP(), + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "foo_non_exist", + Namespace: api.NamespaceDefault, + }, + }, + }}, + &api.NodeList{Items: []api.Node{ + { + ObjectMeta: api.ObjectMeta{ + Name: "foo_node_1", + }, + Status: api.NodeStatus{ + Addresses: []api.NodeAddress{ + { + Type: api.NodeLegacyHostIP, + Address: "10.0.0.1", + }, { + Type: api.NodeExternalIP, + Address: "10.0.0.2", + }, + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "foo_node_2", + }, + Status: api.NodeStatus{ + Addresses: []api.NodeAddress{ + { + Type: api.NodeLegacyHostIP, + Address: "11.0.0.1", + }, + { + Type: api.NodeExternalIP, + Address: "11.0.0.2", + }, + }, + }, + }, + }}, + &api.EndpointsList{Items: []api.Endpoints{ + { + ObjectMeta: api.ObjectMeta{ + Name: "ingress-controller-leader", + Namespace: api.NamespaceDefault, + }, + }}}, + &extensions.IngressList{Items: buildExtensionsIngresses()}, + ) +} + +func fakeSynFn(interface{}) error { + return nil +} + +func buildExtensionsIngresses() []extensions.Ingress { + return []extensions.Ingress{ + { + ObjectMeta: api.ObjectMeta{ + Name: "foo_ingress_1", + Namespace: api.NamespaceDefault, + }, + Status: extensions.IngressStatus{ + LoadBalancer: api.LoadBalancerStatus{ + Ingress: []api.LoadBalancerIngress{ + { + IP: "10.0.0.1", + Hostname: "foo1", + }, + }, + }, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "foo_ingress_2", + Namespace: api.NamespaceDefault, + }, + Status: extensions.IngressStatus{ + LoadBalancer: api.LoadBalancerStatus{ + Ingress: []api.LoadBalancerIngress{}, + }, + }, + }, + } +} + +func buildIngressLIstener() cache_store.StoreToIngressLister { + store := cache.NewStore(cache.MetaNamespaceKeyFunc) + ids := sets.NewString("foo_ingress_non_01") + for id := range ids { + store.Add(&extensions.Ingress{ + ObjectMeta: api.ObjectMeta{ + Name: id, + Namespace: api.NamespaceDefault, + }}) + } + store.Add(&extensions.Ingress{ + ObjectMeta: api.ObjectMeta{ + Name: "foo_ingress_1", + Namespace: api.NamespaceDefault, + }, + Status: extensions.IngressStatus{ + LoadBalancer: api.LoadBalancerStatus{ + Ingress: buildLoadBalancerIngressByIP(), + }, + }, + }) + return cache_store.StoreToIngressLister{store} +} + +func buildStatusSync() statusSync { + return statusSync{ + pod: &k8s.PodInfo{ + Name: "foo_base_pod", + Namespace: api.NamespaceDefault, + Labels: map[string]string{ + "lable_sig": "foo_pod", + }, + }, + runLock: &sync.Mutex{}, + syncQueue: task.NewTaskQueue(fakeSynFn), + Config: Config{ + Client: buildSimpleClientSet(), + PublishService: api.NamespaceDefault + "/" + "foo", + IngressLister: buildIngressLIstener(), + }, + } +} + +func TestStatusActions(t *testing.T) { + // make sure election can be created + os.Setenv("POD_NAME", "foo1") + os.Setenv("POD_NAMESPACE", api.NamespaceDefault) + c := Config{ + Client: buildSimpleClientSet(), + PublishService: "", + IngressLister: buildIngressLIstener(), + } + // create object + fkSync := NewStatusSyncer(c) + if fkSync == nil { + t.Fatalf("expected a valid Sync") + } + + fk := fkSync.(statusSync) + + ns := make(chan struct{}) + // start it and wait for the election and syn actions + go fk.Run(ns) + // wait for the election + time.Sleep(100 * time.Millisecond) + // execute sync + fk.sync("just-test") + // PublishService is empty, so the running address is: ["11.0.0.2"] + // after updated, the ingress's ip should only be "11.0.0.2" + newIPs := []api.LoadBalancerIngress{{ + IP: "11.0.0.2", + }} + fooIngress1, err1 := fk.Client.Extensions().Ingresses(api.NamespaceDefault).Get("foo_ingress_1") + if err1 != nil { + t.Fatalf("unexpected error") + } + fooIngress1CurIPs := fooIngress1.Status.LoadBalancer.Ingress + if !ingressSliceEqual(fooIngress1CurIPs, newIPs) { + t.Fatalf("returned %v but expected %v", fooIngress1CurIPs, newIPs) + } + + // execute shutdown + fk.Shutdown() + // ingress should be empty + newIPs2 := []api.LoadBalancerIngress{} + fooIngress2, err2 := fk.Client.Extensions().Ingresses(api.NamespaceDefault).Get("foo_ingress_1") + if err2 != nil { + t.Fatalf("unexpected error") + } + fooIngress2CurIPs := fooIngress2.Status.LoadBalancer.Ingress + if !ingressSliceEqual(fooIngress2CurIPs, newIPs2) { + t.Fatalf("returned %v but expected %v", fooIngress2CurIPs, newIPs2) + } + + // end test + ns <- struct{}{} +} + +func TestCallback(t *testing.T) { + fk := buildStatusSync() + // do nothing + fk.callback("foo_base_pod") +} + +func TestKeyfunc(t *testing.T) { + fk := buildStatusSync() + i := "foo_base_pod" + r, err := fk.keyfunc(i) + + if err != nil { + t.Fatalf("unexpected error") + } + if r != i { + t.Errorf("returned %v but expected %v", r, i) + } +} + +func TestRunningAddresessWithPublishService(t *testing.T) { + fk := buildStatusSync() + + r, _ := fk.runningAddresess() + if r == nil { + t.Fatalf("returned nil but expected valid []string") + } + rl := len(r) + if len(r) != 4 { + t.Errorf("returned %v but expected %v", rl, 4) + } +} + +func TestRunningAddresessWithPods(t *testing.T) { + fk := buildStatusSync() + fk.PublishService = "" + + r, _ := fk.runningAddresess() + if r == nil { + t.Fatalf("returned nil but expected valid []string") + } + rl := len(r) + if len(r) != 1 { + t.Fatalf("returned %v but expected %v", rl, 1) + } + rv := r[0] + if rv != "11.0.0.2" { + t.Errorf("returned %v but expected %v", rv, "11.0.0.2") + } +} + +func TestUpdateStatus(t *testing.T) { + fk := buildStatusSync() + newIPs := buildLoadBalancerIngressByIP() + sort.Sort(loadBalancerIngressByIP(newIPs)) + fk.updateStatus(newIPs) + + fooIngress1, err1 := fk.Client.Extensions().Ingresses(api.NamespaceDefault).Get("foo_ingress_1") + if err1 != nil { + t.Fatalf("unexpected error") + } + fooIngress1CurIPs := fooIngress1.Status.LoadBalancer.Ingress + if !ingressSliceEqual(fooIngress1CurIPs, newIPs) { + t.Fatalf("returned %v but expected %v", fooIngress1CurIPs, newIPs) + } + + fooIngress2, err2 := fk.Client.Extensions().Ingresses(api.NamespaceDefault).Get("foo_ingress_2") + if err2 != nil { + t.Fatalf("unexpected error") + } + fooIngress2CurIPs := fooIngress2.Status.LoadBalancer.Ingress + if !ingressSliceEqual(fooIngress2CurIPs, []api.LoadBalancerIngress{}) { + t.Fatalf("returned %v but expected %v", fooIngress2CurIPs, []api.LoadBalancerIngress{}) + } +} + +func TestSliceToStatus(t *testing.T) { + fkEndpoints := []string{ + "10.0.0.1", + "2001:db8::68", + "opensource-k8s-ingress", + } + + r := sliceToStatus(fkEndpoints) + + if r == nil { + t.Fatalf("returned nil but expected a valid []api.LoadBalancerIngress") + } + rl := len(r) + if rl != 3 { + t.Fatalf("returned %v but expected %v", rl, 3) + } + re1 := r[0] + if re1.Hostname != "opensource-k8s-ingress" { + t.Fatalf("returned %v but expected %v", re1, api.LoadBalancerIngress{Hostname: "opensource-k8s-ingress"}) + } + re2 := r[1] + if re2.IP != "10.0.0.1" { + t.Fatalf("returned %v but expected %v", re2, api.LoadBalancerIngress{IP: "10.0.0.1"}) + } + re3 := r[2] + if re3.IP != "2001:db8::68" { + t.Fatalf("returned %v but expected %v", re3, api.LoadBalancerIngress{IP: "2001:db8::68"}) + } +} + +func TestIngressSliceEqual(t *testing.T) { + fk1 := buildLoadBalancerIngressByIP() + fk2 := append(buildLoadBalancerIngressByIP(), api.LoadBalancerIngress{ + IP: "10.0.0.5", + Hostname: "foo5", + }) + fk3 := buildLoadBalancerIngressByIP() + fk3[0].Hostname = "foo_no_01" + fk4 := buildLoadBalancerIngressByIP() + fk4[2].IP = "11.0.0.3" + + fooTests := []struct { + lhs []api.LoadBalancerIngress + rhs []api.LoadBalancerIngress + er bool + }{ + {fk1, fk1, true}, + {fk2, fk1, false}, + {fk3, fk1, false}, + {fk4, fk1, false}, + {fk1, nil, false}, + {nil, nil, true}, + {[]api.LoadBalancerIngress{}, []api.LoadBalancerIngress{}, true}, + } + + for _, fooTest := range fooTests { + r := ingressSliceEqual(fooTest.lhs, fooTest.rhs) + if r != fooTest.er { + t.Errorf("returned %v but expected %v", r, fooTest.er) + } + } +} + +func TestLoadBalancerIngressByIPLen(t *testing.T) { + fooTests := []struct { + ips loadBalancerIngressByIP + el int + }{ + {[]api.LoadBalancerIngress{}, 0}, + {buildLoadBalancerIngressByIP(), 4}, + {nil, 0}, + } + + for _, fooTest := range fooTests { + r := fooTest.ips.Len() + if r != fooTest.el { + t.Errorf("returned %v but expected %v ", r, fooTest.el) + } + } +} + +func TestLoadBalancerIngressByIPSwap(t *testing.T) { + fooTests := []struct { + ips loadBalancerIngressByIP + i int + j int + }{ + {buildLoadBalancerIngressByIP(), 0, 1}, + {buildLoadBalancerIngressByIP(), 2, 1}, + } + + for _, fooTest := range fooTests { + fooi := fooTest.ips[fooTest.i] + fooj := fooTest.ips[fooTest.j] + fooTest.ips.Swap(fooTest.i, fooTest.j) + if fooi.IP != fooTest.ips[fooTest.j].IP || + fooj.IP != fooTest.ips[fooTest.i].IP { + t.Errorf("failed to swap for loadBalancerIngressByIP") + } + } +} + +func TestLoadBalancerIngressByIPLess(t *testing.T) { + fooTests := []struct { + ips loadBalancerIngressByIP + i int + j int + er bool + }{ + {buildLoadBalancerIngressByIP(), 0, 1, true}, + {buildLoadBalancerIngressByIP(), 2, 1, false}, + } + + for _, fooTest := range fooTests { + r := fooTest.ips.Less(fooTest.i, fooTest.j) + if r != fooTest.er { + t.Errorf("returned %v but expected %v ", r, fooTest.er) + } + } +} From 9aa601f8df2d25ad3f4d1d6a340dc922c93e31f9 Mon Sep 17 00:00:00 2001 From: chentao1596 Date: Fri, 10 Feb 2017 16:08:31 +0800 Subject: [PATCH 4/4] add unit test cases for core.pkg.ingress.status.election --- core/pkg/ingress/status/election_test.go | 130 +++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 core/pkg/ingress/status/election_test.go diff --git a/core/pkg/ingress/status/election_test.go b/core/pkg/ingress/status/election_test.go new file mode 100644 index 000000000..4726aa8af --- /dev/null +++ b/core/pkg/ingress/status/election_test.go @@ -0,0 +1,130 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 status + +import ( + "encoding/json" + "testing" + "time" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + tc "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" + "k8s.io/kubernetes/pkg/client/leaderelection/resourcelock" +) + +func TestGetCurrentLeaderLeaderExist(t *testing.T) { + fkER := resourcelock.LeaderElectionRecord{ + HolderIdentity: "currentLeader", + LeaseDurationSeconds: 30, + AcquireTime: unversioned.Now(), + RenewTime: unversioned.Now(), + LeaderTransitions: 3, + } + leaderInfo, _ := json.Marshal(fkER) + fkEndpoints := api.Endpoints{ + ObjectMeta: api.ObjectMeta{ + Name: "ingress-controller-test", + Namespace: api.NamespaceSystem, + Annotations: map[string]string{ + resourcelock.LeaderElectionRecordAnnotationKey: string(leaderInfo), + }, + }, + } + fk := tc.NewSimpleClientset(&api.EndpointsList{Items: []api.Endpoints{fkEndpoints}}) + identity, endpoints, err := getCurrentLeader("ingress-controller-test", api.NamespaceSystem, fk) + if err != nil { + t.Fatalf("expected identitiy and endpoints but returned error %s", err) + } + + if endpoints == nil { + t.Fatalf("returned nil but expected an endpoints") + } + + if identity != "currentLeader" { + t.Fatalf("returned %v but expected %v", identity, "currentLeader") + } +} + +func TestGetCurrentLeaderLeaderNotExist(t *testing.T) { + fkEndpoints := api.Endpoints{ + ObjectMeta: api.ObjectMeta{ + Name: "ingress-controller-test", + Namespace: api.NamespaceSystem, + Annotations: map[string]string{}, + }, + } + fk := tc.NewSimpleClientset(&api.EndpointsList{Items: []api.Endpoints{fkEndpoints}}) + identity, endpoints, err := getCurrentLeader("ingress-controller-test", api.NamespaceSystem, fk) + if err != nil { + t.Fatalf("unexpeted error: %v", err) + } + + if endpoints == nil { + t.Fatalf("returned nil but expected an endpoints") + } + + if identity != "" { + t.Fatalf("returned %s but expected %s", identity, "") + } +} + +func TestGetCurrentLeaderAnnotationError(t *testing.T) { + fkEndpoints := api.Endpoints{ + ObjectMeta: api.ObjectMeta{ + Name: "ingress-controller-test", + Namespace: api.NamespaceSystem, + Annotations: map[string]string{ + resourcelock.LeaderElectionRecordAnnotationKey: "just-test-error-leader-annotation", + }, + }, + } + fk := tc.NewSimpleClientset(&api.EndpointsList{Items: []api.Endpoints{fkEndpoints}}) + _, _, err := getCurrentLeader("ingress-controller-test", api.NamespaceSystem, fk) + if err == nil { + t.Errorf("expected error") + } +} + +func TestNewElection(t *testing.T) { + fk := tc.NewSimpleClientset(&api.EndpointsList{Items: []api.Endpoints{ + { + ObjectMeta: api.ObjectMeta{ + Name: "ingress-controller-test", + Namespace: api.NamespaceSystem, + }, + }, + { + ObjectMeta: api.ObjectMeta{ + Name: "ingress-controller-test-020", + Namespace: api.NamespaceSystem, + }, + }, + }}) + + ne, err := NewElection("ingress-controller-test", "startLeader", api.NamespaceSystem, 4*time.Second, func(leader string) { + // do nothing + go t.Logf("execute callback fun, leader is: %s", leader) + }, fk) + if err != nil { + t.Fatalf("unexpected error %v", err) + } + + if ne == nil { + t.Fatalf("unexpected nil") + } +}