@@ -54,8 +54,19 @@ var _ multicluster.Provider = &Provider{}
54
54
const (
55
55
labelKeyClusterInventoryConsumer = "x-k8s.io/cluster-inventory-consumer"
56
56
labelKeyClusterProfile = "x-k8s.io/cluster-profile"
57
+ dataKeyKubeConfig = "Config" // data key in the Secret that contains the kubeconfig.
57
58
)
58
59
60
+ // KubeconfigStrategy defines how the kubeconfig for a cluster profile is managed.
61
+ // It is used to fetch the kubeconfig for a cluster profile and can be extended to support different strategies.
62
+ type KubeconfigStrategy struct {
63
+ // GetKubeConfig is a function that returns the kubeconfig secret for a cluster profile.
64
+ GetKubeConfig func (ctx context.Context , cli client.Client , clp * clusterinventoryv1alpha1.ClusterProfile ) (* rest.Config , error )
65
+
66
+ // CustomWatches can add custom watches to the provider controller
67
+ CustomWatches []CustomWatch
68
+ }
69
+
59
70
// Options are the options for the Cluster-API cluster Provider.
60
71
type Options struct {
61
72
// ConsumerName is the name of the consumer that will use the cluster inventory API.
@@ -64,15 +75,17 @@ type Options struct {
64
75
// ClusterOptions are the options passed to the cluster constructor.
65
76
ClusterOptions []cluster.Option
66
77
67
- // GetKubeConfig is a function that returns the kubeconfig secret for a cluster profile.
68
- GetKubeConfig func (ctx context.Context , cli client.Client , clp * clusterinventoryv1alpha1.ClusterProfile ) (* rest.Config , error )
78
+ // KubeconfigStrategy defines how the kubeconfig for the cluster profile is managed.
79
+ // It is used to fetch the kubeconfig for a cluster profile and can be extended to support different strategies.
80
+ // The default strategy is KubeconfigStrategySecret(consumerName) which fetches the kubeconfig from a Secret
81
+ // labeled with "x-k8s.io/cluster-inventory-consumer" and "x-k8s.io/cluster-profile" labels.
82
+ // This is the "Push Model via Credentials in Secret" as described in KEP-4322: ClusterProfile API.
83
+ // ref: https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/4322-cluster-inventory/README.md#push-model-via-credentials-in-secret-not-recommended
84
+ KubeconfigStrategy * KubeconfigStrategy
69
85
70
86
// NewCluster is a function that creates a new cluster from a rest.Config.
71
87
// The cluster will be started by the provider.
72
88
NewCluster func (ctx context.Context , clp * clusterinventoryv1alpha1.ClusterProfile , cfg * rest.Config , opts ... cluster.Option ) (cluster.Cluster , error )
73
-
74
- // CustomWatches can add custom watches to the provider controller
75
- CustomWatches []CustomWatch
76
89
}
77
90
78
91
// CustomWatch specifies a custom watch spec that can be added to the provider controller.
@@ -102,79 +115,75 @@ type Provider struct {
102
115
indexers []index
103
116
}
104
117
105
- // GetKubeConfigFromSecret returns a function that fetches the kubeconfig for a specified consumer for ClusterProfile from Secret
106
- // It supposes that the Secrets for ClusterProfiles are managed by following "Push Model via Credentials in Secret" in "KEP-4322: ClusterProfile API"
118
+ // KubeconfigManagementStrategySecret returns a KubeconfigStrategy that fetches the kubeconfig from a Secret
119
+ // labeled with "x-k8s.io/cluster-inventory-consumer" and "x-k8s.io/cluster-profile" labels.
120
+ // This is the "Push Model via Credentials in Secret" as described in KEP-4322: ClusterProfile API.
107
121
// ref: https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/4322-cluster-inventory/README.md#push-model-via-credentials-in-secret-not-recommended
108
- func GetKubeConfigFromSecret (consumerName string ) func (ctx context.Context , cli client.Client , clp * clusterinventoryv1alpha1.ClusterProfile ) (* rest.Config , error ) {
109
- return func (ctx context.Context , cli client.Client , clp * clusterinventoryv1alpha1.ClusterProfile ) (* rest.Config , error ) {
110
- secrets := corev1.SecretList {}
111
- if err := cli .List (ctx , & secrets , client .InNamespace (clp .Namespace ), client.MatchingLabels {
112
- labelKeyClusterInventoryConsumer : consumerName ,
113
- labelKeyClusterProfile : clp .Name ,
114
- }); err != nil {
115
- return nil , fmt .Errorf ("failed to list secrets: %w" , err )
116
- }
117
-
118
- if len (secrets .Items ) == 0 {
119
- return nil , fmt .Errorf ("no secrets found" )
120
- }
122
+ func KubeconfigStrategySecret (consumerName string ) * KubeconfigStrategy {
123
+ return & KubeconfigStrategy {
124
+ GetKubeConfig : func (ctx context.Context , cli client.Client , clp * clusterinventoryv1alpha1.ClusterProfile ) (* rest.Config , error ) {
125
+ secrets := corev1.SecretList {}
126
+ if err := cli .List (ctx , & secrets , client .InNamespace (clp .Namespace ), client.MatchingLabels {
127
+ labelKeyClusterInventoryConsumer : consumerName ,
128
+ labelKeyClusterProfile : clp .Name ,
129
+ }); err != nil {
130
+ return nil , fmt .Errorf ("failed to list secrets: %w" , err )
131
+ }
121
132
122
- if len (secrets .Items ) > 1 {
123
- return nil , fmt .Errorf ("multiple secrets found, expected one, got %d" , len ( secrets . Items ) )
124
- }
133
+ if len (secrets .Items ) == 0 {
134
+ return nil , fmt .Errorf ("no secrets found" )
135
+ }
125
136
126
- secret := secrets .Items [0 ]
137
+ if len (secrets .Items ) > 1 {
138
+ return nil , fmt .Errorf ("multiple secrets found, expected one, got %d" , len (secrets .Items ))
139
+ }
127
140
128
- data , ok := secret .Data ["Config" ]
129
- if ! ok {
130
- return nil , fmt .Errorf ("secret %s/%s does not contain Config data" , secret .Namespace , secret .Name )
131
- }
132
- return clientcmd .RESTConfigFromKubeConfig (data )
133
- }
134
- }
141
+ secret := secrets .Items [0 ]
135
142
136
- // WatchKubeConfigSecret returns a CustomWatch that watches for kubeconfig secrets for specified consumer of ClusterProfile
137
- // It supposes that the Secrets for ClusterProfiles are managed by following "Push Model via Credentials in Secret" in "KEP-4322: ClusterProfile API"
138
- // ref: https://github.com/kubernetes/enhancements/blob/master/keps/sig-multicluster/4322-cluster-inventory/README.md#push-model-via-credentials-in-secret-not-recommended
139
- func WatchKubeConfigSecret (consumerName string ) CustomWatch {
140
- return CustomWatch {
141
- Object : & corev1.Secret {},
142
- EventHandler : handler .EnqueueRequestsFromMapFunc (func (ctx context.Context , obj client.Object ) []reconcile.Request {
143
- secret , ok := obj .(* corev1.Secret )
143
+ data , ok := secret .Data [dataKeyKubeConfig ]
144
144
if ! ok {
145
- return nil
145
+ return nil , fmt . Errorf ( "secret %s/%s does not contain Config data" , secret . Namespace , secret . Name )
146
146
}
147
-
148
- if secret .GetLabels () == nil ||
149
- secret .GetLabels ()[labelKeyClusterInventoryConsumer ] != consumerName ||
150
- secret .GetLabels ()[labelKeyClusterProfile ] == "" {
151
- return nil
152
- }
153
-
154
- return []reconcile.Request {{
155
- NamespacedName : types.NamespacedName {
156
- Namespace : secret .GetNamespace (),
157
- Name : secret .GetLabels ()[labelKeyClusterProfile ],
158
- },
159
- }}
160
- }),
161
- Opts : []builder.WatchesOption {
162
- builder .WithPredicates (predicate .NewPredicateFuncs (func (object client.Object ) bool {
163
- secret , ok := object .(* corev1.Secret )
147
+ return clientcmd .RESTConfigFromKubeConfig (data )
148
+ },
149
+ CustomWatches : []CustomWatch {CustomWatch {
150
+ Object : & corev1.Secret {},
151
+ EventHandler : handler .EnqueueRequestsFromMapFunc (func (ctx context.Context , obj client.Object ) []reconcile.Request {
152
+ secret , ok := obj .(* corev1.Secret )
164
153
if ! ok {
165
- return false
154
+ return nil
166
155
}
167
- return secret .GetLabels ()[labelKeyClusterInventoryConsumer ] == consumerName &&
168
- secret .GetLabels ()[labelKeyClusterProfile ] != ""
169
- })),
170
- },
156
+
157
+ if secret .GetLabels () == nil ||
158
+ secret .GetLabels ()[labelKeyClusterInventoryConsumer ] != consumerName ||
159
+ secret .GetLabels ()[labelKeyClusterProfile ] == "" {
160
+ return nil
161
+ }
162
+
163
+ return []reconcile.Request {{
164
+ NamespacedName : types.NamespacedName {
165
+ Namespace : secret .GetNamespace (),
166
+ Name : secret .GetLabels ()[labelKeyClusterProfile ],
167
+ },
168
+ }}
169
+ }),
170
+ Opts : []builder.WatchesOption {
171
+ builder .WithPredicates (predicate .NewPredicateFuncs (func (object client.Object ) bool {
172
+ secret , ok := object .(* corev1.Secret )
173
+ if ! ok {
174
+ return false
175
+ }
176
+ return secret .GetLabels ()[labelKeyClusterInventoryConsumer ] == consumerName &&
177
+ secret .GetLabels ()[labelKeyClusterProfile ] != ""
178
+ })),
179
+ },
180
+ }},
171
181
}
172
182
}
173
183
174
184
func setDefaults (opts * Options , cli client.Client ) {
175
- if opts .GetKubeConfig == nil {
176
- opts .GetKubeConfig = GetKubeConfigFromSecret (opts .ConsumerName )
177
- opts .CustomWatches = append (opts .CustomWatches , WatchKubeConfigSecret (opts .ConsumerName ))
185
+ if opts .KubeconfigStrategy == nil {
186
+ opts .KubeconfigStrategy = KubeconfigStrategySecret (opts .ConsumerName )
178
187
}
179
188
if opts .NewCluster == nil {
180
189
opts .NewCluster = func (ctx context.Context , clp * clusterinventoryv1alpha1.ClusterProfile , cfg * rest.Config , opts ... cluster.Option ) (cluster.Cluster , error ) {
@@ -202,7 +211,7 @@ func New(localMgr manager.Manager, opts Options) (*Provider, error) {
202
211
WithOptions (controller.Options {MaxConcurrentReconciles : 1 }) // no parallelism.
203
212
204
213
// Apply any custom watches provided by the user
205
- for _ , customWatch := range p .opts .CustomWatches {
214
+ for _ , customWatch := range p .opts .KubeconfigStrategy . CustomWatches {
206
215
controllerBuilder .Watches (
207
216
customWatch .Object ,
208
217
customWatch .EventHandler ,
@@ -286,7 +295,7 @@ func (p *Provider) Reconcile(ctx context.Context, req reconcile.Request) (reconc
286
295
}
287
296
288
297
// get kubeconfig
289
- cfg , err := p .opts .GetKubeConfig (ctx , p .client , clp )
298
+ cfg , err := p .opts .KubeconfigStrategy . GetKubeConfig (ctx , p .client , clp )
290
299
if err != nil {
291
300
log .Error (err , "Failed to get kubeconfig for ClusterProfile" )
292
301
return reconcile.Result {}, fmt .Errorf ("failed to get kubeconfig for ClusterProfile=%s: %w" , key , err )
0 commit comments