Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 26 additions & 3 deletions commands/common/cmd_worker_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
// 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.
func FetchWorkerDetails(c model.IntFlagProvider, serverUrl string, accessToken string, workerKey string, projectKey string) (*model.WorkerDetails, error) {
details := new(model.WorkerDetails)

err := CallWorkerApi(c, ApiCallParams{
Method: http.MethodGet,
ServerUrl: serverUrl,
Expand All @@ -22,9 +21,9 @@ func FetchWorkerDetails(c model.IntFlagProvider, serverUrl string, accessToken s
Path: []string{"workers", workerKey},
OnContent: func(content []byte) error {
if len(content) == 0 {
log.Debug("No worker details returned from the server")
return nil
}
log.Info(fmt.Sprintf("Worker %s details returned from the server", details.Key))
return json.Unmarshal(content, details)
},
})
Expand All @@ -33,9 +32,9 @@ func FetchWorkerDetails(c model.IntFlagProvider, serverUrl string, accessToken s
}

if details.Key == "" {
log.Info(fmt.Sprintf("Worker %s does not exist", workerKey))
return nil, nil
}

return details, nil
}

Expand Down Expand Up @@ -64,3 +63,27 @@ func FetchActions(c model.IntFlagProvider, serverUrl string, accessToken string,

return metadata, nil
}

func FetchOptions(c model.IntFlagProvider, serverUrl string, accessToken string) (*OptionsMetadata, error) {
metadata := new(OptionsMetadata)

err := CallWorkerApi(c, ApiCallParams{
Method: http.MethodGet,
ServerUrl: serverUrl,
ServerToken: accessToken,
OkStatuses: []int{http.StatusOK},
ApiVersion: ApiVersionV1,
Path: []string{"options"},
OnContent: func(content []byte) error {
if len(content) == 0 {
log.Debug("No options returned from the server")
return nil
}
return json.Unmarshal(content, &metadata)
},
})
if err != nil {
return nil, fmt.Errorf("cannot fetch options: %w", err)
}
return metadata, nil
}
8 changes: 8 additions & 0 deletions commands/common/cmd_worker_api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,11 @@ func TestFetchActions(t *testing.T) {
})
}
}

func TestFetchOptions(t *testing.T) {
samples := LoadSampleOptions(t)
s, token := NewMockWorkerServer(t, NewServerStub(t).WithOptionsEndpoint().WithT(t))
got, err := FetchOptions(IntFlagMap{}, s.BaseUrl(), token)
require.NoError(t, err)
assert.Equal(t, got, samples)
}
16 changes: 16 additions & 0 deletions commands/common/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package common

type EditionOptions struct {
MaxCodeChars int `json:"maxCodeChars"`
MaxVersionNumberChars int `json:"maxVersionNumberChars"`
MaxVersionCommitShaChars int `json:"maxVersionCommitShaChars"`
MaxVersionDescriptionChars int `json:"maxVersionDescriptionChars"`
}

