Skip to content

Commit 5ab2f04

Browse files
committed
Sync master-user-data secret
1 parent 140a1fa commit 5ab2f04

File tree

4 files changed

+263
-207
lines changed

4 files changed

+263
-207
lines changed

docs/controllers/secretsync.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Overview
44

5-
[Secret sync controller](../../pkg/controllers/secretsync/secret_sync_controller.go) is responsible for syncing `worker-user-data` secret that is created by installer in `openshift-machine-api` namespace. The secret is used to store ignition configuration data for worker nodes.
5+
[Secret sync controller](../../pkg/controllers/secretsync/secret_sync_controller.go) is responsible for syncing `worker-user-data` and `master-user-data` secrets created by the installer in the `openshift-machine-api` namespace. These secrets store ignition configuration data for worker and control plane nodes, respectively.
66

77
## Behavior
88

pkg/controllers/secretsync/secret_sync_controller.go

Lines changed: 56 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
corev1 "k8s.io/api/core/v1"
2727
apierrors "k8s.io/apimachinery/pkg/api/errors"
2828
"k8s.io/apimachinery/pkg/runtime"
29+
"k8s.io/utils/ptr"
2930
ctrl "sigs.k8s.io/controller-runtime"
3031
"sigs.k8s.io/controller-runtime/pkg/builder"
3132
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -36,7 +37,8 @@ import (
3637
)
3738

