From 970bc31977871895dcf0bb5622fb6a0ec9c13631 Mon Sep 17 00:00:00 2001 From: Nathan LeClaire Date: Mon, 29 Aug 2016 16:03:27 -0700 Subject: [PATCH] Add ability to upload diagnostics to S3 Signed-off-by: Nathan LeClaire --- alpine/packages/diagnostics/http.go | 111 +++++++++++++++++++++++++--- 1 file changed, 99 insertions(+), 12 deletions(-) diff --git a/alpine/packages/diagnostics/http.go b/alpine/packages/diagnostics/http.go index 13840aec3..f3cdbc0e1 100644 --- a/alpine/packages/diagnostics/http.go +++ b/alpine/packages/diagnostics/http.go @@ -2,16 +2,24 @@ package main import ( "archive/tar" + "bytes" + "fmt" + "io" "io/ioutil" "log" "net/http" "os" + "strconv" + "strings" + "time" ) const ( - dockerSock = "/var/run/docker.sock" - lgtm = "LGTM" - httpMagicPort = ":44554" // chosen arbitrarily due to IANA availability -- might change + dockerSock = "/var/run/docker.sock" + lgtm = "LGTM" + httpMagicPort = ":44554" // chosen arbitrarily due to IANA availability -- might change + bucket = "editionsdiagnostics" + sessionIDField = "session" ) var ( @@ -41,24 +49,103 @@ func (h HTTPDiagnosticListener) Listen() { }) http.HandleFunc("/diagnose", func(w http.ResponseWriter, r *http.Request) { - dir, err := ioutil.TempDir("", "diagnostics") - if err != nil { - log.Println("Error creating temp dir on diagnostic request:", err) + diagnosticsSessionID := r.FormValue(sessionIDField) + + if diagnosticsSessionID == "" { + http.Error(w, "No 'session' field specified for diagnostics run", http.StatusBadRequest) return } - file, err := ioutil.TempFile(dir, "diagnostics") + hostname, err := os.Hostname() if err != nil { - log.Println("Error creating temp file on diagnostic request:", err) + http.Error(w, "Error getting hostname:"+err.Error(), http.StatusInternalServerError) return } - tarWriter := tar.NewWriter(file) + // To keep URL cleaner + hostname = strings.Replace(hostname, ".", "-", -1) - Capture(tarWriter, cloudCaptures) + if _, err := w.Write([]byte("OK hostname=" + hostname + " session=" + diagnosticsSessionID + "\n")); err != nil { + http.Error(w, "Error writing: "+err.Error(), http.StatusInternalServerError) + return + } - // TODO: upload written (and gzipped?) tar file to our S3 - // bucket with specific path convention (per-user? by date?) + // Do the actual capture and uplaod to S3 in the background. + // No need to make caller sit and wait for the result. They + // probably have a lot of other things going on, like other + // servers to request diagnostics for. + // + // TODO(nathanleclaire): Potentially, endpoint to check the + // result of this capture and upload process as well. + go func() { + dir, err := ioutil.TempDir("", "diagnostics") + if err != nil { + log.Println("Error creating temp dir on diagnostic request:: ", err) + return + } + + file, err := ioutil.TempFile(dir, "diagnostics") + if err != nil { + log.Println("Error creating temp file on diagnostic request:", err) + return + } + + tarWriter := tar.NewWriter(file) + + Capture(tarWriter, cloudCaptures) + + if err := tarWriter.Close(); err != nil { + log.Println("Error closing archive writer: ", err) + return + } + + if err := file.Close(); err != nil { + log.Println("Error closing file: ", err) + return + } + + readFile, err := os.Open(file.Name()) + if err != nil { + log.Println("Error opening report file to upload: ", err) + return + } + defer readFile.Close() + + buf := &bytes.Buffer{} + contentLength, err := io.Copy(buf, readFile) + if err != nil { + log.Println("Error copying to buffer: ", err) + return + } + + reportURI := fmt.Sprintf("https://%s.s3.amazonaws.com/%s-%s.tar", bucket, diagnosticsSessionID, hostname) + + uploadReq, err := http.NewRequest("PUT", reportURI, buf) + if err != nil { + log.Println("Error getting bucket request: ", err) + return + } + + uploadReq.Header.Set("x-amz-acl", "bucket-owner-full-control") + uploadReq.Header.Set("Date", time.Now().UTC().Format(http.TimeFormat)) + uploadReq.Header.Set("Content-Length", strconv.Itoa(int(contentLength))) + + client := &http.Client{} + + if uploadResp, err := client.Do(uploadReq); err != nil { + log.Println("Error writing: ", err) + body, err := ioutil.ReadAll(uploadResp.Body) + if err != nil { + log.Println("Error reading response body: ", err) + } + log.Println(string(body)) + return + } else { + log.Println("No error sending S3 request") + } + + log.Println("Diagnostics request finished") + }() }) // Start HTTP server to indicate general Docker health.