Skip to content

Commit a3e78f3

Browse files
committed
certcontroller: update unit tests
1 parent 11f0dc6 commit a3e78f3

File tree

2 files changed

+95
-20
lines changed

2 files changed

+95
-20
lines changed

pkg/controller/certrotation/certrotation_controller.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -220,23 +220,6 @@ func (c *CertRotationController) Run(ctx context.Context, workers int) {
220220
<-ctx.Done()
221221
}
222222

223-
// This should not be directly called; it is only to be used for unit tests.
224-
func (c *CertRotationController) Sync() error {
225-
syncCtx := factory.NewSyncContext("mco-cert-rotation-sync", c.recorder)
226-
227-
if err := c.syncHostnames(); err != nil {
228-
return err
229-
}
230-
231-
for _, certRotator := range c.certRotators {
232-
if err := certRotator.Sync(context.TODO(), syncCtx); err != nil {
233-
return err
234-
}
235-
}
236-
return nil
237-
238-
}
239-
240223
func getServerIPsFromInfra(cfg *configv1.Infrastructure) []string {
241224
if cfg.Status.PlatformStatus == nil {
242225
return []string{}

pkg/controller/certrotation/certrotation_controller_test.go

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"github.com/stretchr/testify/require"
1313

1414
configv1 "github.com/openshift/api/config/v1"
15+
configinformers "github.com/openshift/client-go/config/informers/externalversions"
16+
"github.com/openshift/library-go/pkg/controller/factory"
1517
"github.com/openshift/library-go/pkg/operator/certrotation"
1618
corev1 "k8s.io/api/core/v1"
1719
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -43,12 +45,14 @@ type fixture struct {
4345
maoSecretLister []*corev1.Secret
4446
mcoSecretLister []*corev1.Secret
4547
mcoConfigMapLister []*corev1.ConfigMap
48+
infraLister []*configv1.Infrastructure
4649

4750
objects []runtime.Object
4851
configObjects []runtime.Object
4952
machineObjects []runtime.Object
5053
aroObjects []runtime.Object
5154
k8sI kubeinformers.SharedInformerFactory
55+
infraInformer configinformers.SharedInformerFactory
5256

5357
controller *CertRotationController
5458
}
@@ -80,14 +84,15 @@ func (f *fixture) newController() *CertRotationController {
8084
Status: configv1.InfrastructureStatus{
8185
ControlPlaneTopology: configv1.HighlyAvailableTopologyMode,
8286
PlatformStatus: platformStatus,
83-
APIServerInternalURL: "test-url"},
87+
APIServerInternalURL: "https://10.0.0.1:6443"},
8488
})
8589

8690
f.kubeClient = fake.NewSimpleClientset(f.objects...)
8791
f.configClient = fakeconfigv1client.NewSimpleClientset(f.configObjects...)
8892
f.machineClient = fakemachineclientset.NewSimpleClientset(f.machineObjects...)
8993
f.aroClient = fakearoclientset.NewSimpleClientset(f.aroObjects...)
9094
f.k8sI = kubeinformers.NewSharedInformerFactory(f.kubeClient, noResyncPeriodFunc())
95+
f.infraInformer = configinformers.NewSharedInformerFactory(f.configClient, noResyncPeriodFunc())
9196