3839
const (
39-
managedUserDataSecretName = "worker-user-data"
40+
workerUserDataSecretName = "worker-user-data"
41+
masterUserDataSecretName = "master-user-data"
4042

4143
// SecretSourceNamespace is the source namespace to copy the user data secret from.
4244
SecretSourceNamespace = "openshift-machine-api"
@@ -63,86 +65,87 @@ type UserDataSecretController struct {
6365
// Reconcile reconciles the user data secret.
6466
func (r *UserDataSecretController) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
6567
log := ctrl.LoggerFrom(ctx).WithName(controllerName)
66-
log.Info("reconciling worker user data secret")
68+
secretName := req.Name
69+
log.Info("reconciling user data secret", "secretName", secretName)
6770

68-
defaultSourceSecretObjectKey := client.ObjectKey{
69-
Name: managedUserDataSecretName, Namespace: SecretSourceNamespace,
71+
if !isValidUserDataSecretName(secretName) {
72+
log.Info("ignoring request for unknown secret", "secretName", secretName)
73+
return ctrl.Result{}, nil
7074
}
71-
sourceSecret := &corev1.Secret{}
72-
73-
if err := r.Get(ctx, defaultSourceSecretObjectKey, sourceSecret); err != nil {
74-
log.Error(err, "unable to get source secret for sync")
7575

76+
if err := r.reconcileUserDataSecret(ctx, log, secretName); err != nil {
7677
if err := r.setDegradedCondition(ctx, log); err != nil {
7778
return ctrl.Result{}, fmt.Errorf("failed to set conditions for secret sync controller: %w", err)
7879
}
7980

80-
return ctrl.Result{}, fmt.Errorf("failed to get source secret: %w", err)
81+
return ctrl.Result{}, err
8182
}
8283

83-
targetSecret := &corev1.Secret{}
84-
targetSecretKey := client.ObjectKey{
85-
Namespace: r.ManagedNamespace,
86-
Name: managedUserDataSecretName,
84+
if err := r.setAvailableCondition(ctx, log); err != nil {
85+
return ctrl.Result{}, fmt.Errorf("failed to set conditions for user data secret controller: %w", err)
8786
}
8887

89-
// If the secret does not exist, it will be created later, so we can ignore a Not Found error
90-
if err := r.Get(ctx, targetSecretKey, targetSecret); err != nil && !apierrors.IsNotFound(err) {
91-
log.Error(err, "unable to get target secret for sync")
88+
return ctrl.Result{}, nil
89+
}
9290

93-
if err := r.setDegradedCondition(ctx, log); err != nil {
94-
return ctrl.Result{}, fmt.Errorf("failed to set conditions for secret controller: %w", err)
95-
}
91+
// reconcileUserDataSecret performs the actual reconciliation for a specific user data secret.
92+
func (r *UserDataSecretController) reconcileUserDataSecret(ctx context.Context, log logr.Logger, secretName string) error {
93+
sourceSecret := &corev1.Secret{}
94+
targetSecret := &corev1.Secret{}
9695

97-
return ctrl.Result{}, fmt.Errorf("failed to get target secret: %w", err)
96+
sourceSecretObjectKey := client.ObjectKey{
97+
Name: secretName, Namespace: SecretSourceNamespace,
9898
}
99-
100-
if r.areSecretsEqual(sourceSecret, targetSecret) {
101-
log.Info("user data in source and target secrets is the same, no sync needed")
102-
103-
if err := r.setAvailableCondition(ctx, log); err != nil {
104-
return ctrl.Result{}, fmt.Errorf("failed to set conditions for user data secret controller: %w", err)
105-
}
106-
107-
return ctrl.Result{}, nil
99+
if err := r.Get(ctx, sourceSecretObjectKey, sourceSecret); err != nil {
100+
log.Error(err, "unable to get source secret for sync", "secretName", secretName)
101+
return fmt.Errorf("failed to get source secret %s: %w", secretName, err)
108102
}
109103

110-
if err := r.syncSecretData(ctx, sourceSecret, targetSecret); err != nil {
111-
log.Error(err, "unable to sync user data secret")
112-
113-
if err := r.setDegradedCondition(ctx, log); err != nil {
114-
return ctrl.Result{}, fmt.Errorf("failed to set conditions for user data secret controller: %w", err)
115-
}
104+
targetSecretObjectKey := client.ObjectKey{
105+
Namespace: r.ManagedNamespace,
106+
Name: secretName,
107+
}
108+
// If the secret does not exist, it will be created later, so we can ignore a Not Found error
109+
if err := r.Get(ctx, targetSecretObjectKey, targetSecret); err != nil && !apierrors.IsNotFound(err) {
110+
log.Error(err, "unable to get target secret for sync", "secretName", secretName)
111+
return fmt.Errorf("failed to get target secret %s: %w", secretName, err)
112+
}
116113

117-
return ctrl.Result{}, err
114+
if r.areSecretsEqual(sourceSecret, targetSecret) {
115+
log.Info("user data in source and target secrets is the same, no sync needed", "secretName", secretName)
116+
return nil
118117
}
119118

120-
if err := r.setAvailableCondition(ctx, log); err != nil {
121-
return ctrl.Result{}, fmt.Errorf("failed to set conditions for user data secret controller: %w", err)
119+
if err := r.syncSecretData(ctx, sourceSecret, targetSecret, secretName); err != nil {
120+
log.Error(err, "unable to sync user data secret", "secretName", secretName)
121+
return fmt.Errorf("failed to sync secret %s: %w", secretName, err)
122122
}
123123

124-
return ctrl.Result{}, nil
124+
log.Info("user data secret synced successfully", "secretName", secretName)
125+
126+
return nil
125127
}
126128

127-
func (r *UserDataSecretController) areSecretsEqual(source *corev1.Secret, target *corev1.Secret) bool {
128-
return source.Immutable == target.Immutable &&
129-
reflect.DeepEqual(source.Data[mapiUserDataKey], target.Data[capiUserDataKey]) && reflect.DeepEqual(source.StringData, target.StringData) &&
129+
func (r *UserDataSecretController) areSecretsEqual(source, target *corev1.Secret) bool {
130+
immutableEqual := ptr.Deref(source.Immutable, false) == ptr.Deref(target.Immutable, false)
131+
132+
return immutableEqual &&
133+
reflect.DeepEqual(source.Data[mapiUserDataKey], target.Data[capiUserDataKey]) &&
130134
source.Type == target.Type
131135
}
132136

133-
func (r *UserDataSecretController) syncSecretData(ctx context.Context, source *corev1.Secret, target *corev1.Secret) error {
137+
func (r *UserDataSecretController) syncSecretData(ctx context.Context, source *corev1.Secret, target *corev1.Secret, secretName string) error {
134138
userData := source.Data[mapiUserDataKey]
135139
if userData == nil {
136140
return errSourceSecretMissingUserData
137141
}
138142

139-
target.SetName(managedUserDataSecretName)
143+
target.SetName(secretName)
140144
target.SetNamespace(r.ManagedNamespace)
141145
target.Data = map[string][]byte{
142-
"value": userData,
143-
"format": []byte("ignition"),
146+
capiUserDataKey: userData,
147+
"format": []byte("ignition"),
144148
}
145-
target.StringData = source.StringData
146149
target.Immutable = source.Immutable
147150
target.Type = source.Type
148151

@@ -175,7 +178,7 @@ func (r *UserDataSecretController) SetupWithManager(mgr ctrl.Manager) error {
175178
).
176179
Watches(
177180
&corev1.Secret{},
178-
handler.EnqueueRequestsFromMapFunc(toUserDataSecret),
181+
handler.EnqueueRequestsFromMapFunc(toUserDataSecret(r.ManagedNamespace)),
179182
builder.WithPredicates(userDataSecretPredicate(SecretSourceNamespace)),
180183
).
181184
Complete(r); err != nil {
@@ -185,6 +188,11 @@ func (r *UserDataSecretController) SetupWithManager(mgr ctrl.Manager) error {
185188
return nil
186189
}
187190

191+
// isValidUserDataSecretName checks if the given secret name is a user data secret we should sync.
192+
func isValidUserDataSecretName(secretName string) bool {
193+
return secretName == workerUserDataSecretName || secretName == masterUserDataSecretName
194+
}
195+
188196
func (r *UserDataSecretController) setAvailableCondition(ctx context.Context, log logr.Logger) error {
189197
co, err := r.GetOrCreateClusterOperator(ctx)
190198
if err != nil {

0 commit comments

Comments
 (0)