Skip to content

Commit

Permalink
Artifactory Release Lifecycle Management - Add Import bundle command (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
EyalDelarea committed Apr 8, 2024
1 parent 656f4dd commit 011060f
Show file tree
Hide file tree
Showing 12 changed files with 189 additions and 71 deletions.
36 changes: 36 additions & 0 deletions artifactory_test.go
Expand Up @@ -224,6 +224,22 @@ func TestArtifactorySimpleUploadSpecUsingConfig(t *testing.T) {
inttestutils.VerifyExistInArtifactory(tests.GetSimpleUploadExpectedRepo1(), searchFilePath, serverDetails, t)
cleanArtifactoryTest()
}
func TestReleaseBundleImportOnPrem(t *testing.T) {
// Cleanup
defer func() {
deleteReceivedReleaseBundle(t, "cli-tests", "2")
cleanArtifactoryTest()
}()
initArtifactoryTest(t, "")
initLifecycleCli()
// Sets the public key in Artifactory to accept the signed release bundle.
sendArtifactoryTrustedPublicKey(t, artHttpDetails)
// Import the release bundle
wd, err := os.Getwd()
assert.NoError(t, err)
testFilePath := filepath.Join(wd, "testdata", "lifecycle", "import", "cli-tests-2.zip")
assert.NoError(t, lcCli.Exec("rbi", testFilePath))
}

func TestArtifactoryUploadPathWithSpecialCharsAsNoRegex(t *testing.T) {
initArtifactoryTest(t, "")
Expand Down Expand Up @@ -5807,3 +5823,23 @@ func downloadModuleAndVerify() clientutils.ExecutionHandlerFunc {
return false, nil
}
}

func sendArtifactoryTrustedPublicKey(t *testing.T, artHttpDetails httputils.HttpClientDetails) {
// Send trusted public key to Artifactory
publicKeyPath := filepath.Join(tests.GetTestResourcesPath(), "lifecycle", "keys", "public.txt")
publicKey, err := os.ReadFile(publicKeyPath)
assert.NoError(t, err)
client, err := httpclient.ClientBuilder().Build()
assert.NoError(t, err)
requestBody := fmt.Sprintf(inttestutils.ArtifactoryGpgKeyCreatePattern, publicKey)
_, _, err = client.SendPost(*tests.JfrogUrl+"artifactory/api/security/keys/trusted", []byte(requestBody), artHttpDetails, "")
assert.NoError(t, err)
}

func deleteReceivedReleaseBundle(t *testing.T, bundleName, bundleVersion string) {
client, err := httpclient.ClientBuilder().Build()
assert.NoError(t, err)
deleteApi := path.Join("artifactory/api/release/bundles/", bundleName, bundleVersion)
_, _, err = client.SendDelete(*tests.JfrogUrl+deleteApi, []byte{}, artHttpDetails, "Deleting release bundle")
assert.NoError(t, err)
}
13 changes: 13 additions & 0 deletions docs/lifecycle/importbundle/help.go
@@ -0,0 +1,13 @@
package importbundle

var Usage = []string{"rbi [command options] <path to archive>"}

func GetDescription() string {
return "Import a local release bundle archive to Artifactory"
}

func GetArguments() string {
return ` path to archive
Path to the release bundle archive on the filesystem
`
}
6 changes: 3 additions & 3 deletions go.mod
Expand Up @@ -137,11 +137,11 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)

// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240327155856-8054e0dc39f5
replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240408074156-13680c04f22e

// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240327154209-77a304635e42
replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240408071430-62ee0279ac58

// replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.0.5-0.20240321101458-c86ea9b28dfd
replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.0.6-0.20240408061620-c9b84da33d5e

// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev

