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 package evidence using cli #21

Merged
merged 20 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from 19 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
2 changes: 1 addition & 1 deletion evidence/cli/command_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (ebc *evidenceBuildCommand) CreateEvidence(ctx *components.Context, serverD
ebc.ctx.GetStringFlagValue(predicate),
ebc.ctx.GetStringFlagValue(predicateType),
ebc.ctx.GetStringFlagValue(key),
ebc.ctx.GetStringFlagValue(keyId),
ebc.ctx.GetStringFlagValue(KeyAlias),
ebc.ctx.GetStringFlagValue(project),
ebc.ctx.GetStringFlagValue(buildName),
ebc.ctx.GetStringFlagValue(buildNumber))
Expand Down
3 changes: 3 additions & 0 deletions evidence/cli/command_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func createEvidence(ctx *components.Context) error {
command = NewEvidenceReleaseBundleCommand(ctx, execFunc)
case buildName:
command = NewEvidenceBuildCommand(ctx, execFunc)
case packageName:
command = NewEvidencePackageCommand(ctx, execFunc)
default:
return errors.New("unsupported subject")
}
Expand Down Expand Up @@ -109,6 +111,7 @@ func evidenceDetailsByFlags(ctx *components.Context) (*coreConfig.ServerDetails,
func platformToEvidenceUrls(rtDetails *coreConfig.ServerDetails) {
rtDetails.ArtifactoryUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "artifactory/"
rtDetails.EvidenceUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "evidence/"
rtDetails.MetadataUrl = utils.AddTrailingSlashIfNeeded(rtDetails.Url) + "metadata/"
}

func assertValueProvided(c *components.Context, fieldName string) error {
Expand Down
37 changes: 37 additions & 0 deletions evidence/cli/command_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,43 @@ func TestCreateEvidence_Context(t *testing.T) {
},
expectErr: false,
},
{
name: "ValidContext - Package",
flags: []components.Flag{
setDefaultValue(predicate, predicate),
setDefaultValue(predicateType, "InToto"),
setDefaultValue(key, "PGP"),
setDefaultValue(packageName, packageName),
setDefaultValue(packageVersion, packageVersion),
setDefaultValue(packageRepoName, packageRepoName),
setDefaultValue("url", "url"),
},
expectErr: false,
},
{
name: "InvalidContext - Missing package version",
flags: []components.Flag{
setDefaultValue(predicate, predicate),
setDefaultValue(predicateType, "InToto"),
setDefaultValue(key, "PGP"),
setDefaultValue(packageName, packageName),
setDefaultValue(packageRepoName, packageRepoName),
setDefaultValue("url", "url"),
},
expectErr: true,
},
{
name: "InvalidContext - Missing package repository key",
flags: []components.Flag{
setDefaultValue(predicate, predicate),
setDefaultValue(predicateType, "InToto"),
setDefaultValue(key, "PGP"),
setDefaultValue(packageName, packageName),
setDefaultValue(packageVersion, packageVersion),
setDefaultValue("url", "url"),
},
expectErr: true,
},
}

for _, tt := range tests {
Expand Down
2 changes: 1 addition & 1 deletion evidence/cli/command_custom.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ func (ecc *evidenceCustomCommand) CreateEvidence(_ *components.Context, serverDe
ecc.ctx.GetStringFlagValue(predicate),
ecc.ctx.GetStringFlagValue(predicateType),
ecc.ctx.GetStringFlagValue(key),
ecc.ctx.GetStringFlagValue(keyId),
ecc.ctx.GetStringFlagValue(KeyAlias),
ecc.ctx.GetStringFlagValue(subjectRepoPath),
ecc.ctx.GetStringFlagValue(subjectSha256))
return ecc.execute(createCmd)
Expand Down
48 changes: 48 additions & 0 deletions evidence/cli/command_package.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
)

type evidencePackageCommand struct {
ctx *components.Context
execute execCommandFunc
}

func NewEvidencePackageCommand(ctx *components.Context, execute execCommandFunc) EvidenceCommands {
return &evidencePackageCommand{
ctx: ctx,
execute: execute,
}
}

func (epc *evidencePackageCommand) CreateEvidence(ctx *components.Context, serverDetails *coreConfig.ServerDetails) error {
err := epc.validateEvidencePackageContext(ctx)
if err != nil {
return err
}

createCmd := evidence.NewCreateEvidencePackage(
serverDetails,
epc.ctx.GetStringFlagValue(predicate),
epc.ctx.GetStringFlagValue(predicateType),
epc.ctx.GetStringFlagValue(key),
epc.ctx.GetStringFlagValue(KeyAlias),
epc.ctx.GetStringFlagValue(packageName),
epc.ctx.GetStringFlagValue(packageVersion),
epc.ctx.GetStringFlagValue(packageRepoName))
return epc.execute(createCmd)
}

