diff --git a/pkg/operator/configobservation/cloudprovider/observe_cloudprovider.go b/pkg/operator/configobservation/cloudprovider/observe_cloudprovider.go index 7f0410cad..01ddf40ab 100644 --- a/pkg/operator/configobservation/cloudprovider/observe_cloudprovider.go +++ b/pkg/operator/configobservation/cloudprovider/observe_cloudprovider.go @@ -3,45 +3,118 @@ package cloudprovider import ( "fmt" + "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" configv1 "github.com/openshift/api/config/v1" "github.com/openshift/library-go/pkg/operator/configobserver" "github.com/openshift/library-go/pkg/operator/events" + "github.com/openshift/library-go/pkg/operator/resourcesynccontroller" "github.com/openshift/cluster-kube-controller-manager-operator/pkg/operator/configobservation" ) -// ObserveCloudProviderNames observes the loud provider from the global cluster infrastructure resource. +const ( + targetNamespaceName = "openshift-kube-controller-manager" + cloudProviderConfFilePath = "/etc/kubernetes/static-pod-resources/configmaps/cloud-config/%s" + configNamespace = "openshift-config" +) + +// ObserveCloudProviderNames observes the cloud provider from the global cluster infrastructure resource. func ObserveCloudProviderNames(genericListers configobserver.Listers, recorder events.Recorder, existingConfig map[string]interface{}) (map[string]interface{}, []error) { listers := genericListers.(configobservation.Listers) var errs []error cloudProvidersPath := []string{"extendedArguments", "cloud-provider"} - + cloudProviderConfPath := []string{"extendedArguments", "cloud-config"} previouslyObservedConfig := map[string]interface{}{} + + existingCloudConfig, _, err := unstructured.NestedStringSlice(existingConfig, cloudProviderConfPath...) + if err != nil { + return previouslyObservedConfig, append(errs, err) + } + if currentCloudProvider, _, _ := unstructured.NestedStringSlice(existingConfig, cloudProvidersPath...); len(currentCloudProvider) > 0 { if err := unstructured.SetNestedStringSlice(previouslyObservedConfig, currentCloudProvider, cloudProvidersPath...); err != nil { errs = append(errs, err) } } + if len(existingCloudConfig) > 0 { + if err := unstructured.SetNestedStringSlice(previouslyObservedConfig, existingCloudConfig, cloudProviderConfPath...); err != nil { + errs = append(errs, err) + } + } + observedConfig := map[string]interface{}{} infrastructure, err := listers.InfrastructureLister.Get("cluster") if errors.IsNotFound(err) { - recorder.Warningf("ObserveRestrictedCIDRFailed", "Required infrastructures.%s/cluster not found", configv1.GroupName) + recorder.Warningf("ObserveCloudProviderNames", "Required infrastructures.%s/cluster not found", configv1.GroupName) return observedConfig, errs } if err != nil { return previouslyObservedConfig, errs } + cloudProvider := getPlatformName(infrastructure.Status.Platform, recorder) + if len(cloudProvider) > 0 { + if err := unstructured.SetNestedStringSlice(observedConfig, []string{cloudProvider}, cloudProvidersPath...); err != nil { + errs = append(errs, err) + } + } + + sourceCloudConfigMap := infrastructure.Spec.CloudConfig.Name + sourceCloudConfigNamespace := configNamespace + sourceLocation := resourcesynccontroller.ResourceLocation{ + Namespace: sourceCloudConfigNamespace, + Name: sourceCloudConfigMap, + } + // we set cloudprovider configmap values only for vsphere. + if cloudProvider != "vsphere" { + sourceCloudConfigMap = "" + } + + if len(sourceCloudConfigMap) == 0 { + sourceLocation = resourcesynccontroller.ResourceLocation{} + } + + err = listers.ResourceSyncer().SyncConfigMap( + resourcesynccontroller.ResourceLocation{ + Namespace: targetNamespaceName, + Name: "cloud-config", + }, + sourceLocation) + + if err != nil { + errs = append(errs, err) + return observedConfig, errs + } + + if len(sourceCloudConfigMap) == 0 { + return observedConfig, errs + } + + // usually key will be simply config but we should refer it just in case + staticCloudConfFile := fmt.Sprintf(cloudProviderConfFilePath, infrastructure.Spec.CloudConfig.Key) + + if err := unstructured.SetNestedStringSlice(observedConfig, []string{staticCloudConfFile}, cloudProviderConfPath...); err != nil { + recorder.Warningf("ObserveCloudProviderNames", "Failed setting cloud-config : %v", err) + errs = append(errs, err) + } + + if !equality.Semantic.DeepEqual(existingCloudConfig, []string{staticCloudConfFile}) { + recorder.Eventf("ObserveCloudProviderNamesChanges", "CloudProvider config file changed to %s", staticCloudConfFile) + } + + return observedConfig, errs +} + +func getPlatformName(platformType configv1.PlatformType, recorder events.Recorder) string { cloudProvider := "" - switch infrastructure.Status.Platform { + switch platformType { case "": recorder.Warningf("ObserveCloudProvidersFailed", "Required status.platform field is not set in infrastructures.%s/cluster", configv1.GroupName) - return previouslyObservedConfig, errs case configv1.AWSPlatformType: cloudProvider = "aws" case configv1.AzurePlatformType: @@ -58,12 +131,5 @@ func ObserveCloudProviderNames(genericListers configobserver.Listers, recorder e // TODO find a way to indicate to the user that we didn't honor their choice recorder.Warningf("ObserveCloudProvidersFailed", fmt.Sprintf("No recognized cloud provider platform found in infrastructures.%s/cluster.status.platform", configv1.GroupName)) } - - if len(cloudProvider) > 0 { - if err := unstructured.SetNestedStringSlice(observedConfig, []string{cloudProvider}, cloudProvidersPath...); err != nil { - errs = append(errs, err) - } - } - - return observedConfig, errs + return cloudProvider } diff --git a/pkg/operator/configobservation/cloudprovider/observe_cloudprovider_test.go b/pkg/operator/configobservation/cloudprovider/observe_cloudprovider_test.go index cfa9fc6dd..c1cfc89d8 100644 --- a/pkg/operator/configobservation/cloudprovider/observe_cloudprovider_test.go +++ b/pkg/operator/configobservation/cloudprovider/observe_cloudprovider_test.go @@ -11,10 +11,21 @@ import ( configv1 "github.com/openshift/api/config/v1" configlistersv1 "github.com/openshift/client-go/config/listers/config/v1" "github.com/openshift/library-go/pkg/operator/events" + "github.com/openshift/library-go/pkg/operator/resourcesynccontroller" "github.com/openshift/cluster-kube-controller-manager-operator/pkg/operator/configobservation" ) +type FakeResourceSyncer struct{} + +func (fakeSyncer *FakeResourceSyncer) SyncConfigMap(destination, source resourcesynccontroller.ResourceLocation) error { + return nil +} + +func (fakeSyncer *FakeResourceSyncer) SyncSecret(destination, source resourcesynccontroller.ResourceLocation) error { + return nil +} + func TestObserveCloudProviderNames(t *testing.T) { cases := []struct { platform configv1.PlatformType @@ -52,6 +63,7 @@ func TestObserveCloudProviderNames(t *testing.T) { } listers := configobservation.Listers{ InfrastructureLister: configlistersv1.NewInfrastructureLister(indexer), + ResourceSync: &FakeResourceSyncer{}, } result, errs := ObserveCloudProviderNames(listers, events.NewInMemoryRecorder("cloud"), map[string]interface{}{}) if len(errs) > 0 { diff --git a/pkg/operator/starter.go b/pkg/operator/starter.go index b6e2153f6..d9259cff4 100644 --- a/pkg/operator/starter.go +++ b/pkg/operator/starter.go @@ -184,6 +184,7 @@ var deploymentConfigMaps = []revision.RevisionResource{ {Name: "config"}, {Name: "controller-manager-kubeconfig"}, + {Name: "cloud-config", Optional: true}, {Name: "serviceaccount-ca"}, {Name: "service-ca"}, }