Expand Down
12 changes: 6 additions & 6 deletions go.sum
Expand Up @@ -135,12 +135,12 @@ github.com/jfrog/gofrog v1.6.3 h1:F7He0+75HcgCe6SGTSHLFCBDxiE2Ja0tekvvcktW6wc=
github.com/jfrog/gofrog v1.6.3/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg=
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
github.com/jfrog/jfrog-cli-core/v2 v2.50.0 h1:QmjSIktMKAbNH7OGY+eVZKx9husqgMANSI5kB8MlvlA=
github.com/jfrog/jfrog-cli-core/v2 v2.50.0/go.mod h1:95AsjwlMLNWU0v71/3dS715e1RAQfvPO47RRHz2xKh8=
github.com/jfrog/jfrog-cli-security v1.0.5 h1:tBJWiSTiBYpFB5LlHTUeBZ1wYdnrsG0ILNcHGKCiPgc=
github.com/jfrog/jfrog-cli-security v1.0.5/go.mod h1:ipG+b3qQqF0M8SFKkZ7DlMUsgMpdL+iIHc40j9JEs3E=
github.com/jfrog/jfrog-client-go v1.39.0 h1:GZ1qbpUDzYz8ZEycYicDkbVMN2H0VSCuz8mUNTyf7tc=
github.com/jfrog/jfrog-client-go v1.39.0/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240408074156-13680c04f22e h1:PjCzGWHyJqK4j1MP3osPDDAW6KBXMJlBypOxKtp/ZKo=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240408074156-13680c04f22e/go.mod h1:qXAP68g+DlyX2wk5znNbQdK2CcEHfOLOfYXPzdlnkxI=
github.com/jfrog/jfrog-cli-security v1.0.6-0.20240408061620-c9b84da33d5e h1:cB+UwVdZuds5fZ5BEcnvb6GqUvXFbo3oZa0PKdkylFc=
github.com/jfrog/jfrog-cli-security v1.0.6-0.20240408061620-c9b84da33d5e/go.mod h1:Bxir0QA3vDFCqnGP4GgPTPb0sWRaVONwm9+npDGx8kg=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240408071430-62ee0279ac58 h1:yyhOfECY3WGs6MsnJQWm/U7DYNIzxBiVlEwQ3RvqxwQ=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240408071430-62ee0279ac58/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k=
github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI=
github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down
4 changes: 2 additions & 2 deletions inttestutils/distribution.go
Expand Up @@ -26,7 +26,7 @@ import (

const (
distributionGpgKeyCreatePattern = `{"public_key":"%s","private_key":"%s"}`
artifactoryGpgKeyCreatePattern = `{"alias":"cli tests distribution key","public_key":"%s"}`
ArtifactoryGpgKeyCreatePattern = `{"alias":"cli tests distribution key","public_key":"%s"}`
)

type distributableDistributionStatus string
Expand Down Expand Up @@ -91,7 +91,7 @@ func SendGpgKeys(artHttpDetails httputils.HttpClientDetails, distHttpDetails htt
}

// Send public key to Artifactory
content = fmt.Sprintf(artifactoryGpgKeyCreatePattern, publicKey)
content = fmt.Sprintf(ArtifactoryGpgKeyCreatePattern, publicKey)
resp, body, err = client.SendPost(*tests.JfrogUrl+"artifactory/api/security/keys/trusted", []byte(content), artHttpDetails, "")
coreutils.ExitOnErr(err)
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusCreated, http.StatusConflict); err != nil {
Expand Down
44 changes: 44 additions & 0 deletions lifecycle/cli.go
Expand Up @@ -15,6 +15,7 @@ import (
rbDeleteRemote "github.com/jfrog/jfrog-cli/docs/lifecycle/deleteremote"
rbDistribute "github.com/jfrog/jfrog-cli/docs/lifecycle/distribute"
rbExport "github.com/jfrog/jfrog-cli/docs/lifecycle/export"
rbImport "github.com/jfrog/jfrog-cli/docs/lifecycle/importbundle"
rbPromote "github.com/jfrog/jfrog-cli/docs/lifecycle/promote"
"github.com/jfrog/jfrog-cli/utils/cliutils"
"github.com/jfrog/jfrog-cli/utils/distribution"
Expand Down Expand Up @@ -102,6 +103,18 @@ func GetCommands() []cli.Command {
Category: lcCategory,
Action: deleteRemote,
},
{
Name: "release-bundle-import",
Aliases: []string{"rbi"},
Flags: cliutils.GetCommandFlags(cliutils.ReleaseBundleImport),
Usage: rbImport.GetDescription(),
HelpName: coreCommon.CreateUsage("rbi", rbImport.GetDescription(), rbImport.Usage),
UsageText: rbImport.GetArguments(),
ArgsUsage: common.CreateEnvVars(),
BashComplete: coreCommon.CreateBashCompletionFunc(),
Category: lcCategory,
Action: releaseBundleImport,
},
})
}

Expand Down Expand Up @@ -279,6 +292,13 @@ func deleteRemote(c *cli.Context) error {
}

func export(c *cli.Context) error {
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
return err
}

if c.NArg() < 2 {
return cliutils.WrongNumberOfArgumentsHandler(c)
}
lcDetails, err := createLifecycleDetailsByFlags(c)
if err != nil {
return err
Expand All @@ -296,6 +316,30 @@ func export(c *cli.Context) error {
return commands.Exec(exportCmd)
}

func releaseBundleImport(c *cli.Context) error {
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
return err
}

if c.NArg() != 1 {
return cliutils.WrongNumberOfArgumentsHandler(c)
}

rtDetails, err := createLifecycleDetailsByFlags(c)
if err != nil {
return err
}
importCmd := lifecycle.NewReleaseBundleImportCommand()
if err != nil {
return err
}
importCmd.
SetServerDetails(rtDetails).
SetFilepath(c.Args().Get(0))

return commands.Exec(importCmd)
}

func validateDistributeCommand(c *cli.Context) error {
if err := distribution.ValidateReleaseBundleDistributeCmd(c); err != nil {
return err
Expand Down
12 changes: 12 additions & 0 deletions lifecycle_test.go
Expand Up @@ -159,6 +159,18 @@ func TestLifecycleFullFlow(t *testing.T) {

}

// Import bundles only work on onPerm platforms
func TestImportReleaseBundle(t *testing.T) {
cleanCallback := initLifecycleTest(t)
defer cleanCallback()
wd, err := os.Getwd()
assert.NoError(t, err)
testFilePath := filepath.Join(wd, "testdata", "lifecycle", "import", "cli-tests-2.zip")
// Verify not supported
err = lcCli.Exec("rbi", testFilePath)
assert.Error(t, err)
}

func deleteExportedReleaseBundle(t *testing.T, rbName string) {
assert.NoError(t, os.RemoveAll(rbName))
}
Expand Down
5 changes: 5 additions & 0 deletions testdata/import_bundle_repo_dependency.json
@@ -0,0 +1,5 @@
{
"key": "npm-prod",
"rclass": "local",
"packageType": "generic"
}
Binary file added testdata/lifecycle/import/cli-tests-2.zip
Binary file not shown.
4 changes: 4 additions & 0 deletions utils/cliutils/commandsflags.go
Expand Up @@ -124,6 +124,7 @@ const (
ReleaseBundleDeleteLocal = "release-bundle-delete-local"
ReleaseBundleDeleteRemote = "release-bundle-delete-remote"
ReleaseBundleExport = "release-bundle-export"
ReleaseBundleImport = "release-bundle-import"

// Access Token Create commands keys
AccessTokenCreate = "access-token-create"
Expand Down Expand Up @@ -2024,6 +2025,9 @@ var commandFlags = map[string][]string{
platformUrl, user, password, accessToken, serverId, lcPathMappingTarget, lcPathMappingPattern, Project,
downloadMinSplit, downloadSplitCount,
},
ReleaseBundleImport: {
user, password, accessToken, serverId, platformUrl,
},
// Mission Control's commands
McConfig: {
mcUrl, mcAccessToken, mcInteractive,
Expand Down
59 changes: 31 additions & 28 deletions utils/tests/consts.go
Expand Up @@ -13,6 +13,7 @@ const (
ArchiveEntriesDownload = "archive_entries_download_spec.json"
ArchiveEntriesUpload = "archive_entries_upload_spec.json"
ArtifactoryEndpoint = "artifactory/"
LifecycleEndpoint = "lifecycle/"
BuildAddDepsDoubleRemoteSpec = "build_add_deps_double_remote_spec.json"
BuildAddDepsDoubleSpec = "build_add_deps_double_spec.json"
BuildAddDepsRemoteSpec = "build_add_deps_simple_remote_spec.json"
Expand Down Expand Up @@ -161,38 +162,40 @@ const (
WinSimpleUploadSpec = "win_simple_upload_spec.json"
XrayEndpoint = "xray/"
YarnRemoteRepositoryConfig = "yarn_remote_repository_config.json"
ReleaseLifecycleImportDependencySpec = "import_bundle_repo_dependency.json"
)

var (
// Repositories
DistRepo1 = "cli-dist1"
DistRepo2 = "cli-dist2"
GoRepo = "cli-go"
GoRemoteRepo = "cli-go-remote"
GoVirtualRepo = "cli-go-virtual"
TerraformRepo = "cli-terraform"
GradleRepo = "cli-gradle"
MvnRemoteRepo = "cli-mvn-remote"
GradleRemoteRepo = "cli-gradle-remote"
MvnRepo1 = "cli-mvn1"
MvnRepo2 = "cli-mvn2"
NpmRepo = "cli-npm"
NpmRemoteRepo = "cli-npm-remote"
NugetRemoteRepo = "cli-nuget-remote"
YarnRemoteRepo = "cli-yarn-remote"
PypiRemoteRepo = "cli-pypi-remote"
PypiVirtualRepo = "cli-pypi-virtual"
PipenvRemoteRepo = "cli-pipenv-pypi-remote"
PipenvVirtualRepo = "cli-pipenv-pypi-virtual"
DockerLocalRepo = "cli-docker-local"
DockerLocalPromoteRepo = "cli-docker-local-promote"
DockerRemoteRepo = "cli-docker-remote"
DockerVirtualRepo = "cli-docker-virtual"
RtDebianRepo = "cli-debian"
RtLfsRepo = "cli-lfs"
RtRepo1 = "cli-rt1"
RtRepo2 = "cli-rt2"
RtVirtualRepo = "cli-rt-virtual"
DistRepo1 = "cli-dist1"
DistRepo2 = "cli-dist2"
GoRepo = "cli-go"
GoRemoteRepo = "cli-go-remote"
GoVirtualRepo = "cli-go-virtual"
TerraformRepo = "cli-terraform"
GradleRepo = "cli-gradle"
MvnRemoteRepo = "cli-mvn-remote"
GradleRemoteRepo = "cli-gradle-remote"
MvnRepo1 = "cli-mvn1"
MvnRepo2 = "cli-mvn2"
NpmRepo = "cli-npm"
NpmRemoteRepo = "cli-npm-remote"
NugetRemoteRepo = "cli-nuget-remote"
YarnRemoteRepo = "cli-yarn-remote"
PypiRemoteRepo = "cli-pypi-remote"
PypiVirtualRepo = "cli-pypi-virtual"
PipenvRemoteRepo = "cli-pipenv-pypi-remote"
PipenvVirtualRepo = "cli-pipenv-pypi-virtual"
DockerLocalRepo = "cli-docker-local"
DockerLocalPromoteRepo = "cli-docker-local-promote"
DockerRemoteRepo = "cli-docker-remote"
DockerVirtualRepo = "cli-docker-virtual"
RtDebianRepo = "cli-debian"
ReleaseLifecycleDependencyRepo = "npm-prod"
RtLfsRepo = "cli-lfs"
RtRepo1 = "cli-rt1"
RtRepo2 = "cli-rt2"
RtVirtualRepo = "cli-rt-virtual"
// Repositories that are assigned to an environment.
RtDevRepo = "cli-rt-dev"
RtProdRepo1 = "cli-rt-prod1"
Expand Down
65 changes: 33 additions & 32 deletions utils/tests/utils.go
Expand Up @@ -241,37 +241,38 @@ func GetBuildInfo(serverDetails *config.ServerDetails, buildName, buildNumber st
}

var reposConfigMap = map[*string]string{
&DistRepo1: DistributionRepoConfig1,
&DistRepo2: DistributionRepoConfig2,
&GoRepo: GoLocalRepositoryConfig,
&GoRemoteRepo: GoRemoteRepositoryConfig,
&GoVirtualRepo: GoVirtualRepositoryConfig,
&GradleRepo: GradleRepositoryConfig,
&MvnRepo1: MavenRepositoryConfig1,
&MvnRepo2: MavenRepositoryConfig2,
&MvnRemoteRepo: MavenRemoteRepositoryConfig,
&GradleRemoteRepo: GradleRemoteRepositoryConfig,
&NpmRepo: NpmLocalRepositoryConfig,
&NpmRemoteRepo: NpmRemoteRepositoryConfig,
&NugetRemoteRepo: NugetRemoteRepositoryConfig,
&YarnRemoteRepo: YarnRemoteRepositoryConfig,
&PypiRemoteRepo: PypiRemoteRepositoryConfig,
&PypiVirtualRepo: PypiVirtualRepositoryConfig,
&PipenvRemoteRepo: PipenvRemoteRepositoryConfig,
&PipenvVirtualRepo: PipenvVirtualRepositoryConfig,
&RtDebianRepo: DebianTestRepositoryConfig,
&RtLfsRepo: GitLfsTestRepositoryConfig,
&RtRepo1: Repo1RepositoryConfig,
&RtRepo2: Repo2RepositoryConfig,
&RtVirtualRepo: VirtualRepositoryConfig,
&TerraformRepo: TerraformLocalRepositoryConfig,
&DockerLocalRepo: DockerLocalRepositoryConfig,
&DockerLocalPromoteRepo: DockerLocalPromoteRepositoryConfig,
&DockerRemoteRepo: DockerRemoteRepositoryConfig,
&DockerVirtualRepo: DockerVirtualRepositoryConfig,
&RtDevRepo: DevRepoRepositoryConfig,
&RtProdRepo1: ProdRepo1RepositoryConfig,
&RtProdRepo2: ProdRepo2RepositoryConfig,
&DistRepo1: DistributionRepoConfig1,
&DistRepo2: DistributionRepoConfig2,
&GoRepo: GoLocalRepositoryConfig,
&GoRemoteRepo: GoRemoteRepositoryConfig,
&GoVirtualRepo: GoVirtualRepositoryConfig,
&GradleRepo: GradleRepositoryConfig,
&MvnRepo1: MavenRepositoryConfig1,
&MvnRepo2: MavenRepositoryConfig2,
&MvnRemoteRepo: MavenRemoteRepositoryConfig,
&GradleRemoteRepo: GradleRemoteRepositoryConfig,
&NpmRepo: NpmLocalRepositoryConfig,
&NpmRemoteRepo: NpmRemoteRepositoryConfig,
&NugetRemoteRepo: NugetRemoteRepositoryConfig,
&YarnRemoteRepo: YarnRemoteRepositoryConfig,
&PypiRemoteRepo: PypiRemoteRepositoryConfig,
&PypiVirtualRepo: PypiVirtualRepositoryConfig,
&PipenvRemoteRepo: PipenvRemoteRepositoryConfig,
&PipenvVirtualRepo: PipenvVirtualRepositoryConfig,
&RtDebianRepo: DebianTestRepositoryConfig,
&RtLfsRepo: GitLfsTestRepositoryConfig,
&RtRepo1: Repo1RepositoryConfig,
&RtRepo2: Repo2RepositoryConfig,
&RtVirtualRepo: VirtualRepositoryConfig,
&TerraformRepo: TerraformLocalRepositoryConfig,
&DockerLocalRepo: DockerLocalRepositoryConfig,
&DockerLocalPromoteRepo: DockerLocalPromoteRepositoryConfig,
&DockerRemoteRepo: DockerRemoteRepositoryConfig,
&DockerVirtualRepo: DockerVirtualRepositoryConfig,
&RtDevRepo: DevRepoRepositoryConfig,
&RtProdRepo1: ProdRepo1RepositoryConfig,
&RtProdRepo2: ProdRepo2RepositoryConfig,
&ReleaseLifecycleDependencyRepo: ReleaseLifecycleImportDependencySpec,
}

var CreatedNonVirtualRepositories map[*string]string
Expand Down Expand Up @@ -304,7 +305,7 @@ func getNeededBuildNames(buildNamesMap map[*bool][]*string) []string {
// Return local and remote repositories for the test suites, respectfully
func GetNonVirtualRepositories() map[*string]string {
nonVirtualReposMap := map[*bool][]*string{
TestArtifactory: {&RtRepo1, &RtRepo2, &RtLfsRepo, &RtDebianRepo, &TerraformRepo},
TestArtifactory: {&RtRepo1, &RtRepo2, &RtLfsRepo, &RtDebianRepo, &TerraformRepo, &ReleaseLifecycleDependencyRepo},
TestArtifactoryProject: {&RtRepo1, &RtRepo2, &RtLfsRepo, &RtDebianRepo},
TestDistribution: {&DistRepo1, &DistRepo2},
TestDocker: {&DockerLocalRepo, &DockerLocalPromoteRepo, &DockerRemoteRepo},
Expand Down

0 comments on commit 011060f

Please sign in to comment.