9297
for _, secret := range f.maoSecretLister {
9398
f.k8sI.Core().V1().Secrets().Informer().GetIndexer().Add(secret)
@@ -101,7 +106,12 @@ func (f *fixture) newController() *CertRotationController {
101106
f.k8sI.Core().V1().ConfigMaps().Informer().GetIndexer().Add(configMap)
102107
}
103108

104-
c, err := New(f.kubeClient, f.configClient, f.machineClient, f.aroClient, f.k8sI.Core().V1().Secrets(), f.k8sI.Core().V1().Secrets(), f.k8sI.Core().V1().ConfigMaps())
109+
for _, infra := range f.configObjects {
110+
f.infraInformer.Config().V1().Infrastructures().Informer().GetIndexer().Add(infra)
111+
f.infraLister = append(f.infraLister, infra.(*configv1.Infrastructure))
112+
}
113+
114+
c, err := New(f.kubeClient, f.configClient, f.machineClient, f.aroClient, f.k8sI.Core().V1().Secrets(), f.k8sI.Core().V1().Secrets(), f.k8sI.Core().V1().ConfigMaps(), f.infraInformer.Config().V1().Infrastructures())
105115
require.NoError(f.t, err)
106116

107117
c.StartInformers()
@@ -110,9 +120,25 @@ func (f *fixture) newController() *CertRotationController {
110120
return c
111121
}
112122

123+
func (f *fixture) sync() error {
124+
syncCtx := factory.NewSyncContext("mco-cert-rotation-sync", f.controller.recorder)
125+
126+
if err := f.controller.syncHostnames(); err != nil {
127+
return err
128+
}
129+
130+
for _, certRotator := range f.controller.certRotators {
131+
if err := certRotator.Sync(context.TODO(), syncCtx); err != nil {
132+
return err
133+
}
134+
}
135+
return nil
136+
137+
}
138+
113139
func (f *fixture) runController() {
114140

115-
err := f.controller.Sync()
141+
err := f.sync()
116142
require.NoError(f.t, err)
117143

118144
f.controller.reconcileUserDataSecrets()
@@ -164,6 +190,72 @@ func (f *fixture) verifyAROIPInTLSCertificate(t *testing.T, expectedIP string) {
164190
t.Logf("Successfully verified ARO IP %s is present in TLS certificate", expectedIP)
165191
}
166192

193+
func TestInfraUpdateTriggersCertResync(t *testing.T) {
194+
f := newFixture(t)
195+
f.objects = append(f.objects, getGoodMAOSecret("test-user-data"))
196+
f.maoSecretLister = append(f.maoSecretLister, getGoodMAOSecret("test-user-data"))
197+
f.machineObjects = append(f.machineObjects, getMachineSet("test-machine"))
198+
199+
f.controller = f.newController()
200+
201+
// Perform initial sync to create initial certificates
202+
f.runController()
203+
204+
// Update the Infrastructure object with a new APIServerInternalURL
205+
infraObj := &configv1.Infrastructure{
206+
ObjectMeta: metav1.ObjectMeta{
207+
Name: "cluster",
208+
},
209+
Status: configv1.InfrastructureStatus{
210+
ControlPlaneTopology: configv1.HighlyAvailableTopologyMode,
211+
APIServerInternalURL: "https://10.0.0.2:6443", // Changed from 10.0.0.1 to 10.0.0.2
212+
},
213+
}
214+
215+
// Update the Infrastructure object
216+
_, err := f.configClient.ConfigV1().Infrastructures().Update(context.TODO(), infraObj, metav1.UpdateOptions{})
217+
require.NoError(t, err)
218+
219+
// Update the informer with the new Infrastructure object
220+
f.infraInformer.Config().V1().Infrastructures().Informer().GetIndexer().Update(infraObj)
221+
222+
// Trigger the sync after Infrastructure update
223+
f.syncListers(t)
224+
f.runController()
225+
226+
// Verify that the TLS certificate was regenerated with the new hostname
227+
tlsSecret, err := f.kubeClient.CoreV1().Secrets(ctrlcommon.MCONamespace).Get(context.TODO(), ctrlcommon.MachineConfigServerTLSSecretName, metav1.GetOptions{})
228+
require.NoError(t, err)
229+
require.NotNil(t, tlsSecret)
230+
231+
// Verify certificate contains new hostname
232+
certData, exists := tlsSecret.Data["tls.crt"]
233+
require.True(t, exists, "TLS certificate should exist in secret")
234+
require.NotEmpty(t, certData, "TLS certificate data should not be empty")
235+
236+
// Decode and parse certificate
237+
block, _ := pem.Decode(certData)
238+
require.NotNil(t, block, "Should be able to decode PEM certificate")
239+
240+
cert, err := x509.ParseCertificate(block.Bytes)
241+
require.NoError(t, err, "Should be able to parse TLS certificate")
242+
243+
// Verify the new hostname is in the certificate's DNS names
244+
expectedHostname := "10.0.0.2"
245+
found := false
246+
for _, dnsName := range cert.DNSNames {
247+
if dnsName == expectedHostname {
248+
found = true
249+
break
250+
}
251+
}
252+
require.True(t, found, "New hostname %s should be present in certificate DNS names", expectedHostname)
253+
t.Logf("Successfully verified hostname %s is present in TLS certificate after Infrastructure update", expectedHostname)
254+
255+
// Verify that user data secrets were updated (should be 1 total update)
256+
f.verifyUserDataSecretUpdateCount(1)
257+
}
258+
167259
func TestMCSCARotation(t *testing.T) {
168260
tests := []struct {
169261
name string

0 commit comments

Comments
 (0)