Skip to content

Commit 2b08cac

Browse files
committed
WKS-1033 - Support worker with projects
1 parent 1015730 commit 2b08cac

40 files changed

+1158
-201
lines changed

Makefile

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ BINARY_CLI = bin
66
WORKSPACE_ROOT = $(shell cd "${PROJECT_DIR}" && pwd)
77
TOOLS_DIR := $(CURDIR)/.tools
88
SCRIPTS_DIR = ${PROJECT_DIR}/.github/scripts
9+
TARGET_DIR = ${PROJECT_DIR}/target
910
LINKERFLAGS = -s -w
1011
COMPILERFLAGS = all=-trimpath=$(WORKSPACE_ROOT)
1112
GOOS = $(shell go env GOOS)
@@ -58,7 +59,19 @@ build-install:: build
5859

5960
########## TEST ##########
6061

61-
test: prereq
62-
$(SCRIPTS_DIR)/gotest.sh ./... ${TEST_ARGS}
62+
test-prereq: prereq
63+
mkdir -p target/reports
64+
65+
test: PACKAGES=./...
66+
test: TEST_ARGS=-short
67+
test: test-prereq do-run-tests
68+
69+
itest: PACKAGES=./test/...
70+
itest: TAGS=-tags=itest
71+
itest: TEST_ARGS=-count=1 -p=1
72+
itest:: test-prereq do-run-tests
73+
74+
do-run-tests::
75+
$(SCRIPTS_DIR)/gotest.sh $$(go list $(TAGS) $(PACKAGES) | grep -v "^.*/mocks$$") -timeout 30m -coverpkg=github.com/jfrog/jfrog-cli-platform-services/... -coverprofile=$(TARGET_DIR)/reports/coverage.out $(TEST_ARGS) $(TAGS)
6376

6477
.PHONY: $(MAKECMDGOALS)

commands/commands_commons.go

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
"fmt"
99
"io"
1010
"net/http"
11+
"net/url"
1112
"os"
12-
"regexp"
1313
"strings"
1414

1515
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
@@ -23,9 +23,8 @@ import (
2323

2424
// Useful to capture output in tests
2525
var (
26-
cliOut io.Writer = os.Stdout
27-
cliIn io.Reader = os.Stdin
28-
importPattern = regexp.MustCompile(`(?ms)^\s*(import\s+[^;]+;\s*)(.*)$`)
26+
cliOut io.Writer = os.Stdout
27+
cliIn io.Reader = os.Stdin
2928
)
3029

3130
func prettifyJson(in []byte) []byte {
@@ -57,6 +56,48 @@ func outputApiResponse(res *http.Response, okStatus int) error {
5756
})
5857
}
5958

