From 1d5e0f155a7fe2763cac335d94f1134372457f77 Mon Sep 17 00:00:00 2001 From: Michael McCune Date: Fri, 25 Feb 2022 09:35:51 -0500 Subject: [PATCH] add user configurable cluster api version This change introduces an environment variable, `CAPI_VERSION`, through which a user can set the API version for the group they are using. This change is being added to address situations where a user might have multiple API versions for the cluster api group and wishes to be explicit about which version is selected. Also adds unit tests and documentation for the new behavior. This change does not break the existing behavior. --- .../cloudprovider/clusterapi/README.md | 17 +++++++ .../clusterapi/clusterapi_controller.go | 19 +++++++- .../clusterapi/clusterapi_controller_test.go | 48 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/cluster-autoscaler/cloudprovider/clusterapi/README.md b/cluster-autoscaler/cloudprovider/clusterapi/README.md index 20581f1b89ac..bd800ffa4f7c 100644 --- a/cluster-autoscaler/cloudprovider/clusterapi/README.md +++ b/cluster-autoscaler/cloudprovider/clusterapi/README.md @@ -180,6 +180,23 @@ the machine annotation on nodes will be `test.8s.io/machine`, the machine deleti annotation will be `test.k8s.io/delete-machine`, and the cluster name label will be `test.k8s.io/cluster-name`. +## Specifying a Custom Resource Version + +When determining the group version for the Cluster API types, by default the autoscaler +will look for the latest version of the group. For example, if `MachineDeployments` +exist in the `cluster.x-k8s.io` group at versions `v1alpha1` and `v1beta1`, the +autoscaler will choose `v1beta1`. + +In some cases it may be desirable to specify which version of the API the cluster +autoscaler should use. This can be useful in debugging scenarios, or in situations +where you have deployed multiple API versions and wish to ensure that the autoscaler +uses a specific version. + +Setting the `CAPI_VERSION` environment variable will instruct the autoscaler to use +the version specified. This works in a similar fashion as the API group environment +variable with the exception that there is no default value. When this variable is not +set, the autoscaler will use the behavior described above. + ## Sample manifest A sample manifest that will create a deployment running the autoscaler is diff --git a/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller.go b/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller.go index 54877b8b6460..1cc7f1989be1 100644 --- a/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller.go +++ b/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller.go @@ -46,7 +46,9 @@ const ( nodeProviderIDIndex = "nodeProviderIDIndex" defaultCAPIGroup = "cluster.x-k8s.io" // CAPIGroupEnvVar contains the environment variable name which allows overriding defaultCAPIGroup. - CAPIGroupEnvVar = "CAPI_GROUP" + CAPIGroupEnvVar = "CAPI_GROUP" + // CAPIVersionEnvVar contains the environment variable name which allows overriding the Cluster API group version. + CAPIVersionEnvVar = "CAPI_VERSION" resourceNameMachine = "machines" resourceNameMachineSet = "machinesets" resourceNameMachineDeployment = "machinedeployments" @@ -305,6 +307,17 @@ func getCAPIGroup() string { return g } +// getCAPIVersion returns a string the specifies the version for the API. +// It will return either the value from the CAPI_VERSION environment variable, +// or an empty string if the variable is not set. +func getCAPIVersion() string { + v := os.Getenv(CAPIVersionEnvVar) + if v != "" { + klog.V(4).Infof("Using API Version %q", v) + } + return v +} + // newMachineController constructs a controller that watches Nodes, // Machines and MachineSet as they are added, updated and deleted on // the cluster. @@ -415,6 +428,10 @@ func groupVersionHasResource(client discovery.DiscoveryInterface, groupVersion, } func getAPIGroupPreferredVersion(client discovery.DiscoveryInterface, APIGroup string) (string, error) { + if version := os.Getenv(CAPIVersionEnvVar); version != "" { + return version, nil + } + groupList, err := client.ServerGroups() if err != nil { return "", fmt.Errorf("failed to get ServerGroups: %v", err) diff --git a/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller_test.go b/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller_test.go index 158c5b78e75e..5413de38a540 100644 --- a/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller_test.go +++ b/cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller_test.go @@ -1369,6 +1369,26 @@ func TestControllerGetAPIVersionGroup(t *testing.T) { } } +func TestControllerGetAPIVersion(t *testing.T) { + expected := "v1beta1" + if err := os.Setenv(CAPIVersionEnvVar, expected); err != nil { + t.Fatalf("unexpected error: %v", err) + } + observed := getCAPIVersion() + if observed != expected { + t.Fatalf("Wrong API Version detected, expected %q, got %q", expected, observed) + } + + if err := os.Unsetenv(CAPIVersionEnvVar); err != nil { + t.Fatalf("unexpected error: %v", err) + } + expected = "" + observed = getCAPIVersion() + if observed != expected { + t.Fatalf("Wrong API Version detected, expected %q, got %q", expected, observed) + } +} + func TestControllerGetAPIVersionGroupWithMachineDeployments(t *testing.T) { testConfig := createMachineDeploymentTestConfig(RandomString(6), RandomString(6), RandomString(6), 1, map[string]string{ nodeGroupMinSizeAnnotationKey: "1", @@ -1424,28 +1444,40 @@ func TestControllerGetAPIVersionGroupWithMachineDeployments(t *testing.T) { } func TestGetAPIGroupPreferredVersion(t *testing.T) { + customVersion := "v1" testCases := []struct { description string APIGroup string preferredVersion string + envVar string error bool }{ { description: "find version for default API group", APIGroup: defaultCAPIGroup, preferredVersion: "v1alpha3", + envVar: "", error: false, }, { description: "find version for another API group", APIGroup: customCAPIGroup, preferredVersion: "v1beta1", + envVar: "", + error: false, + }, + { + description: "find version for another API group while overriding version with env var", + APIGroup: customCAPIGroup, + preferredVersion: customVersion, + envVar: customVersion, error: false, }, { description: "API group does not exist", APIGroup: "does.not.exist", preferredVersion: "", + envVar: "", error: true, }, } @@ -1459,11 +1491,23 @@ func TestGetAPIGroupPreferredVersion(t *testing.T) { { GroupVersion: fmt.Sprintf("%s/v1alpha3", defaultCAPIGroup), }, + { + GroupVersion: fmt.Sprintf("%s/%s", customCAPIGroup, customVersion), + }, }, }, } for _, tc := range testCases { t.Run(tc.description, func(t *testing.T) { + if tc.envVar == "" { + if err := os.Unsetenv(CAPIVersionEnvVar); err != nil { + t.Fatalf("unexpected error: %v", err) + } + } else { + if err := os.Setenv(CAPIVersionEnvVar, tc.envVar); err != nil { + t.Fatalf("unexpected error: %v", err) + } + } version, err := getAPIGroupPreferredVersion(discoveryClient, tc.APIGroup) if (err != nil) != tc.error { t.Errorf("expected to have error: %t. Had an error: %t", tc.error, err != nil) @@ -1473,6 +1517,10 @@ func TestGetAPIGroupPreferredVersion(t *testing.T) { } }) } + + if err := os.Unsetenv(CAPIVersionEnvVar); err != nil { + t.Fatalf("unexpected error: %v", err) + } } func TestGroupVersionHasResource(t *testing.T) {