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  }