-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhandler.go
143 lines (120 loc) · 3.48 KB
/
handler.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
package main
import (
"encoding/base64"
"encoding/json"
"fmt"
"io"
"net/http"
"sync"
"github.com/drone/go-task/task/expression"
)
type Handler struct {
port string
tasks map[string]chan bool
lock sync.Mutex
}
func (h *Handler) HandleTasks(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
h.Assign(w, r)
case http.MethodDelete:
h.Remove(w, r)
case http.MethodGet:
h.Get(w, r)
default:
sendErrorResponse(w, http.StatusMethodNotAllowed, "Method not allowed")
}
}
func (h *Handler) Assign(w http.ResponseWriter, r *http.Request) {
var tasks Tasks
if err := parseRequest(r, &tasks); err != nil {
sendErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
for _, task := range tasks.Tasks {
var params Params
if err := parseParams(&task.EncodedParams, ¶ms, task.Secrets); err != nil {
sendErrorResponse(w, http.StatusBadRequest, err.Error())
return
}
quit := make(chan bool)
go startTask(task.ID, params, quit)
h.lock.Lock()
h.tasks[task.ID] = quit
h.lock.Unlock()
}
sendSuccessResponse(w, Response{TasksMetadata: h.getTasksMetadata()})
}
func (h *Handler) Remove(w http.ResponseWriter, r *http.Request) {
taskIds, ok := r.URL.Query()["taskIds"]
if !ok || len(taskIds) < 1 {
sendErrorResponse(w, http.StatusBadRequest, "task IDs are required")
return
}
h.lock.Lock()
for _, taskId := range taskIds {
if quit, exists := h.tasks[taskId]; exists {
go func(quit chan bool) { quit <- true }(quit)
delete(h.tasks, taskId)
} else {
h.lock.Unlock()
sendErrorResponse(w, http.StatusBadRequest, fmt.Sprintf("the task with ID %s does not exist!", taskId))
return
}
}
h.lock.Unlock()
sendSuccessResponse(w, Response{TasksMetadata: h.getTasksMetadata()})
}
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
sendSuccessResponse(w, Response{TasksMetadata: h.getTasksMetadata()})
}
func (h *Handler) getTasksMetadata() TasksMetadata {
h.lock.Lock()
defer h.lock.Unlock()
var tasksMetadata TasksMetadata
for id := range h.tasks {
tasksMetadata = append(tasksMetadata, TaskMetadata{ID: id})
}
return tasksMetadata
}
func parseRequest(r *http.Request, v interface{}) error {
body, err := io.ReadAll(r.Body)
if err != nil {
return fmt.Errorf("unable to read request body")
}
defer r.Body.Close()
if err := json.Unmarshal(body, v); err != nil {
return fmt.Errorf("invalid payload")
}
return nil
}
func parseParams(e *EncodedParams, p *Params, s map[string]string) error {
// decode base64 data
decoded := make([]byte, base64.StdEncoding.DecodedLen(len(e.Base64Data)))
n, err := base64.StdEncoding.Decode(decoded, e.Base64Data)
if err != nil {
return fmt.Errorf("failed to decode EncodedParams.Base64Data with base64: %w", err)
}
decoded = decoded[:n]
// resolve expressions
resolver := expression.New(s)
resolved, err := resolver.Resolve(decoded)
if err != nil {
return fmt.Errorf("failed to resolve expressions in decoded EncodedParams.Base64Data: %w", err)
}
// unmarshall decoded data into `Params` type
if err := json.Unmarshal(resolved, p); err != nil {
return fmt.Errorf("decoded value of EncodedParams.Base64Data is not valid Params type: %w", err)
}
return nil
}
func sendSuccessResponse(w http.ResponseWriter, response interface{}) {
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(response)
}
func sendErrorResponse(w http.ResponseWriter, status int, errMsg string) {
w.WriteHeader(status)
json.NewEncoder(w).Encode(map[string]string{
"error": errMsg,
})
}