type OptionsMetadata struct {
Edition EditionOptions `json:"edition"`
MinArtifactoryVersionForProjectSupport string `json:"minArtifactoryVersionForProjectSupport"`
IsTutorialAvailable bool `json:"isTutorialAvailable"`
IsFeedbackEnabled bool `json:"isFeedbackEnabled"`
IsHistoryEnabled bool `json:"isHistoryEnabled"`
}
20 changes: 16 additions & 4 deletions commands/common/test_commons.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ type Test interface {

const SecretPassword = "P@ssw0rd!"

//go:embed testdata/actions/*
var sampleActions embed.FS
//go:embed testdata/*
var sampleFiles embed.FS

func SetCliIn(reader io.Reader) {
cliIn = reader
Expand Down Expand Up @@ -159,11 +159,11 @@ func MustEncryptSecret(t require.TestingT, secretValue string, password ...strin
func LoadSampleActions(t require.TestingT) ActionsMetadata {
var metadata ActionsMetadata

actionsFiles, err := sampleActions.ReadDir("testdata/actions")
actionsFiles, err := sampleFiles.ReadDir("testdata/actions")
require.NoError(t, err)

for _, file := range actionsFiles {
content, err := sampleActions.ReadFile("testdata/actions/" + file.Name())
content, err := sampleFiles.ReadFile("testdata/actions/" + file.Name())
require.NoError(t, err)

action := &model.ActionMetadata{}
Expand All @@ -187,6 +187,18 @@ func LoadSampleActionEvents(t require.TestingT) []string {
return events
}

func LoadSampleOptions(t require.TestingT) *OptionsMetadata {
// var metadata OptionsMetadata

content, err := sampleFiles.ReadFile("testdata/options.json")
require.NoError(t, err)
options := &OptionsMetadata{}
err = json.Unmarshal(content, options)
require.NoError(t, err)

return options
}

func TestSetEnv(t Test, key, value string) {
err := os.Setenv(key, value)
require.NoError(t, err)
Expand Down
62 changes: 58 additions & 4 deletions commands/common/test_worker_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ const (

type BodyValidator func(t require.TestingT, content []byte)

type workerDeployPayload struct {
Key string `json:"key"`
Description string `json:"description"`
Enabled bool `json:"enabled"`
Debug bool `json:"debug"`
SourceCode string `json:"sourceCode"`
Action model.Action `json:"action"`
FilterCriteria *model.FilterCriteria `json:"filterCriteria,omitempty"`
Secrets []*model.Secret `json:"secrets"`
ProjectKey string `json:"projectKey"`
Version *model.Version `json:"version,omitempty"`
}

func ValidateJson(expected any) BodyValidator {
return ValidateJsonFunc(expected, func(in any) any {
return in
Expand Down Expand Up @@ -150,7 +163,7 @@ func (s *ServerStub) WithCreateEndpoint(validateBody BodyValidator) *ServerStub
s.endpoints = append(s.endpoints,
mockhttp.NewServerEndpoint().
When(
mockhttp.Request().POST("/worker/api/v1/workers"),
mockhttp.Request().POST("/worker/api/v2/workers"),
).
HandleWith(s.handleSave(http.StatusCreated, validateBody)),
)
Expand All @@ -176,7 +189,7 @@ func (s *ServerStub) WithUpdateEndpoint(validateBody BodyValidator) *ServerStub
s.endpoints = append(s.endpoints,
mockhttp.NewServerEndpoint().
When(
mockhttp.Request().PUT("/worker/api/v1/workers"),
mockhttp.Request().PUT("/worker/api/v2/workers"),
).
HandleWith(s.handleSave(http.StatusNoContent, validateBody)),
)
Expand Down Expand Up @@ -269,6 +282,17 @@ func (s *ServerStub) WithGetExecutionHistoryEndpoint() *ServerStub {
return s
}

func (s *ServerStub) WithOptionsEndpoint() *ServerStub {
s.endpoints = append(s.endpoints,
mockhttp.NewServerEndpoint().
When(
mockhttp.Request().GET("/worker/api/v1/options"),
).
HandleWith(s.handleGetOptions),
)
return s
}

func (s *ServerStub) handleGetAll(res http.ResponseWriter, req *http.Request) {
s.applyDelay()

Expand Down Expand Up @@ -419,10 +443,12 @@ func (s *ServerStub) handleSave(status int, validateBody BodyValidator) http.Han
validateBody(s.test, content)
}

workerDetails := &model.WorkerDetails{}
err = json.Unmarshal(content, workerDetails)
worker := workerDeployPayload{}

err = json.Unmarshal(content, &worker)
require.NoError(s.test, err)

workerDetails := mapWorkerSentToWorkerDetails(worker)
s.workers[workerDetails.Key] = workerDetails

res.WriteHeader(status)
Expand Down Expand Up @@ -456,6 +482,20 @@ func (s *ServerStub) handleGetAllMetadata(metadata ActionsMetadata) http.Handler
}
}

func (s *ServerStub) handleGetOptions(res http.ResponseWriter, req *http.Request) {
s.applyDelay()

if !s.validateToken(res, req) {
return
}

res.WriteHeader(http.StatusOK)

options := LoadSampleOptions(s.test)
_, err := res.Write([]byte(MustJsonMarshal(s.test, options)))
require.NoError(s.test, err)
}

func (s *ServerStub) handle(status int, validateBody BodyValidator, responseBody any) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
s.applyDelay()
Expand Down Expand Up @@ -543,3 +583,17 @@ func (s *ServerStub) applyDelay() {
time.Sleep(s.waitFor)
}
}

func mapWorkerSentToWorkerDetails(workerSent workerDeployPayload) *model.WorkerDetails {
return &model.WorkerDetails{
Key: workerSent.Key,
Description: workerSent.Description,
Enabled: workerSent.Enabled,
Debug: workerSent.Debug,
SourceCode: workerSent.SourceCode,
Action: workerSent.Action.Name, // Map Action.Name
FilterCriteria: workerSent.FilterCriteria,
Secrets: workerSent.Secrets,
ProjectKey: workerSent.ProjectKey,
}
}
12 changes: 12 additions & 0 deletions commands/common/testdata/options.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"edition": {
"maxCodeChars": 100000,
"maxVersionNumberChars": 64,
"maxVersionCommitShaChars": 128,
"maxVersionDescriptionChars": 256
},
"minArtifactoryVersionForProjectSupport": "7.95.0",
"isTutorialAvailable": false,
"isFeedbackEnabled": false,
"isHistoryEnabled": true
}
20 changes: 20 additions & 0 deletions commands/common/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package common

import (
"fmt"

"github.com/jfrog/jfrog-cli-platform-services/model"
)

func ValidateVersion(version *model.Version, options *OptionsMetadata) error {
if len(version.Number) > options.Edition.MaxVersionNumberChars {
return fmt.Errorf("version number exceeds maximum length of %d characters", options.Edition.MaxVersionNumberChars)
}
if len(version.CommitSha) > options.Edition.MaxVersionCommitShaChars {
return fmt.Errorf("commit sha exceeds maximum length of %d characters", options.Edition.MaxVersionCommitShaChars)
}
if len(version.Description) > options.Edition.MaxVersionDescriptionChars {
return fmt.Errorf("description exceeds maximum length of %d characters", options.Edition.MaxVersionDescriptionChars)
}
return nil
}
35 changes: 27 additions & 8 deletions commands/deploy_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ type deployRequest struct {
Enabled bool `json:"enabled"`
Debug bool `json:"debug"`
SourceCode string `json:"sourceCode"`
Action string `json:"action"`
Action model.Action `json:"action"`
FilterCriteria *model.FilterCriteria `json:"filterCriteria,omitempty"`
Secrets []*model.Secret `json:"secrets"`
ProjectKey string `json:"projectKey"`
Version *model.Version `json:"version,omitempty"`
}

func GetDeployCommand() components.Command {
Expand All @@ -35,6 +36,9 @@ func GetDeployCommand() components.Command {
plugins_common.GetServerIdFlag(),
model.GetTimeoutFlag(),
model.GetNoSecretsFlag(),
model.GetChangesVersionFlag(),
model.GetChangesDescriptionFlag(),
model.GetChangesCommitShaFlag(),
},
Action: func(c *components.Context) error {
server, err := model.GetServerDetails(c)
Expand Down Expand Up @@ -71,18 +75,32 @@ func GetDeployCommand() components.Command {
}
}

return runDeployCommand(c, manifest, actionMeta, server.GetUrl(), server.GetAccessToken())
version := &model.Version{
Number: c.GetStringFlagValue(model.FlagChangesVersion),
Description: c.GetStringFlagValue(model.FlagChangesDescription),
CommitSha: c.GetStringFlagValue(model.FlagChangesCommitSha),
}
if !version.IsEmpty() {
options, err := common.FetchOptions(c, server.GetUrl(), server.GetAccessToken())
if err != nil {
return err
}
if err = common.ValidateVersion(version, options); err != nil {
return err
}
}
return runDeployCommand(c, manifest, actionMeta, version, server.GetUrl(), server.GetAccessToken())
},
}
}

func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, serverUrl string, token string) error {
func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, version *model.Version, serverUrl string, token string) error {
existingWorker, err := common.FetchWorkerDetails(ctx, serverUrl, token, manifest.Name, manifest.ProjectKey)
if err != nil {
return err
}

body, err := prepareDeployRequest(ctx, manifest, actionMeta, existingWorker)
body, err := prepareDeployRequest(ctx, manifest, actionMeta, version, existingWorker)
if err != nil {
return err
}
Expand All @@ -101,6 +119,7 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionM
Body: bodyBytes,
OkStatuses: []int{http.StatusCreated},
Path: []string{"workers"},
ApiVersion: common.ApiVersionV2,
})
if err == nil {
log.Info(fmt.Sprintf("Worker '%s' deployed", manifest.Name))
Expand All @@ -116,6 +135,7 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionM
Body: bodyBytes,
OkStatuses: []int{http.StatusNoContent},
Path: []string{"workers"},
ApiVersion: common.ApiVersionV2,
})
if err == nil {
log.Info(fmt.Sprintf("Worker '%s' updated", manifest.Name))
Expand All @@ -124,7 +144,7 @@ func runDeployCommand(ctx *components.Context, manifest *model.Manifest, actionM
return err
}

func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, existingWorker *model.WorkerDetails) (*deployRequest, error) {
func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, actionMeta *model.ActionMetadata, version *model.Version, existingWorker *model.WorkerDetails) (*deployRequest, error) {
sourceCode, err := common.ReadSourceCode(manifest)
if err != nil {
return nil, err
Expand All @@ -136,21 +156,20 @@ func prepareDeployRequest(ctx *components.Context, manifest *model.Manifest, act
if !ctx.GetBoolFlagValue(model.FlagNoSecrets) {
secrets = common.PrepareSecretsUpdate(manifest, existingWorker)
}

payload := &deployRequest{
Key: manifest.Name,
Action: manifest.Action,
Action: actionMeta.Action,
Description: manifest.Description,
Enabled: manifest.Enabled,
Debug: manifest.Debug,
SourceCode: sourceCode,
Secrets: secrets,
ProjectKey: manifest.ProjectKey,
Version: version,
}

if actionMeta.MandatoryFilter {
payload.FilterCriteria = manifest.FilterCriteria
}

return payload, nil
}
Loading
Loading