-
Notifications
You must be signed in to change notification settings - Fork 3
/
gort.go
149 lines (130 loc) · 4.01 KB
/
gort.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package gort
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"strconv"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"github.com/idestis/gort/utils"
)
// Script sctruct will hold an entity to define which script we should run
type Script struct {
// Executor defines the name of
// the interpreter program to be executed
Executor string `json:"executor"`
// Script will refer to script name from the dist folder
Script string `json:"script"`
// EnvVars slice will hold all environment variables for the script
EnvVars []string `json:"env_vars"`
// Args slice will hold additional arguments for the command
Args []string `json:"args"`
}
const (
// defaultPort if not specified as environment variable
defaultPort = 5000
// defaultScriptsDir is a path to find scripts
defaultScriptsDir = "./dist"
)
var (
port int
scriptsDir string
scripts []string
)
// init is here with one reason gort need to be initialized first
func init() {
port, _ = strconv.Atoi(os.Getenv("PORT"))
if port == 0 {
port = defaultPort
}
scriptsDir = os.Getenv("SCRIPTS_DIR")
if scriptsDir == "" {
scriptsDir = defaultScriptsDir
}
if _, err := os.Stat(scriptsDir); os.IsNotExist(err) {
log.Panic(err)
}
scripts = utils.ScanScripts(scriptsDir)
}
func main() {
r := chi.NewRouter()
r.Use(middleware.RealIP)
r.Use(middleware.Logger)
r.Use(middleware.Recoverer)
r.Use(middleware.Compress(9))
r.Route("/v1", func(r chi.Router) {
if os.Getenv("GORT_RATE_LIMIT") != "" {
rl, _ := strconv.Atoi(os.Getenv("GORT_RATE_LIMIT"))
log.Println("GORT_RATE_LIMIT was set globally for", rl)
r.Use(middleware.Throttle(rl))
}
r.Post("/start", StartScriptHandler) // /v1/start
r.Get("/list-dist", ListScriptsHandler) // /v1/list-dist
r.Get("/health", func(w http.ResponseWriter, r *http.Request) { // /v1/health
w.Write([]byte("OK"))
})
})
r.NotFound(NotFoundHandler)
log.Printf("Gort is started on port %d\n", port)
http.ListenAndServe(fmt.Sprintf(":%d", port), r)
}
// ListScriptsHandler will return scripts list from the SCRIPTS_DIR
func ListScriptsHandler(w http.ResponseWriter, r *http.Request) {
if len(scripts) == 0 {
fmt.Fprintf(w, "%s seems like empty", scriptsDir)
return
}
for _, script := range scripts {
fmt.Fprintln(w, script)
}
}
// StartScriptHandler will start requested script and print output to stdout
func StartScriptHandler(w http.ResponseWriter, r *http.Request) {
var script Script
err := json.NewDecoder(r.Body).Decode(&script)
if err != nil {
http.Error(w, "Not able to parse data as valid JSON", 422)
return
}
if script.Executor == "" || script.Script == "" {
http.Error(w, "Required parameters 'executor' and 'script' were not found in the payload", 400)
return
}
_, err = exec.LookPath(script.Executor)
if err != nil {
http.Error(w, "Requested executor is not installed", 500)
return
}
_, found := utils.Find(scripts, script.Script)
if !found {
http.Error(w, "Requested script is not found in the scripts directory", 501)
return
}
command := []string{scriptsDir + "/" + script.Script}
cmd := exec.Command(script.Executor, command...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Append arguments to command if they are present in request
if len(script.Args) >= 1 {
command = append(command, script.Args...)
cmd = exec.Command(script.Executor, command...)
}
// Append arguments to command.Environment if they are present in request
if len(script.EnvVars) >= 1 {
cmd.Env = os.Environ()
for _, envVar := range script.EnvVars {
cmd.Env = append(cmd.Env, envVar)
}
}
// Start and don't wait till execution ends
cmd.Start()
log.Println("Just ran subprocess of", script.Script, "with PID", cmd.Process.Pid)
fmt.Fprintf(w, "The function will be executed in the background. Refer to container logs to see the output")
}
// NotFoundHandler will return custom error message
func NotFoundHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, "This page does not exist!", 404)
}