k8s.io/client-go@v0.31.1/util/testing/fake_openapi_handler.go (about) 1 /* 2 Copyright 2021 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package testing 18 19 import ( 20 "encoding/json" 21 "io" 22 "io/fs" 23 "net/http" 24 "net/http/httptest" 25 "os" 26 "path/filepath" 27 "strings" 28 "sync" 29 30 "k8s.io/kube-openapi/pkg/handler3" 31 "k8s.io/kube-openapi/pkg/spec3" 32 ) 33 34 type FakeOpenAPIServer struct { 35 HttpServer *httptest.Server 36 ServedDocuments map[string]*spec3.OpenAPI 37 RequestCounters map[string]int 38 } 39 40 // Creates a mock OpenAPIV3 server as it would be on a standing kubernetes 41 // API server. 42 // 43 // specsPath - Give a path to some test data organized so that each GroupVersion 44 // has its own OpenAPI V3 JSON file. 45 // 46 // i.e. apps/v1beta1 is stored in <specsPath>/apps/v1beta1.json 47 func NewFakeOpenAPIV3Server(specsPath string) (*FakeOpenAPIServer, error) { 48 mux := &testMux{ 49 counts: map[string]int{}, 50 } 51 server := httptest.NewServer(mux) 52 53 openAPIVersionedService := handler3.NewOpenAPIService() 54 err := openAPIVersionedService.RegisterOpenAPIV3VersionedService("/openapi/v3", mux) 55 if err != nil { 56 return nil, err 57 } 58 59 grouped := make(map[string][]byte) 60 var testV3Specs = make(map[string]*spec3.OpenAPI) 61 62 addSpec := func(path string) { 63 file, err := os.Open(path) 64 if err != nil { 65 panic(err) 66 } 67 68 defer file.Close() 69 vals, err := io.ReadAll(file) 70 if err != nil { 71 panic(err) 72 } 73 74 rel, err := filepath.Rel(specsPath, path) 75 if err == nil { 76 grouped[rel[:(len(rel)-len(filepath.Ext(rel)))]] = vals 77 } 78 } 79 80 filepath.WalkDir(specsPath, func(path string, d fs.DirEntry, err error) error { 81 if filepath.Ext(path) != ".json" || d.IsDir() { 82 return nil 83 } 84 85 addSpec(path) 86 return nil 87 }) 88 89 for gv, jsonSpec := range grouped { 90 spec := &spec3.OpenAPI{} 91 err = json.Unmarshal(jsonSpec, spec) 92 if err != nil { 93 return nil, err 94 } 95 96 testV3Specs[gv] = spec 97 openAPIVersionedService.UpdateGroupVersion(gv, spec) 98 } 99 100 return &FakeOpenAPIServer{ 101 HttpServer: server, 102 ServedDocuments: testV3Specs, 103 RequestCounters: mux.counts, 104 }, nil 105 } 106 107 //////////////////////////////////////////////////////////////////////////////// 108 // Tiny Test HTTP Mux 109 //////////////////////////////////////////////////////////////////////////////// 110 // Implements the mux interface used by handler3 for registering the OpenAPI 111 // handlers 112 113 type testMux struct { 114 lock sync.Mutex 115 prefixMap map[string]http.Handler 116 pathMap map[string]http.Handler 117 counts map[string]int 118 } 119 120 func (t *testMux) Handle(path string, handler http.Handler) { 121 t.lock.Lock() 122 defer t.lock.Unlock() 123 124 if t.pathMap == nil { 125 t.pathMap = make(map[string]http.Handler) 126 } 127 t.pathMap[path] = handler 128 } 129 130 func (t *testMux) HandlePrefix(path string, handler http.Handler) { 131 t.lock.Lock() 132 defer t.lock.Unlock() 133 134 if t.prefixMap == nil { 135 t.prefixMap = make(map[string]http.Handler) 136 } 137 t.prefixMap[path] = handler 138 } 139 140 func (t *testMux) ServeHTTP(w http.ResponseWriter, req *http.Request) { 141 t.lock.Lock() 142 defer t.lock.Unlock() 143 144 if t.counts == nil { 145 t.counts = make(map[string]int) 146 } 147 148 if val, exists := t.counts[req.URL.Path]; exists { 149 t.counts[req.URL.Path] = val + 1 150 } else { 151 t.counts[req.URL.Path] = 1 152 } 153 154 if handler, ok := t.pathMap[req.URL.Path]; ok { 155 handler.ServeHTTP(w, req) 156 return 157 } 158 159 for k, v := range t.prefixMap { 160 if strings.HasPrefix(req.URL.Path, k) { 161 v.ServeHTTP(w, req) 162 return 163 } 164 } 165 166 w.WriteHeader(http.StatusNotFound) 167 }