package main /* #include #include #include #include */ import "C" import ( "encoding/json" "fmt" "io" "log" "math" "net/http" "os" "os/user" "path/filepath" "strconv" _ "stream/docs" "strings" "syscall" httpSwagger "github.com/swaggo/http-swagger" ) type StreamData struct { Data string `json:"data"` Offset int64 `json:"offset"` } type FileInformation struct { Mode string `json:"mode"` Nlink string `json:"nlink"` UID string `json:"uid"` GID string `json:"gid"` Size string `json:"size"` Mtime int64 `json:"mtime"` Path string `json:"path"` } type Result struct { Data []FileInformation `json:"data` } type Error struct { Error string `json:"error"` } // Download a file from sandbox filesystem with given offset and length godoc // @Summary Download a file from Filesystem // @Description Download any file from sandbox logs filesystem // @Accept json // @Produce octet-stream // @Success 200 {object} main.StreamData // @Router /files/download [get] // @Param path query string true "Path" func downloadhandler(w http.ResponseWriter, r *http.Request) { //Get query params params := r.URL.Query() path := params["path"] if len(path) == 0 { log.Println("path not found in URL") error := &Error{ Error: "path not found in URL"} retObj, err := json.Marshal(error) if err != nil { log.Print(err) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(retObj) return } file, err := os.Open(path[0]) if err != nil { log.Print(err) } filename := filepath.Base(path[0]) w.Header().Set("Content-Type", "application/octet-stream") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename)) io.Copy(w, file) file.Close() } // read a file from sandbox filesystem with given offset and length godoc // @Summary Read a file from Filesystem // @Description Reads any file from sandbox logs filesystem and serves as a json object // @Accept json // @Success 200 {object} main.StreamData // @Router /files/read [get] // @Param path query string true "Path" // @Param offset query int true "Offset" // @Param length query int true "Length" // @Param jsonp query string true "jsonp" func readHandler(w http.ResponseWriter, r *http.Request) { // Get query path params params := r.URL.Query() path := params["path"] //check if path exists in the query params if not send error response if len(path) == 0 { log.Println("path not found in URL") error := &Error{ Error: "path not found in URL"} retObj, err := json.Marshal(error) if err != nil { log.Print(err) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(retObj) return } var err error //check if offset exists in the query params if not send error response if len(params["offset"]) == 0 { error := &Error{ Error: "offset not found in URL"} retObj, err := json.Marshal(error) if err != nil { log.Print(err) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(retObj) return } offset, err := strconv.ParseInt(params["offset"][0], 10, 64) if err != nil { log.Print(err) } //check if length exists in the query params if not send error response if len(params["length"]) == 0 { error := &Error{ Error: "length not found in URL"} retObj, err := json.Marshal(error) if err != nil { log.Print(err) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(retObj) return } length, err := strconv.ParseInt(params["length"][0], 10, 64) if err != nil { log.Print(err) } size := getSize(path[0]) // if offset is invalid set it to size of the file if offset == -1 { offset = size } //set length if it is invalid if length == -1 { length = size - offset } // Cap the read length at 16 pages. length = int64(math.Min(float64(length), float64(C.sysconf(C._SC_PAGE_SIZE)*16))) // return empty data when offset is greater than size of the file if offset >= size { staremData := &StreamData{ Data: "", Offset: size} respObj, err := json.Marshal(staremData) if err != nil { fmt.Fprintf(w, "json encode error") return } callbackName := r.URL.Query().Get("jsonp") if callbackName == "" { fmt.Fprintf(w, "Please give callback name in query string") return } w.Header().Set("Content-Type", "application/javascript") w.Header().Set("Access-Control-Allow-Origin", "*") fmt.Fprintf(w, "%s(%s);", callbackName, respObj) return } file, err := os.Open(path[0]) if err != nil { log.Print(err) } defer file.Close() s := io.NewSectionReader(file, 0, size) buf2 := make([]byte, length) _, err = s.ReadAt(buf2, offset) if err != nil { log.Print(err) } staremData := &StreamData{ Data: string(buf2), Offset: offset} respObj, err := json.Marshal(staremData) if err != nil { fmt.Fprintf(w, "json encode error") return } callbackName := r.URL.Query().Get("jsonp") if callbackName == "" { fmt.Fprintf(w, "Please give callback name in query string") return } w.Header().Set("Content-Type", "application/javascript") w.Header().Set("Access-Control-Allow-Origin", "*") fmt.Fprintf(w, "%s(%s);", callbackName, respObj) } // browse sandbox filesystem godoc // @Summary Browse Filesystem // @Description serves sandbox logs filesystem as a json object // @Accept json // @Produce json // @Success 200 {object} main.Result // @Router /files/browse [get] // @Param path query string true "Path" func browseHandler(w http.ResponseWriter, r *http.Request) { //Get query Params params := r.URL.Query() path := params["path"] //check if path exists in the query params if not send error response if len(path) == 0 { log.Println("path not found in URL") error := &Error{ Error: "path not found in URL"} retObj, err := json.Marshal(error) if err != nil { log.Print(err) } w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(retObj) return } // walk through the directory structure and build fileInformation struct and append to result array result := []FileInformation{} //test := strings.Split(path[0], "/")[len(strings.Split(path[0], "/"))-1] //fmt.Print(test, " ") //fmt.Print(test, " ") err := filepath.WalkDir(path[0], func(filePath string, dirEntry os.DirEntry, err error) error { if len(strings.Split(filePath, "/")) < len(strings.Split(path[0], "/"))+2 { if err != nil { return err } fileInfo, err := dirEntry.Info() fileStat, err := os.Stat(filePath) if err != nil { return err } // retrieve file ownership information and number of hardlinks to the file var nlink, uid, gid uint64 if sys := fileStat.Sys(); sys != nil { if stat, ok := sys.(*syscall.Stat_t); ok { nlink = uint64(stat.Nlink) uid = uint64(stat.Uid) gid = uint64(stat.Gid) } } usr, err := user.LookupId(strconv.FormatUint(uid, 10)) group, err := user.LookupGroupId(strconv.FormatUint(gid, 10)) if err != nil { return err } jsonData := FileInformation{ Mode: fileInfo.Mode().String(), Nlink: string(strconv.FormatUint(nlink, 10)), UID: usr.Username, GID: group.Name, Size: strconv.Itoa(int(fileInfo.Size())), Mtime: fileInfo.ModTime().Unix(), Path: filePath, } if err == nil { result = append(result, jsonData) } } return nil }) if err != nil { log.Println(err) } resultdata := Result{result} c, err := json.Marshal(resultdata) if err == nil { w.Header().Set("Content-Type", "application/json") w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(c) } } //Get the size of a file func getSize(p string) int64 { if stat, err := os.Stat(p); err == nil { return stat.Size() } return 0 } // @title k8s Sandbox Go Restful API with Swagger // @version 1.0 // @description Rest API doc for sandbox API's // @contact.name Revanth Chandra // @host localhost:8080 // @BasePath / func main() { http.HandleFunc("/files/read", readHandler) http.HandleFunc("/files/browse", browseHandler) http.HandleFunc("/files/download", downloadhandler) http.HandleFunc("/swagger/", httpSwagger.WrapHandler) http.ListenAndServe(":5051", nil) }