Skip to content

Commit cee05f2

Browse files
added proxy task type (#185)
1 parent 940cb7a commit cee05f2

File tree

7 files changed

+250
-14
lines changed

7 files changed

+250
-14
lines changed

venona/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.4.25
1+
1.4.26

venona/go.mod

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ go 1.14
44

55
require (
66
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
7+
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 // indirect
78
github.com/gin-gonic/gin v1.6.3
89
github.com/go-playground/validator/v10 v10.3.0 // indirect
910
github.com/golang/protobuf v1.4.2 // indirect
11+
github.com/hashicorp/go-retryablehttp v0.6.7
1012
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1
1113
github.com/json-iterator/go v1.1.10 // indirect
1214
github.com/mattn/go-colorable v0.1.6 // indirect
1315
github.com/pkg/errors v0.9.1 // indirect
1416
github.com/spf13/cobra v1.0.0
1517
github.com/spf13/pflag v1.0.5
1618
github.com/spf13/viper v1.7.0
17-
github.com/stretchr/objx v0.2.0 // indirect
19+
github.com/stretchr/objx v0.2.0
1820
github.com/stretchr/testify v1.5.1
1921
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d // indirect
2022
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b // indirect

venona/go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
6868
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
6969
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
7070
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
71+
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 h1:roDmqJ4Qes7hrDOsWsMCce0vQHz3xiMPjJ9m4c2eeNs=
72+
github.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8=
7173
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
7274
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
7375
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
@@ -156,10 +158,15 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t
156158
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
157159
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
158160
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
161+
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
159162
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
163+
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
164+
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
160165
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
161166
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
162167
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
168+
github.com/hashicorp/go-retryablehttp v0.6.7 h1:8/CAEZt/+F7kR7GevNHulKkUjLht3CPmn7egmhieNKo=
169+
github.com/hashicorp/go-retryablehttp v0.6.7/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
163170
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
164171
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
165172
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=

venona/pkg/agent/agent.go

Lines changed: 120 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,28 +15,42 @@
1515
package agent
1616

1717
import (
18+
"bytes"
19+
"encoding/json"
1820
"errors"
21+
"fmt"
22+
"io/ioutil"
1923
"sync"
2024
"time"
2125

2226
"github.com/codefresh-io/go/venona/pkg/codefresh"
2327
"github.com/codefresh-io/go/venona/pkg/logger"
2428
"github.com/codefresh-io/go/venona/pkg/runtime"
2529
"github.com/codefresh-io/go/venona/pkg/task"
30+
retryablehttp "github.com/hashicorp/go-retryablehttp"
31+
"github.com/stretchr/objx"
2632
)
2733

34+
// internal errors
2835
var (
29-
errAlreadyRunning = errors.New("Agent already running")
30-
errAlreadyStopped = errors.New("Agent already stopped")
31-
errOptionsRequired = errors.New("Options are required")
32-
errIDRequired = errors.New("ID options is required")
33-
errRuntimesRequired = errors.New("Runtimes options is required")
34-
errLoggerRequired = errors.New("Logger options is required")
36+
errAlreadyRunning = errors.New("Agent already running")
37+
errAlreadyStopped = errors.New("Agent already stopped")
38+
errOptionsRequired = errors.New("Options are required")
39+
errIDRequired = errors.New("ID options is required")
40+
errRuntimesRequired = errors.New("Runtimes options is required")
41+
errLoggerRequired = errors.New("Logger options is required")
42+
errFailedToParseAgentTask = errors.New("Failed to parse agent task spec")
43+
errUknownAgentTaskType = errors.New("Agent task has unknown type")
44+
errAgentTaskMalformedParams = errors.New("failed to marshal agent task params")
45+
errProxyTaskWithoutURL = errors.New(`url not provided for task of type "proxy"`)
46+
errProxyTaskWithoutToken = errors.New(`token not provided for task of type "proxy"`)
3547
)
3648

3749
const (
3850
defaultTaskPullingInterval = time.Second * 3
3951
defaultStatusReportingInterval = time.Second * 10
52+
defaultProxyRequestTimeout = time.Second * 10
53+
defaultProxyRequestRetries = 3
4054
)
4155

4256
type (
@@ -77,6 +91,14 @@ type (
7791
}
7892
)
7993

94+
var (
95+
httpClient = retryablehttp.NewClient()
96+
97+
agentTaskExecutors = map[string]func(t *task.AgentTask, log logger.Logger) error{
98+
"proxy": proxyRequest,
99+
}
100+
)
101+
80102
// New creates a new Agent instance
81103
func New(opt *Options) (*Agent, error) {
82104
if err := checkOptions(opt); err != nil {
@@ -210,17 +232,33 @@ func pullTasks(client codefresh.Codefresh, logger logger.Logger) []task.Task {
210232
func startTasks(tasks []task.Task, runtimes map[string]runtime.Runtime, logger logger.Logger) {
211233
creationTasks := []task.Task{}
212234
deletionTasks := []task.Task{}
235+
agentTasks := []task.Task{}
236+
237+
// divide tasks by types
213238
for _, t := range tasks {
214-
logger.Debug("Received task", "type", t.Type, "workflow", t.Metadata.Workflow, "runtime", t.Metadata.ReName)
215-
if t.Type == task.TypeCreatePod || t.Type == task.TypeCreatePVC {
239+
logger.Debug("Received task", "type", t.Type, "tid", t.Metadata.Workflow, "runtime", t.Metadata.ReName)
240+
switch t.Type {
241+
case task.TypeCreatePod, task.TypeCreatePVC:
216242
creationTasks = append(creationTasks, t)
243+
case task.TypeDeletePod, task.TypeDeletePVC:
244+
deletionTasks = append(deletionTasks, t)
245+
case task.TypeAgentTask:
246+
agentTasks = append(agentTasks, t)
247+
default:
248+
logger.Error("unrecognized task type", "type", t.Type, "tid", t.Metadata.Workflow, "runtime", t.Metadata.ReName)
217249
}
250+
}
218251

219-
if t.Type == task.TypeDeletePod || t.Type == task.TypeDeletePVC {
220-
deletionTasks = append(deletionTasks, t)
252+
// process agent tasks
253+
for i := range agentTasks {
254+
t := agentTasks[i]
255+
logger.Info("executing agent task", "tid", t.Metadata.Workflow)
256+
if err := executeAgentTask(&t, logger); err != nil {
257+
logger.Error(err.Error())
221258
}
222259
}
223260

261+
// process creation tasks
224262
for _, tasks := range groupTasks(creationTasks) {
225263
reName := tasks[0].Metadata.ReName
226264
runtime, ok := runtimes[reName]
@@ -233,6 +271,8 @@ func startTasks(tasks []task.Task, runtimes map[string]runtime.Runtime, logger l
233271
logger.Error(err.Error())
234272
}
235273
}
274+
275+
// process deletion tasks
236276
for _, tasks := range groupTasks(deletionTasks) {
237277
reName := tasks[0].Metadata.ReName
238278
runtime, ok := runtimes[reName]
@@ -249,6 +289,71 @@ func startTasks(tasks []task.Task, runtimes map[string]runtime.Runtime, logger l
249289
}
250290
}
251291

292+
func executeAgentTask(t *task.Task, log logger.Logger) error {
293+
specJSON, err := json.Marshal(t.Spec)
294+
if err != nil {
295+
return errFailedToParseAgentTask
296+
}
297+
298+
spec := task.AgentTask{}
299+
if err = json.Unmarshal(specJSON, &spec); err != nil {
300+
return errFailedToParseAgentTask
301+
}
302+
303+
e, ok := agentTaskExecutors[spec.Type]
304+
if !ok {
305+
return errUknownAgentTaskType
306+
}
307+
308+
return e(&spec, log)
309+
}
310+
311+
func proxyRequest(t *task.AgentTask, log logger.Logger) error {
312+
spec := objx.Map(t.Params)
313+
vars := objx.Map(spec.Get("runtimeContext.context.variables").MSI())
314+
token := spec.Get("runtimeContext.context.eventReporting.token").Str()
315+
if token == "" {
316+
return errProxyTaskWithoutToken
317+
}
318+
319+
url := vars.Get("proxyUrl").Str()
320+
if url == "" {
321+
return errProxyTaskWithoutURL
322+
}
323+
324+
method := vars.Get("method").Str("POST")
325+
326+
json, err := json.Marshal(t.Params)
327+
if err != nil {
328+
return errAgentTaskMalformedParams
329+
}
330+
if json == nil {
331+
json = []byte{}
332+
}
333+
334+
req, err := retryablehttp.NewRequest(method, url, bytes.NewReader(json))
335+
if err != nil {
336+
return err
337+
}
338+
339+
req.Header.Add("x-req-type", "workflow-request")
340+
req.Header.Add("x-access-token", token)
341+
req.Header.Add("Content-Type", "application/json")
342+
req.Header.Add("Content-Length", fmt.Sprintf("%v", len(json)))
343+
344+
log.Info("executing proxy task", "url", url, "method", method)
345+
346+
resp, err := httpClient.Do(req)
347+
if err != nil {
348+
return err
349+
}
350+
body, _ := ioutil.ReadAll(resp.Body)
351+
352+
log.Info("finished proxy task", "url", url, "method", method, "status", resp.Status, "body", string(body))
353+
354+
return nil
355+
}
356+
252357
func groupTasks(tasks []task.Task) map[string][]task.Task {
253358
candidates := map[string][]task.Task{}
254359
for _, task := range tasks {
@@ -282,3 +387,8 @@ func checkOptions(opt *Options) error {
282387

283388
return nil
284389
}
390+
391+
func init() {
392+
httpClient.RetryMax = defaultProxyRequestRetries
393+
httpClient.HTTPClient.Timeout = defaultProxyRequestTimeout
394+
}

venona/pkg/agent/agent_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,114 @@ func TestNew(t *testing.T) {
224224
}
225225
}
226226

227+
func Test_executeAgentTask(t *testing.T) {
228+
executorCalled := false
229+
okExecutor := func(t *task.AgentTask, log logger.Logger) error {
230+
executorCalled = true
231+
return nil
232+
}
233+
234+
badExecutor := func(t *task.AgentTask, log logger.Logger) error {
235+
executorCalled = true
236+
return errProxyTaskWithoutURL
237+
}
238+
239+
type args struct {
240+
executorName string
241+
executorFunc func(*task.AgentTask, logger.Logger) error
242+
task *task.Task
243+
}
244+
245+
tests := []struct {
246+
name string
247+
args *args
248+
wantErr error
249+
}{
250+
{
251+
name: "should successfully run executor and return nil",
252+
args: &args{
253+
executorName: "test",
254+
executorFunc: okExecutor,
255+
task: &task.Task{
256+
Type: task.TypeAgentTask,
257+
Metadata: task.Metadata{},
258+
Spec: task.AgentTask{
259+
Type: "test",
260+
Params: nil,
261+
},
262+
},
263+
},
264+
wantErr: nil,
265+
},
266+
{
267+
name: "should call an executor and return an error",
268+
args: &args{
269+
executorName: "test",
270+
executorFunc: badExecutor,
271+
task: &task.Task{
272+
Type: task.TypeAgentTask,
273+
Metadata: task.Metadata{},
274+
Spec: task.AgentTask{
275+
Type: "test",
276+
Params: nil,
277+
},
278+
},
279+
},
280+
wantErr: errProxyTaskWithoutURL,
281+
},
282+
{
283+
name: "should pass the agent task spec to the executor",
284+
args: &args{
285+
executorName: "test",
286+
executorFunc: func(t *task.AgentTask, l logger.Logger) error {
287+
executorCalled = true
288+
data, ok := t.Params["data"].(float64)
289+
if !ok {
290+
return fmt.Errorf("expected data to be of type int")
291+
}
292+
if data != 3 {
293+
return fmt.Errorf("expected data to equal 3 but data=%v", data)
294+
}
295+
return nil
296+
},
297+
task: &task.Task{
298+
Type: task.TypeAgentTask,
299+
Metadata: task.Metadata{},
300+
Spec: task.AgentTask{
301+
Type: "test",
302+
Params: map[string]interface{}{
303+
"data": 3,
304+
},
305+
},
306+
},
307+
},
308+
wantErr: nil,
309+
},
310+
}
311+
312+
for _, tt := range tests {
313+
executorCalled = false
314+
agentTaskExecutors[tt.args.executorName] = tt.args.executorFunc
315+
t.Run(tt.name, func(t *testing.T) {
316+
ret := executeAgentTask(tt.args.task, getLoggerMock())
317+
if !executorCalled {
318+
t.Errorf("executor function hasn't been called")
319+
}
320+
if ret == nil && tt.wantErr != nil {
321+
t.Errorf("expected error %v but got nil", tt.wantErr)
322+
}
323+
if ret != nil && tt.wantErr == nil {
324+
t.Errorf("expected nil but got an error: %v", ret)
325+
}
326+
if ret != nil && ret.Error() != tt.wantErr.Error() {
327+
t.Errorf("expected error: %v but got error: %v", tt.wantErr.Error(), ret.Error())
328+
}
329+
330+
})
331+
delete(agentTaskExecutors, tt.args.executorName)
332+
}
333+
}
334+
227335
func createMockAgent() *Agent {
228336
runtimes := make(map[string]runtime.Runtime)
229337
runtimes["x"] = runtime.New(runtime.Options{})

venona/pkg/task/task.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414

1515
package task
1616

17-
import "encoding/json"
17+
import (
18+
"encoding/json"
19+
)
1820

1921
// Const for task types
2022
const (
2123
TypeCreatePod = "CreatePod"
2224
TypeCreatePVC = "CreatePvc"
2325
TypeDeletePod = "DeletePod"
2426
TypeDeletePVC = "DeletePvc"
27+
TypeAgentTask = "AgentTask"
2528
)
2629

2730
// UnmarshalTasks with json
@@ -53,3 +56,9 @@ type Metadata struct {
5356
ReName string `json:"reName"`
5457
Workflow string `json:"workflow"`
5558
}
59+
60+
// AgentTask describes a task of type "AgentTask"
61+
type AgentTask struct {
62+
Type string `json:"type"`
63+
Params map[string]interface{} `json:"params"`
64+
}

venonactl/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.4.25
1+
1.4.26

0 commit comments

Comments
 (0)