Skip to content

Commit 977aeab

Browse files
authored
Merge pull request kubernetes#90987 from andrewsykim/service-controller-fixup
service controller: clean up unit tests
2 parents 98d404d + 758c25d commit 977aeab

File tree

5 files changed

+67
-77
lines changed

5 files changed

+67
-77
lines changed

cmd/cloud-controller-manager/app/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func startServiceController(ctx *cloudcontrollerconfig.CompletedConfig, cloud cl
8989
ctx.SharedInformers.Core().V1().Services(),
9090
ctx.SharedInformers.Core().V1().Nodes(),
9191
ctx.ComponentConfig.KubeCloudShared.ClusterName,
92+
utilfeature.DefaultFeatureGate,
9293
)
9394
if err != nil {
9495
// This error shouldn't fail. It lives like this as a legacy.

cmd/kube-controller-manager/app/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ func startServiceController(ctx ControllerContext) (http.Handler, bool, error) {
8383
ctx.InformerFactory.Core().V1().Services(),
8484
ctx.InformerFactory.Core().V1().Nodes(),
8585
ctx.ComponentConfig.KubeCloudShared.ClusterName,
86+
utilfeature.DefaultFeatureGate,
8687
)
8788
if err != nil {
8889
// This error shouldn't fail. It lives like this as a legacy.

pkg/controller/service/BUILD

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ go_library(
1515
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
1616
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
1717
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
18-
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
1918
"//staging/src/k8s.io/client-go/informers/core/v1:go_default_library",
2019
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
2120
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
@@ -26,6 +25,7 @@ go_library(
2625
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
2726
"//staging/src/k8s.io/cloud-provider:go_default_library",
2827
"//staging/src/k8s.io/cloud-provider/service/helpers:go_default_library",
28+
"//staging/src/k8s.io/component-base/featuregate:go_default_library",
2929
"//staging/src/k8s.io/component-base/metrics/prometheus/ratelimiter:go_default_library",
3030
"//vendor/k8s.io/klog:go_default_library",
3131
],
@@ -36,22 +36,23 @@ go_test(
3636
srcs = ["controller_test.go"],
3737
embed = [":go_default_library"],
3838
deps = [
39-
"//pkg/controller:go_default_library",
4039
"//staging/src/k8s.io/api/core/v1:go_default_library",
4140
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
4241
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
4342
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
4443
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
4544
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
4645
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
47-
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
4846
"//staging/src/k8s.io/client-go/informers:go_default_library",
4947
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
48+
"//staging/src/k8s.io/client-go/kubernetes/scheme:go_default_library",
49+
"//staging/src/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
5050
"//staging/src/k8s.io/client-go/testing:go_default_library",
5151
"//staging/src/k8s.io/client-go/tools/record:go_default_library",
52+
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
5253
"//staging/src/k8s.io/cloud-provider/fake:go_default_library",
5354
"//staging/src/k8s.io/cloud-provider/service/helpers:go_default_library",
54-
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
55+
"//vendor/k8s.io/klog:go_default_library",
5556
],
5657
)
5758

pkg/controller/service/controller.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"k8s.io/apimachinery/pkg/util/runtime"
3030
"k8s.io/apimachinery/pkg/util/sets"
3131
"k8s.io/apimachinery/pkg/util/wait"
32-
utilfeature "k8s.io/apiserver/pkg/util/feature"
3332
coreinformers "k8s.io/client-go/informers/core/v1"
3433
clientset "k8s.io/client-go/kubernetes"
3534
"k8s.io/client-go/kubernetes/scheme"
@@ -40,6 +39,7 @@ import (
4039
"k8s.io/client-go/util/workqueue"
4140
cloudprovider "k8s.io/cloud-provider"
4241
servicehelper "k8s.io/cloud-provider/service/helpers"
42+
"k8s.io/component-base/featuregate"
4343
"k8s.io/component-base/metrics/prometheus/ratelimiter"
4444
"k8s.io/klog"
4545
)
@@ -110,6 +110,9 @@ type Controller struct {
110110
nodeListerSynced cache.InformerSynced
111111
// services that need to be synced
112112
queue workqueue.RateLimitingInterface
113+
// feature gates stored in local field for better testability
114+
legacyNodeRoleFeatureEnabled bool
115+
serviceNodeExclusionFeatureEnabled bool
113116
}
114117

115118
// New returns a new service controller to keep cloud provider service resources
@@ -120,6 +123,7 @@ func New(
120123
serviceInformer coreinformers.ServiceInformer,
121124
nodeInformer coreinformers.NodeInformer,
122125
clusterName string,
126+
featureGate featuregate.FeatureGate,
123127
) (*Controller, error) {
124128
broadcaster := record.NewBroadcaster()
125129
broadcaster.StartLogging(klog.Infof)
@@ -133,16 +137,18 @@ func New(
133137
}
134138

135139
s := &Controller{
136-
cloud: cloud,
137-
knownHosts: []*v1.Node{},
138-
kubeClient: kubeClient,
139-
clusterName: clusterName,
140-
cache: &serviceCache{serviceMap: make(map[string]*cachedService)},
141-
eventBroadcaster: broadcaster,
142-
eventRecorder: recorder,
143-
nodeLister: nodeInformer.Lister(),
144-
nodeListerSynced: nodeInformer.Informer().HasSynced,
145-
queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(minRetryDelay, maxRetryDelay), "service"),
140+
cloud: cloud,
141+
knownHosts: []*v1.Node{},
142+
kubeClient: kubeClient,
143+
clusterName: clusterName,
144+
cache: &serviceCache{serviceMap: make(map[string]*cachedService)},
145+
eventBroadcaster: broadcaster,
146+
eventRecorder: recorder,
147+
nodeLister: nodeInformer.Lister(),
148+
nodeListerSynced: nodeInformer.Informer().HasSynced,
149+
queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(minRetryDelay, maxRetryDelay), "service"),
150+
legacyNodeRoleFeatureEnabled: featureGate.Enabled(legacyNodeRoleBehaviorFeature),
151+
serviceNodeExclusionFeatureEnabled: featureGate.Enabled(serviceNodeExclusionFeature),
146152
}
147153

148154
serviceInformer.Informer().AddEventHandlerWithResyncPeriod(
@@ -202,6 +208,7 @@ func New(
202208
if err := s.init(); err != nil {
203209
return nil, err
204210
}
211+
205212
return s, nil
206213
}
207214

@@ -397,7 +404,7 @@ func (s *Controller) syncLoadBalancerIfNeeded(service *v1.Service, key string) (
397404
}
398405

399406
func (s *Controller) ensureLoadBalancer(service *v1.Service) (*v1.LoadBalancerStatus, error) {
400-
nodes, err := listWithPredicate(s.nodeLister, getNodeConditionPredicate())
407+
nodes, err := listWithPredicate(s.nodeLister, s.getNodeConditionPredicate())
401408
if err != nil {
402409
return nil, err
403410
}
@@ -626,22 +633,22 @@ func nodeSlicesEqualForLB(x, y []*v1.Node) bool {
626633
return nodeNames(x).Equal(nodeNames(y))
627634
}
628635

629-
func getNodeConditionPredicate() NodeConditionPredicate {
636+
func (s *Controller) getNodeConditionPredicate() NodeConditionPredicate {
630637
return func(node *v1.Node) bool {
631638
// We add the master to the node list, but its unschedulable. So we use this to filter
632639
// the master.
633640
if node.Spec.Unschedulable {
634641
return false
635642
}
636643

637-
if utilfeature.DefaultFeatureGate.Enabled(legacyNodeRoleBehaviorFeature) {
644+
if s.legacyNodeRoleFeatureEnabled {
638645
// As of 1.6, we will taint the master, but not necessarily mark it unschedulable.
639646
// Recognize nodes labeled as master, and filter them also, as we were doing previously.
640647
if _, hasMasterRoleLabel := node.Labels[labelNodeRoleMaster]; hasMasterRoleLabel {
641648
return false
642649
}
643650
}
644-
if utilfeature.DefaultFeatureGate.Enabled(serviceNodeExclusionFeature) {
651+
if s.serviceNodeExclusionFeatureEnabled {
645652
if _, hasExcludeBalancerLabel := node.Labels[labelNodeRoleExcludeBalancer]; hasExcludeBalancerLabel {
646653
return false
647654
}
@@ -692,7 +699,7 @@ func nodeReadyConditionStatus(node *v1.Node) v1.ConditionStatus {
692699
func (s *Controller) nodeSyncLoop() {
693700
s.knownHostsLock.Lock()
694701
defer s.knownHostsLock.Unlock()
695-
newHosts, err := listWithPredicate(s.nodeLister, getNodeConditionPredicate())
702+
newHosts, err := listWithPredicate(s.nodeLister, s.getNodeConditionPredicate())
696703
if err != nil {
697704
runtime.HandleError(fmt.Errorf("Failed to retrieve current set of nodes from node lister: %v", err))
698705
return

pkg/controller/service/controller_test.go

Lines changed: 37 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ import (
3333
"k8s.io/apimachinery/pkg/runtime/schema"
3434
"k8s.io/apimachinery/pkg/types"
3535
"k8s.io/apimachinery/pkg/util/intstr"
36-
utilfeature "k8s.io/apiserver/pkg/util/feature"
3736
"k8s.io/client-go/informers"
3837
"k8s.io/client-go/kubernetes/fake"
38+
"k8s.io/client-go/kubernetes/scheme"
39+
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
3940
core "k8s.io/client-go/testing"
4041
"k8s.io/client-go/tools/record"
42+
"k8s.io/client-go/util/workqueue"
4143
fakecloud "k8s.io/cloud-provider/fake"
4244
servicehelper "k8s.io/cloud-provider/service/helpers"
43-
featuregatetesting "k8s.io/component-base/featuregate/testing"
44-
45-
"k8s.io/kubernetes/pkg/controller"
45+
"k8s.io/klog"
4646
)
4747

4848
const region = "us-central"
@@ -72,21 +72,43 @@ func newController() (*Controller, *fakecloud.Cloud, *fake.Clientset) {
7272
cloud := &fakecloud.Cloud{}
7373
cloud.Region = region
7474

75-
client := fake.NewSimpleClientset()
75+
kubeClient := fake.NewSimpleClientset()
7676

77-
informerFactory := informers.NewSharedInformerFactory(client, controller.NoResyncPeriodFunc())
77+
informerFactory := informers.NewSharedInformerFactory(kubeClient, 0)
7878
serviceInformer := informerFactory.Core().V1().Services()
7979
nodeInformer := informerFactory.Core().V1().Nodes()
8080

81-
controller, _ := New(cloud, client, serviceInformer, nodeInformer, "test-cluster")
81+
broadcaster := record.NewBroadcaster()
82+
broadcaster.StartLogging(klog.Infof)
83+
broadcaster.StartRecordingToSink(&v1core.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")})
84+
recorder := broadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "service-controller"})
85+
86+
controller := &Controller{
87+
cloud: cloud,
88+
knownHosts: []*v1.Node{},
89+
kubeClient: kubeClient,
90+
clusterName: "test-cluster",
91+
cache: &serviceCache{serviceMap: make(map[string]*cachedService)},
92+
eventBroadcaster: broadcaster,
93+
eventRecorder: recorder,
94+
nodeLister: nodeInformer.Lister(),
95+
nodeListerSynced: nodeInformer.Informer().HasSynced,
96+
queue: workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(minRetryDelay, maxRetryDelay), "service"),
97+
}
98+
99+
balancer, _ := cloud.LoadBalancer()
100+
controller.balancer = balancer
101+
102+
controller.serviceLister = serviceInformer.Lister()
103+
82104
controller.nodeListerSynced = alwaysReady
83105
controller.serviceListerSynced = alwaysReady
84106
controller.eventRecorder = record.NewFakeRecorder(100)
85107

86-
cloud.Calls = nil // ignore any cloud calls made in init()
87-
client.ClearActions() // ignore any client calls made in init()
108+
cloud.Calls = nil // ignore any cloud calls made in init()
109+
kubeClient.ClearActions() // ignore any client calls made in init()
88110

89-
return controller, cloud, client
111+
return controller, cloud, kubeClient
90112
}
91113

92114
// TODO(@MrHohn): Verify the end state when below issue is resolved:
@@ -477,50 +499,6 @@ func TestUpdateNodesInExternalLoadBalancer(t *testing.T) {
477499
}
478500
}
479501

480-
func TestGetNodeConditionPredicate(t *testing.T) {
481-
tests := []struct {
482-
node v1.Node
483-
expectAccept bool
484-
name string
485-
}{
486-
{
487-
node: v1.Node{},
488-
expectAccept: false,
489-
name: "empty",
490-
},
491-
{
492-
node: v1.Node{
493-
Status: v1.NodeStatus{
494-
Conditions: []v1.NodeCondition{
495-
{Type: v1.NodeReady, Status: v1.ConditionTrue},
496-
},
497-
},
498-
},
499-
expectAccept: true,
500-
name: "basic",
501-
},
502-
{
503-
node: v1.Node{
504-
Spec: v1.NodeSpec{Unschedulable: true},
505-
Status: v1.NodeStatus{
506-
Conditions: []v1.NodeCondition{
507-
{Type: v1.NodeReady, Status: v1.ConditionTrue},
508-
},
509-
},
510-
},
511-
expectAccept: false,
512-
name: "unschedulable",
513-
},
514-
}
515-
pred := getNodeConditionPredicate()
516-
for _, test := range tests {
517-
accept := pred(&test.node)
518-
if accept != test.expectAccept {
519-
t.Errorf("Test failed for %s, expected %v, saw %v", test.name, test.expectAccept, accept)
520-
}
521-
}
522-
}
523-
524502
func TestProcessServiceCreateOrUpdate(t *testing.T) {
525503
controller, _, client := newController()
526504

@@ -1413,10 +1391,12 @@ func Test_getNodeConditionPredicate(t *testing.T) {
14131391
}
14141392
for _, tt := range tests {
14151393
t.Run(tt.name, func(t *testing.T) {
1416-
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, serviceNodeExclusionFeature, tt.enableExclusion)()
1417-
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, legacyNodeRoleBehaviorFeature, tt.enableLegacy)()
1394+
c := &Controller{
1395+
legacyNodeRoleFeatureEnabled: tt.enableLegacy,
1396+
serviceNodeExclusionFeatureEnabled: tt.enableExclusion,
1397+
}
14181398

1419-
if result := getNodeConditionPredicate()(tt.input); result != tt.want {
1399+
if result := c.getNodeConditionPredicate()(tt.input); result != tt.want {
14201400
t.Errorf("getNodeConditionPredicate() = %v, want %v", result, tt.want)
14211401
}
14221402
})

0 commit comments

Comments
 (0)