Skip to content

Commit

Permalink
Added path mappings support for bundle creation and distribution
Browse files Browse the repository at this point in the history
  • Loading branch information
Yarden-Edery committed Nov 15, 2023
1 parent 21998b7 commit c8c4932
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 21 deletions.
6 changes: 6 additions & 0 deletions artifactory/services/utils/specutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ type Aql struct {
ItemsFind string `json:"items.find"`
}

type PathMapping struct {
Input string `json:"input"`
Output string `json:"output"`
}

type CommonParams struct {
Aql Aql
PathMapping PathMapping
Pattern string
Exclusions []string
Target string
Expand Down
12 changes: 9 additions & 3 deletions distribution/services/utils/distributionutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,23 @@ func NewReleaseBundleParams(name, version string) ReleaseBundleParams {

func CreateBundleBody(releaseBundleParams ReleaseBundleParams, dryRun bool) (*ReleaseBundleBody, error) {
var bundleQueries []BundleQuery
var pathMappings []rtUtils.PathMapping

// Create release bundle queries
for _, specFile := range releaseBundleParams.SpecFiles {
// Create path mapping
if specFile.GetSpecType() == rtUtils.AQL {
pathMappings = distribution.CreatePathMappings(specFile.PathMapping.Input, specFile.PathMapping.Output)
} else {
pathMappings = distribution.CreatePathMappingsFromPatternAndTarget(specFile.Pattern, specFile.Target)
}

// Create AQL
aql, err := createAql(specFile)
if err != nil {
return nil, err
}

// Create path mapping
pathMappings := distribution.CreatePathMappings(specFile.Pattern, specFile.Target)

// Create added properties
addedProps := createAddedProps(specFile)

Expand Down
12 changes: 7 additions & 5 deletions distribution/services/utils/releasebundlebody.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package utils

import "github.com/jfrog/jfrog-client-go/utils/distribution"
import (
"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
)

// REST body for create and update a release bundle
type ReleaseBundleBody struct {
Expand All @@ -22,10 +24,10 @@ type BundleSpec struct {
}

type BundleQuery struct {
QueryName string `json:"query_name,omitempty"`
Aql string `json:"aql,omitempty"`
PathMappings []distribution.PathMapping `json:"mappings,omitempty"`
AddedProps []AddedProps `json:"added_props,omitempty"`
QueryName string `json:"query_name,omitempty"`
Aql string `json:"aql,omitempty"`
PathMappings []utils.PathMapping `json:"mappings,omitempty"`
AddedProps []AddedProps `json:"added_props,omitempty"`
}

type AddedProps struct {
Expand Down
5 changes: 3 additions & 2 deletions lifecycle/services/distribute.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package services

import (
"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils/distribution"
Expand Down Expand Up @@ -62,7 +63,7 @@ func (dr *DistributeReleaseBundleService) createDistributeBody() ReleaseBundleDi
return ReleaseBundleDistributeBody{
ReleaseBundleDistributeV1Body: distribution.CreateDistributeV1Body(dr.DistributeParams, dr.DryRun, dr.AutoCreateRepo),
Modifications: Modifications{
PathMappings: distribution.CreatePathMappings(dr.Pattern, dr.Target),
PathMappings: distribution.CreatePathMappingsFromPatternAndTarget(dr.Pattern, dr.Target),
},
}
}
Expand All @@ -73,7 +74,7 @@ type ReleaseBundleDistributeBody struct {
}

type Modifications struct {
PathMappings []distribution.PathMapping `json:"mappings"`
PathMappings []utils.PathMapping `json:"mappings"`
}

type PathMapping struct {
Expand Down
105 changes: 103 additions & 2 deletions tests/distribution_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ func TestDistributionServices(t *testing.T) {
t.Run("createSignDistributeDelete", createSignDistributeDelete)
t.Run("createSignSyncDistributeDelete", createSignSyncDistributeDelete)
t.Run("createDistributeMapping", createDistributeMapping)
t.Run("createDistributeMappingPlaceholder", createDistributeMappingPlaceholder)
t.Run("createDistributeMappingFromPatternAndTarget", createDistributeMappingFromPatternAndTarget)
t.Run("createDistributeMappingWithPlaceholder", createDistributeMappingWithPlaceholder)
t.Run("createDistributeMappingFromPatternAndTargetWithPlaceholder", createDistributeMappingFromPatternAndTargetWithPlaceholder)

artifactoryCleanup(t)
deleteGpgKeys(t)
Expand Down Expand Up @@ -123,6 +125,9 @@ func createUpdate(t *testing.T) {
// Verify was not created.
getLocalBundle(t, bundleName, false)

// Redefine specFiles to create params from scratch
createBundleParams.SpecFiles[0] = &utils.CommonParams{Pattern: getRtTargetRepo() + "b.in"}

// Create unsigned release bundle
summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
if !assert.NoError(t, err) {
Expand All @@ -147,6 +152,9 @@ func createUpdate(t *testing.T) {
// Verify the release bundle was not updated.
assertCreatedLocalBundle(t, bundleName, createBundleParams)

// Redefine specFiles to create params from scratch
updateBundleParams.SpecFiles[0] = &utils.CommonParams{Pattern: getRtTargetRepo() + "test/a.in"}

summary, err = testsBundleUpdateService.UpdateReleaseBundle(updateBundleParams)
if !assert.NoError(t, err) {
return
Expand Down Expand Up @@ -355,6 +363,52 @@ func createSignSyncDistributeDelete(t *testing.T) {
func createDistributeMapping(t *testing.T) {
bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())

// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
createBundleParams.SpecFiles = []*utils.CommonParams{
{
Aql: utils.Aql{
ItemsFind: "{\"$or\":[{\"$and\":[{\"repo\":{\"$match\":\"*\"},\"path\":\".\",\"name\":{\"$match\":\"*\"}}]}]}",
},
PathMapping: utils.PathMapping{
Input: getRtTargetRepo() + "b.in",
Output: getRtTargetRepo() + "b.out",
},
},
}
createBundleParams.SignImmediately = true
summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
if !assert.NoError(t, err) {
return
}
defer deleteRemoteAndLocalBundle(t, bundleName)
assert.NotNil(t, summary)
verifyValidSha256(t, summary.GetSha256())

// Distribute release bundle
distributeBundleParams := distribution.NewDistributeReleaseBundleParams(bundleName, bundleVersion)
distributeBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
testsBundleDistributeService.Sync = true
// On distribution with path mapping, the target repository cannot be auto-created
testsBundleDistributeService.AutoCreateRepo = false
testsBundleDistributeService.DistributeParams = distributeBundleParams
err = testsBundleDistributeService.Distribute()
assert.NoError(t, err)

// Make sure <RtTargetRepo>/b.out does exist in Artifactory
searchParams := artifactoryServices.NewSearchParams()
searchParams.Pattern = getRtTargetRepo() + "b.out"
reader, err := testsSearchService.Search(searchParams)
assert.NoError(t, err)
readerCloseAndAssert(t, reader)
length, err := reader.Length()
assert.NoError(t, err)
assert.Equal(t, 1, length)
}

func createDistributeMappingFromPatternAndTarget(t *testing.T) {
bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())

// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "b.in", Target: getRtTargetRepo() + "b.out"}}
Expand Down Expand Up @@ -388,7 +442,54 @@ func createDistributeMapping(t *testing.T) {
assert.Equal(t, 1, length)
}

func createDistributeMappingPlaceholder(t *testing.T) {
func createDistributeMappingWithPlaceholder(t *testing.T) {
bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())

// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
createBundleParams.SpecFiles = []*utils.CommonParams{
{
Aql: utils.Aql{
ItemsFind: "{\"$or\":[{\"$and\":[{\"repo\":{\"$match\":\"*\"},\"path\":\".\",\"name\":{\"$match\":\"*\"}}]}]}",
},
PathMapping: utils.PathMapping{
Input: "(" + getRtTargetRepo() + ")" + "(.*).in",
Output: "$1$2.out",
},
},
}

createBundleParams.SignImmediately = true
summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
if !assert.NoError(t, err) {
return
}
defer deleteRemoteAndLocalBundle(t, bundleName)
assert.NotNil(t, summary)
verifyValidSha256(t, summary.GetSha256())

// Distribute release bundle
distributeBundleParams := distribution.NewDistributeReleaseBundleParams(bundleName, bundleVersion)
distributeBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
testsBundleDistributeService.Sync = true
// On distribution with path mapping, the target repository cannot be auto-created
testsBundleDistributeService.AutoCreateRepo = false
testsBundleDistributeService.DistributeParams = distributeBundleParams
err = testsBundleDistributeService.Distribute()
assert.NoError(t, err)

// Make sure <RtTargetRepo>/b.out does exist in Artifactory
searchParams := artifactoryServices.NewSearchParams()
searchParams.Pattern = getRtTargetRepo() + "b.out"
reader, err := testsSearchService.Search(searchParams)
assert.NoError(t, err)
readerCloseAndAssert(t, reader)
length, err := reader.Length()
assert.NoError(t, err)
assert.Equal(t, 1, length)
}

func createDistributeMappingFromPatternAndTargetWithPlaceholder(t *testing.T) {
bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())

// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
Expand Down
22 changes: 15 additions & 7 deletions utils/distribution/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ package distribution

import (
"github.com/jfrog/gofrog/stringutils"
"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"regexp"
)

var fileSpecCaptureGroup = regexp.MustCompile(`({\d})`)

// Create the path mapping from the input spec
func CreatePathMappings(pattern, target string) []PathMapping {
// Create the path mapping from the input spec which includes pattern and target
func CreatePathMappingsFromPatternAndTarget(pattern, target string) []utils.PathMapping {
if len(target) == 0 {
return []PathMapping{}
return []utils.PathMapping{}
}

// Convert the file spec pattern and target to match the path mapping input and output specifications, respectfully.
return []PathMapping{{
return []utils.PathMapping{{
// The file spec pattern is wildcard based. Convert it to Regex:
Input: stringutils.WildcardPatternToRegExp(pattern),
// The file spec target contain placeholders-style matching groups, like {1}.
Expand All @@ -26,7 +27,14 @@ func CreatePathMappings(pattern, target string) []PathMapping {
}}
}

type PathMapping struct {
Input string `json:"input"`
Output string `json:"output"`
// Create the path mapping from the input spec
func CreatePathMappings(input, output string) []utils.PathMapping {
if len(input) == 0 || len(output) == 0 {
return []utils.PathMapping{}
}

return []utils.PathMapping{{
Input: input,
Output: output,
}}
}
38 changes: 36 additions & 2 deletions utils/distribution/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"testing"
)

func TestCreatePathMappings(t *testing.T) {
func TestCreatePathMappingsFromPatternAndTarget(t *testing.T) {
tests := []struct {
specPattern string
specTarget string
Expand All @@ -27,7 +27,41 @@ func TestCreatePathMappings(t *testing.T) {
for _, test := range tests {
t.Run(test.specPattern, func(t *testing.T) {
specFile := &utils.CommonParams{Pattern: test.specPattern, Target: test.specTarget}
pathMappings := CreatePathMappings(specFile.Pattern, specFile.Target)
pathMappings := CreatePathMappingsFromPatternAndTarget(specFile.Pattern, specFile.Target)
if test.expectedMappingInput == "" {
assert.Empty(t, pathMappings)
return
}
assert.Len(t, pathMappings, 1)
actualPathMapping := pathMappings[0]
assert.Equal(t, test.expectedMappingInput, actualPathMapping.Input)
assert.Equal(t, test.expectedMappingOutput, actualPathMapping.Output)
})
}
}

func TestCreatePathMappings(t *testing.T) {
tests := []struct {
specInput string
specOutput string
expectedMappingInput string
expectedMappingOutput string
}{
{"", "", "", ""},
{"repo/path/file.in", "", "", ""},
{"", "repo/path/file.out", "", ""},
{"a/b/c", "a/b/x", "a/b/c", "a/b/x"},
}

for _, test := range tests {
t.Run(test.specInput, func(t *testing.T) {
specFile := &utils.CommonParams{
PathMapping: utils.PathMapping{
Input: test.specInput,
Output: test.specOutput,
},
}
pathMappings := CreatePathMappings(specFile.PathMapping.Input, specFile.PathMapping.Output)
if test.expectedMappingInput == "" {
assert.Empty(t, pathMappings)
return
Expand Down

0 comments on commit c8c4932

Please sign in to comment.