Skip to content

Commit ee627f2

Browse files
authored
Merge pull request kubernetes#2934 from enxebre/fix-2932
Fixes 2932: let the capi version to be discovered
2 parents a469c85 + 7082cfe commit ee627f2

File tree

3 files changed

+196
-15
lines changed

3 files changed

+196
-15
lines changed

cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller.go

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ package clusterapi
1919
import (
2020
"context"
2121
"fmt"
22+
"os"
2223

2324
corev1 "k8s.io/api/core/v1"
2425
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2627
"k8s.io/apimachinery/pkg/labels"
2728
"k8s.io/apimachinery/pkg/runtime/schema"
29+
"k8s.io/client-go/discovery"
2830
"k8s.io/client-go/dynamic"
2931
"k8s.io/client-go/dynamic/dynamicinformer"
3032
"k8s.io/client-go/informers"
@@ -38,7 +40,9 @@ import (
3840
const (
3941
machineProviderIDIndex = "machineProviderIDIndex"
4042
nodeProviderIDIndex = "nodeProviderIDIndex"
41-
defaultMachineAPI = "v1alpha2.cluster.x-k8s.io"
43+
defaultCAPIGroup = "cluster.x-k8s.io"
44+
// CAPIGroupEnvVar contains the environment variable name which allows overriding defaultCAPIGroup.
45+
CAPIGroupEnvVar = "CAPI_GROUP"
4246
)
4347

4448
// machineController watches for Nodes, Machines, MachineSets and
@@ -271,37 +275,58 @@ func (c *machineController) machinesInMachineSet(machineSet *MachineSet) ([]*Mac
271275
return result, nil
272276
}
273277

278+
// getCAPIGroup returns a string that specifies the group for the API.
279+
// It will return either the value from the
280+
// CAPI_GROUP environment variable, or the default value i.e cluster.x-k8s.io.
281+
func getCAPIGroup() string {
282+
g := os.Getenv(CAPIGroupEnvVar)
283+
if g == "" {
284+
g = defaultCAPIGroup
285+
}
286+
klog.V(4).Infof("Using API Group %q", g)
287+
return g
288+
}
289+
274290
// newMachineController constructs a controller that watches Nodes,
275291
// Machines and MachineSet as they are added, updated and deleted on
276292
// the cluster.
277293
func newMachineController(
278294
dynamicclient dynamic.Interface,
279295
kubeclient kubeclient.Interface,
296+
discoveryclient discovery.DiscoveryInterface,
280297
) (*machineController, error) {
281298
kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeclient, 0)
282299
informerFactory := dynamicinformer.NewFilteredDynamicSharedInformerFactory(dynamicclient, 0, metav1.NamespaceAll, nil)
283300

284-
// TODO(alberto): let environment variable to override defaultMachineAPI
285-
machineDeploymentResource, _ := schema.ParseResourceArg(fmt.Sprintf("machinedeployments.%v", defaultMachineAPI))
301+
CAPIGroup := getCAPIGroup()
302+
CAPIVersion, err := getAPIGroupPreferredVersion(discoveryclient, CAPIGroup)
303+
if err != nil {
304+
panic("CAPIVersion")
305+
}
306+
klog.Infof("Using version %q for API group %q", CAPIVersion, CAPIGroup)
307+
308+
machineDeploymentResource, _ := schema.ParseResourceArg(fmt.Sprintf("machinedeployments.%v.%v", CAPIVersion, CAPIGroup))
309+
if machineDeploymentResource == nil {
310+
panic("MachineDeployment")
311+
}
286312

287-
machineSetResource, _ := schema.ParseResourceArg(fmt.Sprintf("machinesets.%v", defaultMachineAPI))
313+
machineSetResource, _ := schema.ParseResourceArg(fmt.Sprintf("machinesets.%v.%v", CAPIVersion, CAPIGroup))
288314
if machineSetResource == nil {
289315
panic("MachineSetResource")
290316
}
291317

292-
machineResource, _ := schema.ParseResourceArg(fmt.Sprintf("machines.%v", defaultMachineAPI))
318+
machineResource, _ := schema.ParseResourceArg(fmt.Sprintf("machines.%v.%v", CAPIVersion, CAPIGroup))
293319
if machineResource == nil {
294320
panic("machineResource")
295321
}
322+
296323
machineInformer := informerFactory.ForResource(*machineResource)
297324
machineSetInformer := informerFactory.ForResource(*machineSetResource)
298-
var machineDeploymentInformer informers.GenericInformer
299-
300-
machineDeploymentInformer = informerFactory.ForResource(*machineDeploymentResource)
301-
machineDeploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{})
325+
machineDeploymentInformer := informerFactory.ForResource(*machineDeploymentResource)
302326

303327
machineInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{})
304328
machineSetInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{})
329+
machineDeploymentInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{})
305330