func (epc *evidencePackageCommand) validateEvidencePackageContext(ctx *components.Context) error {
if !ctx.IsFlagSet(packageVersion) || assertValueProvided(ctx, packageVersion) != nil {
return errorutils.CheckErrorf("'packageVersion' is a mandatory field for creating a Package evidence: --%s", packageVersion)

Choose a reason for hiding this comment

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

In this command, and also in the existing build/release-bundle commands: we should not log the internal names of the variables - having packageVersion or packageRepoName in the logs is confusing.

Instead, we should mention only those names, which a client is supposed to use, for example:
--%s is a mandatory parameter for creating a Package evidence

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}
if !ctx.IsFlagSet(packageRepoName) || assertValueProvided(ctx, packageRepoName) != nil {
return errorutils.CheckErrorf("'packageRepoName' is a mandatory field for creating a Package evidence: --%s", packageRepoName)
}
return nil
}
2 changes: 1 addition & 1 deletion evidence/cli/command_relesae_bundle.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func (erc *evidenceReleaseBundleCommand) CreateEvidence(ctx *components.Context,
erc.ctx.GetStringFlagValue(predicate),
erc.ctx.GetStringFlagValue(predicateType),
erc.ctx.GetStringFlagValue(key),
erc.ctx.GetStringFlagValue(keyId),
erc.ctx.GetStringFlagValue(KeyAlias),
erc.ctx.GetStringFlagValue(project),
erc.ctx.GetStringFlagValue(releaseBundle),
erc.ctx.GetStringFlagValue(releaseBundleVersion))
Expand Down
15 changes: 12 additions & 3 deletions evidence/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ const (
releaseBundleVersion = "release-bundle-version"
buildName = "build-name"
buildNumber = "build-number"
packageName = "package-name"
packageVersion = "package-version"
packageRepoName = "package-repo-name"

// Unique evidence flags
predicate = "predicate"
predicateType = "predicate-type"
subjectRepoPath = "subject-repo-path"
subjectSha256 = "subject-sha256"
key = "key"
keyId = "key-name"
KeyAlias = "key-alias"

Choose a reason for hiding this comment

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

No need to export this constant, can be keyAlias.

Copy link
Contributor Author

@osaidwtd osaidwtd Aug 12, 2024

Choose a reason for hiding this comment

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

done

)

// Flag keys mapped to their corresponding components.Flag definition.
Expand All @@ -48,13 +51,16 @@ var flagsMap = map[string]components.Flag{
releaseBundleVersion: components.NewStringFlag(releaseBundleVersion, "Release Bundle version.", func(f *components.StringFlag) { f.Mandatory = false }),
buildName: components.NewStringFlag(buildName, "Build name.", func(f *components.StringFlag) { f.Mandatory = false }),
buildNumber: components.NewStringFlag(buildNumber, "Build number.", func(f *components.StringFlag) { f.Mandatory = false }),
packageName: components.NewStringFlag(packageName, "Package name.", func(f *components.StringFlag) { f.Mandatory = false }),
packageVersion: components.NewStringFlag(packageVersion, "Package version.", func(f *components.StringFlag) { f.Mandatory = false }),
packageRepoName: components.NewStringFlag(packageRepoName, "Package repository Name.", 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 }),
subjectRepoPath: components.NewStringFlag(subjectRepoPath, "Full path to some subject' location.", func(f *components.StringFlag) { f.Mandatory = false }),
subjectSha256: components.NewStringFlag(subjectSha256, "Subject checksum sha256.", func(f *components.StringFlag) { f.Mandatory = false }),
key: components.NewStringFlag(key, "Path to a private key that will sign the DSSE. Supported keys: 'ecdsa','rsa' and 'ed25519'.", func(f *components.StringFlag) { f.Mandatory = true }),
keyId: components.NewStringFlag(keyId, "KeyId", func(f *components.StringFlag) { f.Mandatory = false }),
KeyAlias: components.NewStringFlag(KeyAlias, "Key alias", func(f *components.StringFlag) { f.Mandatory = false }),
}

var commandFlags = map[string][]string{
Expand All @@ -69,12 +75,15 @@ var commandFlags = map[string][]string{
releaseBundleVersion,
buildName,
buildNumber,
packageName,
packageVersion,
packageRepoName,
predicate,
predicateType,
subjectRepoPath,
subjectSha256,
key,
keyId,
KeyAlias,
},
}

Expand Down
1 change: 1 addition & 0 deletions evidence/cli/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ var subjectTypes = []string{
subjectRepoPath,
releaseBundle,
buildName,
packageName,
}
9 changes: 9 additions & 0 deletions evidence/create_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ func (c *createEvidenceBase) createArtifactoryClient() (artifactory.ArtifactoryS
return utils.CreateUploadServiceManager(c.serverDetails, 1, 0, 0, false, nil)
}

