diff --git a/examples/kubevirt-machinedeployment.yaml b/examples/kubevirt-machinedeployment.yaml index 49bb904cd..a40b95703 100644 --- a/examples/kubevirt-machinedeployment.yaml +++ b/examples/kubevirt-machinedeployment.yaml @@ -28,7 +28,9 @@ spec: cloudProviderSpec: auth: kubeconfig: - value: '<< KUBECONFIG >>' + # Can also be set via the env var 'KUBEVIRT_KUBECONFIG' on the machine-controller + # If specified directly, this value should be a base64 encoded kubeconfig in either yaml or json format. + value: "<< KUBECONFIG >>" virtualMachine: template: cpus: "1" @@ -44,7 +46,7 @@ spec: type: "" # Allowed values: "", "soft", "hard" key: "foo" values: - - bar + - bar # Can also be `centos`, must align with he configured registryImage above operatingSystem: "ubuntu" operatingSystemSpec: diff --git a/pkg/cloudprovider/provider/kubevirt/provider.go b/pkg/cloudprovider/provider/kubevirt/provider.go index b31ee13cf..423a42909 100644 --- a/pkg/cloudprovider/provider/kubevirt/provider.go +++ b/pkg/cloudprovider/provider/kubevirt/provider.go @@ -18,6 +18,7 @@ package kubevirt import ( "context" + "encoding/base64" "errors" "fmt" "net/url" @@ -199,10 +200,36 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p } config := Config{} - config.Kubeconfig, err = p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.Auth.Kubeconfig, "KUBEVIRT_KUBECONFIG") + + // Kubeconfig was specified directly in the Machine/MachineDeployment CR. In this case we need to ensure that the value is base64 encoded. + if rawConfig.Auth.Kubeconfig.Value != "" { + val, err := base64.StdEncoding.DecodeString(rawConfig.Auth.Kubeconfig.Value) + if err != nil { + // An error here means that this is not a valid base64 string + // We can be more explicit here with the error for visibility. Webhook will return this error if we hit this scenario. + return nil, nil, fmt.Errorf("failed to decode base64 encoded kubeconfig. Expected value is a base64 encoded Kubeconfig in JSON or YAML format: %w", err) + } + config.Kubeconfig = string(val) + } else { + // Environment variable or secret reference was used for providing the value of kubeconfig + // We have to be lenient in this case and allow unencoded values as well. + config.Kubeconfig, err = p.configVarResolver.GetConfigVarStringValueOrEnv(rawConfig.Auth.Kubeconfig, "KUBEVIRT_KUBECONFIG") + if err != nil { + return nil, nil, fmt.Errorf(`failed to get value of "kubeconfig" field: %w`, err) + } + val, err := base64.StdEncoding.DecodeString(config.Kubeconfig) + // We intentionally ignore errors here with an assumption that an unencoded YAML or JSON must have been passed on + // in this case. + if err == nil { + config.Kubeconfig = string(val) + } + } + + config.RestConfig, err = clientcmd.RESTConfigFromKubeConfig([]byte(config.Kubeconfig)) if err != nil { - return nil, nil, fmt.Errorf(`failed to get value of "kubeconfig" field: %w`, err) + return nil, nil, fmt.Errorf("failed to decode kubeconfig: %w", err) } + config.CPUs, err = p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.Template.CPUs) if err != nil { return nil, nil, fmt.Errorf(`failed to get value of "cpus" field: %w`, err) @@ -232,10 +259,6 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p if err != nil { return nil, nil, fmt.Errorf(`failed to get value of "storageClassName" field: %w`, err) } - config.RestConfig, err = clientcmd.RESTConfigFromKubeConfig([]byte(config.Kubeconfig)) - if err != nil { - return nil, nil, fmt.Errorf("failed to decode kubeconfig: %w", err) - } config.FlavorName, err = p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.Flavor.Name) if err != nil { return nil, nil, fmt.Errorf(`failed to get value of "flavor.name" field: %w`, err) diff --git a/test/e2e/provisioning/all_e2e_test.go b/test/e2e/provisioning/all_e2e_test.go index d8ae02e6b..e725f54dd 100644 --- a/test/e2e/provisioning/all_e2e_test.go +++ b/test/e2e/provisioning/all_e2e_test.go @@ -281,7 +281,7 @@ func addCAToDeployment(ctx context.Context, client ctrlruntimeclient.Client, nam func TestKubevirtProvisioningE2E(t *testing.T) { t.Parallel() - kubevirtKubeconfig := os.Getenv("KUBEVIRT_E2E_TESTS_KUBECONFIG_JSON") + kubevirtKubeconfig := os.Getenv("KUBEVIRT_E2E_TESTS_KUBECONFIG") if kubevirtKubeconfig == "" { t.Fatalf("Unable to run kubevirt tests, KUBEVIRT_E2E_TESTS_KUBECONFIG must be set")