diff --git a/pkg/vmprovider/providers/vsphere2/vmlifecycle/bootstrap_cloudinit.go b/pkg/vmprovider/providers/vsphere2/vmlifecycle/bootstrap_cloudinit.go index e7043283b..33e401ac5 100644 --- a/pkg/vmprovider/providers/vsphere2/vmlifecycle/bootstrap_cloudinit.go +++ b/pkg/vmprovider/providers/vsphere2/vmlifecycle/bootstrap_cloudinit.go @@ -27,6 +27,11 @@ type CloudInitMetadata struct { PublicKeys string `yaml:"public-keys,omitempty"` } +// CloudInitUserDataSecretKeys are the Secret keys that in v1a1 we'd check for the userdata. +// Specifically, CAPBK uses "value" for its key, while "user-data" is the preferred key nowadays. +// The 'value' key lookup will eventually be deprecated. +var CloudInitUserDataSecretKeys = []string{"user-data", "value"} + func BootStrapCloudInit( vmCtx context.VirtualMachineContextA2, config *types.VirtualMachineConfigInfo, @@ -59,11 +64,8 @@ func BootStrapCloudInit( } userdata = data } else if raw := cloudInitSpec.RawCloudConfig; raw != nil { - // Check for the 'user-data' key as per official contract and API documentation. - // Additionally, to support the cluster bootstrap data supplied by CAPBK's secret, - // we check for a 'value' key when 'user-data' is not supplied. - // The 'value' key lookup will eventually be deprecated. - for _, key := range []string{raw.Key, "user-data", "value"} { + keys := []string{raw.Key} + for _, key := range append(keys, CloudInitUserDataSecretKeys...) { if data := bsArgs.BootstrapData.Data[key]; data != "" { userdata = data break diff --git a/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils.go b/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils.go index a3a074c59..1b98ecda6 100644 --- a/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils.go +++ b/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils.go @@ -106,7 +106,7 @@ func getSecretData( vmCtx context.VirtualMachineContextA2, k8sClient ctrlclient.Client, secretName, secretKey string, - configMapFallback bool) (map[string]string, error) { + configMapFallback, isCloudInitSecret bool) (map[string]string, error) { var data map[string]string @@ -141,7 +141,23 @@ func getSecretData( } if secretKey != "" { - if _, ok := data[secretKey]; !ok { + secretKeys := []string{secretKey} + if isCloudInitSecret { + // Hack: the v1a1 bootstrap did not have a Key field so for CloudInit we'd check + // for a few well-known keys. Check for the existence of those other keys when + // dealing with a CloudInit Secret so existing v1a1 users continue to work. + secretKeys = append(secretKeys, vmlifecycle.CloudInitUserDataSecretKeys...) + } + + found := false + for _, k := range secretKeys { + if _, ok := data[k]; ok { + found = true + break + } + } + + if !found { err := fmt.Errorf("required key %q not found in Secret %s", secretKey, secretName) conditions.MarkFalse(vmCtx.VM, vmopv1.VirtualMachineConditionBootstrapReady, "RequiredKeyNotFound", err.Error()) return nil, err @@ -180,7 +196,7 @@ func GetVirtualMachineBootstrap( cloudConfigSecretData = &out } else if raw := v.RawCloudConfig; raw != nil { var err error - data, err = getSecretData(vmCtx, k8sClient, raw.Name, raw.Key, true) + data, err = getSecretData(vmCtx, k8sClient, raw.Name, raw.Key, true, true) if err != nil { reason, msg := errToConditionReasonAndMessage(err) conditions.MarkFalse(vmCtx.VM, vmopv1.VirtualMachineConditionBootstrapReady, reason, msg) @@ -192,7 +208,7 @@ func GetVirtualMachineBootstrap( _ = cooked // TODO Add support for in-line sysprep } else if raw := v.RawSysprep; raw != nil { var err error - data, err = getSecretData(vmCtx, k8sClient, raw.Name, raw.Key, true) + data, err = getSecretData(vmCtx, k8sClient, raw.Name, raw.Key, true, false) if err != nil { reason, msg := errToConditionReasonAndMessage(err) conditions.MarkFalse(vmCtx.VM, vmopv1.VirtualMachineConditionBootstrapReady, reason, msg) @@ -206,7 +222,7 @@ func GetVirtualMachineBootstrap( if vApp.RawProperties != "" { var err error - vAppData, err = getSecretData(vmCtx, k8sClient, vApp.RawProperties, "", true) + vAppData, err = getSecretData(vmCtx, k8sClient, vApp.RawProperties, "", true, false) if err != nil { reason, msg := errToConditionReasonAndMessage(err) conditions.MarkFalse(vmCtx.VM, vmopv1.VirtualMachineConditionBootstrapReady, reason, msg) @@ -223,7 +239,7 @@ func GetVirtualMachineBootstrap( if data, ok := vAppExData[from.Name]; !ok { // Do the easy thing here and carry along each Secret's entire data. We could instead // shoehorn this in the vAppData with a concat key using an invalid k8s name delimiter. - fromData, err := getSecretData(vmCtx, k8sClient, from.Name, from.Key, false) + fromData, err := getSecretData(vmCtx, k8sClient, from.Name, from.Key, false, false) if err != nil { reason, msg := errToConditionReasonAndMessage(err) conditions.MarkFalse(vmCtx.VM, vmopv1.VirtualMachineConditionBootstrapReady, reason, msg) diff --git a/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils_test.go b/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils_test.go index c32542460..5db0216ec 100644 --- a/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils_test.go +++ b/pkg/vmprovider/providers/vsphere2/vmprovider_vm_utils_test.go @@ -319,6 +319,34 @@ func vmUtilTests() { }) }) + When("Secret key fallback for CloudInit", func() { + const value = "should-fallback-to-this-key" + + BeforeEach(func() { + cloudInitSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: dataName, + Namespace: vmCtx.VM.Namespace, + }, + Data: map[string][]byte{ + "value": []byte(value), + }, + } + + initObjects = append(initObjects, bootstrapCM, cloudInitSecret) + + vmCtx.VM.Spec.Bootstrap.CloudInit.RawCloudConfig = &common.SecretKeySelector{} + vmCtx.VM.Spec.Bootstrap.CloudInit.RawCloudConfig.Name = dataName + vmCtx.VM.Spec.Bootstrap.CloudInit.RawCloudConfig.Key = "user-data" + }) + + It("returns success", func() { + bsData, err := vsphere.GetVirtualMachineBootstrap(vmCtx, k8sClient) + Expect(err).ToNot(HaveOccurred()) + Expect(bsData.Data).To(HaveKeyWithValue("value", value)) + Expect(conditions.IsTrue(vmCtx.VM, vmopv1.VirtualMachineConditionBootstrapReady)).To(BeTrue()) + }) + }) }) When("Bootstrap via Sysprep", func() {