Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create Build Evidence. #16

Merged
merged 2 commits into from
Aug 4, 2024
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
31 changes: 31 additions & 0 deletions evidence/cli/command_build.go
Original file line number Diff line number Diff line change
@@ -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)
}
2 changes: 2 additions & 0 deletions evidence/cli/command_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
4 changes: 3 additions & 1 deletion evidence/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (

// RLM flags keys
releaseBundle = "release-bundle"
build = "build"

// Unique evidence flags
evidencePrefix = "evd-"
Expand All @@ -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: <name>:<version>", func(f *components.StringFlag) { f.Mandatory = false }),
build: components.NewStringFlag(build, "Build name and number. Format: <name>:<number>", 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 }),
Expand All @@ -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,
},
}

Expand Down
49 changes: 0 additions & 49 deletions evidence/cli/mocks/command_mock.go

This file was deleted.

1 change: 1 addition & 0 deletions evidence/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ func exec(command commands.Command) error {
var subjectTypes = []string{
repoPath,
releaseBundle,
build,
}
3 changes: 0 additions & 3 deletions evidence/create_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand Down
128 changes: 128 additions & 0 deletions evidence/create_build.go
Original file line number Diff line number Diff line change
@@ -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, predicateType, key, keyId, project, 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, ":")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, raise the question about the input parameters one more time.
This split means that neither Build Name nor Build Number may contain : (this is not the case).
Hence, it means that we do not support Builds with name/number having :.
Needs to be confirmed explicitly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I talked with Noam, there is a chance to change all other operations to be splitter to --name --version.
will take it in a separate Jira ticket.

if len(build) != 2 {
return "", fmt.Errorf("invalid build format. expected format is <name>:<number>")
}
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, 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)
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
}
89 changes: 89 additions & 0 deletions evidence/create_build_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
})
}
}
13 changes: 8 additions & 5 deletions evidence/create_release_bundle_test.go
Original file line number Diff line number Diff line change
@@ -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"`
Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading