From b67f47b6200a6629d89f6efcad18baf880c74feb Mon Sep 17 00:00:00 2001 From: osaidw Date: Wed, 31 Jul 2024 16:55:12 +0300 Subject: [PATCH 1/2] Create Build Evidence. --- evidence/cli/command_build.go | 31 ++++++ evidence/cli/command_controller.go | 2 + evidence/cli/flags.go | 4 +- evidence/cli/mocks/command_mock.go | 49 ---------- evidence/cli/utils.go | 1 + evidence/create_base.go | 3 - evidence/create_build.go | 128 +++++++++++++++++++++++++ evidence/create_build_test.go | 89 +++++++++++++++++ evidence/create_release_bundle_test.go | 13 ++- evidence/utils/datex.go | 9 ++ 10 files changed, 271 insertions(+), 58 deletions(-) create mode 100644 evidence/cli/command_build.go delete mode 100644 evidence/cli/mocks/command_mock.go create mode 100644 evidence/create_build.go create mode 100644 evidence/create_build_test.go create mode 100644 evidence/utils/datex.go diff --git a/evidence/cli/command_build.go b/evidence/cli/command_build.go new file mode 100644 index 0000000..b7742f1 --- /dev/null +++ b/evidence/cli/command_build.go @@ -0,0 +1,31 @@ +package cli + +import ( + "github.com/jfrog/jfrog-cli-artifactory/evidence" + "github.com/jfrog/jfrog-cli-core/v2/plugins/components" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" +) + +type evidenceBuildCommand struct { + ctx *components.Context + execute execCommandFunc +} + +func NewEvidenceBuildCommand(ctx *components.Context, execute execCommandFunc) EvidenceCommands { + return &evidenceBuildCommand{ + ctx: ctx, + execute: execute, + } +} + +func (erc *evidenceBuildCommand) CreateEvidence(serverDetails *coreConfig.ServerDetails) error { + createCmd := evidence.NewCreateEvidenceBuild( + serverDetails, + erc.ctx.GetStringFlagValue(predicate), + erc.ctx.GetStringFlagValue(predicateType), + erc.ctx.GetStringFlagValue(key), + erc.ctx.GetStringFlagValue(keyId), + erc.ctx.GetStringFlagValue(project), + erc.ctx.GetStringFlagValue(build)) + return erc.execute(createCmd) +} diff --git a/evidence/cli/command_controller.go b/evidence/cli/command_controller.go index b9234ff..fee5a7b 100644 --- a/evidence/cli/command_controller.go +++ b/evidence/cli/command_controller.go @@ -49,6 +49,8 @@ func createEvidence(c *components.Context) error { command = NewEvidenceCustomCommand(c, execFunc) case releaseBundle: command = NewEvidenceReleaseBundleCommand(c, execFunc) + case build: + command = NewEvidenceBuildCommand(c, execFunc) default: return errors.New("unsupported subject") } diff --git a/evidence/cli/flags.go b/evidence/cli/flags.go index 82ff283..0cf036f 100644 --- a/evidence/cli/flags.go +++ b/evidence/cli/flags.go @@ -21,6 +21,7 @@ const ( // RLM flags keys releaseBundle = "release-bundle" + build = "build" // Unique evidence flags evidencePrefix = "evd-" @@ -42,6 +43,7 @@ var flagsMap = map[string]components.Flag{ project: components.NewStringFlag(project, "Project key associated with the created evidence.", func(f *components.StringFlag) { f.Mandatory = false }), releaseBundle: components.NewStringFlag(releaseBundle, "Release Bundle name and version. Format: :", func(f *components.StringFlag) { f.Mandatory = false }), + build: components.NewStringFlag(build, "Build name and number. Format: :", func(f *components.StringFlag) { f.Mandatory = false }), predicate: components.NewStringFlag(predicate, "Path to the predicate, arbitrary JSON.", func(f *components.StringFlag) { f.Mandatory = true }), predicateType: components.NewStringFlag(predicateType, "Type of the predicate.", func(f *components.StringFlag) { f.Mandatory = true }), @@ -52,7 +54,7 @@ var flagsMap = map[string]components.Flag{ var commandFlags = map[string][]string{ CreateEvidence: { - url, user, password, accessToken, ServerId, project, releaseBundle, predicate, predicateType, repoPath, key, keyId, + url, user, password, accessToken, ServerId, project, releaseBundle, build, predicate, predicateType, repoPath, key, keyId, }, } diff --git a/evidence/cli/mocks/command_mock.go b/evidence/cli/mocks/command_mock.go deleted file mode 100644 index 65aa509..0000000 --- a/evidence/cli/mocks/command_mock.go +++ /dev/null @@ -1,49 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: command.go - -// Package mock_cli is a generated GoMock package. -package mock_cli - -import ( - reflect "reflect" - - gomock "github.com/golang/mock/gomock" - config "github.com/jfrog/jfrog-cli-core/v2/utils/config" -) - -// MockEvidenceCommands is a mock of EvidenceCommands interface. -type MockEvidenceCommands struct { - ctrl *gomock.Controller - recorder *MockEvidenceCommandsMockRecorder -} - -// MockEvidenceCommandsMockRecorder is the mock recorder for MockEvidenceCommands. -type MockEvidenceCommandsMockRecorder struct { - mock *MockEvidenceCommands -} - -// NewMockEvidenceCommands creates a new mock instance. -func NewMockEvidenceCommands(ctrl *gomock.Controller) *MockEvidenceCommands { - mock := &MockEvidenceCommands{ctrl: ctrl} - mock.recorder = &MockEvidenceCommandsMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use. -func (m *MockEvidenceCommands) EXPECT() *MockEvidenceCommandsMockRecorder { - return m.recorder -} - -// CreateEvidence mocks base method. -func (m *MockEvidenceCommands) CreateEvidence(arg0 *config.ServerDetails) error { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "CreateEvidence", arg0) - ret0, _ := ret[0].(error) - return ret0 -} - -// CreateEvidence indicates an expected call of CreateEvidence. -func (mr *MockEvidenceCommandsMockRecorder) CreateEvidence(arg0 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateEvidence", reflect.TypeOf((*MockEvidenceCommands)(nil).CreateEvidence), arg0) -} diff --git a/evidence/cli/utils.go b/evidence/cli/utils.go index e90fa80..f1ce84c 100644 --- a/evidence/cli/utils.go +++ b/evidence/cli/utils.go @@ -11,4 +11,5 @@ func exec(command commands.Command) error { var subjectTypes = []string{ repoPath, releaseBundle, + build, } diff --git a/evidence/create_base.go b/evidence/create_base.go index 39bf4ce..c2c5910 100644 --- a/evidence/create_base.go +++ b/evidence/create_base.go @@ -54,7 +54,6 @@ func (c *createEvidenceBase) buildIntotoStatementJson(subject string) ([]byte, e artifactoryClient, err := c.createArtifactoryClient() if err != nil { - log.Error("failed to create Artifactory client", err) return nil, err } @@ -74,7 +73,6 @@ func (c *createEvidenceBase) buildIntotoStatementJson(subject string) ([]byte, e func (c *createEvidenceBase) uploadEvidence(envelope []byte, repoPath string) error { evidenceManager, err := utils.CreateEvidenceServiceManager(c.serverDetails, false) if err != nil { - log.Error("failed to create Evidence client", err) return err } @@ -84,7 +82,6 @@ func (c *createEvidenceBase) uploadEvidence(envelope []byte, repoPath string) er } body, err := evidenceManager.UploadEvidence(evidenceDetails) if err != nil { - log.Error("failed to upload evidence file", err) return err } diff --git a/evidence/create_build.go b/evidence/create_build.go new file mode 100644 index 0000000..aa8e9fc --- /dev/null +++ b/evidence/create_build.go @@ -0,0 +1,128 @@ +package evidence + +import ( + "fmt" + "github.com/jfrog/jfrog-cli-artifactory/evidence/utils" + "github.com/jfrog/jfrog-cli-core/v2/utils/config" + coreConfig "github.com/jfrog/jfrog-cli-core/v2/utils/config" + "github.com/jfrog/jfrog-client-go/artifactory" + "github.com/jfrog/jfrog-client-go/artifactory/services" + "github.com/jfrog/jfrog-client-go/utils/errorutils" + "github.com/jfrog/jfrog-client-go/utils/log" + "strings" +) + +type createEvidenceBuild struct { + createEvidenceBase + project string + build string +} + +func NewCreateEvidenceBuild(serverDetails *coreConfig.ServerDetails, predicateFilePath string, predicateType string, key string, keyId string, + project string, build string) Command { + return &createEvidenceBuild{ + createEvidenceBase: createEvidenceBase{ + serverDetails: serverDetails, + predicateFilePath: predicateFilePath, + predicateType: predicateType, + key: key, + keyId: keyId, + }, + project: project, + build: build, + } +} + +func (c *createEvidenceBuild) CommandName() string { + return "create-build-evidence" +} + +func (c *createEvidenceBuild) ServerDetails() (*config.ServerDetails, error) { + return c.serverDetails, nil +} + +func (c *createEvidenceBuild) Run() error { + artifactoryClient, err := c.createArtifactoryClient() + if err != nil { + log.Error("failed to create Artifactory client", err) + return err + } + subject, err := c.buildBuildInfoSubjectPath(artifactoryClient) + if err != nil { + return err + } + envelope, err := c.createEnvelope(subject) + if err != nil { + return err + } + err = c.uploadEvidence(envelope, subject) + if err != nil { + return err + } + + return nil +} + +func (c *createEvidenceBuild) buildBuildInfoSubjectPath(artifactoryClient artifactory.ArtifactoryServicesManager) (string, error) { + build := strings.Split(c.build, ":") + if len(build) != 2 { + return "", fmt.Errorf("invalid build format. expected format is :") + } + name := build[0] + number := build[1] + + timestamp, err := getBuildLatestTimestamp(name, number, c.project, artifactoryClient) + if err != nil { + return "", err + } + + repoKey := buildBuildInfoRepoKey(c.project) + buildInfoPath := buildBuildInfoPath(repoKey, name, number, timestamp) + buildInfoChecksum, err := getBuildInfoPathChecksum(buildInfoPath, artifactoryClient) + if err != nil { + return "", err + } + return buildInfoPath + "@" + buildInfoChecksum, nil +} + +func getBuildLatestTimestamp(name string, number string, project string, artifactoryClient artifactory.ArtifactoryServicesManager) (string, error) { + buildInfo := services.BuildInfoParams{ + BuildName: name, + BuildNumber: number, + ProjectKey: project, + } + res, b, err := artifactoryClient.GetBuildInfo(buildInfo) + if !b { + errorMessage := fmt.Sprintf("failed to find build, name:%s, number:%s, project: %s", name, number, project) + if err != nil { + return "", err + } + return "", errorutils.CheckErrorf(errorMessage) + } + timestamp, err := utils.ParseIsoTimestamp(res.BuildInfo.Started) + if err != nil { + return "", err + } + return fmt.Sprintf("%d", timestamp.UnixMilli()), nil +} + +func buildBuildInfoRepoKey(project string) string { + if project == "" || project == "default" { + return "artifactory-build-info" + } + return fmt.Sprintf("%s-build-info", project) +} + +func buildBuildInfoPath(repoKey string, name string, number string, timestamp string) string { + jsonFile := fmt.Sprintf("%s-%s.json", number, timestamp) + return fmt.Sprintf("%s/%s/%s", repoKey, name, jsonFile) +} + +func getBuildInfoPathChecksum(buildInfoPath string, artifactoryClient artifactory.ArtifactoryServicesManager) (string, error) { + res, err := artifactoryClient.FileInfo(buildInfoPath) + if err != nil { + log.Warn(fmt.Sprintf("build info json path '%s' does not exist.", buildInfoPath)) + return "", err + } + return res.Checksums.Sha256, nil +} diff --git a/evidence/create_build_test.go b/evidence/create_build_test.go new file mode 100644 index 0000000..3b39f77 --- /dev/null +++ b/evidence/create_build_test.go @@ -0,0 +1,89 @@ +package evidence + +import ( + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" + "github.com/stretchr/testify/assert" + "testing" +) + +import ( + buildinfo "github.com/jfrog/build-info-go/entities" + "github.com/jfrog/jfrog-client-go/artifactory" + "github.com/jfrog/jfrog-client-go/artifactory/services" +) + +type mockArtifactoryServicesManagerBuild struct { + artifactory.EmptyArtifactoryServicesManager +} + +func (m *mockArtifactoryServicesManagerBuild) FileInfo(relativePath string) (*utils.FileInfo, error) { + fi := &utils.FileInfo{ + Checksums: struct { + Sha1 string `json:"sha1,omitempty"` + Sha256 string `json:"sha256,omitempty"` + Md5 string `json:"md5,omitempty"` + }{ + Sha256: "dummy_sha256", + }, + } + return fi, nil +} + +func (m *mockArtifactoryServicesManagerBuild) GetBuildInfo(services.BuildInfoParams) (*buildinfo.PublishedBuildInfo, bool, error) { + buildInfo := &buildinfo.PublishedBuildInfo{ + BuildInfo: buildinfo.BuildInfo{ + Started: "2024-01-17T15:04:05.000-0700", + }, + } + return buildInfo, true, nil +} + +func TestBuildInfo(t *testing.T) { + tests := []struct { + name string + project string + build string + expectedPath string + expectError bool + }{ + { + name: "Valid build with project", + project: "myProject", + build: "buildName:1", + expectedPath: "myProject-build-info/buildName/1-1705529045000.json@dummy_sha256", + expectError: false, + }, + { + name: "Valid build default project", + project: "default", + build: "buildName:1", + expectedPath: "artifactory-build-info/buildName/1-1705529045000.json@dummy_sha256", + expectError: false, + }, + { + name: "Invalid build format", + project: "myProject", + build: "buildName-1", + expectedPath: "", + expectError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &createEvidenceBuild{ + project: tt.project, + build: tt.build, + } + aa := &mockArtifactoryServicesManagerBuild{} + path, err := c.buildBuildInfoSubjectPath(aa) + if tt.expectError { + assert.Error(t, err) + assert.Empty(t, path) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedPath, path) + } + }) + } +} diff --git a/evidence/create_release_bundle_test.go b/evidence/create_release_bundle_test.go index a98689c..5653665 100644 --- a/evidence/create_release_bundle_test.go +++ b/evidence/create_release_bundle_test.go @@ -1,17 +1,20 @@ package evidence import ( - "github.com/jfrog/jfrog-client-go/artifactory" - "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "github.com/stretchr/testify/assert" "testing" ) -type mockArtifactoryServicesManager struct { +import ( + "github.com/jfrog/jfrog-client-go/artifactory" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" +) + +type mockReleaseBundleArtifactoryServicesManager struct { artifactory.EmptyArtifactoryServicesManager } -func (m *mockArtifactoryServicesManager) FileInfo(relativePath string) (*utils.FileInfo, error) { +func (m *mockReleaseBundleArtifactoryServicesManager) FileInfo(relativePath string) (*utils.FileInfo, error) { fi := &utils.FileInfo{ Checksums: struct { Sha1 string `json:"sha1,omitempty"` @@ -75,7 +78,7 @@ func TestReleaseBundle(t *testing.T) { project: tt.project, releaseBundle: tt.releaseBundle, } - aa := &mockArtifactoryServicesManager{} + aa := &mockReleaseBundleArtifactoryServicesManager{} path, err := c.buildReleaseBundleSubjectPath(aa) if tt.expectError { assert.Error(t, err) diff --git a/evidence/utils/datex.go b/evidence/utils/datex.go new file mode 100644 index 0000000..27b27ad --- /dev/null +++ b/evidence/utils/datex.go @@ -0,0 +1,9 @@ +package utils + +import "time" + +const IsoDateTimeLayout = "2006-01-02T15:04:05.000-0700" + +func ParseIsoTimestamp(isoTimestamp string) (time.Time, error) { + return time.Parse(IsoDateTimeLayout, isoTimestamp) +} From d9c3eed00267d37a317c47bb9a645085897882b8 Mon Sep 17 00:00:00 2001 From: osaidw Date: Sun, 4 Aug 2024 10:09:52 +0300 Subject: [PATCH 2/2] Create Build Evidence. --- evidence/create_build.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evidence/create_build.go b/evidence/create_build.go index aa8e9fc..fd60cc2 100644 --- a/evidence/create_build.go +++ b/evidence/create_build.go @@ -18,8 +18,8 @@ type createEvidenceBuild struct { build string } -func NewCreateEvidenceBuild(serverDetails *coreConfig.ServerDetails, predicateFilePath string, predicateType string, key string, keyId string, - project string, build string) Command { +func NewCreateEvidenceBuild(serverDetails *coreConfig.ServerDetails, + predicateFilePath, predicateType, key, keyId, project, build string) Command { return &createEvidenceBuild{ createEvidenceBase: createEvidenceBase{ serverDetails: serverDetails, @@ -91,12 +91,12 @@ func getBuildLatestTimestamp(name string, number string, project string, artifac BuildNumber: number, ProjectKey: project, } - res, b, err := artifactoryClient.GetBuildInfo(buildInfo) - if !b { + res, ok, err := artifactoryClient.GetBuildInfo(buildInfo) + if err != nil { + return "", err + } + if !ok { errorMessage := fmt.Sprintf("failed to find build, name:%s, number:%s, project: %s", name, number, project) - if err != nil { - return "", err - } return "", errorutils.CheckErrorf(errorMessage) } timestamp, err := utils.ParseIsoTimestamp(res.BuildInfo.Started)