func (c *createEvidenceBase) getFileChecksum(path string, artifactoryClient artifactory.ArtifactoryServicesManager) (string, error) {
res, err := artifactoryClient.FileInfo(path)
if err != nil {
log.Warn(fmt.Sprintf("file path '%s' does not exist.", path))
return "", err
}
return res.Checksums.Sha256, nil
}

func createAndSignEnvelope(payloadJson []byte, key string, keyId string) (*dsse.Envelope, error) {
// Load private key from file if ec.key is not a path to a file then try to load it as a key
keyFile := []byte(key)
Expand Down
129 changes: 129 additions & 0 deletions evidence/create_package.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package evidence

import (
"encoding/json"
"fmt"
"github.com/jfrog/jfrog-cli-artifactory/evidence/model"
"github.com/jfrog/jfrog-cli-core/v2/artifactory/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/metadata"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
)

const leadArtifactQueryTemplate = `{
Copy link
Contributor

Choose a reason for hiding this comment

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

Why putting a const if you dont use it as const?
Also, it is used only in one place just copy it to line 124

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 tried to change it, but i think also is not bad to have it private const here since its too long and a bit confusing to have it directly inside the small method.

"query": "{versions(filter: {packageId: \"%s\", name: \"%s\", repositoriesIn: [{name: \"%s\"}]}) { edges { node { repos { name leadFilePath } } } } }"
}`

type createEvidencePackage struct {
createEvidenceBase
packageName string
packageVersion string
packageRepoName string
}

func NewCreateEvidencePackage(serverDetails *coreConfig.ServerDetails, predicateFilePath, predicateType, key, keyId, packageName,
packageVersion, packageRepoName string) Command {
return &createEvidencePackage{
createEvidenceBase: createEvidenceBase{
serverDetails: serverDetails,
predicateFilePath: predicateFilePath,
predicateType: predicateType,
key: key,
keyId: keyId,
},
packageName: packageName,
packageVersion: packageVersion,
packageRepoName: packageRepoName,
}
}

func (c *createEvidencePackage) CommandName() string {
return "create-package-evidence"
}

func (c *createEvidencePackage) ServerDetails() (*config.ServerDetails, error) {
return c.serverDetails, nil
}

func (c *createEvidencePackage) Run() error {
artifactoryClient, err := c.createArtifactoryClient()
if err != nil {
log.Error("failed to create Artifactory client", err)
return err
}
metadataClient, err := utils.CreateMetadataServiceManager(c.serverDetails, false)
if err != nil {
return err
}

packageType, err := c.getPackageType(artifactoryClient)
if err != nil {
return err
}

leadArtifact, err := c.getPackageVersionLeadArtifact(packageType, metadataClient)
if err != nil {
return err
}
leadArtifactPath := c.buildLeadArtifactPath(leadArtifact)
leadArtifactChecksum, err := c.getFileChecksum(leadArtifactPath, artifactoryClient)
if err != nil {
return err
}
envelope, err := c.createEnvelope(leadArtifactPath, leadArtifactChecksum)
if err != nil {
return err
}
err = c.uploadEvidence(envelope, leadArtifactPath)
if err != nil {
return err
}

return nil
}

func (c *createEvidencePackage) getPackageType(artifactoryClient artifactory.ArtifactoryServicesManager) (string, error) {
var request services.RepositoryDetails
err := artifactoryClient.GetRepository(c.packageRepoName, &request)
if err != nil {
return "", errorutils.CheckErrorf("No such package: %s/%s", c.packageRepoName, c.packageVersion)
}
return request.PackageType, nil
}

func (c *createEvidencePackage) getPackageVersionLeadArtifact(packageType string, metadataClient metadata.Manager) (string, error) {
body, err := metadataClient.GraphqlQuery(c.createQuery(packageType))
if err != nil {
return "", err
}

res := &model.GraphqlResponse{}
err = json.Unmarshal(body, res)
if err != nil {
return "", err
}
if len(res.Data.Versions.Edges) == 0 {
return "", errorutils.CheckErrorf("No such package: %s/%s", c.packageRepoName, c.packageVersion)
}

// Fetch the leadFilePath based on repoName
for _, repo := range res.Data.Versions.Edges[0].Node.Repos {
if repo.Name == c.packageRepoName {
return repo.LeadFilePath, nil
}
}
return "", errorutils.CheckErrorf("Can't find lead artifact of pacakge: %s/%s", c.packageRepoName, c.packageVersion)
}

func (c *createEvidencePackage) createQuery(packageType string) []byte {
packageId := packageType + "://" + c.packageName
return []byte(fmt.Sprintf(leadArtifactQueryTemplate, packageId, c.packageVersion, c.packageRepoName))
}

func (c *createEvidencePackage) buildLeadArtifactPath(leadArtifact string) string {
return fmt.Sprintf("%s/%s", c.packageRepoName, leadArtifact)
}
Loading
Loading