11package server
22
33import (
4+ "errors"
45 "testing"
56
7+ "github.com/rancher/webhook/pkg/health"
68 "github.com/rancher/wrangler/v3/pkg/generic/fake"
79 "github.com/stretchr/testify/assert"
810 "github.com/stretchr/testify/require"
911 "go.uber.org/mock/gomock"
1012 v1 "k8s.io/api/admissionregistration/v1"
11- "k8s.io/apimachinery/pkg/api/errors"
13+ corev1 "k8s.io/api/core/v1"
14+ apierrors "k8s.io/apimachinery/pkg/api/errors"
1215 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1316 "k8s.io/apimachinery/pkg/runtime/schema"
1417)
@@ -23,14 +26,14 @@ func TestSecretHandlerEnsureWebhookConfigurationCreate(t *testing.T) {
2326
2427 ctrl := gomock .NewController (t )
2528 validatingController := fake.NewMockNonNamespacedClientInterface [* v1.ValidatingWebhookConfiguration , * v1.ValidatingWebhookConfigurationList ](ctrl )
26- validatingController .EXPECT ().Get (configName , gomock .Any ()).Return (nil , errors .NewNotFound (schema.GroupResource {Group : v1 .GroupName , Resource : "validatingwebhookconfiguration" }, configName )).Times (1 )
29+ validatingController .EXPECT ().Get (configName , gomock .Any ()).Return (nil , apierrors .NewNotFound (schema.GroupResource {Group : v1 .GroupName , Resource : "validatingwebhookconfiguration" }, configName )).Times (1 )
2730 validatingController .EXPECT ().Create (gomock .Any ()).DoAndReturn (func (obj * v1.ValidatingWebhookConfiguration ) (* v1.ValidatingWebhookConfiguration , error ) {
2831 storedValidatingConfig = obj .DeepCopy ()
2932 return obj , nil
3033 }).Times (1 )
3134
3235 mutatingController := fake.NewMockNonNamespacedClientInterface [* v1.MutatingWebhookConfiguration , * v1.MutatingWebhookConfigurationList ](ctrl )
33- mutatingController .EXPECT ().Get (configName , gomock .Any ()).Return (nil , errors .NewNotFound (schema.GroupResource {Group : v1 .GroupName , Resource : "mutatingwebhookconfiguration" }, configName )).Times (1 )
36+ mutatingController .EXPECT ().Get (configName , gomock .Any ()).Return (nil , apierrors .NewNotFound (schema.GroupResource {Group : v1 .GroupName , Resource : "mutatingwebhookconfiguration" }, configName )).Times (1 )
3437 mutatingController .EXPECT ().Create (gomock .Any ()).DoAndReturn (func (obj * v1.MutatingWebhookConfiguration ) (* v1.MutatingWebhookConfiguration , error ) {
3538 storedMutatingConfig = obj .DeepCopy ()
3639 return obj , nil
@@ -73,3 +76,113 @@ func TestSecretHandlerEnsureWebhookConfigurationCreate(t *testing.T) {
7376 require .Len (t , storedMutatingConfig .Webhooks , 1 )
7477 assert .Equal (t , mutatingConfig .Webhooks [0 ].Name , storedMutatingConfig .Webhooks [0 ].Name )
7578}
79+
80+ // TestSyncLeaderLogic tests the logic within the sync function related to leader election.
81+ func TestSyncLeaderLogic (t * testing.T ) {
82+ t .Parallel ()
83+ configName := "rancher.cattle.io"
84+ secret := & corev1.Secret {
85+ ObjectMeta : metav1.ObjectMeta {
86+ Name : caName ,
87+ Namespace : namespace ,
88+ },
89+ Data : map [string ][]byte {
90+ corev1 .TLSCertKey : []byte ("cert-data" ),
91+ },
92+ }
93+ testErr := errors .New ("test error" )
94+
95+ tests := []struct {
96+ name string
97+ isLeader bool
98+ getMutatingErr error
99+ getValidatingErr error
100+ createMutatingErr error
101+ createValidatingErr error
102+ expectedErr error
103+ expectControllersRun bool
104+ }{
105+ {
106+ name : "follower becomes healthy" ,
107+ isLeader : false ,
108+ expectedErr : nil ,
109+ },
110+ {
111+ name : "leader becomes healthy on create" ,
112+ isLeader : true ,
113+ getMutatingErr : apierrors .NewNotFound (schema.GroupResource {}, "" ),
114+ getValidatingErr : apierrors .NewNotFound (schema.GroupResource {}, "" ),
115+ expectedErr : nil ,
116+ expectControllersRun : true ,
117+ },
118+ {
119+ name : "leader becomes unhealthy on create error" ,
120+ isLeader : true ,
121+ getValidatingErr : apierrors .NewNotFound (schema.GroupResource {}, "" ),
122+ createValidatingErr : testErr ,
123+ expectedErr : testErr ,
124+ expectControllersRun : true ,
125+ },
126+ {
127+ name : "leader becomes healthy on update" ,
128+ isLeader : true ,
129+ expectedErr : nil ,
130+ expectControllersRun : true ,
131+ },
132+ }
133+
134+ for _ , tt := range tests {
135+ tt := tt
136+ t .Run (tt .name , func (t * testing.T ) {
137+ t .Parallel ()
138+ ctrl := gomock .NewController (t )
139+ validatingController := fake.NewMockNonNamespacedClientInterface [* v1.ValidatingWebhookConfiguration , * v1.ValidatingWebhookConfigurationList ](ctrl )
140+ mutatingController := fake.NewMockNonNamespacedClientInterface [* v1.MutatingWebhookConfiguration , * v1.MutatingWebhookConfigurationList ](ctrl )
141+ errChecker := health .NewErrorChecker ("test" )
142+ errChecker .Store (errors .New ("initial error" ))
143+
144+ handler := & secretHandler {
145+ validatingController : validatingController ,
146+ mutatingController : mutatingController ,
147+ errChecker : errChecker ,
148+ }
149+
150+ leaderFlag .Store (tt .isLeader )
151+
152+ if tt .expectControllersRun {
153+ validatingController .EXPECT ().Get (configName , gomock .Any ()).Return (& v1.ValidatingWebhookConfiguration {}, tt .getValidatingErr ).Times (1 )
154+ if apierrors .IsNotFound (tt .getValidatingErr ) {
155+ validatingController .EXPECT ().Create (gomock .Any ()).Return (& v1.ValidatingWebhookConfiguration {}, tt .createValidatingErr ).Times (1 )
156+ } else if tt .getValidatingErr == nil {
157+ validatingController .EXPECT ().Update (gomock .Any ()).Return (& v1.ValidatingWebhookConfiguration {}, nil ).Times (1 )
158+ }
159+
160+ // Only expect calls to the mutating controller if the validating part is expected to succeed.
161+ if (tt .getValidatingErr == nil || apierrors .IsNotFound (tt .getValidatingErr )) && tt .createValidatingErr == nil {
162+ mutatingController .EXPECT ().Get (configName , gomock .Any ()).Return (& v1.MutatingWebhookConfiguration {}, tt .getMutatingErr ).Times (1 )
163+ if apierrors .IsNotFound (tt .getMutatingErr ) {
164+ mutatingController .EXPECT ().Create (gomock .Any ()).Return (& v1.MutatingWebhookConfiguration {}, tt .createMutatingErr ).Times (1 )
165+ } else if tt .getMutatingErr == nil {
166+ mutatingController .EXPECT ().Update (gomock .Any ()).Return (& v1.MutatingWebhookConfiguration {}, nil ).Times (1 )
167+ }
168+ }
169+ }
170+
171+ _ , err := handler .sync ("test-sync" , secret )
172+
173+ // The only error we might get is a transient one from ensureWebhookConfiguration.
174+ if tt .expectedErr != nil {
175+ assert .ErrorContains (t , err , tt .expectedErr .Error (), "expected an error when ensuring webhook config" )
176+ } else {
177+ require .NoError (t , err )
178+ }
179+
180+ healthErr := errChecker .Check (nil )
181+ if tt .expectedErr == nil {
182+ assert .NoError (t , healthErr , "expected pod to be healthy" )
183+ } else {
184+ assert .Error (t , healthErr , "expected pod to be unhealthy" )
185+ }
186+ })
187+ }
188+ }
0 commit comments