diff --git a/control-plane-operator/controllers/hostedcontrolplane/cloud/azure/providerconfig.go b/control-plane-operator/controllers/hostedcontrolplane/cloud/azure/providerconfig.go index 0cf682ebfe4..c4eb918fee6 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/cloud/azure/providerconfig.go +++ b/control-plane-operator/controllers/hostedcontrolplane/cloud/azure/providerconfig.go @@ -123,4 +123,5 @@ type AzureConfig struct { LoadBalancerSku string `json:"loadBalancerSku"` DisableOutboundSNAT bool `json:"disableOutboundSNAT"` LoadBalancerName string `json:"loadBalancerName"` + AADClientCertPath string `json:"aadClientCertPath"` } diff --git a/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go b/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go index 0d88997afa1..75662d3e299 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go +++ b/control-plane-operator/controllers/hostedcontrolplane/hostedcontrolplane_controller.go @@ -4969,6 +4969,50 @@ func (r *HostedControlPlaneReconciler) reconcileCSISnapshotControllerOperator(ct func (r *HostedControlPlaneReconciler) reconcileClusterStorageOperator(ctx context.Context, hcp *hyperv1.HostedControlPlane, releaseImageProvider, userReleaseImageProvider *imageprovider.SimpleReleaseImageProvider, createOrUpdate upsert.CreateOrUpdateFN) error { params := storage.NewParams(hcp, userReleaseImageProvider.Version(), releaseImageProvider, userReleaseImageProvider, r.SetDefaultSecurityContext) + if hyperazureutil.IsAroHCP() { + // Reconcile SecretProviderClasses + azureDiskSecretProviderClass := manifests.ManagedAzureSecretProviderClass(config.ManagedAzureDiskCSISecretStoreProviderClassName, hcp.Namespace) + if _, err := createOrUpdate(ctx, r, azureDiskSecretProviderClass, func() error { + secretproviderclass.ReconcileManagedAzureSecretProviderClass(azureDiskSecretProviderClass, hcp, hcp.Spec.Platform.Azure.ManagedIdentities.ControlPlane.Disk.CertificateName) + return nil + }); err != nil { + return fmt.Errorf("failed to reconcile Azure Disk Secret Provider Class: %w", err) + } + + azureFileSecretProviderClass := manifests.ManagedAzureSecretProviderClass(config.ManagedAzureFileCSISecretStoreProviderClassName, hcp.Namespace) + if _, err := createOrUpdate(ctx, r, azureFileSecretProviderClass, func() error { + secretproviderclass.ReconcileManagedAzureSecretProviderClass(azureFileSecretProviderClass, hcp, hcp.Spec.Platform.Azure.ManagedIdentities.ControlPlane.File.CertificateName) + return nil + }); err != nil { + return fmt.Errorf("failed to reconcile Azure File Secret Provider Class: %w", err) + } + + // Get the credentials secret so we can retrieve the tenant ID for the configuration + credentialsSecret := manifests.AzureCredentialInformation(hcp.Namespace) + if err := r.Client.Get(ctx, client.ObjectKeyFromObject(credentialsSecret), credentialsSecret); err != nil { + return fmt.Errorf("failed to get Azure credentials secret: %w", err) + } + tenantID := string(credentialsSecret.Data["AZURE_TENANT_ID"]) + + // Reconcile the secret needed for azure-disk-csi-controller + // This is related to https://github.com/openshift/csi-operator/pull/290. + azureDiskCSISecret := manifests.AzureDiskConfigWithCredentials(hcp.Namespace) + if _, err := createOrUpdate(ctx, r, azureDiskCSISecret, func() error { + return storage.ReconcileAzureDiskCSISecret(azureDiskCSISecret, hcp, tenantID) + }); err != nil { + return fmt.Errorf("failed to reconcile Azure Disk CSI config: %w", err) + } + + // Reconcile the secret needed for azure-disk-csi-controller + // This is related to https://github.com/openshift/csi-operator/pull/290. + azureFileCSISecret := manifests.AzureFileConfigWithCredentials(hcp.Namespace) + if _, err := createOrUpdate(ctx, r, azureDiskCSISecret, func() error { + return storage.ReconcileAzureFileCSISecret(azureFileCSISecret, hcp, tenantID) + }); err != nil { + return fmt.Errorf("failed to reconcile Azure File CSI config: %w", err) + } + } + if hcp.Spec.Platform.Type == hyperv1.AzurePlatform { credentialsSecret := manifests.AzureCredentialInformation(hcp.Namespace) if err := r.Client.Get(ctx, client.ObjectKeyFromObject(credentialsSecret), credentialsSecret); err != nil { diff --git a/control-plane-operator/controllers/hostedcontrolplane/storage/azure.go b/control-plane-operator/controllers/hostedcontrolplane/storage/azure.go new file mode 100644 index 00000000000..e77cc9afc2d --- /dev/null +++ b/control-plane-operator/controllers/hostedcontrolplane/storage/azure.go @@ -0,0 +1,61 @@ +package storage + +import ( + "encoding/json" + "fmt" + + hyperv1 "github.com/openshift/hypershift/api/hypershift/v1beta1" + "github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/cloud/azure" + + corev1 "k8s.io/api/core/v1" +) + +// initializeAzureCSIControllerConfig initializes an AzureConfig object which will be used to populate the secrets +// needed by azure-disk-csi-controller and azure-file-csi-controller. +func initializeAzureCSIControllerConfig(hcp *hyperv1.HostedControlPlane, tenantID string) azure.AzureConfig { + azureConfig := azure.AzureConfig{ + Cloud: hcp.Spec.Platform.Azure.Cloud, + TenantID: tenantID, + SubscriptionID: hcp.Spec.Platform.Azure.SubscriptionID, + ResourceGroup: hcp.Spec.Platform.Azure.ResourceGroupName, + Location: hcp.Spec.Platform.Azure.Location, + } + + return azureConfig +} + +// ReconcileAzureDiskCSISecret reconciles the configuration for the secret as expected by azure-disk-csi-controller +func ReconcileAzureDiskCSISecret(secret *corev1.Secret, hcp *hyperv1.HostedControlPlane, tenantID string) error { + config := initializeAzureCSIControllerConfig(hcp, tenantID) + config.AADClientID = hcp.Spec.Platform.Azure.ManagedIdentities.ControlPlane.Disk.ClientID + config.AADClientCertPath = hcp.Spec.Platform.Azure.ManagedIdentities.ControlPlane.Disk.CertificateName + + serializedConfig, err := json.MarshalIndent(config, "", " ") + if err != nil { + return fmt.Errorf("failed to serialize cloudconfig: %w", err) + } + + if secret.Data == nil { + secret.Data = map[string][]byte{} + } + secret.Data[azure.CloudConfigKey] = serializedConfig + return nil +} + +// ReconcileAzureFileCSISecret reconciles the configuration for the secret as expected by azure-file-csi-controller +func ReconcileAzureFileCSISecret(secret *corev1.Secret, hcp *hyperv1.HostedControlPlane, tenantID string) error { + config := initializeAzureCSIControllerConfig(hcp, tenantID) + config.AADClientID = hcp.Spec.Platform.Azure.ManagedIdentities.ControlPlane.File.ClientID + config.AADClientCertPath = hcp.Spec.Platform.Azure.ManagedIdentities.ControlPlane.File.CertificateName + + serializedConfig, err := json.MarshalIndent(config, "", " ") + if err != nil { + return fmt.Errorf("failed to serialize cloudconfig: %w", err) + } + + if secret.Data == nil { + secret.Data = map[string][]byte{} + } + secret.Data[azure.CloudConfigKey] = serializedConfig + return nil +} diff --git a/control-plane-operator/controllers/hostedcontrolplane/storage/operator.go b/control-plane-operator/controllers/hostedcontrolplane/storage/operator.go index 03f559846de..8b7d04a0175 100644 --- a/control-plane-operator/controllers/hostedcontrolplane/storage/operator.go +++ b/control-plane-operator/controllers/hostedcontrolplane/storage/operator.go @@ -6,6 +6,8 @@ import ( "github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/kas" "github.com/openshift/hypershift/control-plane-operator/controllers/hostedcontrolplane/storage/assets" assets2 "github.com/openshift/hypershift/support/assets" + "github.com/openshift/hypershift/support/azureutil" + "github.com/openshift/hypershift/support/config" "github.com/openshift/hypershift/support/util" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -31,6 +33,24 @@ func ReconcileOperatorDeployment( case "cluster-storage-operator": deployment.Spec.Template.Spec.Containers[i].Image = params.StorageOperatorImage params.ImageReplacer.replaceEnvVars(deployment.Spec.Template.Spec.Containers[i].Env) + + // For managed Azure, we need to supply a couple of environment variables for CSO to pass on to the CSI controllers for disk and file. + // CSO passes those on to the CSI deployment here - https://github.com/openshift/cluster-storage-operator/pull/517/files. + // CSI then mounts the Secrets Provider Class here - https://github.com/openshift/csi-operator/pull/309/files. + if azureutil.IsAroHCP() { + if deployment.Spec.Template.Spec.Containers[i].Env == nil { + deployment.Spec.Template.Spec.Containers[i].Env = make([]corev1.EnvVar, 0) + } + deployment.Spec.Template.Spec.Containers[i].Env = append(deployment.Spec.Template.Spec.Containers[i].Env, + corev1.EnvVar{ + Name: "ARO_HCP_SECRET_PROVIDER_CLASS_FOR_DISK", + Value: config.ManagedAzureDiskCSISecretStoreProviderClassName, + }, + corev1.EnvVar{ + Name: "ARO_HCP_SECRET_PROVIDER_CLASS_FOR_FILE", + Value: config.ManagedAzureFileCSISecretStoreProviderClassName, + }) + } } }