2017-03-05 17:26:04 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
2023-09-06 17:54:02 +00:00
|
|
|
"io/fs"
|
2017-03-05 17:26:04 +00:00
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"time"
|
|
|
|
|
2019-07-06 16:45:48 +00:00
|
|
|
"github.com/Luzifer/rconfig/v2"
|
2023-09-06 10:28:03 +00:00
|
|
|
"github.com/pkg/errors"
|
2018-09-17 09:54:14 +00:00
|
|
|
|
|
|
|
"github.com/gofrs/uuid"
|
2017-03-05 17:26:04 +00:00
|
|
|
"github.com/gorilla/mux"
|
2023-09-06 10:28:03 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
filenameInput = "input.zip"
|
|
|
|
filenameStatus = "status.json"
|
|
|
|
filenameStatusTemp = "status.tmp.json"
|
|
|
|
filenameOutputDir = "output"
|
|
|
|
sleepBase = 1.5
|
2017-03-05 17:26:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
cfg = struct {
|
2023-09-06 12:27:36 +00:00
|
|
|
DefaultEnv string `flag:"default-env" default:"" description:"Environment to copy to the job before unpacking"`
|
2018-09-17 10:05:34 +00:00
|
|
|
Script string `flag:"script" default:"tex-build.sh" description:"Script to execute (needs to generate output directory)"`
|
|
|
|
Listen string `flag:"listen" default:":3000" description:"IP/Port to listen on"`
|
|
|
|
StorageDir string `flag:"storage-dir" default:"/storage" description:"Where to store uploaded ZIPs and resulting files"`
|
|
|
|
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
|
2017-03-05 17:26:04 +00:00
|
|
|
}{}
|
|
|
|
|
|
|
|
version = "dev"
|
|
|
|
router = mux.NewRouter()
|
|
|
|
)
|
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
func initApp() error {
|
2018-09-17 10:05:34 +00:00
|
|
|
rconfig.AutoEnv(true)
|
2017-03-05 17:26:04 +00:00
|
|
|
if err := rconfig.Parse(&cfg); err != nil {
|
2023-09-06 10:28:03 +00:00
|
|
|
return errors.Wrap(err, "parsing cli options")
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
return nil
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func main() {
|
2023-09-06 10:28:03 +00:00
|
|
|
var err error
|
|
|
|
if err = initApp(); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("app initialization failed")
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
if cfg.VersionAndExit {
|
|
|
|
logrus.WithField("version", version).Info("tex-api")
|
|
|
|
os.Exit(0)
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
router.HandleFunc("/job", startNewJob).
|
|
|
|
Methods("POST").
|
|
|
|
Name("startNewJob")
|
2017-03-05 17:26:04 +00:00
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
router.HandleFunc("/job/{uid:[0-9a-z-]{36}}", getJobStatus).
|
|
|
|
Methods("GET").
|
|
|
|
Name("getJobStatus")
|
2017-03-05 17:26:04 +00:00
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
router.HandleFunc("/job/{uid:[0-9a-z-]{36}}/wait", waitForJob).
|
|
|
|
Methods("GET").
|
|
|
|
Name("waitForJob")
|
2017-03-05 17:26:04 +00:00
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
router.HandleFunc("/job/{uid:[0-9a-z-]{36}}/download", downloadAssets).
|
|
|
|
Methods("GET").
|
|
|
|
Name("downloadAssets")
|
2017-03-05 17:26:04 +00:00
|
|
|
|
2023-09-06 10:28:03 +00:00
|
|
|
server := &http.Server{
|
|
|
|
Addr: cfg.Listen,
|
|
|
|
Handler: router,
|
|
|
|
ReadHeaderTimeout: time.Second,
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
2023-09-06 12:27:36 +00:00
|
|
|
logrus.WithFields(logrus.Fields{
|
|
|
|
"addr": cfg.Listen,
|
|
|
|
"version": version,
|
|
|
|
}).Info("tex-api started")
|
2023-09-06 10:28:03 +00:00
|
|
|
if err := server.ListenAndServe(); err != nil {
|
|
|
|
logrus.WithError(err).Fatal("HTTP server exited with error")
|
2017-03-05 22:50:13 +00:00
|
|
|
}
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func downloadAssets(res http.ResponseWriter, r *http.Request) {
|
|
|
|
vars := mux.Vars(r)
|
|
|
|
uid, err := uuid.FromString(vars["uid"])
|
|
|
|
if err != nil {
|
|
|
|
http.Error(res, "UUID had unexpected format!", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
2018-09-17 09:14:49 +00:00
|
|
|
content io.Reader
|
|
|
|
contentType = "application/zip"
|
|
|
|
filename string
|
2017-03-05 17:26:04 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
switch r.Header.Get("Accept") {
|
2018-09-17 09:45:26 +00:00
|
|
|
case "application/tar", "application/x-tar", "application/x-gtar", "multipart/x-tar", "application/x-compress", "application/x-compressed":
|
2018-09-17 09:14:49 +00:00
|
|
|
contentType = "application/tar"
|
2017-03-05 23:17:35 +00:00
|
|
|
content, err = buildAssetsTAR(uid)
|
|
|
|
filename = uid.String() + ".tar"
|
2023-09-06 10:28:03 +00:00
|
|
|
|
|
|
|
case "application/pdf":
|
|
|
|
contentType = "application/pdf"
|
|
|
|
filename = uid.String() + ".pdf"
|
2023-09-06 17:54:02 +00:00
|
|
|
content, err = getAssetsFile(uid, ".pdf")
|
|
|
|
|
|
|
|
if errors.Is(err, fs.ErrNotExist) && r.URL.Query().Has("log-on-error") {
|
|
|
|
contentType = "application/octet-stream"
|
|
|
|
filename = uid.String() + ".log"
|
|
|
|
content, err = getAssetsFile(uid, ".log")
|
|
|
|
}
|
2023-09-06 10:28:03 +00:00
|
|
|
|
2017-03-05 17:26:04 +00:00
|
|
|
default:
|
|
|
|
content, err = buildAssetsZIP(uid)
|
|
|
|
filename = uid.String() + ".zip"
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
2023-09-06 10:28:03 +00:00
|
|
|
serverErrorf(res, err, "generating downloadable asset")
|
2017-03-05 17:26:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
res.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%q", filename))
|
2018-09-17 09:14:49 +00:00
|
|
|
res.Header().Set("Content-Type", contentType)
|
2017-03-05 17:26:04 +00:00
|
|
|
res.WriteHeader(http.StatusOK)
|
|
|
|
|
2023-09-06 12:27:36 +00:00
|
|
|
if _, err = io.Copy(res, content); err != nil {
|
|
|
|
serverErrorf(res, err, "writing content")
|
|
|
|
return
|
|
|
|
}
|
2017-03-05 17:26:04 +00:00
|
|
|
}
|