package main import ( "bytes" "crypto/tls" "encoding/json" "flag" "fmt" "html/template" "io" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "strings" ) var flags struct { addr string proxyURL string proxyClientCert string proxyClientKey string } func init() { flag.StringVar(&flags.addr, "addr", "localhost:8080", "Address to listen on") flag.StringVar(&flags.proxyURL, "proxy-url", "", "Proxy requests to this URL") flag.StringVar(&flags.proxyClientCert, "proxy-client-cert", "", "Client certificate to use when connecting to proxy") flag.StringVar(&flags.proxyClientKey, "proxy-client-key", "", "Client key to use when connecting to proxy") } var responses = []Response{ JSONResponse("GET", "/api", `{"kind": "APIVersions", "versions": ["v1"]}`), JSONResponse("GET", "/apis", `{}`), JSONResponse("GET", "/api/v1", `{"kind": "APIResourceList", "resources": [{"name": "pods", "namespaced": true, "kind": "Pod", "verbs": ["get", "list"], "categories": ["all"]}]}`), JSONResponse("GET", "/api/v1/namespaces/default/pods", `{"kind": "PodList", "apiVersion": "v1", "items": [{"metadata": {"name": "oops-v1-214fbj25k"}, "status": {"phase": "Running", "conditions": [{"type": "Ready", "status": "True"}], "startTime": "2018-06-08T09:48:22Z"}}]}`), } func main() { flag.Parse() requestLog := make([]Request, 0) http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { var resp *http.Response if flags.proxyURL != "" { resp = respondWithProxy(flags.proxyURL, w, req) } else { resp = respondWithStub(responses, w, req) } userAgent := req.Header.Get("User-Agent") log.Printf("%s %s - %d (%s, %q)", req.Method, req.URL, resp.StatusCode, req.RemoteAddr, userAgent) buf := new(bytes.Buffer) out, err := httputil.DumpRequest(req, true) if err != nil { log.Printf("Error: Dumping request: %s", err) return } buf.Write(out) if resp.Header.Get("Content-Type") == "application/json" { pretty, err := prettyfyJSON(resp.Body) if err != nil { log.Printf("Error: Prettyfying JSON: %s", err) } else { resp.Body = ioutil.NopCloser(bytes.NewReader(pretty)) } } respOut, err := httputil.DumpResponse(resp, true) if err != nil { log.Printf("Error: Dumping response: %s", err) return } buf.Write([]byte("\n")) buf.Write(respOut) buf.Write([]byte("\n")) requestLog = append(requestLog, buf.Bytes()) }) http.HandleFunc("/_log", func(w http.ResponseWriter, req *http.Request) { for i := len(requestLog) - 1; i >= 0; i-- { w.Write([]byte("------------------------------------------------------\n")) w.Write(requestLog[i]) } }) http.HandleFunc("/_stub", func(w http.ResponseWriter, req *http.Request) { switch req.Method { case "GET": err := stubTmpl.Execute(w, nil) if err != nil { log.Printf("Error: Rendering stub template: %s", err) return } case "POST": err := req.ParseForm() if err != nil { log.Printf("Error: Parsing form: %s", err) return } responses = append(responses, readResponse(req.Form)) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } }) http.HandleFunc("/_stubs", func(w http.ResponseWriter, req *http.Request) { w.Header().Set("Content-Type", "text/html") fmt.Fprintf(w, "
%s
%s