306331
nodeInformer := kubeInformerFactory.Core().V1().Nodes().Informer()
307332
nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{})
@@ -332,6 +357,21 @@ func newMachineController(
332357
}, nil
333358
}
334359

360+
func getAPIGroupPreferredVersion(client discovery.DiscoveryInterface, APIGroup string) (string, error) {
361+
groupList, err := client.ServerGroups()
362+
if err != nil {
363+
return "", fmt.Errorf("failed to get ServerGroups: %v", err)
364+
}
365+
366+
for _, group := range groupList.Groups {
367+
if group.Name == APIGroup {
368+
return group.PreferredVersion.Version, nil
369+
}
370+
}
371+
372+
return "", fmt.Errorf("failed to find API group %q", APIGroup)
373+
}
374+
335375
func (c *machineController) machineSetProviderIDs(machineSet *MachineSet) ([]string, error) {
336376
machines, err := c.machinesInMachineSet(machineSet)
337377
if err != nil {

cluster-autoscaler/cloudprovider/clusterapi/clusterapi_controller_test.go

Lines changed: 139 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package clusterapi
1818

1919
import (
2020
"fmt"
21+
"os"
2122
"path"
2223
"reflect"
2324
"sort"
@@ -29,8 +30,10 @@ import (
2930
"k8s.io/apimachinery/pkg/labels"
3031
"k8s.io/apimachinery/pkg/runtime"
3132
"k8s.io/apimachinery/pkg/types"
33+
fakediscovery "k8s.io/client-go/discovery/fake"
3234
fakedynamic "k8s.io/client-go/dynamic/fake"
3335
fakekube "k8s.io/client-go/kubernetes/fake"
36+
clientgotesting "k8s.io/client-go/testing"
3437
"k8s.io/utils/pointer"
3538
)
3639

@@ -53,6 +56,8 @@ type testSpec struct {
5356
rootIsMachineDeployment bool
5457
}
5558

59+
const customCAPIGroup = "custom.x-k8s.io"
60+
5661
func mustCreateTestController(t *testing.T, testConfigs ...*testConfig) (*machineController, testControllerShutdownFunc) {
5762
t.Helper()
5863

@@ -76,7 +81,19 @@ func mustCreateTestController(t *testing.T, testConfigs ...*testConfig) (*machin
7681

7782
kubeclientSet := fakekube.NewSimpleClientset(nodeObjects...)
7883
dynamicClientset := fakedynamic.NewSimpleDynamicClient(runtime.NewScheme(), machineObjects...)
79-
controller, err := newMachineController(dynamicClientset, kubeclientSet)
84+
discoveryClient := &fakediscovery.FakeDiscovery{
85+
Fake: &clientgotesting.Fake{
86+
Resources: []*v1.APIResourceList{
87+
{
88+
GroupVersion: fmt.Sprintf("%s/v1beta1", customCAPIGroup),
89+
},
90+
{
91+
GroupVersion: fmt.Sprintf("%s/v1alpha3", defaultCAPIGroup),
92+
},
93+
},
94+
},
95+
}
96+
controller, err := newMachineController(dynamicClientset, kubeclientSet, discoveryClient)
8097
if err != nil {
8198
t.Fatal("failed to create test controller")
8299
}
@@ -136,7 +153,7 @@ func createTestConfigs(specs ...testSpec) []*testConfig {
136153

137154
config.machineSet = &MachineSet{
138155
TypeMeta: v1.TypeMeta{
139-
APIVersion: "cluster.x-k8s.io/v1alpha2",
156+
APIVersion: fmt.Sprintf("%s/v1alpha3", defaultCAPIGroup),
140157
Kind: "MachineSet",
141158
},
142159
ObjectMeta: v1.ObjectMeta{
@@ -152,7 +169,7 @@ func createTestConfigs(specs ...testSpec) []*testConfig {
152169
} else {
153170
config.machineDeployment = &MachineDeployment{
154171
TypeMeta: v1.TypeMeta{
155-
APIVersion: "cluster.x-k8s.io/v1alpha2",
172+
APIVersion: fmt.Sprintf("%s/v1alpha3", defaultCAPIGroup),
156173
Kind: "MachineDeployment",
157174
},
158175
ObjectMeta: v1.ObjectMeta{
@@ -211,7 +228,7 @@ func makeLinkedNodeAndMachine(i int, namespace string, owner v1.OwnerReference)
211228

212229
machine := &Machine{
213230
TypeMeta: v1.TypeMeta{
214-
APIVersion: "cluster.x-k8s.io/v1alpha2",
231+
APIVersion: fmt.Sprintf("%s/v1alpha3", defaultCAPIGroup),
215232
Kind: "Machine",
216233
},
217234
ObjectMeta: v1.ObjectMeta{
@@ -991,3 +1008,121 @@ func TestControllerMachineSetNodeNamesUsingStatusNodeRefName(t *testing.T) {
9911008
}
9921009
}
9931010
}
1011+
1012+
func TestControllerGetAPIVersionGroup(t *testing.T) {
1013+
expected := "mygroup"
1014+
if err := os.Setenv(CAPIGroupEnvVar, expected); err != nil {
1015+
t.Fatalf("unexpected error: %v", err)
1016+
}
1017+
observed := getCAPIGroup()
1018+
if observed != expected {
1019+
t.Fatalf("Wrong Version Group detected, expected %q, got %q", expected, observed)
1020+
}
1021+
1022+
expected = defaultCAPIGroup
1023+
if err := os.Setenv(CAPIGroupEnvVar, ""); err != nil {
1024+
t.Fatalf("unexpected error: %v", err)
1025+
}
1026+
observed = getCAPIGroup()
1027+
if observed != expected {
1028+
t.Fatalf("Wrong Version Group detected, expected %q, got %q", expected, observed)
1029+
}
1030+
}
1031+
1032+
func TestControllerGetAPIVersionGroupWithMachineDeployments(t *testing.T) {
1033+
testConfig := createMachineDeploymentTestConfig(testNamespace, 1, map[string]string{
1034+
nodeGroupMinSizeAnnotationKey: "1",
1035+
nodeGroupMaxSizeAnnotationKey: "1",
1036+
})
1037+
if err := os.Setenv(CAPIGroupEnvVar, customCAPIGroup); err != nil {
1038+
t.Fatalf("unexpected error: %v", err)
1039+
}
1040+
1041+
testConfig.machineDeployment.TypeMeta.APIVersion = fmt.Sprintf("%s/v1beta1", customCAPIGroup)
1042+
testConfig.machineSet.TypeMeta.APIVersion = fmt.Sprintf("%s/v1beta1", customCAPIGroup)
1043+
for _, machine := range testConfig.machines {
1044+
machine.TypeMeta.APIVersion = fmt.Sprintf("%s/v1beta1", customCAPIGroup)
1045+
}
1046+
controller, stop := mustCreateTestController(t, testConfig)
1047+
defer stop()
1048+
1049+
machineDeployments, err := controller.listMachineDeployments(testNamespace, labels.Everything())
1050+
if err != nil {
1051+
t.Fatalf("unexpected error: %v", err)
1052+
}
1053+
if l := len(machineDeployments); l != 1 {
1054+
t.Fatalf("Incorrect number of MachineDeployments, expected 1, got %d", l)
1055+
}
1056+
1057+
machineSets, err := controller.listMachineSets(testNamespace, labels.Everything())
1058+
if err != nil {
1059+
t.Fatalf("unexpected error: %v", err)
1060+
}
1061+
if l := len(machineSets); l != 1 {
1062+
t.Fatalf("Incorrect number of MachineSets, expected 1, got %d", l)
1063+
}
1064+
1065+
machines, err := controller.listMachines(testNamespace, labels.Everything())
1066+
if err != nil {
1067+
t.Fatalf("unexpected error: %v", err)
1068+
}
1069+
if l := len(machines); l != 1 {
1070+
t.Fatalf("Incorrect number of Machines, expected 1, got %d", l)
1071+
}
1072+
1073+
if err := os.Unsetenv(CAPIGroupEnvVar); err != nil {
1074+
t.Fatalf("unexpected error: %v", err)
1075+
}
1076+
}
1077+
1078+
func TestGetAPIGroupPreferredVersion(t *testing.T) {
1079+
testCases := []struct {
1080+
description string
1081+
APIGroup string
1082+
preferredVersion string
1083+
error bool
1084+
}{
1085+
{
1086+
description: "find version for default API group",
1087+
APIGroup: defaultCAPIGroup,
1088+
preferredVersion: "v1alpha3",
1089+
error: false,
1090+
},
1091+
{
1092+
description: "find version for another API group",
1093+
APIGroup: customCAPIGroup,
1094+
preferredVersion: "v1beta1",
1095+
error: false,
1096+
},
1097+
{
1098+
description: "API group does not exist",
1099+
APIGroup: "does.not.exist",
1100+
preferredVersion: "",
1101+
error: true,
1102+
},
1103+
}
1104+
1105+
discoveryClient := &fakediscovery.FakeDiscovery{
1106+
Fake: &clientgotesting.Fake{
1107+
Resources: []*v1.APIResourceList{
1108+
{
1109+
GroupVersion: fmt.Sprintf("%s/v1beta1", customCAPIGroup),
1110+
},
1111+
{
1112+
GroupVersion: fmt.Sprintf("%s/v1alpha3", defaultCAPIGroup),
1113+
},
1114+
},
1115+
},
1116+
}
1117+
for _, tc := range testCases {
1118+
t.Run(tc.description, func(t *testing.T) {
1119+
version, err := getAPIGroupPreferredVersion(discoveryClient, tc.APIGroup)
1120+
if (err != nil) != tc.error {
1121+
t.Errorf("expected to have error: %t. Had an error: %t", tc.error, err != nil)
1122+
}
1123+
if version != tc.preferredVersion {
1124+
t.Errorf("expected %v, got: %v", tc.preferredVersion, version)
1125+
}
1126+
})
1127+
}
1128+
}

cluster-autoscaler/cloudprovider/clusterapi/clusterapi_provider.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"k8s.io/autoscaler/cluster-autoscaler/cloudprovider"
2525
"k8s.io/autoscaler/cluster-autoscaler/config"
2626
"k8s.io/autoscaler/cluster-autoscaler/utils/errors"
27+
"k8s.io/client-go/discovery"
2728
"k8s.io/client-go/dynamic"
2829
"k8s.io/client-go/kubernetes"
2930
"k8s.io/client-go/tools/clientcmd"
@@ -146,12 +147,17 @@ func BuildClusterAPI(opts config.AutoscalingOptions, do cloudprovider.NodeGroupD
146147
klog.Fatalf("could not generate dynamic client for config")
147148
}
148149

149-
kubeclient, err := kubernetes.NewForConfig(externalConfig)
150+
kubeClient, err := kubernetes.NewForConfig(externalConfig)
150151
if err != nil {
151152
klog.Fatalf("create kube clientset failed: %v", err)
152153
}
153154

154-
controller, err := newMachineController(dc, kubeclient)
155+
discoveryClient, err := discovery.NewDiscoveryClientForConfig(externalConfig)
156+
if err != nil {
157+
klog.Fatalf("create discovery client failed: %v", err)
158+
}
159+
160+
controller, err := newMachineController(dc, kubeClient, discoveryClient)
155161
if err != nil {
156162
klog.Fatal(err)
157163
}

0 commit comments

Comments
 (0)