Skip to content

Commit a3b41e7

Browse files
committed
feat: add support for identity service server in GKE controlplane status
1 parent e5a8122 commit a3b41e7

File tree

4 files changed

+108
-22
lines changed

4 files changed

+108
-22
lines changed

cloud/services/container/clusters/kubeconfig.go

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -41,34 +41,35 @@ const (
4141
GkeScope = "https://www.googleapis.com/auth/cloud-platform"
4242
)
4343

44-
func (s *Service) reconcileKubeconfig(ctx context.Context, cluster *containerpb.Cluster, log *logr.Logger) error {
44+
func (s *Service) reconcileKubeconfig(ctx context.Context, cluster *containerpb.Cluster, log *logr.Logger) (clientcmd.ClientConfig, error) {
4545
log.Info("Reconciling kubeconfig")
4646
clusterRef := types.NamespacedName{
4747
Name: s.scope.Cluster.Name,
4848
Namespace: s.scope.Cluster.Namespace,
4949
}
50+
var kubeConfig *api.Config
5051

5152
configSecret, err := secret.GetFromNamespacedName(ctx, s.scope.Client(), clusterRef, secret.Kubeconfig)
5253
if err != nil {
5354
if !apierrors.IsNotFound(err) {
5455
log.Error(err, "getting kubeconfig secret", "name", clusterRef)
55-
return fmt.Errorf("getting kubeconfig secret %s: %w", clusterRef, err)
56+
return nil, fmt.Errorf("getting kubeconfig secret %s: %w", clusterRef, err)
5657
}
5758
log.Info("kubeconfig secret not found, creating")
5859

59-
if createErr := s.createCAPIKubeconfigSecret(
60+
if kubeConfig, err = s.createCAPIKubeconfigSecret(
6061
ctx,
6162
cluster,
6263
&clusterRef,
6364
log,
64-
); createErr != nil {
65-
return fmt.Errorf("creating kubeconfig secret: %w", createErr)
65+
); err != nil {
66+
return nil, fmt.Errorf("creating kubeconfig secret: %w", err)
6667
}
67-
} else if updateErr := s.updateCAPIKubeconfigSecret(ctx, configSecret); updateErr != nil {
68-
return fmt.Errorf("updating kubeconfig secret: %w", err)
68+
} else if kubeConfig, err = s.updateCAPIKubeconfigSecret(ctx, configSecret); err != nil {
69+
return nil, fmt.Errorf("updating kubeconfig secret: %w", err)
6970
}
7071

71-
return nil
72+
return clientcmd.NewDefaultClientConfig(*kubeConfig, nil), nil
7273
}
7374

7475
func (s *Service) reconcileAdditionalKubeconfigs(ctx context.Context, cluster *containerpb.Cluster, log *logr.Logger) error {
@@ -133,21 +134,21 @@ func (s *Service) createUserKubeconfigSecret(ctx context.Context, cluster *conta
133134
return nil
134135
}
135136

136-
func (s *Service) createCAPIKubeconfigSecret(ctx context.Context, cluster *containerpb.Cluster, clusterRef *types.NamespacedName, log *logr.Logger) error {
137+
func (s *Service) createCAPIKubeconfigSecret(ctx context.Context, cluster *containerpb.Cluster, clusterRef *types.NamespacedName, log *logr.Logger) (*api.Config, error) {
137138
controllerOwnerRef := *metav1.NewControllerRef(s.scope.GCPManagedControlPlane, infrav1exp.GroupVersion.WithKind("GCPManagedControlPlane"))
138139

139140
contextName := s.getKubeConfigContextName(false)
140141

141142
cfg, err := s.createBaseKubeConfig(contextName, cluster)
142143
if err != nil {
143144
log.Error(err, "failed creating base config")
144-
return fmt.Errorf("creating base kubeconfig: %w", err)
145+
return nil, fmt.Errorf("creating base kubeconfig: %w", err)
145146
}
146147

147148
token, err := s.generateToken(ctx)
148149
if err != nil {
149150
log.Error(err, "failed generating token")
150-
return err
151+
return nil, err
151152
}
152153
cfg.AuthInfos = map[string]*api.AuthInfo{
153154
contextName: {
@@ -158,50 +159,50 @@ func (s *Service) createCAPIKubeconfigSecret(ctx context.Context, cluster *conta
158159
out, err := clientcmd.Write(*cfg)
159160
if err != nil {
160161
log.Error(err, "failed serializing kubeconfig to yaml")
161-
return fmt.Errorf("serialize kubeconfig to yaml: %w", err)
162+
return nil, fmt.Errorf("serialize kubeconfig to yaml: %w", err)
162163
}
163164

164165
kubeconfigSecret := kubeconfig.GenerateSecretWithOwner(*clusterRef, out, controllerOwnerRef)
165166
if err := s.scope.Client().Create(ctx, kubeconfigSecret); err != nil {
166167
log.Error(err, "failed creating secret")
167-
return fmt.Errorf("creating secret: %w", err)
168+
return nil, fmt.Errorf("creating secret: %w", err)
168169
}
169170

170-
return nil
171+
return cfg, nil
171172
}
172173

173-
func (s *Service) updateCAPIKubeconfigSecret(ctx context.Context, configSecret *corev1.Secret) error {
174+
func (s *Service) updateCAPIKubeconfigSecret(ctx context.Context, configSecret *corev1.Secret) (*api.Config, error) {
174175
data, ok := configSecret.Data[secret.KubeconfigDataName]
175176
if !ok {
176-
return errors.Errorf("missing key %q in secret data", secret.KubeconfigDataName)
177+
return nil, errors.Errorf("missing key %q in secret data", secret.KubeconfigDataName)
177178
}
178179

179180
config, err := clientcmd.Load(data)
180181
if err != nil {
181-
return errors.Wrap(err, "failed to convert kubeconfig Secret into a clientcmdapi.Config")
182+
return nil, errors.Wrap(err, "failed to convert kubeconfig Secret into a clientcmdapi.Config")
182183
}
183184

184185
token, err := s.generateToken(ctx)
185186
if err != nil {
186-
return err
187+
return nil, err
187188
}
188189

189190
contextName := s.getKubeConfigContextName(false)
190191
config.AuthInfos[contextName].Token = token
191192

192193
out, err := clientcmd.Write(*config)
193194
if err != nil {
194-
return errors.Wrap(err, "failed to serialize config to yaml")
195+
return nil, errors.Wrap(err, "failed to serialize config to yaml")
195196
}
196197

197198
configSecret.Data[secret.KubeconfigDataName] = out
198199

199200
err = s.scope.Client().Update(ctx, configSecret)
200201
if err != nil {
201-
return fmt.Errorf("updating kubeconfig secret: %w", err)
202+
return nil, fmt.Errorf("updating kubeconfig secret: %w", err)
202203
}
203204

204-
return nil
205+
return config, nil
205206
}
206207

207208
func (s *Service) getKubeConfigContextName(isUser bool) string {

cloud/services/container/clusters/reconcile.go

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ import (
3131
"github.com/googleapis/gax-go/v2/apierror"
3232
"github.com/pkg/errors"
3333
"google.golang.org/grpc/codes"
34+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
"k8s.io/apimachinery/pkg/runtime"
36+
"k8s.io/apimachinery/pkg/runtime/schema"
37+
"k8s.io/client-go/dynamic"
38+
"k8s.io/client-go/tools/clientcmd"
3439
infrav1exp "sigs.k8s.io/cluster-api-provider-gcp/exp/api/v1beta1"
3540
"sigs.k8s.io/cluster-api-provider-gcp/util/reconciler"
3641
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -155,7 +160,7 @@ func (s *Service) Reconcile(ctx context.Context) (ctrl.Result, error) {
155160
conditions.MarkFalse(s.scope.ConditionSetter(), infrav1exp.GKEControlPlaneUpdatingCondition, infrav1exp.GKEControlPlaneUpdatedReason, clusterv1.ConditionSeverityInfo, "")
156161

157162
// Reconcile kubeconfig
158-
err = s.reconcileKubeconfig(ctx, cluster, &log)
163+
kubeConfig, err := s.reconcileKubeconfig(ctx, cluster, &log)
159164
if err != nil {
160165
log.Error(err, "Failed to reconcile CAPI kubeconfig")
161166
return ctrl.Result{}, err
@@ -166,6 +171,11 @@ func (s *Service) Reconcile(ctx context.Context) (ctrl.Result, error) {
166171
return ctrl.Result{}, err
167172
}
168173

174+
err = s.reconcileIdentityService(ctx, kubeConfig, &log)
175+
if err != nil {
176+
return ctrl.Result{}, err
177+
}
178+
169179
s.scope.SetEndpoint(cluster.GetEndpoint())
170180
conditions.MarkTrue(s.scope.ConditionSetter(), clusterv1.ReadyCondition)
171181
conditions.MarkTrue(s.scope.ConditionSetter(), infrav1exp.GKEControlPlaneReadyCondition)
@@ -512,3 +522,70 @@ func compareMasterAuthorizedNetworksConfig(a, b *containerpb.MasterAuthorizedNet
512522
}
513523
return true
514524
}
525+
526+
// reconcileIdentityService set the identity service server in the status of the GCPManagedControlPlane.
527+
func (s *Service) reconcileIdentityService(ctx context.Context, kubeConfig clientcmd.ClientConfig, log *logr.Logger) error {
528+
identityServiceServer, err := s.getIdentityServiceServer(ctx, kubeConfig)
529+
if err != nil {
530+
err = fmt.Errorf("failed to retrieve identity service: %w", err)
531+
log.Error(err, "Failed to retrieve identity service server")
532+
return err
533+
}
534+
535+
s.scope.GCPManagedControlPlane.Status.IdentityServiceServer = identityServiceServer
536+
537+
return nil
538+
}
539+
540+
// getIdentityServiceServer retrieve the server to use for authentication using the identity service.
541+
func (s *Service) getIdentityServiceServer(ctx context.Context, kubeConfig clientcmd.ClientConfig) (string, error) {
542+
/*
543+
# Example of the ClientConfig (see https://cloud.google.com/kubernetes-engine/docs/how-to/oidc#configuring_on_a_cluster):
544+
apiVersion: authentication.gke.io/v2alpha1
545+
kind: ClientConfig
546+
metadata:
547+
name: default
548+
namespace: kube-public
549+
spec:
550+
server: https://192.168.0.1:6443
551+
*/
552+
553+
if !s.scope.GCPManagedControlPlane.Spec.EnableIdentityService {
554+
// Identity service is not enabled, skipping
555+
return "", nil
556+
}
557+
558+
if kubeConfig == nil {
559+
return "", errors.New("provided kubernetes configuration is nil")
560+
}
561+
562+
config, err := kubeConfig.ClientConfig()
563+
if err != nil {
564+
return "", err
565+
}
566+
567+
dynamicClient, err := dynamic.NewForConfig(config)
568+
if err != nil {
569+
return "", err
570+
}
571+
572+
resourceID := schema.GroupVersionResource{
573+
Group: "authentication.gke.io",
574+
Version: "v2alpha1",
575+
Resource: "clientconfigs",
576+
}
577+
578+
unstructured, err := dynamicClient.Resource(resourceID).Namespace("kube-public").Get(ctx, "default", metav1.GetOptions{})
579+
if err != nil {
580+
return "", err
581+
}
582+
583+
gkeClientConfig := struct {
584+
Spec struct {
585+
Server string `json:"server"`
586+
} `json:"spec"`
587+
}{}
588+
err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.Object, &gkeClientConfig)
589+
590+
return gkeClientConfig.Spec.Server, err
591+
}

config/crd/bases/infrastructure.cluster.x-k8s.io_gcpmanagedcontrolplanes.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ spec:
275275
description: CurrentVersion shows the current version of the GKE control
276276
plane.
277277
type: string
278+
identityServiceServer:
279+
description: IdentityServiceServer indicates when the identity service
280+
is enabled, the server for external authentication.
281+
type: string
278282
initialized:
279283
description: |-
280284
Initialized is true when the control plane is available for initial contact.

exp/api/v1beta1/gcpmanagedcontrolplane_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ type GCPManagedControlPlaneStatus struct {
185185
// CurrentVersion shows the current version of the GKE control plane.
186186
// +optional
187187
CurrentVersion string `json:"currentVersion,omitempty"`
188+
189+
// IdentityServiceServer indicates when the identity service is enabled, the server for external authentication.
190+
// +optional
191+
IdentityServiceServer string `json:"identityServiceServer,omitempty"`
188192
}
189193

190194
// +kubebuilder:object:root=true

0 commit comments

Comments
 (0)