github.com/deemoprobe/k8s-first-commit@v0.0.0-20230430165612-a541f1982be3/pkg/apiserver/api_server.go (about) 1 /* 2 Copyright 2014 Google Inc. All rights reserved. 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 apiserver is ... 18 package apiserver 19 20 import ( 21 "encoding/json" 22 "fmt" 23 "io/ioutil" 24 "log" 25 "net/http" 26 "net/url" 27 "strings" 28 ) 29 30 // RESTStorage is a generic interface for RESTful storage services 31 type RESTStorage interface { 32 List(*url.URL) (interface{}, error) 33 Get(id string) (interface{}, error) 34 Delete(id string) error 35 Extract(body string) (interface{}, error) 36 Create(interface{}) error 37 Update(interface{}) error 38 } 39 40 // Status is a return value for calls that don't return other objects 41 type Status struct { 42 success bool 43 } 44 45 // ApiServer is an HTTPHandler that delegates to RESTStorage objects. 46 // It handles URLs of the form: 47 // ${prefix}/${storage_key}[/${object_name}] 48 // Where 'prefix' is an arbitrary string, and 'storage_key' points to a RESTStorage object stored in storage. 49 // 50 // TODO: consider migrating this to go-restful which is a more full-featured version of the same thing. 51 type ApiServer struct { 52 prefix string 53 storage map[string]RESTStorage 54 } 55 56 // New creates a new ApiServer object. 57 // 'storage' contains a map of handlers. 58 // 'prefix' is the hosting path prefix. 59 func New(storage map[string]RESTStorage, prefix string) *ApiServer { 60 return &ApiServer{ 61 storage: storage, 62 prefix: prefix, 63 } 64 } 65 66 func (server *ApiServer) handleIndex(w http.ResponseWriter) { 67 w.WriteHeader(http.StatusOK) 68 // TODO: serve this out of a file? 69 data := "<html><body>Welcome to Kubernetes</body></html>" 70 fmt.Fprint(w, data) 71 } 72 73 // HTTP Handler interface 74 func (server *ApiServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { 75 log.Printf("%s %s", req.Method, req.RequestURI) 76 url, err := url.ParseRequestURI(req.RequestURI) 77 if err != nil { 78 server.error(err, w) 79 return 80 } 81 if url.Path == "/index.html" || url.Path == "/" || url.Path == "" { 82 server.handleIndex(w) 83 return 84 } 85 if !strings.HasPrefix(url.Path, server.prefix) { 86 server.notFound(req, w) 87 return 88 } 89 requestParts := strings.Split(url.Path[len(server.prefix):], "/")[1:] 90 if len(requestParts) < 1 { 91 server.notFound(req, w) 92 return 93 } 94 storage := server.storage[requestParts[0]] 95 if storage == nil { 96 server.notFound(req, w) 97 return 98 } else { 99 server.handleREST(requestParts, url, req, w, storage) 100 } 101 } 102 103 func (server *ApiServer) notFound(req *http.Request, w http.ResponseWriter) { 104 w.WriteHeader(404) 105 fmt.Fprintf(w, "Not Found: %#v", req) 106 } 107 108 func (server *ApiServer) write(statusCode int, object interface{}, w http.ResponseWriter) { 109 w.WriteHeader(statusCode) 110 output, err := json.MarshalIndent(object, "", " ") 111 if err != nil { 112 server.error(err, w) 113 return 114 } 115 w.Write(output) 116 } 117 118 func (server *ApiServer) error(err error, w http.ResponseWriter) { 119 w.WriteHeader(500) 120 fmt.Fprintf(w, "Internal Error: %#v", err) 121 } 122 123 func (server *ApiServer) readBody(req *http.Request) (string, error) { 124 defer req.Body.Close() 125 body, err := ioutil.ReadAll(req.Body) 126 return string(body), err 127 } 128 129 func (server *ApiServer) handleREST(parts []string, url *url.URL, req *http.Request, w http.ResponseWriter, storage RESTStorage) { 130 switch req.Method { 131 case "GET": 132 switch len(parts) { 133 case 1: 134 controllers, err := storage.List(url) 135 if err != nil { 136 server.error(err, w) 137 return 138 } 139 server.write(200, controllers, w) 140 case 2: 141 task, err := storage.Get(parts[1]) 142 if err != nil { 143 server.error(err, w) 144 return 145 } 146 if task == nil { 147 server.notFound(req, w) 148 return 149 } 150 server.write(200, task, w) 151 default: 152 server.notFound(req, w) 153 } 154 return 155 case "POST": 156 if len(parts) != 1 { 157 server.notFound(req, w) 158 return 159 } 160 body, err := server.readBody(req) 161 if err != nil { 162 server.error(err, w) 163 return 164 } 165 obj, err := storage.Extract(body) 166 if err != nil { 167 server.error(err, w) 168 return 169 } 170 storage.Create(obj) 171 server.write(200, obj, w) 172 return 173 case "DELETE": 174 if len(parts) != 2 { 175 server.notFound(req, w) 176 return 177 } 178 err := storage.Delete(parts[1]) 179 if err != nil { 180 server.error(err, w) 181 return 182 } 183 server.write(200, Status{success: true}, w) 184 return 185 case "PUT": 186 if len(parts) != 2 { 187 server.notFound(req, w) 188 return 189 } 190 body, err := server.readBody(req) 191 if err != nil { 192 server.error(err, w) 193 } 194 obj, err := storage.Extract(body) 195 if err != nil { 196 server.error(err, w) 197 return 198 } 199 err = storage.Update(obj) 200 if err != nil { 201 server.error(err, w) 202 return 203 } 204 server.write(200, obj, w) 205 return 206 default: 207 server.notFound(req, w) 208 } 209 }