@@ -12,6 +12,8 @@ import (
12
12
"github.com/stretchr/testify/require"
13
13
14
14
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"
15
17
"github.com/openshift/library-go/pkg/operator/certrotation"
16
18
corev1 "k8s.io/api/core/v1"
17
19
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -43,12 +45,14 @@ type fixture struct {
43
45
maoSecretLister []* corev1.Secret
44
46
mcoSecretLister []* corev1.Secret
45
47
mcoConfigMapLister []* corev1.ConfigMap
48
+ infraLister []* configv1.Infrastructure
46
49
47
50
objects []runtime.Object
48
51
configObjects []runtime.Object
49
52
machineObjects []runtime.Object
50
53
aroObjects []runtime.Object
51
54
k8sI kubeinformers.SharedInformerFactory
55
+ infraInformer configinformers.SharedInformerFactory
52
56
53
57
controller * CertRotationController
54
58
}
@@ -80,14 +84,15 @@ func (f *fixture) newController() *CertRotationController {
80
84
Status : configv1.InfrastructureStatus {
81
85
ControlPlaneTopology : configv1 .HighlyAvailableTopologyMode ,
82
86
PlatformStatus : platformStatus ,
83
- APIServerInternalURL : "test-url " },
87
+ APIServerInternalURL : "https://10.0.0.1:6443 " },
84
88
})
85
89
86
90
f .kubeClient = fake .NewSimpleClientset (f .objects ... )
87
91
f .configClient = fakeconfigv1client .NewSimpleClientset (f .configObjects ... )
88
92
f .machineClient = fakemachineclientset .NewSimpleClientset (f .machineObjects ... )
89
93
f .aroClient = fakearoclientset .NewSimpleClientset (f .aroObjects ... )
90
94
f .k8sI = kubeinformers .NewSharedInformerFactory (f .kubeClient , noResyncPeriodFunc ())
95
+ f .infraInformer = configinformers .NewSharedInformerFactory (f .configClient , noResyncPeriodFunc ())
91
96
92
97
for _ , secret := range f .maoSecretLister {
93
98
f .k8sI .Core ().V1 ().Secrets ().Informer ().GetIndexer ().Add (secret )
@@ -101,7 +106,12 @@ func (f *fixture) newController() *CertRotationController {
101
106
f .k8sI .Core ().V1 ().ConfigMaps ().Informer ().GetIndexer ().Add (configMap )
102
107
}
103
108
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 ())
105
115
require .NoError (f .t , err )
106
116
107
117
c .StartInformers ()
@@ -110,9 +120,25 @@ func (f *fixture) newController() *CertRotationController {
110
120
return c
111
121
}
112
122
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
+
113
139
func (f * fixture ) runController () {
114
140
115
- err := f .controller . Sync ()
141
+ err := f .sync ()
116
142
require .NoError (f .t , err )
117
143
118
144
f .controller .reconcileUserDataSecrets ()
@@ -164,6 +190,72 @@ func (f *fixture) verifyAROIPInTLSCertificate(t *testing.T, expectedIP string) {
164
190
t .Logf ("Successfully verified ARO IP %s is present in TLS certificate" , expectedIP )
165
191
}
166
192
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
+
167
259
func TestMCSCARotation (t * testing.T ) {
168
260
tests := []struct {
169
261
name string
0 commit comments