Skip to content

Commit 38cbb53

Browse files
committed
Generate a default cloud.conf on AWS
OpenShift clusters prior to v4.14 did not explicitly need a cloud.conf file for AWS. In v.4.19, they do. This change makes sure that there is a minimally usable default cloud configuration present. Signed-off-by: Nolan Brubaker <[email protected]>
1 parent 86f8c6d commit 38cbb53

File tree

4 files changed

+51
-26
lines changed

4 files changed

+51
-26
lines changed

pkg/cloud/aws/aws_config_transformer.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ import (
1313
awsconfig "k8s.io/cloud-provider-aws/pkg/providers/v1/config"
1414
)
1515

16+
const defaultConfig = `[Global]
17+
`
18+
1619
// CloudConfigTransformer is used to inject OpenShift configuration defaults into the Cloud Provider config
17-
// for the AWS Cloud Provider.
20+
// for the AWS Cloud Provider. If an empty source string is provided, a minimal default configuration will be created.
1821
func CloudConfigTransformer(source string, infra *configv1.Infrastructure, network *configv1.Network) (string, error) {
1922
cfg, err := readAWSConfig(source)
2023
if err != nil {
@@ -26,13 +29,19 @@ func CloudConfigTransformer(source string, infra *configv1.Infrastructure, netwo
2629
return marshalAWSConfig(cfg)
2730
}
2831

32+
// readAWSConfig will parse a source string into a proper *awsconfig.CloudConfig.
33+
// If an empty source string is provided, a default configuration will be used.
2934
func readAWSConfig(source string) (*awsconfig.CloudConfig, error) {
35+
cfg := &awsconfig.CloudConfig{}
36+
37+
// There are cases in which a cloud config was not installed with a cluster, and this is valid.
38+
// We should, however, populate the configuration so that it doesn't fail on later versions that require
39+
// a cloud.conf.
3040
if len(source) == 0 {
31-
return nil, fmt.Errorf("empty INI file")
41+
source = defaultConfig
3242
}
3343

3444
// Use the same method the AWS CCM uses to load configuration.
35-
cfg := &awsconfig.CloudConfig{}
3645
if err := gcfg.FatalOnly(gcfg.ReadStringInto(cfg, source)); err != nil {
3746
return nil, fmt.Errorf("failed to parse INI file: %w", err)
3847
}

pkg/cloud/cloud.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ import (
2525
// consumer of the transformer.
2626
type cloudConfigTransformer func(source string, infra *configv1.Infrastructure, network *configv1.Network) (string, error)
2727

28+
// defaultCloudConfig returns a default cloud configuration in the event that a cluster does not have a source config map.
29+
// Clusters created prior to 4.14 do not always have a source config map.
30+
type defaultCloudConfig func(platform configv1.PlatformType) (string, error)
31+
2832
// GetCloudConfigTransformer returns the function that should be used to transform
2933
// the cloud configuration config map, and a boolean to indicate if the config should
3034
// be synced from the CCO namespace before applying the transformation.

pkg/controllers/cloud_config_sync_controller.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,13 @@ func (r *CloudConfigReconciler) Reconcile(ctx context.Context, req ctrl.Request)
113113
Name: infra.Spec.CloudConfig.Name,
114114
Namespace: OpenshiftConfigNamespace,
115115
}
116-
if err := r.Get(ctx, openshiftUnmanagedCMKey, sourceCM); err != nil {
116+
if err := r.Get(ctx, openshiftUnmanagedCMKey, sourceCM); errors.IsNotFound(err) {
117+
klog.Warningf("unmanaged cloud-config is not found, falling back to default cloud config.")
118+
} else if err != nil {
117119
klog.Errorf("unable to get cloud-config for sync")
118120
if err := r.setDegradedCondition(ctx); err != nil {
119121
return ctrl.Result{}, fmt.Errorf("failed to set conditions for cloud config controller: %v", err)
120122
}
121-
return ctrl.Result{}, err
122123
}
123124
}
124125

@@ -188,40 +189,46 @@ func (r *CloudConfigReconciler) isCloudConfigSyncNeeded(platformStatus *configv1
188189
return false, fmt.Errorf("platformStatus is required")
189190
}
190191
switch platformStatus.Type {
191-
case configv1.AzurePlatformType,
192+
case configv1.AWSPlatformType,
193+
configv1.AzurePlatformType,
192194
configv1.GCPPlatformType,
193195
configv1.VSpherePlatformType,
194196
configv1.IBMCloudPlatformType,
195197
configv1.PowerVSPlatformType,
196198
configv1.OpenStackPlatformType,
197199
configv1.NutanixPlatformType:
198200
return true, nil
199-
case configv1.AWSPlatformType:
200-
// Some of AWS regions might require to sync a cloud-config, in such case reference in infra resource will be presented
201-
return infraCloudConfigRef.Name != "", nil
202201
default:
203202
return false, nil
204203
}
205204
}
206205

206+
// prepareSourceConfigMap creates a usable ConfigMap for further processing into a cloud.conf file.
207207
func (r *CloudConfigReconciler) prepareSourceConfigMap(source *corev1.ConfigMap, infra *configv1.Infrastructure) (*corev1.ConfigMap, error) {
208+
cloudConfCm := source.DeepCopy()
209+
// We might have an empty ConfigMap in clusters created before 4.14.
210+
if cloudConfCm.Data == nil {
211+
cloudConfCm.Data = make(map[string]string)
212+
}
213+
208214
// Keys might be different between openshift-config/cloud-config and openshift-config-managed/kube-cloud-config
209215
// Always use "cloud.conf" which is default one across openshift
210-
cloudConfCm := source.DeepCopy()
211216
if _, ok := cloudConfCm.Data[defaultConfigKey]; ok {
212217
return cloudConfCm, nil
213218
}
214219

220+
// If a user provides their own cloud config, copy that over into the default key.
215221
infraConfigKey := infra.Spec.CloudConfig.Key
216222
if val, ok := cloudConfCm.Data[infraConfigKey]; ok {
217223
cloudConfCm.Data[defaultConfigKey] = val
218224
delete(cloudConfCm.Data, infraConfigKey)
219225
return cloudConfCm, nil
220226
}
221-
return nil, fmt.Errorf(
222-
"key %s specified in infra resource does not found in source configmap %s",
223-
infraConfigKey, client.ObjectKeyFromObject(source),
224-
)
227+
228+
// If there was no data in the configmap, make a default entry for it.
229+
cloudConfCm.Data[defaultConfigKey] = ""
230+
231+
return cloudConfCm, nil
225232
}
226233

227234
func (r *CloudConfigReconciler) isCloudConfigEqual(source *corev1.ConfigMap, target *corev1.ConfigMap) bool {

pkg/controllers/cloud_config_sync_controller_test.go

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const (
2828
infraCloudConfKey = "foo"
2929

3030
defaultAzureConfig = `{"cloud":"AzurePublicCloud","tenantId":"0000000-0000-0000-0000-000000000000","Entries":null,"subscriptionId":"0000000-0000-0000-0000-000000000000","vmType":"standard","putVMSSVMBatchSize":0,"enableMigrateToIPBasedBackendPoolAPI":false,"clusterServiceLoadBalancerHealthProbeMode":"shared"}`
31+
defaultAWSConfig = `[Global]
32+
`
3133
)
3234

3335
func makeInfrastructureResource(platform configv1.PlatformType) *configv1.Infrastructure {
@@ -84,8 +86,7 @@ func makeInfraStatus(platform configv1.PlatformType) configv1.InfrastructureStat
8486
}
8587

8688
func makeInfraCloudConfig(platform configv1.PlatformType) *corev1.ConfigMap {
87-
defaultConfig := `[Global]
88-
`
89+
defaultConfig := defaultAWSConfig
8990

9091
if platform == configv1.AzurePlatformType {
9192
defaultConfig = defaultAzureConfig
@@ -98,8 +99,7 @@ func makeInfraCloudConfig(platform configv1.PlatformType) *corev1.ConfigMap {
9899
}
99100

100101
func makeManagedCloudConfig(platform configv1.PlatformType) *corev1.ConfigMap {
101-
defaultConfig := `[Global]
102-
`
102+
defaultConfig := defaultAWSConfig
103103

104104
if platform == configv1.AzurePlatformType {
105105
defaultConfig = defaultAzureConfig
@@ -151,12 +151,14 @@ var _ = Describe("prepareSourceConfigMap reconciler method", func() {
151151
Expect(reconciler.isCloudConfigEqual(preparedConfig, managedCloudConfig)).Should(BeTrue())
152152
})
153153

154-
It("config preparation should fail if key from infra resource does not found", func() {
155-
brokenInfraConfig := infraCloudConfig.DeepCopy()
156-
brokenInfraConfig.Data = map[string]string{"hehehehehe": "bar"}
157-
_, err := reconciler.prepareSourceConfigMap(brokenInfraConfig, infra)
158-
Expect(err).Should(Not(Succeed()))
159-
Expect(err.Error()).Should(BeEquivalentTo("key foo specified in infra resource does not found in source configmap openshift-config/test-config"))
154+
It("config preparation should create required key if key isn't found in infrastructure", func() {
155+
infraConfig := infraCloudConfig.DeepCopy()
156+
infraConfig.Data = map[string]string{"hehehehehe": "bar"}
157+
preparedConfig, err := reconciler.prepareSourceConfigMap(infraConfig, infra)
158+
Expect(err).Should(Succeed())
159+
_, ok := preparedConfig.Data[defaultConfigKey]
160+
Expect(ok).Should(BeTrue())
161+
Expect(len(preparedConfig.Data[defaultConfigKey])).Should(BeEquivalentTo(0))
160162
})
161163

162164
It("config preparation should not touch extra fields in infra ConfigMap", func() {
@@ -467,7 +469,7 @@ var _ = Describe("Cloud config sync reconciler", func() {
467469
Expect(cl.Create(ctx, makeInfraCloudConfig(configv1.AWSPlatformType))).To(Succeed())
468470
})
469471

470-
It("should skip config sync for AWS platform if there is no reference in infra resource", func() {
472+
It("should sync for a default config AWS platform if there is no reference in infra resource", func() {
471473
infraResource := makeInfrastructureResource(configv1.AWSPlatformType)
472474
infraResource.Spec.CloudConfig.Name = ""
473475
Expect(cl.Create(ctx, infraResource)).To(Succeed())
@@ -481,7 +483,10 @@ var _ = Describe("Cloud config sync reconciler", func() {
481483
allCMs := &corev1.ConfigMapList{}
482484
Expect(cl.List(ctx, allCMs, &client.ListOptions{Namespace: targetNamespaceName})).To(Succeed())
483485

484-
Expect(len(allCMs.Items)).To(BeZero())
486+
Expect(len(allCMs.Items)).To(BeEquivalentTo(1))
487+
// Our code ensures that there is at a minimum a global section.
488+
// The CCM itself may end up defaulting values for us, so don't use exact string matching.
489+
Expect(allCMs.Items[0].Data[defaultConfigKey]).To(HavePrefix(defaultAWSConfig))
485490
})
486491

487492
It("should perform config sync for AWS platform if there is a reference in infra resource", func() {

0 commit comments

Comments
 (0)