integration-test/webserver/main.go (175 lines of code) (raw):

package main import ( "context" "crypto/tls" "encoding/json" "flag" "fmt" "log" "net/http" "strings" "time" ) const ( // Response body key names ResponseBodyKeyApplicationHealthState = "ApplicationHealthState" ResponseBodyKeyCustomMetrics = "CustomMetrics" // Application Health State flags ApplicationHealthStateInvalidFlag = "i" ResponseTimeoutFlag = "t" ResponseTimeoutInSeconds = 35 // Custom Metrics flags CustomMetricsValidFlag = "valid" CustomMetricsInvalidFlag = "invalid" CustomMetricsNilFlag = "nil" CustomMetricsEmptyFlag = "empty" CustomMetricsEmptyObjectFlag = "emptyobj" CustomMetricsValidValue = `{"rollingUpgradePolicy": { "phase": 2, "doNotUpgrade": true, "dummy": "yes" } }` CustomMetricsInvalidValue = `[ "hello", "world" ]` CustomMetricsEmptyValue = "" CustomMetricsEmptyObjectValue = "{}" ) // Flags passed to webserver in command line args to send correct health state values var healthStateFlagMapping = map[string]string{ "h": "Healthy", "u": "Unhealthy", } func HandleFlag(flagStr string) (int, map[string]interface{}) { statusCode := 200 responseBody := make(map[string]interface{}) if flagStr == "" { return statusCode, responseBody } flags := strings.Split(flagStr, "-") statusCodeAndHealthStateFlags := []rune(flags[0]) // E.g '3' -> StatusCode: 300 statusCode = HandleStatusCodeFlag(string(statusCodeAndHealthStateFlags[0])) // E.g '2h' -> StatusCode: 200, ResponseBody: { "ApplicationHealthState" : "Healthy" } if healthStateOrTimeoutFlag := ApplicationHealthStateFlagPresent(flags); healthStateOrTimeoutFlag != "" { switch healthStateOrTimeoutFlag { case ResponseTimeoutFlag: log.Printf("Sleeping for %d seconds", ResponseTimeoutInSeconds) time.Sleep(ResponseTimeoutInSeconds * time.Second) default: key, value := HandleApplicationHealthStateFlag(healthStateOrTimeoutFlag) responseBody[key] = value } } // E.g '2h-valid' -> StatusCode: 200, ResponseBody: { "ApplicationHealthState" : "Healthy", "CustomMetrics": "<a raw json string>" } if customMetricFlag := CustomMetricsFlagPresent(flags); customMetricFlag != "" { key, value := HandleCustomMetricFlag(customMetricFlag) responseBody[key] = value } if len(responseBody) == 0 { log.Printf("Sending no response body with status code %v", statusCode) } else { log.Printf("Sending response body with status code %v: %v", statusCode, responseBody) } return statusCode, responseBody } func ApplicationHealthStateFlagPresent(flags []string) string { statusCodeAndHealthStateFlags := []rune(flags[0]) if len(statusCodeAndHealthStateFlags) > 1 { return string(statusCodeAndHealthStateFlags[1]) } return "" } func CustomMetricsFlagPresent(flags []string) string { if len(flags) == 2 { return flags[1] } return "" } func HandleStatusCodeFlag(flag string) int { var statusCode int fmt.Sscan(flag, &statusCode) statusCode *= 100 return statusCode } func HandleApplicationHealthStateFlag(flag string) (string, string) { switch flag { case ApplicationHealthStateInvalidFlag: return ResponseBodyKeyApplicationHealthState, "Hello!" default: return ResponseBodyKeyApplicationHealthState, getHealthState(flag) } } func HandleCustomMetricFlag(flag string) (string, interface{}) { switch flag { case CustomMetricsValidFlag: return ResponseBodyKeyCustomMetrics, CustomMetricsValidValue case CustomMetricsInvalidFlag: return ResponseBodyKeyCustomMetrics, CustomMetricsInvalidValue case CustomMetricsNilFlag: return ResponseBodyKeyCustomMetrics, nil case CustomMetricsEmptyFlag: return ResponseBodyKeyCustomMetrics, CustomMetricsEmptyValue case CustomMetricsEmptyObjectFlag: return ResponseBodyKeyCustomMetrics, CustomMetricsEmptyObjectValue } return "Hello", "world" } func healthHandler(w http.ResponseWriter, r *http.Request, arguments *[]string) { log.Printf("Arguments: %v, len: %v", arguments, len(*arguments)) statusCode, responseBody := HandleFlag((*arguments)[0]) *arguments = (*arguments)[1:] w.WriteHeader(statusCode) w.Header().Set("Content-Type", "application/json") respBody, err := json.Marshal(responseBody) if err != nil { log.Printf("Error happened in JSON marshal. Err: %s", err) } w.Write(respBody) } func getSecurityProtocolVersion(securityProtocol string) uint16 { switch securityProtocol { case "ssl3.0": return tls.VersionSSL30 case "tls1.0": return tls.VersionTLS10 case "tls1.1": return tls.VersionTLS11 case "tls1.2": return tls.VersionTLS12 case "tls1.3": return tls.VersionTLS13 default: return tls.VersionTLS13 } } func getHealthState(flag string) string { if healthState, ok := healthStateFlagMapping[flag]; ok { return healthState } else { return "" } } func main() { args := flag.String("args", "", `Example usage: '2h-valid' to send StatusCode: 200, ResponseBody: { "ApplicationHealthState": "Healthy", "CustomMetrics": "<valid json>"}`) securityProtocol := flag.String("securityProtocol", "tls1.3", "Specifies the security protocol to use for the HTTPS server. Valid options are: tls1.0, tls1.1, tls1.2, tls1.3, ssl3.0. Default is tls1.3.") flag.Parse() originalArgs := strings.Split(*args, ",") arguments := strings.Split(*args, ",") var shouldExitOnEmptyArgs = len(arguments) > 0 httpMutex := http.NewServeMux() httpServer := http.Server{ Addr: ":8080", Handler: httpMutex} httpsServer := http.Server{ Addr: ":4430", //changing default port from 443 to 4430 to avoid conflict with other services Handler: httpMutex, TLSConfig: &tls.Config{ MinVersion: getSecurityProtocolVersion(*securityProtocol), MaxVersion: getSecurityProtocolVersion(*securityProtocol)}, } // sends json resonse body with application health state expected by extension // looks at the first state in the healthStates array and dequeues that element after its iterated httpMutex.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { healthHandler(w, r, &arguments) // if arguments is non-empty, this means that the test is only meant to run till we iterate over all arguments, so the servers are shutdown if shouldExitOnEmptyArgs && len(arguments) == 0 { go func() { log.Printf("Finished serving arguments: %v", originalArgs) log.Printf("Shutting down http and https server") httpServer.Shutdown(context.Background()) httpsServer.Shutdown(context.Background()) }() } }) log.Printf("Arguments: %v, len: %v", arguments, len(arguments)) log.Printf("Starting http server...") go httpServer.ListenAndServe() log.Printf("Starting https server...") log.Fatal(httpsServer.ListenAndServeTLS("webservercert.pem", "webserverkey.pem")) log.Printf("Servers stopped...") }