1/* 2Copyright 2015 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package discovery_test 18 19import ( 20 "bytes" 21 "encoding/json" 22 "errors" 23 "io" 24 "io/ioutil" 25 "net/http" 26 "strings" 27 "testing" 28 29 "k8s.io/api/core/v1" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 "k8s.io/apimachinery/pkg/util/sets" 34 "k8s.io/client-go/discovery" 35 "k8s.io/client-go/kubernetes/scheme" 36 restclient "k8s.io/client-go/rest" 37 "k8s.io/client-go/rest/fake" 38) 39 40func objBody(object interface{}) io.ReadCloser { 41 output, err := json.MarshalIndent(object, "", "") 42 if err != nil { 43 panic(err) 44 } 45 return ioutil.NopCloser(bytes.NewReader([]byte(output))) 46} 47 48func TestServerSupportsVersion(t *testing.T) { 49 tests := []struct { 50 name string 51 requiredVersion schema.GroupVersion 52 serverVersions []string 53 expectErr func(err error) bool 54 sendErr error 55 statusCode int 56 }{ 57 { 58 name: "explicit version supported", 59 requiredVersion: schema.GroupVersion{Version: "v1"}, 60 serverVersions: []string{"/version1", v1.SchemeGroupVersion.String()}, 61 statusCode: http.StatusOK, 62 }, 63 { 64 name: "explicit version not supported on server", 65 requiredVersion: schema.GroupVersion{Version: "v1"}, 66 serverVersions: []string{"version1"}, 67 expectErr: func(err error) bool { return strings.Contains(err.Error(), `server does not support API version "v1"`) }, 68 statusCode: http.StatusOK, 69 }, 70 { 71 name: "connection refused error", 72 serverVersions: []string{"version1"}, 73 sendErr: errors.New("connection refused"), 74 expectErr: func(err error) bool { return strings.Contains(err.Error(), "connection refused") }, 75 statusCode: http.StatusOK, 76 }, 77 { 78 name: "discovery fails due to 404 Not Found errors and thus serverVersions is empty, use requested GroupVersion", 79 requiredVersion: schema.GroupVersion{Version: "version1"}, 80 statusCode: http.StatusNotFound, 81 }, 82 } 83 84 for _, test := range tests { 85 fakeClient := &fake.RESTClient{ 86 NegotiatedSerializer: scheme.Codecs, 87 Resp: &http.Response{ 88 StatusCode: test.statusCode, 89 Body: objBody(&metav1.APIVersions{Versions: test.serverVersions}), 90 }, 91 Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { 92 if test.sendErr != nil { 93 return nil, test.sendErr 94 } 95 header := http.Header{} 96 header.Set("Content-Type", runtime.ContentTypeJSON) 97 return &http.Response{StatusCode: test.statusCode, Header: header, Body: objBody(&metav1.APIVersions{Versions: test.serverVersions})}, nil 98 }), 99 } 100 c := discovery.NewDiscoveryClientForConfigOrDie(&restclient.Config{}) 101 c.RESTClient().(*restclient.RESTClient).Client = fakeClient.Client 102 err := discovery.ServerSupportsVersion(c, test.requiredVersion) 103 if err == nil && test.expectErr != nil { 104 t.Errorf("expected error, got nil for [%s].", test.name) 105 } 106 if err != nil { 107 if test.expectErr == nil || !test.expectErr(err) { 108 t.Errorf("unexpected error for [%s]: %v.", test.name, err) 109 } 110 continue 111 } 112 } 113} 114 115func TestFilteredBy(t *testing.T) { 116 all := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool { 117 return true 118 }) 119 none := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool { 120 return false 121 }) 122 onlyV2 := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool { 123 return strings.HasSuffix(gv, "/v2") || gv == "v2" 124 }) 125 onlyBar := discovery.ResourcePredicateFunc(func(gv string, r *metav1.APIResource) bool { 126 return r.Kind == "Bar" 127 }) 128 129 foo := []*metav1.APIResourceList{ 130 { 131 GroupVersion: "foo/v1", 132 APIResources: []metav1.APIResource{ 133 {Name: "bar", Kind: "Bar"}, 134 {Name: "test", Kind: "Test"}, 135 }, 136 }, 137 { 138 GroupVersion: "foo/v2", 139 APIResources: []metav1.APIResource{ 140 {Name: "bar", Kind: "Bar"}, 141 {Name: "test", Kind: "Test"}, 142 }, 143 }, 144 { 145 GroupVersion: "foo/v3", 146 APIResources: []metav1.APIResource{}, 147 }, 148 } 149 150 tests := []struct { 151 input []*metav1.APIResourceList 152 pred discovery.ResourcePredicate 153 expectedResources []string 154 }{ 155 {nil, all, []string{}}, 156 {[]*metav1.APIResourceList{ 157 {GroupVersion: "foo/v1"}, 158 }, all, []string{}}, 159 {foo, all, []string{"foo/v1.bar", "foo/v1.test", "foo/v2.bar", "foo/v2.test"}}, 160 {foo, onlyV2, []string{"foo/v2.bar", "foo/v2.test"}}, 161 {foo, onlyBar, []string{"foo/v1.bar", "foo/v2.bar"}}, 162 {foo, none, []string{}}, 163 } 164 for i, test := range tests { 165 filtered := discovery.FilteredBy(test.pred, test.input) 166 167 if expected, got := sets.NewString(test.expectedResources...), sets.NewString(stringify(filtered)...); !expected.Equal(got) { 168 t.Errorf("[%d] unexpected group versions: expected=%v, got=%v", i, test.expectedResources, stringify(filtered)) 169 } 170 } 171} 172 173func stringify(rls []*metav1.APIResourceList) []string { 174 result := []string{} 175 for _, rl := range rls { 176 for _, r := range rl.APIResources { 177 result = append(result, rl.GroupVersion+"."+r.Name) 178 } 179 if len(rl.APIResources) == 0 { 180 result = append(result, rl.GroupVersion) 181 } 182 } 183 return result 184} 185