59+
type stringFlagAware interface {
60+
GetStringFlagValue(string) string
61+
}
62+
63+
// Extracts the project key and worker key from the command context. If the project key is not provided, it will be taken from the manifest.
64+
// There workerKey could either be the first argument or the name in the manifest.
65+
// The first argument will only be considered as the workerKey if total arguments are greater than minArgument.
66+
func extractProjectAndKeyFromCommandContext(c stringFlagAware, args []string, minArguments int, onlyGeneric bool) (string, string, error) {
67+
var workerKey string
68+
69+
projectKey := c.GetStringFlagValue(model.FlagProjectKey)
70+
71+
if len(args) > 0 && len(args) > minArguments {
72+
workerKey = args[0]
73+
}
74+
75+
if workerKey == "" || projectKey == "" {
76+
manifest, err := model.ReadManifest()
77+
if err != nil {
78+
return "", "", err
79+
}
80+
81+
if err = manifest.Validate(); err != nil {
82+
return "", "", err
83+
}
84+
85+
if onlyGeneric && manifest.Action != "GENERIC_EVENT" {
86+
return "", "", fmt.Errorf("only the GENERIC_EVENT actions are executable. Got %s", manifest.Action)
87+
}
88+
89+
if workerKey == "" {
90+
workerKey = manifest.Name
91+
}
92+
93+
if projectKey == "" {
94+
projectKey = manifest.ProjectKey
95+
}
96+
}
97+
98+
return workerKey, projectKey, nil
99+
}
100+
60101
func discardApiResponse(res *http.Response, okStatus int) error {
61102
return processApiResponse(res, func(content []byte, statusCode int) error {
62103
var err error
@@ -93,13 +134,26 @@ func processApiResponse(res *http.Response, doWithContent func(content []byte, s
93134
return doWithContent(responseBytes, res.StatusCode)
94135
}
95136

96-
func callWorkerApi(c *components.Context, serverUrl string, serverToken string, method string, body []byte, api ...string) (*http.Response, func(), error) {
137+
func callWorkerApi(c *components.Context, serverUrl string, serverToken string, method string, body []byte, queryParams map[string]string, api ...string) (*http.Response, func(), error) {
97138
timeout, err := model.GetTimeoutParameter(c)
98139
if err != nil {
99140
return nil, nil, err
100141
}
101142

102-
url := fmt.Sprintf("%sworker/api/v1/%s", utils.AddTrailingSlashIfNeeded(serverUrl), strings.Join(api, "/"))
143+
apiEndpoint := fmt.Sprintf("%sworker/api/v1/%s", utils.AddTrailingSlashIfNeeded(serverUrl), strings.Join(api, "/"))
144+
145+
if queryParams != nil {
146+
var query string
147+
for key, value := range queryParams {
148+
if query != "" {
149+
query += "&"
150+
}
151+
query += fmt.Sprintf("%s=%s", key, url.QueryEscape(value))
152+
}
153+
if query != "" {
154+
apiEndpoint += "?" + query
155+
}
156+
}
103157

104158
reqCtx, cancelReq := context.WithTimeout(context.Background(), timeout)
105159

@@ -108,7 +162,7 @@ func callWorkerApi(c *components.Context, serverUrl string, serverToken string,
108162
bodyReader = bytes.NewBuffer(body)
109163
}
110164

111-
req, err := http.NewRequestWithContext(reqCtx, method, url, bodyReader)
165+
req, err := http.NewRequestWithContext(reqCtx, method, apiEndpoint, bodyReader)
112166
if err != nil {
113167
return nil, cancelReq, err
114168
}
@@ -128,8 +182,8 @@ func callWorkerApi(c *components.Context, serverUrl string, serverToken string,
128182
return res, cancelReq, nil
129183
}
130184

131-
func callWorkerApiWithOutput(c *components.Context, serverUrl string, serverToken string, method string, body []byte, okStatus int, api ...string) error {
132-
res, discardReq, err := callWorkerApi(c, serverUrl, serverToken, method, body, api...)
185+
func callWorkerApiWithOutput(c *components.Context, serverUrl string, serverToken string, method string, body []byte, okStatus int, queryParams map[string]string, api ...string) error {
186+
res, discardReq, err := callWorkerApi(c, serverUrl, serverToken, method, body, queryParams, api...)
133187
if discardReq != nil {
134188
defer discardReq()
135189
}
@@ -139,8 +193,8 @@ func callWorkerApiWithOutput(c *components.Context, serverUrl string, serverToke
139193
return outputApiResponse(res, okStatus)
140194
}
141195

142-
func callWorkerApiSilent(c *components.Context, serverUrl string, serverToken string, method string, body []byte, okStatus int, api ...string) error {
143-
res, discardReq, err := callWorkerApi(c, serverUrl, serverToken, method, body, api...)
196+
func callWorkerApiSilent(c *components.Context, serverUrl string, serverToken string, method string, body []byte, okStatus int, queryParams map[string]string, api ...string) error {
197+
res, discardReq, err := callWorkerApi(c, serverUrl, serverToken, method, body, queryParams, api...)
144198
if discardReq != nil {
145199
defer discardReq()
146200
}
@@ -151,8 +205,13 @@ func callWorkerApiSilent(c *components.Context, serverUrl string, serverToken st
151205
}
152206

153207
// fetchWorkerDetails Fetch a worker by its name. Returns nil if the worker does not exist (statusCode=404). Any other statusCode other than 200 will result as an error.
154-
func fetchWorkerDetails(c *components.Context, serverUrl string, accessToken string, workerKey string) (*model.WorkerDetails, error) {
155-
res, discardReq, err := callWorkerApi(c, serverUrl, accessToken, http.MethodGet, nil, "workers", workerKey)
208+
func fetchWorkerDetails(c *components.Context, serverUrl string, accessToken string, workerKey string, projectKey string) (*model.WorkerDetails, error) {
209+
queryParams := make(map[string]string)
210+
if projectKey != "" {
211+
queryParams["projectKey"] = projectKey
212+
}
213+
214+
res, discardReq, err := callWorkerApi(c, serverUrl, accessToken, http.MethodGet, nil, queryParams, "workers", workerKey)
156215
if discardReq != nil {
157216
defer discardReq()
158217
}
@@ -212,13 +271,3 @@ func prepareSecretsUpdate(mf *model.Manifest, existingWorker *model.WorkerDetail
212271

213272
return secrets
214273
}
215-
216-
func cleanImports(source string) string {
217-
out := source
218-
match := importPattern.FindAllStringSubmatch(out, -1)
219-
for len(match) == 1 && len(match[0]) == 3 {
220-
out = match[0][2]
221-
match = importPattern.FindAllStringSubmatch(out, -1)
222-
}
223-
return out
224-
}

commands/commands_commons_test.go

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"testing"
99
"text/template"
1010

11+
"github.com/google/uuid"
12+
1113
"github.com/stretchr/testify/assert"
1214

1315
"github.com/jfrog/jfrog-cli-core/v2/plugins"
@@ -44,12 +46,104 @@ func Test_cleanImports(t *testing.T) {
4446

4547
for _, tt := range tests {
4648
t.Run(tt.name, func(t *testing.T) {
47-
got := cleanImports(tt.source)
49+
got := model.CleanImports(tt.source)
4850
assert.Equal(t, tt.want, got)
4951
})
5052
}
5153
}
5254

55+
func Test_extractProjectAndKeyFromCommandContext(t *testing.T) {
56+
tests := []struct {
57+
name string
58+
c stringFlagAware
59+
args []string
60+
minArguments int
61+
onlyGeneric bool
62+
eventType string
63+
assert func(t *testing.T, manifestWorkerKey, manifestProjectKey, extractedWorkerKey, extractedProjectKey string, err error)
64+
}{
65+
{
66+
name: "project and worker key from args",
67+
c: mockStringFlagAware{map[string]string{model.FlagProjectKey: "proj1"}},
68+
args: []string{"worker1"},
69+
minArguments: 0,
70+
onlyGeneric: false,
71+
assert: func(t *testing.T, _, _, extractedWorkerKey, extractedProjectKey string, err error) {
72+
require.NoError(t, err)
73+
assert.Equal(t, "worker1", extractedWorkerKey)
74+
assert.Equal(t, "proj1", extractedProjectKey)
75+
},
76+
},
77+
{
78+
name: "project and worker key from manifest",
79+
c: mockStringFlagAware{map[string]string{}},
80+
args: []string{},
81+
minArguments: 0,
82+
onlyGeneric: false,
83+
assert: func(t *testing.T, manifestWorkerKey, manifestProjectKey, extractedWorkerKey, extractedProjectKey string, err error) {
84+
require.NoError(t, err)
85+
assert.Equal(t, manifestWorkerKey, extractedWorkerKey)
86+
assert.Equal(t, manifestProjectKey, extractedProjectKey)
87+
},
88+
},
89+
{
90+
name: "only generic event allowed",
91+
c: mockStringFlagAware{map[string]string{model.FlagProjectKey: ""}},
92+
args: []string{},
93+
minArguments: 0,
94+
onlyGeneric: true,
95+
eventType: "BEFORE_DOWNLOAD",
96+
assert: func(t *testing.T, _, _, _, _ string, err error) {
97+
assert.EqualError(t, err, "only the GENERIC_EVENT actions are executable. Got BEFORE_DOWNLOAD")
98+
},
99+
},
100+
{
101+
name: "min arguments count not satisfied",
102+
c: mockStringFlagAware{map[string]string{model.FlagProjectKey: ""}},
103+
args: []string{"@jsonPayload.json"},
104+
minArguments: 1,
105+
onlyGeneric: false,
106+
assert: func(t *testing.T, manifestWorkerKey, manifestProjectKey, extractedWorkerKey, extractedProjectKey string, err error) {
107+
require.NoError(t, err)
108+
assert.Equal(t, manifestWorkerKey, extractedWorkerKey)
109+
assert.Equal(t, manifestProjectKey, extractedProjectKey)
110+
},
111+
},
112+
}
113+
114+
for _, tt := range tests {
115+
t.Run(tt.name, func(t *testing.T) {
116+
dir, manifestWorkerKey := prepareWorkerDirForTest(t)
117+
manifestProjectKey := uuid.NewString()
118+
119+
mf := model.Manifest{
120+
Name: manifestWorkerKey,
121+
ProjectKey: manifestProjectKey,
122+
Action: tt.eventType,
123+
SourceCodePath: "./worker.ts",
124+
}
125+
126+
if mf.Action == "" {
127+
mf.Action = "GENERIC_EVENT"
128+
}
129+
130+
require.NoError(t, mf.Save(dir))
131+
132+
workerKey, projectKey, err := extractProjectAndKeyFromCommandContext(tt.c, tt.args, tt.minArguments, tt.onlyGeneric)
133+
134+
tt.assert(t, manifestWorkerKey, manifestProjectKey, workerKey, projectKey, err)
135+
})
136+
}
137+
}
138+
139+
type mockStringFlagAware struct {
140+
data map[string]string
141+
}
142+
143+
func (m mockStringFlagAware) GetStringFlagValue(flag string) string {
144+
return m.data[flag]
145+
}
146+
53147
func prepareWorkerDirForTest(t *testing.T) (string, string) {
54148
dir, err := os.MkdirTemp("", "worker-*-init")
55149
require.NoError(t, err)

commands/deploy_cmd.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ type deployRequest struct {
1616
Key string `json:"key"`
1717
Description string `json:"description"`
1818
Enabled bool `json:"enabled"`
19+
Debug bool `json:"debug"`
1920
SourceCode string `json:"sourceCode"`
2021
Action string `json:"action"`
2122
FilterCriteria model.FilterCriteria `json:"filterCriteria,omitempty"`
2223
Secrets []*model.Secret `json:"secrets"`
24+
ProjectKey string `json:"projectKey"`
2325
}
2426

2527
func GetDeployCommand() components.Command {
@@ -59,7 +61,7 @@ func GetDeployCommand() components.Command {
5961
}
6062

6163
func runDeployCommand(ctx *components.Context, manifest *model.Manifest, serverUrl string, token string) error {
62-
existingWorker, err := fetchWorkerDetails(ctx, serverUrl, token, manifest.Name)
64+
existingWorker, err := fetchWorkerDetails(ctx, serverUrl, token, manifest.Name, manifest.ProjectKey)
6365
if err != nil {
6466
return err
6567
}
@@ -76,15 +78,15 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, serverU
7678

7779
if existingWorker == nil {
7880
log.Info(fmt.Sprintf("Deploying worker '%s'", manifest.Name))
79-
err = callWorkerApiWithOutput(ctx, serverUrl, token, http.MethodPost, bodyBytes, http.StatusCreated, "workers")
81+
err = callWorkerApiWithOutput(ctx, serverUrl, token, http.MethodPost, bodyBytes, http.StatusCreated, nil, "workers")
8082
if err == nil {
8183
log.Info(fmt.Sprintf("Worker '%s' deployed", manifest.Name))
8284
}
8385
return err
8486
}
8587

8688
log.Info(fmt.Sprintf("Updating worker '%s'", manifest.Name))
87-
err = callWorkerApiWithOutput(ctx, serverUrl, token, http.MethodPut, bodyBytes, http.StatusNoContent, "workers")
89+
err = callWorkerApiWithOutput(ctx, serverUrl, token, http.MethodPut, bodyBytes, http.StatusNoContent, nil, "workers")
8890
if err == nil {
8991
log.Info(fmt.Sprintf("Worker '%s' updated", manifest.Name))
9092
}
@@ -97,7 +99,7 @@ func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, exi
9799
if err != nil {
98100
return nil, err
99101
}
100-
sourceCode = cleanImports(sourceCode)
102+
sourceCode = model.CleanImports(sourceCode)
101103

102104
var secrets []*model.Secret
103105

@@ -110,9 +112,11 @@ func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, exi
110112
Action: manifest.Action,
111113
Description: manifest.Description,
112114
Enabled: manifest.Enabled,
115+
Debug: manifest.Debug,
113116
FilterCriteria: manifest.FilterCriteria,
114117
SourceCode: sourceCode,
115118
Secrets: secrets,
119+
ProjectKey: manifest.ProjectKey,
116120
}
117121

118122
return payload, nil

0 commit comments

Comments
 (0)