From 1f52a0ab376341420ae31e9355ac2b71d5c8600d Mon Sep 17 00:00:00 2001 From: Michael Hohn Date: Mon, 20 May 2024 13:53:39 -0700 Subject: [PATCH] wip: port functions reachable from DownloadResponse --- pkg/server/server.go | 67 +++++++++++++++++++++++++++--- pkg/storage/storage.go | 93 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+), 6 deletions(-) diff --git a/pkg/server/server.go b/pkg/server/server.go index 55a2cbb..9834b88 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -13,6 +13,8 @@ import ( "strconv" "strings" + "mrvacommander/pkg/storage" + "github.com/gorilla/mux" "github.com/hohn/ghes-mirva-server/analyze" "github.com/hohn/ghes-mirva-server/api" @@ -135,9 +137,9 @@ func (c *CommanderSingle) MirvaStatus(w http.ResponseWriter, r *http.Request) { // So we simply report the status of a job as the status of all. spec := store.GetJobList(id) if spec == nil { - slog.Error("No jobs found for given job id", - "id", vars["codeql_variant_analysis_id"]) - http.Error(w, err.Error(), http.StatusUnprocessableEntity) + msg := "No jobs found for given job id" + slog.Error(msg, "id", vars["codeql_variant_analysis_id"]) + http.Error(w, msg, http.StatusUnprocessableEntity) return } @@ -150,13 +152,11 @@ func (c *CommanderSingle) MirvaStatus(w http.ResponseWriter, r *http.Request) { ji := store.GetJobInfo(js) - analyze.StatusResponse(w, js, ji, id) c.StatusResponse(w, js, ji, id) } // Download artifacts func (c *CommanderSingle) MirvaDownloadArtifact(w http.ResponseWriter, r *http.Request) { - // TODO Port this function from ghes-mirva-server vars := mux.Vars(r) slog.Info("MRVA artifact download", "controller_owner", vars["controller_owner"], @@ -179,7 +179,62 @@ func (c *CommanderSingle) MirvaDownloadArtifact(w http.ResponseWriter, r *http.R Repo: vars["repo_name"], }, } - analyze.DownloadResponse(w, js, vaid) + c.DownloadResponse(w, js, vaid) +} + +func (c *CommanderSingle) DownloadResponse(w http.ResponseWriter, js co.JobSpec, vaid int) { + slog.Debug("Forming download response", "session", vaid, "job", js) + + astat := store.GetStatus(vaid, js.OwnerRepo) + + var dlr api.DownloadResponse + if astat == co.StatusSuccess { + + au, err := storage.ArtifactURL(js, vaid) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + dlr = api.DownloadResponse{ + Repository: api.DownloadRepo{ + Name: js.Repo, + FullName: fmt.Sprintf("%s/%s", js.Owner, js.Repo), + }, + AnalysisStatus: astat.ToExternalString(), + ResultCount: 123, // FIXME + ArtifactSizeBytes: 123, // FIXME + DatabaseCommitSha: "do-we-use-dcs-p", + SourceLocationPrefix: "do-we-use-slp-p", + ArtifactURL: au, + } + } else { + dlr = api.DownloadResponse{ + Repository: api.DownloadRepo{ + Name: js.Repo, + FullName: fmt.Sprintf("%s/%s", js.Owner, js.Repo), + }, + AnalysisStatus: astat.ToExternalString(), + ResultCount: 0, + ArtifactSizeBytes: 0, + DatabaseCommitSha: "", + SourceLocationPrefix: "/not/relevant/here", + ArtifactURL: "", + } + } + + // Encode the response as JSON + jdlr, err := json.Marshal(dlr) + if err != nil { + slog.Error("Error encoding response as JSON:", + "error", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Send analysisReposJSON via ResponseWriter + w.Header().Set("Content-Type", "application/json") + w.Write(jdlr) } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 8ca28dd..947b5a0 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -1,17 +1,25 @@ package storage import ( + "archive/zip" "errors" "fmt" + "io" "io/fs" "log/slog" "os" "path" "path/filepath" + "sync" co "github.com/hohn/ghes-mirva-server/common" ) +var ( + mutex sync.Mutex + result map[co.JobSpec]co.AnalyzeResult = make(map[co.JobSpec]co.AnalyzeResult) +) + type StorageSingle struct { CurrentID int } @@ -81,3 +89,88 @@ func (s *StorageSingle) FindAvailableDBs(analysisReposRequested []co.OwnerRepo) } return not_found_repos, analysisRepos } + +func ArtifactURL(js co.JobSpec, vaid int) (string, error) { + // We're looking for paths like + // codeql/sarif/google/flatbuffers/google_flatbuffers.sarif + + ar := GetResult(js) + + hostname, err := os.Hostname() + if err != nil { + slog.Error("No host name found") + return "", nil + } + + zfpath, err := PackageResults(ar, js.OwnerRepo, vaid) + if err != nil { + slog.Error("Error packaging results:", "error", err) + return "", err + } + au := fmt.Sprintf("http://%s:8080/download-server/%s", hostname, zfpath) + return au, nil +} + +func GetResult(js co.JobSpec) co.AnalyzeResult { + mutex.Lock() + defer mutex.Unlock() + ar := result[js] + return ar +} + +func PackageResults(ar co.AnalyzeResult, owre co.OwnerRepo, vaid int) (zipPath string, e error) { + slog.Debug("Readying zip file with .sarif/.bqrs", "analyze-result", ar) + + cwd, err := os.Getwd() + if err != nil { + slog.Error("No working directory") + panic(err) + } + + // Ensure the output directory exists + dirpath := path.Join(cwd, "var", "codeql", "localrun", "results") + if err := os.MkdirAll(dirpath, 0755); err != nil { + slog.Error("Unable to create results output directory", + "dir", dirpath) + return "", err + } + + // Create a new zip file + zpath := path.Join(dirpath, fmt.Sprintf("results-%s-%s-%d.zip", owre.Owner, owre.Repo, vaid)) + + zfile, err := os.Create(zpath) + if err != nil { + return "", err + } + defer zfile.Close() + + // Create a new zip writer + zwriter := zip.NewWriter(zfile) + defer zwriter.Close() + + // Add each result file to the zip archive + names := []([]string){{ar.RunAnalysisSARIF, "results.sarif"}} + for _, fpath := range names { + file, err := os.Open(fpath[0]) + if err != nil { + return "", err + } + defer file.Close() + + // Create a new file in the zip archive with custom name + // The client is very specific: + // if zf.Name != "results.sarif" && zf.Name != "results.bqrs" { continue } + + zipEntry, err := zwriter.Create(fpath[1]) + if err != nil { + return "", err + } + + // Copy the contents of the file to the zip entry + _, err = io.Copy(zipEntry, file) + if err != nil { + return "", err + } + } + return zpath, nil +}