diff --git a/function-run/fetcher/Dockerfile b/function-run/fetcher/Dockerfile new file mode 100644 index 00000000..f97e4ef2 --- /dev/null +++ b/function-run/fetcher/Dockerfile @@ -0,0 +1,5 @@ +FROM alpine:3.4 + +ADD fetcher / + +EXPOSE 8000 diff --git a/function-run/fetcher/build.sh b/function-run/fetcher/build.sh new file mode 100755 index 00000000..a0f34b7a --- /dev/null +++ b/function-run/fetcher/build.sh @@ -0,0 +1 @@ +GOOS=linux GOARCH=386 go build -o fetcher . diff --git a/function-run/fetcher/fetcher.go b/function-run/fetcher/fetcher.go new file mode 100644 index 00000000..d873d943 --- /dev/null +++ b/function-run/fetcher/fetcher.go @@ -0,0 +1,97 @@ +package main + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "path/filepath" +) + +type FetchRequest struct { + Url string `json:"url"` + Filename string `json:"filename"` +} + +type Fetcher struct { + sharedVolumePath string +} + +func MakeFetcher(sharedVolumePath string) *Fetcher { + return &Fetcher{sharedVolumePath: sharedVolumePath} +} + +func (fetcher *Fetcher) handler(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + http.Error(w, "", 404) + return + } + + // parse request + body, err := ioutil.ReadAll(r.Body) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + req := FetchRequest{} + err = json.Unmarshal(body, &req) + if err != nil { + http.Error(w, err.Error(), 500) + return + } + + // fetch the file and save it to tmp path + resp, err := http.Get(req.Url) + if err != nil { + e := fmt.Sprintf("Failed to fetch from url: %v", err) + http.Error(w, e, 400) + return + } + defer resp.Body.Close() + body, err = ioutil.ReadAll(resp.Body) + if err != nil { + e := fmt.Sprintf("Failed to read from url: %v", err) + http.Error(w, e, 400) + return + } + tmpFile := req.Filename + ".tmp" + tmpPath := filepath.Join(fetcher.sharedVolumePath, tmpFile) + err = ioutil.WriteFile(tmpPath, body, 0600) + if err != nil { + e := fmt.Sprintf("Failed to write file: %v", err) + http.Error(w, e, 500) + return + } + + // TODO: add signature verification + + // move tmp file to requested filename + err = os.Rename(tmpPath, filepath.Join(fetcher.sharedVolumePath, req.Filename)) + if err != nil { + e := fmt.Sprintf("Failed to move file: %v", err) + http.Error(w, e, 500) + return + } + + // all done + w.WriteHeader(http.StatusOK) +} + +// Usage: fetcher +func main() { + dir := os.Args[1] + if _, err := os.Stat(dir); err != nil { + if os.IsNotExist(err) { + err = os.MkdirAll(dir, os.ModeDir|0700) + if err != nil { + log.Fatalf("Error creating directory: %v", err) + } + } + } + fetcher := MakeFetcher(dir) + mux := http.NewServeMux() + mux.HandleFunc("/", fetcher.handler) + http.ListenAndServe(":8000", mux) +}