Skip to content

Commit 56f2fb5

Browse files
antoninbastnqn
andauthored
Fix race condition in pkg/apiserver/certificate unit tests (#6004)
There was some "interference" between TestSelfSignedCertProviderRotate and TestSelfSignedCertProviderRun. The root cause is that the certutil.GenerateSelfSignedCertKey does not support a custom clock implementation and always calls time.Now() to determine the current time. It then adds a year to the current time to set the expiration time of the certificate. This means that when rotateSelfSignedCertificate() is called as part of TestSelfSignedCertProviderRotate, the new certificate is already expired, and rotateSelfSignedCertificate() will be called immediately a second time. By this time however, TestSelfSignedCertProviderRotate has already exited, and we are already running the next test, TestSelfSignedCertProviderRun. This creates a race condition because the next test will overwrite generateSelfSignedCertKey with a mock version, right as it is called by the second call to rotateSelfSignedCertificate() from the previous test's provider. To avoid this race condition, we make generateSelfSignedCertKey a member of selfSignedCertProvider. Fixes #5977 Signed-off-by: Antonin Bas <[email protected]> Co-authored-by: Quan Tian <[email protected]>
1 parent 5789889 commit 56f2fb5

File tree

2 files changed

+44
-29
lines changed

2 files changed

+44
-29
lines changed

pkg/apiserver/certificate/selfsignedcert_provider.go

+35-12
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ import (
4444
"antrea.io/antrea/pkg/util/env"
4545
)
4646

47-
var (
48-
loopbackAddresses = []net.IP{net.ParseIP("127.0.0.1"), net.IPv6loopback}
49-
// Declared for unit testing.
50-
generateSelfSignedCertKey = certutil.GenerateSelfSignedCertKey
51-
)
47+
var loopbackAddresses = []net.IP{net.ParseIP("127.0.0.1"), net.IPv6loopback}
48+
49+
// generateSelfSignedCertKeyFn represents a function which can create a self-signed certificate and
50+
// key for the given host.
51+
type generateSelfSignedCertKeyFn func(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error)
5252

5353
type selfSignedCertProvider struct {
5454
client kubernetes.Interface
@@ -69,23 +69,46 @@ type selfSignedCertProvider struct {
6969
cert []byte
7070
key []byte
7171
verifyOptions *x509.VerifyOptions
72+
73+
// generateSelfSignedCertKey is the function used to generate self-signed certificates and keys.
74+
// We use a struct member for unit testing.
75+
generateSelfSignedCertKey generateSelfSignedCertKeyFn
7276
}
7377

7478
var _ dynamiccertificates.CAContentProvider = &selfSignedCertProvider{}
7579
var _ dynamiccertificates.ControllerRunner = &selfSignedCertProvider{}
7680

77-
func newSelfSignedCertProvider(client kubernetes.Interface, secureServing *options.SecureServingOptionsWithLoopback, caConfig *CAConfig) (*selfSignedCertProvider, error) {
81+
type providerOption func(p *selfSignedCertProvider)
82+
83+
func withGenerateSelfSignedCertKeyFn(fn generateSelfSignedCertKeyFn) providerOption {
84+
return func(p *selfSignedCertProvider) {
85+
p.generateSelfSignedCertKey = fn
86+
}
87+
}
88+
89+
func withClock(clock clockutils.Clock) providerOption {
90+
return func(p *selfSignedCertProvider) {
91+
p.clock = clock
92+
}
93+
}
94+
95+
func newSelfSignedCertProvider(client kubernetes.Interface, secureServing *options.SecureServingOptionsWithLoopback, caConfig *CAConfig, options ...providerOption) (*selfSignedCertProvider, error) {
7896
// Set the CertKey and CertDirectory to generate the certificate files.
7997
secureServing.ServerCert.CertDirectory = caConfig.SelfSignedCertDir
8098
secureServing.ServerCert.CertKey.CertFile = filepath.Join(caConfig.SelfSignedCertDir, caConfig.PairName+".crt")
8199
secureServing.ServerCert.CertKey.KeyFile = filepath.Join(caConfig.SelfSignedCertDir, caConfig.PairName+".key")
82100

83101
provider := &selfSignedCertProvider{
84-
client: client,
85-
secureServing: secureServing,
86-
caConfig: caConfig,
87-
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "selfSignedCertProvider"),
88-
clock: clockutils.RealClock{},
102+
client: client,
103+
secureServing: secureServing,
104+
caConfig: caConfig,
105+
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "selfSignedCertProvider"),
106+
clock: clockutils.RealClock{},
107+
generateSelfSignedCertKey: certutil.GenerateSelfSignedCertKey,
108+
}
109+
110+
for _, option := range options {
111+
option(provider)
89112
}
90113

91114
if caConfig.TLSSecretName != "" {
@@ -233,7 +256,7 @@ func (p *selfSignedCertProvider) rotateSelfSignedCertificate() error {
233256
}
234257
if p.shouldRotateCertificate(cert) {
235258
klog.InfoS("Generating self-signed cert")
236-
if cert, key, err = generateSelfSignedCertKey(p.caConfig.ServiceName, loopbackAddresses, GetAntreaServerNames(p.caConfig.ServiceName)); err != nil {
259+
if cert, key, err = p.generateSelfSignedCertKey(p.caConfig.ServiceName, loopbackAddresses, GetAntreaServerNames(p.caConfig.ServiceName)); err != nil {
237260
return fmt.Errorf("unable to generate self-signed cert: %v", err)
238261
}
239262
// If Secret is specified, we should save the new certificate and key to it.

pkg/apiserver/certificate/selfsignedcert_provider_test.go

+9-17
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ var (
4848
testOneYearCert3, testOneYearKey3, _ = certutil.GenerateSelfSignedCertKeyWithFixtures("localhost", loopbackAddresses, nil, "")
4949
)
5050

51-
func newTestSelfSignedCertProvider(t *testing.T, client *fakeclientset.Clientset, tlsSecretName string, minValidDuration time.Duration) *selfSignedCertProvider {
51+
func newTestSelfSignedCertProvider(t *testing.T, client *fakeclientset.Clientset, tlsSecretName string, minValidDuration time.Duration, options ...providerOption) *selfSignedCertProvider {
5252
secureServing := genericoptions.NewSecureServingOptions().WithLoopback()
5353
caConfig := &CAConfig{
5454
TLSSecretName: tlsSecretName,
@@ -57,7 +57,7 @@ func newTestSelfSignedCertProvider(t *testing.T, client *fakeclientset.Clientset
5757
ServiceName: testServiceName,
5858
PairName: testPairName,
5959
}
60-
p, err := newSelfSignedCertProvider(client, secureServing, caConfig)
60+
p, err := newSelfSignedCertProvider(client, secureServing, caConfig, options...)
6161
require.NoError(t, err)
6262
return p
6363
}
@@ -107,8 +107,7 @@ func TestSelfSignedCertProviderRotate(t *testing.T) {
107107
defer cancel()
108108
client := fakeclientset.NewSimpleClientset()
109109
fakeClock := clocktesting.NewFakeClock(time.Now())
110-
p := newTestSelfSignedCertProvider(t, client, testSecretName, time.Hour*24*90)
111-
p.clock = fakeClock
110+
p := newTestSelfSignedCertProvider(t, client, testSecretName, time.Hour*24*90, withClock(fakeClock))
112111
certInFile, err := os.ReadFile(p.secureServing.ServerCert.CertKey.CertFile)
113112
require.NoError(t, err)
114113
keyInFile, _ := os.ReadFile(p.secureServing.ServerCert.CertKey.KeyFile)
@@ -161,7 +160,7 @@ func TestSelfSignedCertProviderRotate(t *testing.T) {
161160
assert.NotEqual(c, map[string][]byte{
162161
corev1.TLSCertKey: testOneYearCert,
163162
corev1.TLSPrivateKeyKey: testOneYearKey,
164-
}, gotSecret.Data, "Secret doesn't match")
163+
}, gotSecret.Data, "Secret should not match")
165164
}, 2*time.Second, 50*time.Millisecond)
166165
}
167166

@@ -264,15 +263,18 @@ func TestSelfSignedCertProviderRun(t *testing.T) {
264263
}
265264
for _, tt := range tests {
266265
t.Run(tt.name, func(t *testing.T) {
267-
defer mockGenerateSelfSignedCertKey(testOneYearCert2, testOneYearKey2)()
268266
ctx, cancel := context.WithCancel(context.Background())
269267
defer cancel()
270268
var objs []runtime.Object
271269
if tt.existingSecret != nil {
272270
objs = append(objs, tt.existingSecret)
273271
}
274272
client := fakeclientset.NewSimpleClientset(objs...)
275-
p := newTestSelfSignedCertProvider(t, client, tt.tlsSecretName, tt.minValidDuration)
273+
// mock the generateSelfSignedCertKey fuction
274+
generateSelfSignedCertKey := func(_ string, _ []net.IP, _ []string) ([]byte, []byte, error) {
275+
return testOneYearCert2, testOneYearKey2, nil
276+
}
277+
p := newTestSelfSignedCertProvider(t, client, tt.tlsSecretName, tt.minValidDuration, withGenerateSelfSignedCertKeyFn(generateSelfSignedCertKey))
276278
go p.Run(ctx, 1)
277279
if tt.updatedSecret != nil {
278280
client.CoreV1().Secrets(tt.updatedSecret.Namespace).Update(ctx, tt.updatedSecret, metav1.UpdateOptions{})
@@ -291,13 +293,3 @@ func TestSelfSignedCertProviderRun(t *testing.T) {
291293
})
292294
}
293295
}
294-
295-
func mockGenerateSelfSignedCertKey(cert, key []byte) func() {
296-
originalFn := generateSelfSignedCertKey
297-
generateSelfSignedCertKey = func(_ string, _ []net.IP, _ []string) ([]byte, []byte, error) {
298-
return cert, key, nil
299-
}
300-
return func() {
301-
generateSelfSignedCertKey = originalFn
302-
}
303-
}

0 commit comments

Comments
 (0)