@@ -37,6 +37,7 @@ import (
37
37
kubeclient "k8s.io/client-go/kubernetes"
38
38
"k8s.io/client-go/rest"
39
39
"k8s.io/klog/v2"
40
+ "k8s.io/utils/pointer"
40
41
runtimeclient "sigs.k8s.io/controller-runtime/pkg/client"
41
42
42
43
fedv1b1 "sigs.k8s.io/kubefed/pkg/apis/core/v1beta1"
@@ -93,15 +94,15 @@ type joinFederation struct {
93
94
}
94
95
95
96
type joinFederationOptions struct {
96
- secretName string
97
- scope apiextv1.ResourceScope
98
- errorOnExisting bool
97
+ hostClusterSecretName string
98
+ scope apiextv1.ResourceScope
99
+ errorOnExisting bool
99
100
}
100
101
101
102
// Bind adds the join specific arguments to the flagset passed in as an
102
103
// argument.
103
104
func (o * joinFederationOptions ) Bind (flags * pflag.FlagSet ) {
104
- flags .StringVar (& o .secretName , "secret-name" , "" ,
105
+ flags .StringVar (& o .hostClusterSecretName , "secret-name" , "" ,
105
106
"Name of the secret where the cluster's credentials will be stored in the host cluster. This name should be a valid RFC 1035 label. If unspecified, defaults to a generated name containing the cluster name." )
106
107
flags .BoolVar (& o .errorOnExisting , "error-on-existing" , true ,
107
108
"Whether the join operation will throw an error if it encounters existing artifacts with the same name as those it's trying to create. If false, the join operation will update existing artifacts to match its own specification." )
@@ -160,7 +161,7 @@ func (j *joinFederation) Complete(args []string) error {
160
161
161
162
klog .V (2 ).Infof ("Args and flags: name %s, host: %s, host-system-namespace: %s, kubeconfig: %s, cluster-context: %s, secret-name: %s, dry-run: %v" ,
162
163
j .ClusterName , j .HostClusterContext , j .KubeFedNamespace , j .Kubeconfig , j .ClusterContext ,
163
- j .secretName , j .DryRun )
164
+ j .hostClusterSecretName , j .DryRun )
164
165
165
166
return nil
166
167
}
@@ -197,7 +198,7 @@ func (j *joinFederation) Run(cmdOut io.Writer, config util.FedConfig) error {
197
198
}
198
199
199
200
_ , err = JoinCluster (hostConfig , clusterConfig , j .KubeFedNamespace ,
200
- hostClusterName , j .ClusterName , j .secretName , j .joinFederationOptions .scope , j .DryRun , j .errorOnExisting )
201
+ hostClusterName , j .ClusterName , j .hostClusterSecretName , j .joinFederationOptions .scope , j .DryRun , j .errorOnExisting )
201
202
202
203
return err
203
204
}
@@ -206,18 +207,18 @@ func (j *joinFederation) Run(cmdOut io.Writer, config util.FedConfig) error {
206
207
// KubeFed namespace in the joining cluster will be the same as in the
207
208
// host cluster.
208
209
func JoinCluster (hostConfig , clusterConfig * rest.Config , kubefedNamespace ,
209
- hostClusterName , joiningClusterName , secretName string ,
210
+ hostClusterName , joiningClusterName , hostClusterSecretName string ,
210
211
scope apiextv1.ResourceScope , dryRun , errorOnExisting bool ) (* fedv1b1.KubeFedCluster , error ) {
211
212
return joinClusterForNamespace (hostConfig , clusterConfig , kubefedNamespace ,
212
- kubefedNamespace , hostClusterName , joiningClusterName , secretName ,
213
+ kubefedNamespace , hostClusterName , joiningClusterName , hostClusterSecretName ,
213
214
scope , dryRun , errorOnExisting )
214
215
}
215
216
216
217
// joinClusterForNamespace registers a cluster with a KubeFed control
217
218
// plane. The KubeFed namespace in the joining cluster is provided by
218
219
// the joiningNamespace parameter.
219
220
func joinClusterForNamespace (hostConfig , clusterConfig * rest.Config , kubefedNamespace ,
220
- joiningNamespace , hostClusterName , joiningClusterName , secretName string ,
221
+ joiningNamespace , hostClusterName , joiningClusterName , hostClusterSecretName string ,
221
222
scope apiextv1.ResourceScope , dryRun , errorOnExisting bool ) (* fedv1b1.KubeFedCluster , error ) {
222
223
start := time .Now ()
223
224
@@ -254,15 +255,16 @@ func joinClusterForNamespace(hostConfig, clusterConfig *rest.Config, kubefedName
254
255
}
255
256
klog .V (2 ).Infof ("Created %s namespace in joining cluster" , joiningNamespace )
256
257
257
- saName , err := createAuthorizedServiceAccount (clusterClientset ,
258
+ joiningClusterSATokenSecretName , err := createAuthorizedServiceAccount (clusterClientset ,
258
259
joiningNamespace , joiningClusterName , hostClusterName ,
259
260
scope , dryRun , errorOnExisting )
260
261
if err != nil {
261
262
return nil , err
262
263
}
263
264
264
265
secret , caBundle , err := populateSecretInHostCluster (clusterClientset , hostClientset ,
265
- saName , kubefedNamespace , joiningNamespace , joiningClusterName , secretName , dryRun , errorOnExisting )
266
+ joiningClusterSATokenSecretName , kubefedNamespace , joiningNamespace , joiningClusterName ,
267
+ hostClusterSecretName , dryRun , errorOnExisting )
266
268
if err != nil {
267
269
klog .V (2 ).Infof ("Error creating secret in host cluster: %s due to: %v" , hostClusterName , err )
268
270
return nil , err
@@ -417,13 +419,13 @@ func createKubeFedNamespace(clusterClientset kubeclient.Interface, kubefedNamesp
417
419
return fedNamespace , nil
418
420
}
419
421
420
- // createAuthorizedServiceAccount creates a service account and grants
421
- // the privileges required by the KubeFed control plane to manage
422
+ // createAuthorizedServiceAccount creates a service account and service account token secret
423
+ // and grants the privileges required by the KubeFed control plane to manage
422
424
// resources in the joining cluster. The name of the created service
423
425
// account is returned on success.
424
426
func createAuthorizedServiceAccount (joiningClusterClientset kubeclient.Interface ,
425
427
namespace , joiningClusterName , hostClusterName string ,
426
- scope apiextv1.ResourceScope , dryRun , errorOnExisting bool ) (string , error ) {
428
+ scope apiextv1.ResourceScope , dryRun , errorOnExisting bool ) (saTokenSecretName string , err error ) {
427
429
klog .V (2 ).Infof ("Creating service account in joining cluster: %s" , joiningClusterName )
428
430
429
431
saName , err := createServiceAccount (joiningClusterClientset , namespace ,
@@ -436,6 +438,16 @@ func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface
436
438
437
439
klog .V (2 ).Infof ("Created service account: %s in joining cluster: %s" , saName , joiningClusterName )
438
440
441
+ saTokenSecretName , err = createServiceAccountTokenSecret (saName , joiningClusterClientset , namespace ,
442
+ joiningClusterName , hostClusterName , dryRun , errorOnExisting )
443
+ if err != nil {
444
+ klog .V (2 ).Infof ("Error creating service account token secret: %s in joining cluster: %s due to: %v" ,
445
+ saName , joiningClusterName , err )
446
+ return "" , err
447
+ }
448
+
449
+ klog .V (2 ).Infof ("Created service account token secret: %s in joining cluster: %s" , saTokenSecretName , joiningClusterName )
450
+
439
451
if scope == apiextv1 .NamespaceScoped {
440
452
klog .V (2 ).Infof ("Creating role and binding for service account: %s in joining cluster: %s" , saName , joiningClusterName )
441
453
@@ -474,7 +486,7 @@ func createAuthorizedServiceAccount(joiningClusterClientset kubeclient.Interface
474
486
saName , joiningClusterName )
475
487
}
476
488
477
- return saName , nil
489
+ return saTokenSecretName , nil
478
490
}
479
491
480
492
// createServiceAccount creates a service account in the cluster associated
@@ -487,7 +499,11 @@ func createServiceAccount(clusterClientset kubeclient.Interface, namespace,
487
499
ObjectMeta : metav1.ObjectMeta {
488
500
Name : saName ,
489
501
Namespace : namespace ,
502
+ Annotations : map [string ]string {
503
+ "kubernetes.io/enforce-mountable-secrets" : "true" ,
504
+ },
490
505
},
506
+ AutomountServiceAccountToken : pointer .Bool (false ),
491
507
}
492
508
493
509
if dryRun {
@@ -510,6 +526,45 @@ func createServiceAccount(clusterClientset kubeclient.Interface, namespace,
510
526
}
511
527
}
512
528
529
+ // createServiceAccountTokenSecret creates a service account token secret in the cluster associated
530
+ // with clusterClientset with credentials that will be used by the host cluster
531
+ // to access its API server.
532
+ func createServiceAccountTokenSecret (saName string , clusterClientset kubeclient.Interface , namespace ,
533
+ joiningClusterName , hostClusterName string , dryRun , errorOnExisting bool ) (string , error ) {
534
+ saTokenSecretName := util .ClusterServiceAccountTokenSecretName (joiningClusterName , hostClusterName )
535
+ saTokenSecret := & corev1.Secret {
536
+ ObjectMeta : metav1.ObjectMeta {
537
+ Name : saTokenSecretName ,
538
+ Namespace : namespace ,
539
+ Annotations : map [string ]string {
540
+ "kubernetes.io/service-account.name" : saName ,
541
+ },
542
+ },
543
+ Type : corev1 .SecretTypeServiceAccountToken ,
544
+ }
545
+
546
+ if dryRun {
547
+ return saName , nil
548
+ }
549
+
550
+ // Create a new service account.
551
+ _ , err := clusterClientset .CoreV1 ().Secrets (namespace ).Create (
552
+ context .Background (), saTokenSecret , metav1.CreateOptions {},
553
+ )
554
+ switch {
555
+ case apierrors .IsAlreadyExists (err ) && errorOnExisting :
556
+ klog .V (2 ).Infof ("Service account token secret %s/%s already exists in target cluster %s" ,
557
+ namespace , saName , joiningClusterName )
558
+ return "" , err
559
+ case err != nil && ! apierrors .IsAlreadyExists (err ):
560
+ klog .V (2 ).Infof ("Could not create service account token secret %s/%s in target cluster %s due to: %v" ,
561
+ namespace , saName , joiningClusterName , err )
562
+ return "" , err
563
+ default :
564
+ return saTokenSecretName , nil
565
+ }
566
+ }
567
+
513
568
func bindingSubjects (saName , namespace string ) []rbacv1.Subject {
514
569
return []rbacv1.Subject {
515
570
{
@@ -828,7 +883,7 @@ func createHealthCheckClusterRoleAndBinding(clientset kubeclient.Interface, saNa
828
883
// hostClientset, putting it in a secret named secretName in the provided
829
884
// namespace.
830
885
func populateSecretInHostCluster (clusterClientset , hostClientset kubeclient.Interface ,
831
- saName , hostNamespace , joiningNamespace , joiningClusterName , secretName string ,
886
+ saTokenSecretName , hostNamespace , joiningNamespace , joiningClusterName , secretName string ,
832
887
dryRun bool , errorOnExisting bool ) (* corev1.Secret , []byte , error ) {
833
888
klog .V (2 ).Infof ("Creating cluster credentials secret in host cluster" )
834
889
@@ -840,33 +895,22 @@ func populateSecretInHostCluster(clusterClientset, hostClientset kubeclient.Inte
840
895
841
896
// Get the secret from the joining cluster.
842
897
var secret * corev1.Secret
898
+
843
899
err := wait .PollImmediate (1 * time .Second , serviceAccountSecretTimeout , func () (bool , error ) {
844
- sa , err := clusterClientset .CoreV1 ().ServiceAccounts (joiningNamespace ).Get (
845
- context .Background (), saName , metav1.GetOptions {},
900
+ joiningClusterSASecret , err := clusterClientset .CoreV1 ().Secrets (joiningNamespace ).Get (
901
+ context .Background (), saTokenSecretName , metav1.GetOptions {},
846
902
)
847
903
if err != nil {
848
904
return false , nil
849
905
}
850
906
851
- for _ , objReference := range sa .Secrets {
852
- saSecretName := objReference .Name
853
- var err error
854
- secret , err = clusterClientset .CoreV1 ().Secrets (joiningNamespace ).Get (
855
- context .Background (), saSecretName , metav1.GetOptions {},
856
- )
857
- if err != nil {
858
- return false , nil
859
- }
860
- if secret .Type == corev1 .SecretTypeServiceAccountToken {
861
- klog .V (2 ).Infof ("Using secret named: %s" , secret .Name )
862
- return true , nil
863
- }
864
- }
865
- return false , nil
907
+ secret = joiningClusterSASecret
908
+
909
+ return true , nil
866
910
})
867
911
868
912
if err != nil {
869
- klog .V (2 ).Infof ("Could not get service account secret from joining cluster: %v" , err )
913
+ klog .V (2 ).Infof ("Could not get service account token secret from joining cluster: %v" , err )
870
914
return nil , nil , err
871
915
}
872
916
0 commit comments