Skip to content

Commit dc8b38d

Browse files
committed
Handle k8s api deprecations
1 parent 73609c4 commit dc8b38d

File tree

3 files changed

+208
-6
lines changed

3 files changed

+208
-6
lines changed

pkg/collect/cluster_resources.go

Lines changed: 103 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,13 @@ import (
1313
authorizationv1 "k8s.io/api/authorization/v1"
1414
corev1 "k8s.io/api/core/v1"
1515
rbacv1 "k8s.io/api/rbac/v1"
16+
apiextensionsv1clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1"
1617
apiextensionsv1beta1clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
1718
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1819
"k8s.io/client-go/kubernetes"
20+
"k8s.io/client-go/rest"
21+
22+
"github.com/replicatedhq/troubleshoot/pkg/k8sutil/discovery"
1923
)
2024

2125
func ClusterResources(c *Collector, clusterResourcesCollector *troubleshootv1beta2.ClusterResources) (CollectorResult, error) {
@@ -105,11 +109,7 @@ func ClusterResources(c *Collector, clusterResourcesCollector *troubleshootv1bet
105109
output.SaveResult(c.BundlePath, "cluster-resources/storage-errors.json", marshalErrors(storageErrors))
106110

107111
// crds
108-
crdClient, err := apiextensionsv1beta1clientset.NewForConfig(c.ClientConfig)
109-
if err != nil {
110-
return nil, err
111-
}
112-
customResourceDefinitions, crdErrors := crds(ctx, crdClient)
112+
customResourceDefinitions, crdErrors := crds(ctx, client, c.ClientConfig)
113113
output.SaveResult(c.BundlePath, "cluster-resources/custom-resource-definitions.json", bytes.NewBuffer(customResourceDefinitions))
114114
output.SaveResult(c.BundlePath, "cluster-resources/custom-resource-definitions-errors.json", marshalErrors(crdErrors))
115115

@@ -355,6 +355,41 @@ func cronJobs(ctx context.Context, client *kubernetes.Clientset, namespaces []st
355355
}
356356

357357
func ingress(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
358+
ok, err := discovery.HasResource(client, "networking.k8s.io/v1", "Ingress")
359+
if err != nil {
360+
return nil, map[string]string{"": err.Error()}
361+
}
362+
if ok {
363+
return ingressV1(ctx, client, namespaces)
364+
}
365+
366+
return ingressV1beta(ctx, client, namespaces)
367+
}
368+
369+
func ingressV1(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
370+
ingressByNamespace := make(map[string][]byte)
371+
errorsByNamespace := make(map[string]string)
372+
373+
for _, namespace := range namespaces {
374+
ingress, err := client.NetworkingV1().Ingresses(namespace).List(ctx, metav1.ListOptions{})
375+
if err != nil {
376+
errorsByNamespace[namespace] = err.Error()
377+
continue
378+
}
379+
380+
b, err := json.MarshalIndent(ingress.Items, "", " ")
381+
if err != nil {
382+
errorsByNamespace[namespace] = err.Error()
383+
continue
384+
}
385+
386+
ingressByNamespace[namespace+".json"] = b
387+
}
388+
389+
return ingressByNamespace, errorsByNamespace
390+
}
391+
392+
func ingressV1beta(ctx context.Context, client *kubernetes.Clientset, namespaces []string) (map[string][]byte, map[string]string) {
358393
ingressByNamespace := make(map[string][]byte)
359394
errorsByNamespace := make(map[string]string)
360395

@@ -378,6 +413,32 @@ func ingress(ctx context.Context, client *kubernetes.Clientset, namespaces []str
378413
}
379414

380415
func storageClasses(ctx context.Context, client *kubernetes.Clientset) ([]byte, []string) {
416+
ok, err := discovery.HasResource(client, "storage.k8s.io/v1", "StorageClass")
417+
if err != nil {
418+
return nil, []string{err.Error()}
419+
}
420+
if ok {
421+
return storageClassesV1(ctx, client)
422+
}
423+
424+
return storageClassesV1beta(ctx, client)
425+
}
426+
427+
func storageClassesV1(ctx context.Context, client *kubernetes.Clientset) ([]byte, []string) {
428+
storageClasses, err := client.StorageV1().StorageClasses().List(ctx, metav1.ListOptions{})
429+
if err != nil {
430+
return nil, []string{err.Error()}
431+
}
432+
433+
b, err := json.MarshalIndent(storageClasses.Items, "", " ")
434+
if err != nil {
435+
return nil, []string{err.Error()}
436+
}
437+
438+
return b, nil
439+
}
440+
441+
func storageClassesV1beta(ctx context.Context, client *kubernetes.Clientset) ([]byte, []string) {
381442
storageClasses, err := client.StorageV1beta1().StorageClasses().List(ctx, metav1.ListOptions{})
382443
if err != nil {
383444
return nil, []string{err.Error()}
@@ -391,7 +452,43 @@ func storageClasses(ctx context.Context, client *kubernetes.Clientset) ([]byte,
391452
return b, nil
392453
}
393454

394-
func crds(ctx context.Context, client *apiextensionsv1beta1clientset.ApiextensionsV1beta1Client) ([]byte, []string) {
455+
func crds(ctx context.Context, client *kubernetes.Clientset, config *rest.Config) ([]byte, []string) {
456+
ok, err := discovery.HasResource(client, "apiextensions.k8s.io/v1", "CustomResourceDefinition")
457+
if err != nil {
458+
return nil, []string{err.Error()}
459+
}
460+
if ok {
461+
return crdsV1(ctx, config)
462+
}
463+
464+
return crdsV1beta(ctx, config)
465+
}
466+
467+
func crdsV1(ctx context.Context, config *rest.Config) ([]byte, []string) {
468+
client, err := apiextensionsv1clientset.NewForConfig(config)
469+
if err != nil {
470+
return nil, []string{err.Error()}
471+
}
472+
473+
crds, err := client.CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
474+
if err != nil {
475+
return nil, []string{err.Error()}
476+
}
477+
478+
b, err := json.MarshalIndent(crds.Items, "", " ")
479+
if err != nil {
480+
return nil, []string{err.Error()}
481+
}
482+
483+
return b, nil
484+
}
485+
486+
func crdsV1beta(ctx context.Context, config *rest.Config) ([]byte, []string) {
487+
client, err := apiextensionsv1beta1clientset.NewForConfig(config)
488+
if err != nil {
489+
return nil, []string{err.Error()}
490+
}
491+
395492
crds, err := client.CustomResourceDefinitions().List(ctx, metav1.ListOptions{})
396493
if err != nil {
397494
return nil, []string{err.Error()}

pkg/k8sutil/discovery/discovery.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package discovery
2+
3+
import (
4+
"k8s.io/client-go/discovery"
5+
)
6+
7+
// HasResource takes an api version and a kind of a resource and checks if the resource
8+
// is supported by the k8s api server.
9+
func HasResource(dc discovery.DiscoveryInterface, apiVersion, kind string) (bool, error) {
10+
_, apiLists, err := dc.ServerGroupsAndResources()
11+
if err != nil {
12+
return false, err
13+
}
14+
// Compare the resource api version and kind and find the resource.
15+
for _, apiList := range apiLists {
16+
if apiList.GroupVersion == apiVersion {
17+
for _, r := range apiList.APIResources {
18+
if r.Kind == kind {
19+
return true, nil
20+
}
21+
}
22+
}
23+
}
24+
return false, nil
25+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package discovery
2+
3+
import (
4+
"testing"
5+
6+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7+
fakediscovery "k8s.io/client-go/discovery/fake"
8+
fakeclientset "k8s.io/client-go/kubernetes/fake"
9+
)
10+
11+
func TestHasResource(t *testing.T) {
12+
testKind := "Foo"
13+
testKindGroupVersion := "v1"
14+
15+
testcases := []struct {
16+
name string
17+
apiResourceList []*metav1.APIResourceList
18+
wantResult bool
19+
}{
20+
{
21+
name: "empty api resource list",
22+
apiResourceList: []*metav1.APIResourceList{},
23+
wantResult: false,
24+
},
25+
{
26+
name: "have resource",
27+
apiResourceList: []*metav1.APIResourceList{
28+
{
29+
GroupVersion: "v1",
30+
APIResources: []metav1.APIResource{
31+
{
32+
Kind: "Foo",
33+
},
34+
{
35+
Kind: "Bar",
36+
},
37+
},
38+
},
39+
},
40+
wantResult: true,
41+
},
42+
{
43+
name: "have resource but different version",
44+
apiResourceList: []*metav1.APIResourceList{
45+
{
46+
GroupVersion: "v2",
47+
APIResources: []metav1.APIResource{
48+
{
49+
Kind: "Foo",
50+
},
51+
{
52+
Kind: "Bar",
53+
},
54+
},
55+
},
56+
},
57+
wantResult: false,
58+
},
59+
}
60+
61+
for _, tc := range testcases {
62+
tc := tc
63+
t.Run(tc.name, func(t *testing.T) {
64+
client := fakeclientset.NewSimpleClientset()
65+
fakeDiscovery, ok := client.Discovery().(*fakediscovery.FakeDiscovery)
66+
if !ok {
67+
t.Fatalf("could not convert Discovery() to *FakeDiscovery")
68+
}
69+
fakeDiscovery.Resources = tc.apiResourceList
70+
71+
exists, err := HasResource(fakeDiscovery, testKindGroupVersion, testKind)
72+
if err != nil {
73+
t.Fatalf("failed to check if resource exists: %v", err)
74+
}
75+
if exists != tc.wantResult {
76+
t.Errorf("unexpected result for HasResource:\n\t(WNT) %t\n\t(GOT) %t", tc.wantResult, exists)
77+
}
78+
})
79+
}
80+
}

0 commit comments

Comments
 (0)