diff options
Diffstat (limited to 'cmd/blubberoid/main.go')
-rw-r--r-- | cmd/blubberoid/main.go | 90 |
1 files changed, 67 insertions, 23 deletions
diff --git a/cmd/blubberoid/main.go b/cmd/blubberoid/main.go index d436390..19604c1 100644 --- a/cmd/blubberoid/main.go +++ b/cmd/blubberoid/main.go @@ -3,27 +3,32 @@ package main import ( - "encoding/json" + "bytes" "fmt" "io/ioutil" "log" "mime" "net/http" + "net/url" "os" "path" + "strings" + "text/template" "github.com/pborman/getopt/v2" "gerrit.wikimedia.org/r/blubber/config" "gerrit.wikimedia.org/r/blubber/docker" + "gerrit.wikimedia.org/r/blubber/meta" ) var ( - showHelp = getopt.BoolLong("help", 'h', "show help/usage") - address = getopt.StringLong("address", 'a', ":8748", "socket address/port to listen on (default ':8748')", "address:port") - endpoint = getopt.StringLong("endpoint", 'e', "/", "server endpoint (default '/')", "path") - policyURI = getopt.StringLong("policy", 'p', "", "policy file URI", "uri") - policy *config.Policy + showHelp = getopt.BoolLong("help", 'h', "show help/usage") + address = getopt.StringLong("address", 'a', ":8748", "socket address/port to listen on (default ':8748')", "address:port") + endpoint = getopt.StringLong("endpoint", 'e', "/", "server endpoint (default '/')", "path") + policyURI = getopt.StringLong("policy", 'p', "", "policy file URI", "uri") + policy *config.Policy + openAPISpec []byte ) func main() { @@ -51,7 +56,10 @@ func main() { *endpoint += "/" } - log.Printf("listening on %s for requests to %s[variant]\n", *address, *endpoint) + // Evaluate OpenAPI spec template and store results for ?spec requests + openAPISpec = readOpenAPISpec() + + log.Printf("listening on %s for requests to %sv1/[variant]\n", *address, *endpoint) http.HandleFunc(*endpoint, blubberoid) log.Fatal(http.ListenAndServe(*address, nil)) @@ -59,12 +67,35 @@ func main() { func blubberoid(res http.ResponseWriter, req *http.Request) { if len(req.URL.Path) <= len(*endpoint) { + if req.URL.RawQuery == "spec" { + res.Header().Set("Content-Type", "text/plain") + res.Write(openAPISpec) + return + } + res.WriteHeader(http.StatusNotFound) - res.Write(responseBody("request a variant at %s[variant]", *endpoint)) + res.Write(responseBody("request a variant at %sv1/[variant]", *endpoint)) + return + } + + requestPath := req.URL.Path[len(*endpoint):] + pathSegments := strings.Split(requestPath, "/") + + // Request should have been to v1/[variant] + if len(pathSegments) != 2 || pathSegments[0] != "v1" { + res.WriteHeader(http.StatusNotFound) + res.Write(responseBody("request a variant at %sv1/[variant]", *endpoint)) + return + } + + variant, err := url.PathUnescape(pathSegments[1]) + + if err != nil { + res.WriteHeader(http.StatusInternalServerError) + log.Printf("failed to unescape variant name '%s': %s\n", pathSegments[1], err) return } - variant := req.URL.Path[len(*endpoint):] body, err := ioutil.ReadAll(req.Body) if err != nil { @@ -73,25 +104,25 @@ func blubberoid(res http.ResponseWriter, req *http.Request) { return } - switch mt, _, _ := mime.ParseMediaType(req.Header.Get("content-type")); mt { + var cfg *config.Config + mediaType, _, _ := mime.ParseMediaType(req.Header.Get("content-type")) + + // Default to application/json + if mediaType == "" { + mediaType = "application/json" + } + + switch mediaType { case "application/json": - // Enforce strict JSON syntax if specified, even though the config parser - // would technically handle anything that's at least valid YAML - if !json.Valid(body) { - res.WriteHeader(http.StatusBadRequest) - res.Write(responseBody("'%s' media type given but request contains invalid JSON", mt)) - return - } + cfg, err = config.ReadConfig(body) case "application/yaml", "application/x-yaml": - // Let the config parser validate YAML syntax + cfg, err = config.ReadYAMLConfig(body) default: res.WriteHeader(http.StatusUnsupportedMediaType) - res.Write(responseBody("'%s' media type is not supported", mt)) + res.Write(responseBody("'%s' media type is not supported", mediaType)) return } - cfg, err := config.ReadYAMLConfig(body) - if err != nil { if config.IsValidationError(err) { res.WriteHeader(http.StatusUnprocessableEntity) @@ -101,8 +132,8 @@ func blubberoid(res http.ResponseWriter, req *http.Request) { res.WriteHeader(http.StatusBadRequest) res.Write(responseBody( - "Failed to read config YAML from request body. "+ - "Was it formatted correctly and encoded as binary data?\nerror: %s", + "Failed to read '%s' config from request body. Error: %s", + mediaType, err.Error(), )) return @@ -136,3 +167,16 @@ func blubberoid(res http.ResponseWriter, req *http.Request) { func responseBody(msg string, a ...interface{}) []byte { return []byte(fmt.Sprintf(msg+"\n", a...)) } + +func readOpenAPISpec() []byte { + var buffer bytes.Buffer + tmpl, _ := template.New("spec").Parse(openAPISpecTemplate) + + tmpl.Execute(&buffer, struct { + Version string + }{ + Version: meta.FullVersion(), + }) + + return buffer.Bytes() +} |