1
16
17 package discovery_test
18
19 import (
20 "bytes"
21 "encoding/json"
22 "errors"
23 "io"
24 "net/http"
25 "strings"
26 "testing"
27
28 v1 "k8s.io/api/core/v1"
29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 "k8s.io/apimachinery/pkg/runtime"
31 "k8s.io/apimachinery/pkg/runtime/schema"
32 "k8s.io/apimachinery/pkg/util/sets"
33 "k8s.io/client-go/discovery"
34 restclient "k8s.io/client-go/rest"
35 "k8s.io/client-go/rest/fake"
36 )
37
38 func objBody(object interface{}) io.ReadCloser {
39 output, err := json.MarshalIndent(object, "", "")
40 if err != nil {
41 panic(err)
42 }
43 return io.NopCloser(bytes.NewReader([]byte(output)))
44 }
45
46 func TestServerSupportsVersion(t *testing.T) {
47 tests := []struct {
48 name string
49 requiredVersion schema.GroupVersion
50 serverVersions []string
51 expectErr func(err error) bool
52 sendErr error
53 statusCode int
54 }{
55 {
56 name: "explicit version supported",
57 requiredVersion: schema.GroupVersion{Version: "v1"},
58 serverVersions: []string{"/version1", v1.SchemeGroupVersion.String()},
59 statusCode: http.StatusOK,
60 },
61 {
62 name: "explicit version not supported on server",
63 requiredVersion: schema.GroupVersion{Version: "v1"},
64 serverVersions: []string{"version1"},
65 expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) },
66 statusCode: http.StatusOK,
67 },
68 {
69 name: "Status 403 Forbidden for core/v1 group returns error and is unsupported",
70 requiredVersion: schema.GroupVersion{Version: "v1"},
71 serverVersions: []string{"/version1", v1.SchemeGroupVersion.String()},
72 expectErr: func(err error) bool { return strings.Contains(err.Error(), "unknown") },
73 statusCode: http.StatusForbidden,
74 },
75 {
76 name: "Status 404 Not Found for core/v1 group returns empty and is unsupported",
77 requiredVersion: schema.GroupVersion{Version: "v1"},
78 serverVersions: []string{"/version1", v1.SchemeGroupVersion.String()},
79 expectErr: func(err error) bool {
80 return strings.Contains(err.Error(), "server could not find the requested resource")
81 },
82 statusCode: http.StatusNotFound,
83 },
84 {
85 name: "connection refused error",
86 serverVersions: []string{"version1"},
87 sendErr: errors.New("connection refused"),
88 expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") },
89 statusCode: http.StatusOK,
90 },
91 }
92
93 for _, test := range tests {
94 fakeClient := fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
95 if test.sendErr != nil {
96 return nil, test.sendErr
97 }
98 header := http.Header{}
99 header.Set("Content-Type", runtime.ContentTypeJSON)
100 return &http.Response{StatusCode: test.statusCode, Header: header, Body: objBody(&metav1.APIVersions{Versions: test.serverVersions})}, nil
101 })
102 c := discovery.NewDiscoveryClientForConfigOrDie(&restclient.Config{})
103 c.RESTClient().(*restclient.RESTClient).Client = fakeClient
104 err := discovery.ServerSupportsVersion(c, test.requiredVersion)
105 if err == nil && test.expectErr != nil {
106 t.Errorf("expected error, got nil for [%s].", test.name)
107 }
108 if err != nil {
109 if test.expectErr == nil || !test.expectErr(err) {
110 t.Errorf("unexpected error for [%s]: %v.", test.name, err)
111 }
112 continue
113 }
114 }
115 }
116
117 func TestFilteredBy(t *testing.T) {
118 all := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
119 return true
120 })
121 none := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
122 return false
123 })
124 onlyV2 := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
125 return strings.HasSuffix(gv, "/v2") || gv == "v2"
126 })
127 onlyBar := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool {
128 return r.Kind == "Bar"
129 })
130
131 foo := []*metav1.APIResourceList{
132 {
133 GroupVersion: "foo/v1",
134 APIResources: []metav1.APIResource{
135 {Name: "bar", Kind: "Bar"},
136 {Name: "test", Kind: "Test"},
137 },
138 },
139 {
140 GroupVersion: "foo/v2",
141 APIResources: []metav1.APIResource{
142 {Name: "bar", Kind: "Bar"},
143 {Name: "test", Kind: "Test"},
144 },
145 },
146 {
147 GroupVersion: "foo/v3",
148 APIResources: []metav1.APIResource{},
149 },
150 }
151
152 tests := []struct {
153 input []*metav1.APIResourceList
154 pred discovery.ResourcePredicate
155 expectedResources []string
156 }{
157 {nil, all, []string{}},
158 {[]*metav1.APIResourceList{
159 {GroupVersion: "foo/v1"},
160 }, all, []string{}},
161 {foo, all, []string{"foo/v1.bar", "foo/v1.test", "foo/v2.bar", "foo/v2.test"}},
162 {foo, onlyV2, []string{"foo/v2.bar", "foo/v2.test"}},
163 {foo, onlyBar, []string{"foo/v1.bar", "foo/v2.bar"}},
164 {foo, none, []string{}},
165 }
166 for i, test := range tests {
167 filtered := discovery.FilteredBy(test.pred, test.input)
168
169 if expected, got := sets.NewString(test.expectedResources...), sets.NewString(stringify(filtered)...); !expected.Equal(got) {
170 t.Errorf("[%d] unexpected group versions: expected=%v, got=%v", i, test.expectedResources, stringify(filtered))
171 }
172 }
173 }
174
175 func stringify(rls []*metav1.APIResourceList) []string {
176 result := []string{}
177 for _, rl := range rls {
178 for _, r := range rl.APIResources {
179 result = append(result, rl.GroupVersion+"."+r.Name)
180 }
181 if len(rl.APIResources) == 0 {
182 result = append(result, rl.GroupVersion)
183 }
184 }
185 return result
186 }
187
View as plain text