diff --git a/api/v1beta1/azuremanagedcontrolplane_types.go b/api/v1beta1/azuremanagedcontrolplane_types.go index 2c6972a8479c..e8217fed585d 100644 --- a/api/v1beta1/azuremanagedcontrolplane_types.go +++ b/api/v1beta1/azuremanagedcontrolplane_types.go @@ -191,6 +191,10 @@ type AzureManagedControlPlaneSpec struct { // Immutable. // +optional HTTPProxyConfig *HTTPProxyConfig `json:"httpProxyConfig,omitempty"` + + // OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. + // +optional + OIDCIssuerProfile *OIDCIssuerProfile `json:"oidcIssuerProfile,omitempty"` } // HTTPProxyConfig is the HTTP proxy configuration for the cluster. @@ -349,6 +353,21 @@ type AzureManagedControlPlaneStatus struct { // next reconciliation loop. // +optional LongRunningOperationStates Futures `json:"longRunningOperationStates,omitempty"` + + // OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. + // +optional + OIDCIssuerProfile *OIDCIssuerProfileStatus `json:"oidcIssuerProfile,omitempty"` +} + +// OIDCIssuerProfileStatus is the OIDC issuer profile of the Managed Cluster. +type OIDCIssuerProfileStatus struct { + // Enabled is whether the OIDC issuer is enabled. + // +optional + Enabled *bool `json:"enabled,omitempty"` + + // IssuerURL is the OIDC issuer url of the Managed Cluster. + // +optional + IssuerURL *string `json:"issuerURL,omitempty"` } // AutoScalerProfile parameters to be applied to the cluster-autoscaler. @@ -485,6 +504,16 @@ type Identity struct { UserAssignedIdentityResourceID string `json:"userAssignedIdentityResourceID,omitempty"` } +// OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. +// See also [AKS doc]. +// +// [AKS doc]: https://learn.microsoft.com/en-us/azure/aks/use-oidc-issuer +type OIDCIssuerProfile struct { + // Enabled is whether the OIDC issuer is enabled. + // +optional + Enabled *bool `json:"enabled,omitempty"` +} + // +kubebuilder:object:root=true // +kubebuilder:resource:path=azuremanagedcontrolplanes,scope=Namespaced,categories=cluster-api,shortName=amcp // +kubebuilder:storageversion diff --git a/api/v1beta1/azuremanagedcontrolplane_webhook.go b/api/v1beta1/azuremanagedcontrolplane_webhook.go index 6ceedbce04ed..b5bf1a2c9ee1 100644 --- a/api/v1beta1/azuremanagedcontrolplane_webhook.go +++ b/api/v1beta1/azuremanagedcontrolplane_webhook.go @@ -280,6 +280,7 @@ func (m *AzureManagedControlPlane) Validate(cli client.Client) error { m.validateManagedClusterNetwork, m.validateAutoScalerProfile, m.validateIdentity, + m.validateOIDCIssuerProfile, } var errs []error @@ -713,3 +714,25 @@ func (m *AzureManagedControlPlane) validateIdentity(_ client.Client) error { return nil } + +// validateOIDCIssuerProfile validates an OIDCIssuerProfile. +func (m *AzureManagedControlPlane) validateOIDCIssuerProfile(_ client.Client) error { + var allErrs field.ErrorList + + if m.Status.OIDCIssuerProfile != nil { + if ptr.Deref(m.Status.OIDCIssuerProfile.Enabled, false) && !ptr.Deref(ptr.Deref(m.Spec.OIDCIssuerProfile, OIDCIssuerProfile{}).Enabled, true) { + allErrs = append(allErrs, + field.Forbidden( + field.NewPath("Spec", "OIDCIssuerProfile", "Enabled"), + "cannot be disabled", + ), + ) + } + } + + if len(allErrs) > 0 { + return kerrors.NewAggregate(allErrs.ToAggregate().Errors()) + } + + return nil +} diff --git a/api/v1beta1/azuremanagedcontrolplane_webhook_test.go b/api/v1beta1/azuremanagedcontrolplane_webhook_test.go index 0cee7916b403..84779946679f 100644 --- a/api/v1beta1/azuremanagedcontrolplane_webhook_test.go +++ b/api/v1beta1/azuremanagedcontrolplane_webhook_test.go @@ -662,6 +662,186 @@ func TestValidatingWebhook(t *testing.T) { }, expectErr: true, }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled nil -> nil OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: nil, + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: nil, + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled nil -> false OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: nil, + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(false), + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled nil -> true OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: nil, + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(true), + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled false -> nil OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: ptr.To(false), + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: nil, + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled false -> false OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: ptr.To(false), + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(false), + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled false -> true OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: ptr.To(false), + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(true), + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled true -> nil OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: ptr.To(true), + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: nil, + }, + }, + }, + expectErr: false, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled true -> false err", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: ptr.To(true), + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(false), + }, + }, + }, + expectErr: true, + }, + { + name: "AzureManagedControlPlane OIDCIssuerProfile.Enabled true -> true OK", + amcp: AzureManagedControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-cluster", + }, + Status: AzureManagedControlPlaneStatus{ + OIDCIssuerProfile: &OIDCIssuerProfileStatus{ + Enabled: ptr.To(true), + }, + }, + Spec: AzureManagedControlPlaneSpec{ + Version: "v0.0.0", + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(true), + }, + }, + }, + expectErr: false, + }, } for _, tt := range tests { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 1d948e2b4526..786637db9c99 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1237,6 +1237,11 @@ func (in *AzureManagedControlPlaneSpec) DeepCopyInto(out *AzureManagedControlPla *out = new(HTTPProxyConfig) (*in).DeepCopyInto(*out) } + if in.OIDCIssuerProfile != nil { + in, out := &in.OIDCIssuerProfile, &out.OIDCIssuerProfile + *out = new(OIDCIssuerProfile) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureManagedControlPlaneSpec. @@ -1264,6 +1269,11 @@ func (in *AzureManagedControlPlaneStatus) DeepCopyInto(out *AzureManagedControlP *out = make(Futures, len(*in)) copy(*out, *in) } + if in.OIDCIssuerProfile != nil { + in, out := &in.OIDCIssuerProfile, &out.OIDCIssuerProfile + *out = new(OIDCIssuerProfileStatus) + (*in).DeepCopyInto(*out) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureManagedControlPlaneStatus. @@ -2403,6 +2413,51 @@ func (in *NetworkTemplateSpec) DeepCopy() *NetworkTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCIssuerProfile) DeepCopyInto(out *OIDCIssuerProfile) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCIssuerProfile. +func (in *OIDCIssuerProfile) DeepCopy() *OIDCIssuerProfile { + if in == nil { + return nil + } + out := new(OIDCIssuerProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OIDCIssuerProfileStatus) DeepCopyInto(out *OIDCIssuerProfileStatus) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = new(bool) + **out = **in + } + if in.IssuerURL != nil { + in, out := &in.IssuerURL, &out.IssuerURL + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCIssuerProfileStatus. +func (in *OIDCIssuerProfileStatus) DeepCopy() *OIDCIssuerProfileStatus { + if in == nil { + return nil + } + out := new(OIDCIssuerProfileStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OSDisk) DeepCopyInto(out *OSDisk) { *out = *in diff --git a/azure/scope/managedcontrolplane.go b/azure/scope/managedcontrolplane.go index 3ce88419d9e0..e48932ec0f20 100644 --- a/azure/scope/managedcontrolplane.go +++ b/azure/scope/managedcontrolplane.go @@ -588,6 +588,12 @@ func (s *ManagedControlPlaneScope) ManagedClusterSpec() azure.ResourceSpecGetter } } + if s.ControlPlane.Spec.OIDCIssuerProfile != nil { + managedClusterSpec.OIDCIssuerProfile = &managedclusters.OIDCIssuerProfile{ + Enabled: s.ControlPlane.Spec.OIDCIssuerProfile.Enabled, + } + } + return &managedClusterSpec } @@ -820,3 +826,8 @@ func (s *ManagedControlPlaneScope) PrivateEndpointSpecs() []azure.ResourceSpecGe return privateEndpointSpecs } + +// SetOIDCIssuerProfileStatus sets the status for the OIDC issuer profile config. +func (s *ManagedControlPlaneScope) SetOIDCIssuerProfileStatus(oidc *infrav1.OIDCIssuerProfileStatus) { + s.ControlPlane.Status.OIDCIssuerProfile = oidc +} diff --git a/azure/services/managedclusters/managedclusters.go b/azure/services/managedclusters/managedclusters.go index 51f34df44ae9..9d6774b2c91d 100644 --- a/azure/services/managedclusters/managedclusters.go +++ b/azure/services/managedclusters/managedclusters.go @@ -45,6 +45,7 @@ type ManagedClusterScope interface { MakeEmptyKubeConfigSecret() corev1.Secret GetKubeConfigData() []byte SetKubeConfigData([]byte) + SetOIDCIssuerProfileStatus(*infrav1.OIDCIssuerProfileStatus) } // Service provides operations on azure resources. @@ -112,6 +113,13 @@ func (s *Service) Reconcile(ctx context.Context) error { if id := managedCluster.Properties.IdentityProfile[kubeletIdentityKey]; id != nil && id.ResourceID != nil { s.Scope.SetKubeletIdentity(*id.ResourceID) } + + if managedCluster.Properties.OidcIssuerProfile != nil { + s.Scope.SetOIDCIssuerProfileStatus(&infrav1.OIDCIssuerProfileStatus{ + Enabled: managedCluster.Properties.OidcIssuerProfile.Enabled, + IssuerURL: managedCluster.Properties.OidcIssuerProfile.IssuerURL, + }) + } } s.Scope.UpdatePutStatus(infrav1.ManagedClusterRunningCondition, serviceName, resultErr) return resultErr diff --git a/azure/services/managedclusters/managedclusters_test.go b/azure/services/managedclusters/managedclusters_test.go index 52ed19d3657a..5040ab20d8d6 100644 --- a/azure/services/managedclusters/managedclusters_test.go +++ b/azure/services/managedclusters/managedclusters_test.go @@ -70,6 +70,10 @@ func TestReconcile(t *testing.T) { ResourceID: ptr.To("kubelet-id"), }, }, + OidcIssuerProfile: &armcontainerservice.ManagedClusterOIDCIssuerProfile{ + Enabled: ptr.To(true), + IssuerURL: ptr.To("oidc issuer url"), + }, }, }, nil) s.SetControlPlaneEndpoint(clusterv1.APIEndpoint{ @@ -79,6 +83,10 @@ func TestReconcile(t *testing.T) { m.GetCredentials(gomockinternal.AContext(), "my-rg", "my-managedcluster").Return([]byte("credentials"), nil) s.SetKubeConfigData([]byte("credentials")) s.SetKubeletIdentity("kubelet-id") + s.SetOIDCIssuerProfileStatus(&infrav1.OIDCIssuerProfileStatus{ + Enabled: ptr.To(true), + IssuerURL: ptr.To("oidc issuer url"), + }) s.UpdatePutStatus(infrav1.ManagedClusterRunningCondition, serviceName, nil) }, }, diff --git a/azure/services/managedclusters/mock_managedclusters/managedclusters_mock.go b/azure/services/managedclusters/mock_managedclusters/managedclusters_mock.go index da051e8bd50c..e158d4732a1b 100644 --- a/azure/services/managedclusters/mock_managedclusters/managedclusters_mock.go +++ b/azure/services/managedclusters/mock_managedclusters/managedclusters_mock.go @@ -255,6 +255,18 @@ func (mr *MockManagedClusterScopeMockRecorder) SetLongRunningOperationState(arg0 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetLongRunningOperationState", reflect.TypeOf((*MockManagedClusterScope)(nil).SetLongRunningOperationState), arg0) } +// SetOIDCIssuerProfileStatus mocks base method. +func (m *MockManagedClusterScope) SetOIDCIssuerProfileStatus(arg0 *v1beta1.OIDCIssuerProfileStatus) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "SetOIDCIssuerProfileStatus", arg0) +} + +// SetOIDCIssuerProfileStatus indicates an expected call of SetOIDCIssuerProfileStatus. +func (mr *MockManagedClusterScopeMockRecorder) SetOIDCIssuerProfileStatus(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetOIDCIssuerProfileStatus", reflect.TypeOf((*MockManagedClusterScope)(nil).SetOIDCIssuerProfileStatus), arg0) +} + // SubscriptionID mocks base method. func (m *MockManagedClusterScope) SubscriptionID() string { m.ctrl.T.Helper() diff --git a/azure/services/managedclusters/spec.go b/azure/services/managedclusters/spec.go index 430ba9ce132d..5f9129b72c31 100644 --- a/azure/services/managedclusters/spec.go +++ b/azure/services/managedclusters/spec.go @@ -116,6 +116,9 @@ type ManagedClusterSpec struct { // HTTPProxyConfig is the HTTP proxy configuration for the cluster. HTTPProxyConfig *HTTPProxyConfig + + // OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. + OIDCIssuerProfile *OIDCIssuerProfile } // HTTPProxyConfig is the HTTP proxy configuration for the cluster. @@ -231,6 +234,12 @@ type AutoScalerProfile struct { SkipNodesWithSystemPods *string } +// OIDCIssuerProfile is the OIDC issuer profile of the Managed Cluster. +type OIDCIssuerProfile struct { + // Enabled is whether the OIDC issuer is enabled. + Enabled *bool +} + var _ azure.ResourceSpecGetterWithHeaders = (*ManagedClusterSpec)(nil) // ResourceName returns the name of the AKS cluster. @@ -444,6 +453,12 @@ func (s *ManagedClusterSpec) Parameters(ctx context.Context, existing interface{ } } + if s.OIDCIssuerProfile != nil { + managedCluster.Properties.OidcIssuerProfile = &armcontainerservice.ManagedClusterOIDCIssuerProfile{ + Enabled: s.OIDCIssuerProfile.Enabled, + } + } + if existing != nil { existingMC, ok := existing.(armcontainerservice.ManagedCluster) if !ok { @@ -698,6 +713,19 @@ func computeDiffOfNormalizedClusters(managedCluster armcontainerservice.ManagedC existingMCClusterNormalized.SKU = existingMC.SKU } + if existingMC.Properties.OidcIssuerProfile != nil { + existingMCClusterNormalized.Properties.OidcIssuerProfile = &armcontainerservice.ManagedClusterOIDCIssuerProfile{ + Enabled: existingMC.Properties.OidcIssuerProfile.Enabled, + } + } + if managedCluster.Properties.OidcIssuerProfile != nil { + clusterNormalized.Properties.OidcIssuerProfile = &armcontainerservice.ManagedClusterOIDCIssuerProfile{ + Enabled: managedCluster.Properties.OidcIssuerProfile.Enabled, + } + } else { + clusterNormalized.Properties.OidcIssuerProfile = existingMCClusterNormalized.Properties.OidcIssuerProfile + } + diff := cmp.Diff(clusterNormalized, existingMCClusterNormalized) return diff } diff --git a/azure/services/managedclusters/spec_test.go b/azure/services/managedclusters/spec_test.go index 276d9d48662a..65daba7905c0 100644 --- a/azure/services/managedclusters/spec_test.go +++ b/azure/services/managedclusters/spec_test.go @@ -71,6 +71,9 @@ func TestParameters(t *testing.T) { Version: "v1.22.0", LoadBalancerSKU: "standard", SSHPublicKey: base64.StdEncoding.EncodeToString([]byte("test-ssh-key")), + OIDCIssuerProfile: &OIDCIssuerProfile{ + Enabled: ptr.To(true), + }, GetAllAgentPools: func() ([]azure.ResourceSpecGetter, error) { return []azure.ResourceSpecGetter{ &agentpools.AgentPoolSpec{ @@ -379,6 +382,24 @@ func TestParameters(t *testing.T) { g.Expect(result).To(BeNil()) }, }, + { + name: "no update needed when oidc issuer profile is nil", + existing: getExistingCluster(), + spec: &ManagedClusterSpec{ + Name: "test-managedcluster", + ResourceGroup: "test-rg", + Location: "test-location", + Tags: map[string]string{ + "test-tag": "test-value", + }, + Version: "v1.22.0", + LoadBalancerSKU: "standard", + OIDCIssuerProfile: nil, + }, + expect: func(g *WithT, result interface{}) { + g.Expect(result).To(BeNil()) + }, + }, } for _, tc := range testcases { tc := tc @@ -529,6 +550,9 @@ func getSampleManagedCluster() armcontainerservice.ManagedCluster { NetworkProfile: &armcontainerservice.NetworkProfile{ LoadBalancerSKU: ptr.To(armcontainerservice.LoadBalancerSKUStandard), }, + OidcIssuerProfile: &armcontainerservice.ManagedClusterOIDCIssuerProfile{ + Enabled: ptr.To(true), + }, }, Identity: &armcontainerservice.ManagedClusterIdentity{ Type: ptr.To(armcontainerservice.ResourceIdentityTypeSystemAssigned), diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedcontrolplanes.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedcontrolplanes.yaml index 088d5a80a9ff..ea967ce59998 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedcontrolplanes.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_azuremanagedcontrolplanes.yaml @@ -387,6 +387,14 @@ spec: containing cluster IaaS resources. Will be populated to default in webhook. Immutable. type: string + oidcIssuerProfile: + description: OIDCIssuerProfile is the OIDC issuer profile of the Managed + Cluster. + properties: + enabled: + description: Enabled is whether the OIDC issuer is enabled. + type: boolean + type: object outboundType: description: Outbound configuration used by Nodes. Immutable. enum: @@ -649,6 +657,17 @@ spec: - type type: object type: array + oidcIssuerProfile: + description: OIDCIssuerProfile is the OIDC issuer profile of the Managed + Cluster. + properties: + enabled: + description: Enabled is whether the OIDC issuer is enabled. + type: boolean + issuerURL: + description: IssuerURL is the OIDC issuer url of the Managed Cluster. + type: string + type: object ready: description: Ready is true when the provider resource is ready. type: boolean diff --git a/test/e2e/aks_oidc.go b/test/e2e/aks_oidc.go new file mode 100644 index 000000000000..7b64e3022ab5 --- /dev/null +++ b/test/e2e/aks_oidc.go @@ -0,0 +1,83 @@ +//go:build e2e +// +build e2e + +/* +Copyright 2023 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + infrav1 "sigs.k8s.io/cluster-api-provider-azure/api/v1beta1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +type AKSOIDCIssuerSpecInput struct { + Cluster *clusterv1.Cluster + WaitForUpdate []interface{} +} + +func AKSOIDCIssuerSpec(ctx context.Context, inputGetter func() AKSOIDCIssuerSpecInput) { + input := inputGetter() + + mgmtClient := bootstrapClusterProxy.GetClient() + Expect(mgmtClient).NotTo(BeNil()) + + infraControlPlane := &infrav1.AzureManagedControlPlane{} + err := mgmtClient.Get(ctx, client.ObjectKey{ + Namespace: input.Cluster.Spec.ControlPlaneRef.Namespace, + Name: input.Cluster.Spec.ControlPlaneRef.Name, + }, infraControlPlane) + Expect(err).NotTo(HaveOccurred()) + + Expect(infraControlPlane.Status.OIDCIssuerProfile).NotTo(BeNil()) + Expect(infraControlPlane.Status.OIDCIssuerProfile.Enabled).NotTo(BeNil()) + Expect(*infraControlPlane.Status.OIDCIssuerProfile.Enabled).To(BeFalse()) + + checkOIDC := func(g Gomega) { + err := mgmtClient.Get(ctx, client.ObjectKeyFromObject(infraControlPlane), infraControlPlane) + g.Expect(err).NotTo(HaveOccurred()) + + if infraControlPlane.Spec.OIDCIssuerProfile == nil { + return + } + + g.Expect(infraControlPlane.Status.OIDCIssuerProfile).NotTo(BeNil()) + g.Expect(infraControlPlane.Status.OIDCIssuerProfile.Enabled).NotTo(BeNil()) + g.Expect(*infraControlPlane.Status.OIDCIssuerProfile.Enabled).To(Equal(*infraControlPlane.Spec.OIDCIssuerProfile.Enabled)) + if *infraControlPlane.Status.OIDCIssuerProfile.Enabled { + g.Expect(infraControlPlane.Status.OIDCIssuerProfile.IssuerURL).NotTo(BeNil()) + g.Expect(*infraControlPlane.Status.OIDCIssuerProfile.IssuerURL).NotTo(BeEmpty()) + } + } + + By("Enabling OIDC issuer") + oidcIssuerEnabled := true + Eventually(func(g Gomega) { + g.Expect(mgmtClient.Get(ctx, client.ObjectKeyFromObject(infraControlPlane), infraControlPlane)).To(Succeed()) + infraControlPlane.Spec.OIDCIssuerProfile = &infrav1.OIDCIssuerProfile{ + Enabled: &oidcIssuerEnabled, + } + g.Expect(mgmtClient.Update(ctx, infraControlPlane)).To(Succeed()) + }, inputGetter().WaitForUpdate...).Should(Succeed()) + Eventually(checkOIDC, input.WaitForUpdate...).Should(Succeed()) + + // OIDC issuer cannot be disabled. +} diff --git a/test/e2e/azure_test.go b/test/e2e/azure_test.go index 3a5642282200..98c7af264a12 100644 --- a/test/e2e/azure_test.go +++ b/test/e2e/azure_test.go @@ -839,6 +839,15 @@ var _ = Describe("Workload cluster creation", func() { } }) }) + + By("modifying OIDC issuer configuration", func() { + AKSOIDCIssuerSpec(ctx, func() AKSOIDCIssuerSpecInput { + return AKSOIDCIssuerSpecInput{ + Cluster: result.Cluster, + WaitForUpdate: e2eConfig.GetIntervals(specName, "wait-control-plane"), + } + }) + }) }) })