Skip to content

Commit d2dea55

Browse files
committed
1 parent f169414 commit d2dea55

File tree

3 files changed

+146
-0
lines changed

3 files changed

+146
-0
lines changed

cmd/manager/main.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ func main() {
9191
operatorControllerVersion bool
9292
systemNamespace string
9393
caCertDir string
94+
globalPullSecretName string
95+
globalPullSecretNamespace string
9496
)
9597
flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.")
9698
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
@@ -101,6 +103,8 @@ func main() {
101103
flag.StringVar(&cachePath, "cache-path", "/var/cache", "The local directory path used for filesystem based caching")
102104
flag.BoolVar(&operatorControllerVersion, "version", false, "Prints operator-controller version information")
103105
flag.StringVar(&systemNamespace, "system-namespace", "", "Configures the namespace that gets used to deploy system resources.")
106+
flag.StringVar(&globalPullSecretName, "global-pull-secret-name", "", "The name of the global pull secret that is going to be used to pull bundle images.")
107+
flag.StringVar(&globalPullSecretNamespace, "global-pull-secret-namespace", "", "The namespace of the global pull secret.")
104108

105109
klog.InitFlags(flag.CommandLine)
106110

@@ -115,6 +119,11 @@ func main() {
115119

116120
ctrl.SetLogger(textlogger.NewLogger(textlogger.NewConfig()))
117121

122+
if (globalPullSecretName != "" && globalPullSecretNamespace == "") || (globalPullSecretName == "" && globalPullSecretNamespace != "") {
123+
setupLog.Error(fmt.Errorf("Both --global-pull-secret-name and --global-pull-secret-namespace must be provided together."), "incorrect flag usage")
124+
os.Exit(1)
125+
}
126+
118127
setupLog.Info("starting up the controller", "version info", version.String())
119128

120129
if systemNamespace == "" {
@@ -289,6 +298,18 @@ func main() {
289298
os.Exit(1)
290299
}
291300

301+
if globalPullSecretName != "" && globalPullSecretNamespace != "" {
302+
err := (&controllers.SecretSyncerReconciler{
303+
Client: mgr.GetClient(),
304+
SecretName: globalPullSecretName,
305+
SecretNamespace: globalPullSecretNamespace,
306+
}).SetupWithManager(mgr)
307+
if err != nil {
308+
setupLog.Error(err, "unable to create controller", "controller", "SecretSyncer")
309+
os.Exit(1)
310+
}
311+
}
312+
292313
//+kubebuilder:scaffold:builder
293314

294315
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {

config/base/rbac/role.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ kind: ClusterRole
44
metadata:
55
name: manager-role
66
rules:
7+
- apiGroups:
8+
- ""
9+
resources:
10+
- secrets
11+
verbs:
12+
- get
13+
- list
14+
- watch
715
- apiGroups:
816
- apiextensions.k8s.io
917
resources:
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
Copyright 2024.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package controllers
18+
19+
import (
20+
"context"
21+
"encoding/json"
22+
"fmt"
23+
"os"
24+
25+
"github.com/go-logr/logr"
26+
corev1 "k8s.io/api/core/v1"
27+
apierrors "k8s.io/apimachinery/pkg/api/errors"
28+
ctrl "sigs.k8s.io/controller-runtime"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
"sigs.k8s.io/controller-runtime/pkg/log"
31+
"sigs.k8s.io/controller-runtime/pkg/predicate"
32+
)
33+
34+
// SecretSyncerReconciler reconciles a specific secret object
35+
type SecretSyncerReconciler struct {
36+
client.Client
37+
SecretName string
38+
SecretNamespace string
39+
AuthFilePath string
40+
}
41+
42+
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
43+
44+
func (r *SecretSyncerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
45+
logger := log.FromContext(ctx)
46+
47+
// Ensure that the request matches the specific secret you want to watch
48+
if req.Name != r.SecretName || req.Namespace != r.SecretNamespace {
49+
logger.Info("Received unexpected request for secret", req.Name, "in namespace", req.Namespace)
50+
return ctrl.Result{}, nil
51+
}
52+
53+
secret := &corev1.Secret{}
54+
err := r.Get(ctx, req.NamespacedName, secret)
55+
if err != nil {
56+
if apierrors.IsNotFound(err) {
57+
logger.Info("Secret", req.Name, "in namespace", req.Namespace, "deleted.")
58+
return r.deleteSecretFile(logger)
59+
}
60+
logger.Error(err, "Failed to get Secret")
61+
return ctrl.Result{}, err
62+
}
63+
64+
return r.writeSecretToFile(secret, logger)
65+
}
66+
67+
// SetupWithManager sets up the controller with the Manager.
68+
func (r *SecretSyncerReconciler) SetupWithManager(mgr ctrl.Manager) error {
69+
_, err := ctrl.NewControllerManagedBy(mgr).
70+
For(&corev1.Secret{}).
71+
WithEventFilter(newSecretPredicate(r.SecretName, r.SecretNamespace)).
72+
Build(r)
73+
74+
return err
75+
}
76+
77+
func newSecretPredicate(secretName, secretNamespace string) predicate.Predicate {
78+
return predicate.NewPredicateFuncs(func(obj client.Object) bool {
79+
secret, ok := obj.(*corev1.Secret)
80+
if !ok {
81+
return false
82+
}
83+
return secret.Name == secretName && secret.Namespace == secretNamespace
84+
})
85+
}
86+
87+
// writeSecretToFile writes the secret data to the specified file
88+
func (r *SecretSyncerReconciler) writeSecretToFile(secret *corev1.Secret, logger logr.Logger) (ctrl.Result, error) {
89+
jsonData, err := json.Marshal(secret.Data)
90+
if err != nil {
91+
return ctrl.Result{}, fmt.Errorf("failed to marshal secret data: %w", err)
92+
}
93+
err = os.WriteFile(r.AuthFilePath, jsonData, 0644)
94+
if err != nil {
95+
return ctrl.Result{}, fmt.Errorf("failed to write secret data to file: %w", err)
96+
}
97+
logger.Info("Saved Secret data locally")
98+
return ctrl.Result{}, nil
99+
}
100+
101+
// deleteSecretFile deletes the secret file if the secret is deleted
102+
func (r *SecretSyncerReconciler) deleteSecretFile(logger logr.Logger) (ctrl.Result, error) {
103+
logger.Info("Deleting local auth file.")
104+
if _, err := os.Stat(r.AuthFilePath); err == nil {
105+
err := os.Remove(r.AuthFilePath)
106+
if err != nil {
107+
return ctrl.Result{}, fmt.Errorf("failed to delete secret file: %w", err)
108+
}
109+
logger.Info("Auth file deleted successfully", "file", r.AuthFilePath)
110+
} else if os.IsNotExist(err) {
111+
logger.Info("Secret file does not exist, nothing to delete", "file", r.AuthFilePath)
112+
} else {
113+
return ctrl.Result{}, fmt.Errorf("failed to check secret file: %w", err)
114+
}
115+
116+
return ctrl.Result{}, nil
117+
}

0 commit comments

Comments
 (0)