123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332 |
- /*
- Copyright 2015 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 service
- import (
- "reflect"
- "testing"
- "k8s.io/kubernetes/pkg/api"
- "k8s.io/kubernetes/pkg/api/testapi"
- "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
- fakecloud "k8s.io/kubernetes/pkg/cloudprovider/providers/fake"
- "k8s.io/kubernetes/pkg/types"
- )
- const region = "us-central"
- func newService(name string, uid types.UID, serviceType api.ServiceType) *api.Service {
- return &api.Service{ObjectMeta: api.ObjectMeta{Name: name, Namespace: "namespace", UID: uid, SelfLink: testapi.Default.SelfLink("services", name)}, Spec: api.ServiceSpec{Type: serviceType}}
- }
- func TestCreateExternalLoadBalancer(t *testing.T) {
- table := []struct {
- service *api.Service
- expectErr bool
- expectCreateAttempt bool
- }{
- {
- service: &api.Service{
- ObjectMeta: api.ObjectMeta{
- Name: "no-external-balancer",
- Namespace: "default",
- },
- Spec: api.ServiceSpec{
- Type: api.ServiceTypeClusterIP,
- },
- },
- expectErr: false,
- expectCreateAttempt: false,
- },
- {
- service: &api.Service{
- ObjectMeta: api.ObjectMeta{
- Name: "udp-service",
- Namespace: "default",
- SelfLink: testapi.Default.SelfLink("services", "udp-service"),
- },
- Spec: api.ServiceSpec{
- Ports: []api.ServicePort{{
- Port: 80,
- Protocol: api.ProtocolUDP,
- }},
- Type: api.ServiceTypeLoadBalancer,
- },
- },
- expectErr: false,
- expectCreateAttempt: true,
- },
- {
- service: &api.Service{
- ObjectMeta: api.ObjectMeta{
- Name: "basic-service1",
- Namespace: "default",
- SelfLink: testapi.Default.SelfLink("services", "basic-service1"),
- },
- Spec: api.ServiceSpec{
- Ports: []api.ServicePort{{
- Port: 80,
- Protocol: api.ProtocolTCP,
- }},
- Type: api.ServiceTypeLoadBalancer,
- },
- },
- expectErr: false,
- expectCreateAttempt: true,
- },
- }
- for _, item := range table {
- cloud := &fakecloud.FakeCloud{}
- cloud.Region = region
- client := &fake.Clientset{}
- controller, _ := New(cloud, client, "test-cluster")
- controller.init()
- cloud.Calls = nil // ignore any cloud calls made in init()
- client.ClearActions() // ignore any client calls made in init()
- err, _ := controller.createLoadBalancerIfNeeded("foo/bar", item.service)
- if !item.expectErr && err != nil {
- t.Errorf("unexpected error: %v", err)
- } else if item.expectErr && err == nil {
- t.Errorf("expected error creating %v, got nil", item.service)
- }
- actions := client.Actions()
- if !item.expectCreateAttempt {
- if len(cloud.Calls) > 0 {
- t.Errorf("unexpected cloud provider calls: %v", cloud.Calls)
- }
- if len(actions) > 0 {
- t.Errorf("unexpected client actions: %v", actions)
- }
- } else {
- var balancer *fakecloud.FakeBalancer
- for k := range cloud.Balancers {
- if balancer == nil {
- b := cloud.Balancers[k]
- balancer = &b
- } else {
- t.Errorf("expected one load balancer to be created, got %v", cloud.Balancers)
- break
- }
- }
- if balancer == nil {
- t.Errorf("expected one load balancer to be created, got none")
- } else if balancer.Name != controller.loadBalancerName(item.service) ||
- balancer.Region != region ||
- balancer.Ports[0].Port != item.service.Spec.Ports[0].Port {
- t.Errorf("created load balancer has incorrect parameters: %v", balancer)
- }
- actionFound := false
- for _, action := range actions {
- if action.GetVerb() == "update" && action.GetResource().Resource == "services" {
- actionFound = true
- }
- }
- if !actionFound {
- t.Errorf("expected updated service to be sent to client, got these actions instead: %v", actions)
- }
- }
- }
- }
- // TODO: Finish converting and update comments
- func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
- hosts := []string{"node0", "node1", "node73"}
- table := []struct {
- services []*api.Service
- expectedUpdateCalls []fakecloud.FakeUpdateBalancerCall
- }{
- {
- // No services present: no calls should be made.
- services: []*api.Service{},
- expectedUpdateCalls: nil,
- },
- {
- // Services do not have external load balancers: no calls should be made.
- services: []*api.Service{
- newService("s0", "111", api.ServiceTypeClusterIP),
- newService("s1", "222", api.ServiceTypeNodePort),
- },
- expectedUpdateCalls: nil,
- },
- {
- // Services does have an external load balancer: one call should be made.
- services: []*api.Service{
- newService("s0", "333", api.ServiceTypeLoadBalancer),
- },
- expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
- {newService("s0", "333", api.ServiceTypeLoadBalancer), hosts},
- },
- },
- {
- // Three services have an external load balancer: three calls.
- services: []*api.Service{
- newService("s0", "444", api.ServiceTypeLoadBalancer),
- newService("s1", "555", api.ServiceTypeLoadBalancer),
- newService("s2", "666", api.ServiceTypeLoadBalancer),
- },
- expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
- {newService("s0", "444", api.ServiceTypeLoadBalancer), hosts},
- {newService("s1", "555", api.ServiceTypeLoadBalancer), hosts},
- {newService("s2", "666", api.ServiceTypeLoadBalancer), hosts},
- },
- },
- {
- // Two services have an external load balancer and two don't: two calls.
- services: []*api.Service{
- newService("s0", "777", api.ServiceTypeNodePort),
- newService("s1", "888", api.ServiceTypeLoadBalancer),
- newService("s3", "999", api.ServiceTypeLoadBalancer),
- newService("s4", "123", api.ServiceTypeClusterIP),
- },
- expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
- {newService("s1", "888", api.ServiceTypeLoadBalancer), hosts},
- {newService("s3", "999", api.ServiceTypeLoadBalancer), hosts},
- },
- },
- {
- // One service has an external load balancer and one is nil: one call.
- services: []*api.Service{
- newService("s0", "234", api.ServiceTypeLoadBalancer),
- nil,
- },
- expectedUpdateCalls: []fakecloud.FakeUpdateBalancerCall{
- {newService("s0", "234", api.ServiceTypeLoadBalancer), hosts},
- },
- },
- }
- for _, item := range table {
- cloud := &fakecloud.FakeCloud{}
- cloud.Region = region
- client := &fake.Clientset{}
- controller, _ := New(cloud, client, "test-cluster2")
- controller.init()
- cloud.Calls = nil // ignore any cloud calls made in init()
- var services []*api.Service
- for _, service := range item.services {
- services = append(services, service)
- }
- if err := controller.updateLoadBalancerHosts(services, hosts); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if !reflect.DeepEqual(item.expectedUpdateCalls, cloud.UpdateCalls) {
- t.Errorf("expected update calls mismatch, expected %+v, got %+v", item.expectedUpdateCalls, cloud.UpdateCalls)
- }
- }
- }
- func TestHostsFromNodeList(t *testing.T) {
- tests := []struct {
- nodes *api.NodeList
- expectedHosts []string
- }{
- {
- nodes: &api.NodeList{},
- expectedHosts: []string{},
- },
- {
- nodes: &api.NodeList{
- Items: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: "foo"},
- Status: api.NodeStatus{Phase: api.NodeRunning},
- },
- {
- ObjectMeta: api.ObjectMeta{Name: "bar"},
- Status: api.NodeStatus{Phase: api.NodeRunning},
- },
- },
- },
- expectedHosts: []string{"foo", "bar"},
- },
- {
- nodes: &api.NodeList{
- Items: []api.Node{
- {
- ObjectMeta: api.ObjectMeta{Name: "foo"},
- Status: api.NodeStatus{Phase: api.NodeRunning},
- },
- {
- ObjectMeta: api.ObjectMeta{Name: "bar"},
- Status: api.NodeStatus{Phase: api.NodeRunning},
- },
- {
- ObjectMeta: api.ObjectMeta{Name: "unschedulable"},
- Spec: api.NodeSpec{Unschedulable: true},
- Status: api.NodeStatus{Phase: api.NodeRunning},
- },
- },
- },
- expectedHosts: []string{"foo", "bar"},
- },
- }
- for _, test := range tests {
- hosts := hostsFromNodeList(test.nodes)
- if !reflect.DeepEqual(hosts, test.expectedHosts) {
- t.Errorf("expected: %v, saw: %v", test.expectedHosts, hosts)
- }
- }
- }
- func TestGetNodeConditionPredicate(t *testing.T) {
- tests := []struct {
- node api.Node
- expectAccept bool
- name string
- }{
- {
- node: api.Node{},
- expectAccept: false,
- name: "empty",
- },
- {
- node: api.Node{
- Status: api.NodeStatus{
- Conditions: []api.NodeCondition{
- {Type: api.NodeReady, Status: api.ConditionTrue},
- },
- },
- },
- expectAccept: true,
- name: "basic",
- },
- {
- node: api.Node{
- Spec: api.NodeSpec{Unschedulable: true},
- Status: api.NodeStatus{
- Conditions: []api.NodeCondition{
- {Type: api.NodeReady, Status: api.ConditionTrue},
- },
- },
- },
- expectAccept: false,
- name: "unschedulable",
- },
- }
- pred := getNodeConditionPredicate()
- for _, test := range tests {
- accept := pred(&test.node)
- if accept != test.expectAccept {
- t.Errorf("Test failed for %s, expected %v, saw %v", test.name, test.expectAccept, accept)
- }
- }
- }
- // TODO(a-robinson): Add tests for update/sync/delete.
|