github.com/jfrog/jfrog-client-go@v1.40.2/tests/distribution_test.go (about)

     1  package tests
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	artifactoryServices "github.com/jfrog/jfrog-client-go/artifactory/services"
     7  	"github.com/jfrog/jfrog-client-go/artifactory/services/utils"
     8  	"github.com/jfrog/jfrog-client-go/distribution/services"
     9  	distributionServicesUtils "github.com/jfrog/jfrog-client-go/distribution/services/utils"
    10  	"github.com/jfrog/jfrog-client-go/http/httpclient"
    11  	"github.com/jfrog/jfrog-client-go/utils/distribution"
    12  	"github.com/jfrog/jfrog-client-go/utils/io/httputils"
    13  	"github.com/stretchr/testify/assert"
    14  	"net/http"
    15  	"os"
    16  	"path/filepath"
    17  	"strings"
    18  	"testing"
    19  	"time"
    20  )
    21  
    22  type distributableDistributionStatus string
    23  
    24  const (
    25  	// Release bundle created and open for changes:
    26  	open distributableDistributionStatus = "OPEN"
    27  	// Release bundle is signed, but not stored:
    28  	signed distributableDistributionStatus = "SIGNED"
    29  	// Release bundle is signed and stored, but not scanned by Xray:
    30  	stored distributableDistributionStatus = "STORED"
    31  	// Release bundle is signed, stored and scanned by Xray:
    32  	readyForDistribution distributableDistributionStatus = "READY_FOR_DISTRIBUTION"
    33  
    34  	gpgKeyAlias                    = "client tests distribution key"
    35  	artifactoryGpgKeyCreatePattern = `{"alias":"` + gpgKeyAlias + `","public_key":"%s"}`
    36  	bundleVersion                  = "10"
    37  )
    38  
    39  var httpClient *httpclient.HttpClient
    40  var distHttpDetails httputils.HttpClientDetails
    41  
    42  func TestDistributionServices(t *testing.T) {
    43  	initDistributionTest(t)
    44  	initClients(t)
    45  	sendGpgKeys(t)
    46  
    47  	// Local release bundle tests
    48  	t.Run("createDelete", createDelete)
    49  	t.Run("createUpdate", createUpdate)
    50  	t.Run("createWithProps", createWithProps)
    51  
    52  	// Remote release bundle tests
    53  	t.Run("createSignDistributeDelete", createSignDistributeDelete)
    54  	t.Run("createSignSyncDistributeDelete", createSignSyncDistributeDelete)
    55  	t.Run("createDistributeMapping", createDistributeMapping)
    56  	t.Run("createDistributeMappingFromPatternAndTarget", createDistributeMappingFromPatternAndTarget)
    57  	t.Run("createDistributeMappingWithPlaceholder", createDistributeMappingWithPlaceholder)
    58  	t.Run("createDistributeMappingFromPatternAndTargetWithPlaceholder", createDistributeMappingFromPatternAndTargetWithPlaceholder)
    59  
    60  	artifactoryCleanup(t)
    61  	deleteGpgKeys(t)
    62  }
    63  
    64  func initDistributionTest(t *testing.T) {
    65  	if !*TestDistribution {
    66  		t.Skip("Skipping distribution test. To run distribution test add the '-test.distribution=true' option.")
    67  	}
    68  }
    69  
    70  func initClients(t *testing.T) {
    71  	var err error
    72  	distHttpDetails = GetDistDetails().CreateHttpClientDetails()
    73  	httpClient, err = httpclient.ClientBuilder().Build()
    74  	assert.NoError(t, err)
    75  }
    76  
    77  func setupDistributionTest(t *testing.T, bundleName string) string {
    78  	artifactoryCleanup(t)
    79  	uploadDummyFile(t)
    80  	return bundleName
    81  }
    82  
    83  func initLocalDistributionTest(t *testing.T, bundleName string) string {
    84  	return setupDistributionTest(t, bundleName)
    85  }
    86  
    87  func initRemoteDistributionTest(t *testing.T, bundleName string) string {
    88  	testsBundleDistributeService.Sync = false
    89  	return setupDistributionTest(t, bundleName)
    90  }
    91  
    92  func createDelete(t *testing.T) {
    93  	bundleName := initLocalDistributionTest(t, "client-test-bundle-"+getRunId())
    94  
    95  	// Create signed release bundle
    96  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
    97  	createBundleParams.SignImmediately = true
    98  	createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "b.in"}}
    99  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   100  	if !assert.NoError(t, err) {
   101  		return
   102  	}
   103  	defer deleteLocalBundle(t, bundleName, true)
   104  	assert.NotNil(t, summary)
   105  	verifyValidSha256(t, summary.GetSha256())
   106  	distributionResponse := getLocalBundle(t, bundleName, true)
   107  	if assert.NotNil(t, distributionResponse) {
   108  		assertReleaseBundleSigned(t, distributionResponse.State)
   109  	}
   110  }
   111  
   112  func createUpdate(t *testing.T) {
   113  	bundleName := initLocalDistributionTest(t, "client-test-bundle-"+getRunId())
   114  
   115  	// Create release bundle params
   116  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   117  	createBundleParams.Description = "Release bundle description 1"
   118  	createBundleParams.ReleaseNotes = "Release notes 1"
   119  	createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "b.in"}}
   120  
   121  	// Test DryRun first
   122  	err := createDryRun(createBundleParams)
   123  	if !assert.NoError(t, err) {
   124  		return
   125  	}
   126  	// Verify was not created.
   127  	getLocalBundle(t, bundleName, false)
   128  
   129  	// Redefine specFiles to create params from scratch
   130  	createBundleParams.SpecFiles[0] = &utils.CommonParams{Pattern: getRtTargetRepo() + "b.in"}
   131  
   132  	// Create unsigned release bundle
   133  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   134  	if !assert.NoError(t, err) {
   135  		return
   136  	}
   137  	defer deleteLocalBundle(t, bundleName, true)
   138  	assert.Nil(t, summary)
   139  	distributionResponse := assertCreatedLocalBundle(t, bundleName, createBundleParams)
   140  	spec := distributionResponse.BundleSpec
   141  
   142  	// Create update release bundle params
   143  	updateBundleParams := services.NewUpdateReleaseBundleParams(bundleName, bundleVersion)
   144  	updateBundleParams.Description = "Release bundle description 2"
   145  	updateBundleParams.ReleaseNotes = "Release notes 2"
   146  	updateBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "test/a.in"}}
   147  	updateBundleParams.SignImmediately = false
   148  	// Test DryRun first
   149  	err = updateDryRun(updateBundleParams)
   150  	if !assert.NoError(t, err) {
   151  		return
   152  	}
   153  	// Verify the release bundle was not updated.
   154  	assertCreatedLocalBundle(t, bundleName, createBundleParams)
   155  
   156  	// Redefine specFiles to create params from scratch
   157  	updateBundleParams.SpecFiles[0] = &utils.CommonParams{Pattern: getRtTargetRepo() + "test/a.in"}
   158  
   159  	summary, err = testsBundleUpdateService.UpdateReleaseBundle(updateBundleParams)
   160  	if !assert.NoError(t, err) {
   161  		return
   162  	}
   163  	assert.Nil(t, summary)
   164  	distributionResponse = getLocalBundle(t, bundleName, true)
   165  	assert.Equal(t, open, distributionResponse.State)
   166  	assert.Equal(t, updateBundleParams.Description, distributionResponse.Description)
   167  	assert.Equal(t, updateBundleParams.ReleaseNotes, distributionResponse.ReleaseNotes.Content)
   168  	assert.NotEqual(t, spec, distributionResponse.BundleSpec)
   169  }
   170  
   171  func assertCreatedLocalBundle(t *testing.T, bundleName string, createBundleParams services.CreateReleaseBundleParams) *distributableResponse {
   172  	distributionResponse := getLocalBundle(t, bundleName, true)
   173  	assert.Equal(t, open, distributionResponse.State)
   174  	assert.Equal(t, createBundleParams.Description, distributionResponse.Description)
   175  	assert.Equal(t, createBundleParams.ReleaseNotes, distributionResponse.ReleaseNotes.Content)
   176  	return distributionResponse
   177  }
   178  
   179  func createDryRun(createBundleParams services.CreateReleaseBundleParams) error {
   180  	defer setServicesToDryRunFalse()
   181  	testsBundleCreateService.DryRun = true
   182  	_, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   183  	return err
   184  }
   185  
   186  func updateDryRun(updateBundleParams services.UpdateReleaseBundleParams) error {
   187  	defer setServicesToDryRunFalse()
   188  	testsBundleUpdateService.DryRun = true
   189  	_, err := testsBundleUpdateService.UpdateReleaseBundle(updateBundleParams)
   190  	return err
   191  }
   192  
   193  func distributeDryRun(distributionParams distribution.DistributionParams) error {
   194  	defer setServicesToDryRunFalse()
   195  	testsBundleDistributeService.DryRun = true
   196  	testsBundleDistributeService.AutoCreateRepo = true
   197  	testsBundleDistributeService.DistributeParams = distributionParams
   198  	return testsBundleDistributeService.Distribute()
   199  }
   200  
   201  func setServicesToDryRunFalse() {
   202  	testsBundleCreateService.DryRun = false
   203  	testsBundleUpdateService.DryRun = false
   204  	testsBundleDistributeService.DryRun = false
   205  }
   206  
   207  func createWithProps(t *testing.T) {
   208  	bundleName := initLocalDistributionTest(t, "client-test-bundle-"+getRunId())
   209  
   210  	// Create release bundle with properties
   211  	targetProps, err := utils.ParseProperties("key1=value1;key2=value2,value3")
   212  	assert.NoError(t, err)
   213  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   214  	createBundleParams.SpecFiles = []*utils.CommonParams{{
   215  		Pattern:     getRtTargetRepo() + "b.in",
   216  		TargetProps: targetProps,
   217  	}}
   218  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   219  	if !assert.NoError(t, err) {
   220  		return
   221  	}
   222  	defer deleteLocalBundle(t, bundleName, true)
   223  	assert.Nil(t, summary)
   224  
   225  	// Check results
   226  	distributionResponse := getLocalBundle(t, bundleName, true)
   227  	addedProps := distributionResponse.BundleSpec.Queries[0].AddedProps
   228  	assert.Len(t, addedProps, 2)
   229  
   230  	// Populate prop1Values and prop2Values
   231  	var prop1Values []string
   232  	var prop2Values []string
   233  	switch addedProps[0].Key {
   234  	case "key1":
   235  		assert.Equal(t, "key2", addedProps[1].Key)
   236  		prop1Values = addedProps[0].Values
   237  		prop2Values = addedProps[1].Values
   238  	case "key2":
   239  		assert.Equal(t, "key1", addedProps[1].Key)
   240  		prop1Values = addedProps[1].Values
   241  		prop2Values = addedProps[0].Values
   242  	default:
   243  		assert.Fail(t, "Unexpected key", addedProps[0].Key)
   244  	}
   245  
   246  	// Check prop1Values and prop2Values
   247  	assert.Len(t, prop1Values, 1)
   248  	assert.Len(t, prop2Values, 2)
   249  	if len(prop1Values) == 1 {
   250  		assert.Equal(t, "value1", prop1Values[0])
   251  	}
   252  	if len(prop2Values) == 2 {
   253  		assert.Equal(t, "value2", prop2Values[0])
   254  		assert.Equal(t, "value3", prop2Values[1])
   255  	}
   256  }
   257  
   258  func createSignDistributeDelete(t *testing.T) {
   259  	bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())
   260  
   261  	// Create unsigned release bundle
   262  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   263  	createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "b.in"}}
   264  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   265  	if !assert.NoError(t, err) {
   266  		return
   267  	}
   268  	defer deleteRemoteAndLocalBundle(t, bundleName)
   269  	assert.Nil(t, summary)
   270  	distributionResponse := getLocalBundle(t, bundleName, true)
   271  	assert.Equal(t, open, distributionResponse.State)
   272  
   273  	// Sign release bundle
   274  	signBundleParams := services.NewSignBundleParams(bundleName, bundleVersion)
   275  	summary, err = testsBundleSignService.SignReleaseBundle(signBundleParams)
   276  	if !assert.NoError(t, err) {
   277  		return
   278  	}
   279  	assert.NotNil(t, summary)
   280  	verifyValidSha256(t, summary.GetSha256())
   281  	distributionResponse = getLocalBundle(t, bundleName, true)
   282  	assertReleaseBundleSigned(t, distributionResponse.State)
   283  
   284  	// Create distribute params.
   285  	distributeBundleParams := distribution.NewDistributeReleaseBundleParams(bundleName, bundleVersion)
   286  	distributeBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
   287  
   288  	// Create response params.
   289  	distributionStatusParams := services.DistributionStatusParams{
   290  		Name:    bundleName,
   291  		Version: bundleVersion,
   292  	}
   293  
   294  	// Test DryRun first.
   295  	err = distributeDryRun(distributeBundleParams)
   296  	if !assert.NoError(t, err) {
   297  		return
   298  	}
   299  	// Assert release bundle not in distribution yet.
   300  	response, err := testsBundleDistributionStatusService.GetStatus(distributionStatusParams)
   301  	assert.NoError(t, err)
   302  	assert.Len(t, *response, 0)
   303  
   304  	// Distribute release bundle
   305  	testsBundleDistributeService.AutoCreateRepo = true
   306  	testsBundleDistributeService.DistributeParams = distributeBundleParams
   307  	err = testsBundleDistributeService.Distribute()
   308  	assert.NoError(t, err)
   309  	waitForDistribution(t, bundleName)
   310  
   311  	// Assert release bundle in "completed" status
   312  	response, err = testsBundleDistributionStatusService.GetStatus(distributionStatusParams)
   313  	if assert.NoError(t, err) && assert.NotEmpty(t, *response) {
   314  		assert.Equal(t, distribution.Completed, (*response)[0].Status)
   315  	}
   316  }
   317  
   318  func createSignSyncDistributeDelete(t *testing.T) {
   319  	bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())
   320  
   321  	// Create unsigned release bundle
   322  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   323  	createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "b.in"}}
   324  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   325  	if !assert.NoError(t, err) {
   326  		return
   327  	}
   328  	defer deleteRemoteAndLocalBundle(t, bundleName)
   329  	assert.Nil(t, summary)
   330  	distributionResponse := getLocalBundle(t, bundleName, true)
   331  	assert.Equal(t, open, distributionResponse.State)
   332  
   333  	// Sign release bundle
   334  	signBundleParams := services.NewSignBundleParams(bundleName, bundleVersion)
   335  	summary, err = testsBundleSignService.SignReleaseBundle(signBundleParams)
   336  	if !assert.NoError(t, err) {
   337  		return
   338  	}
   339  	assert.NotNil(t, summary)
   340  	verifyValidSha256(t, summary.GetSha256())
   341  	distributionResponse = getLocalBundle(t, bundleName, true)
   342  	assertReleaseBundleSigned(t, distributionResponse.State)
   343  
   344  	// Distribute release bundle
   345  	distributeBundleParams := distribution.NewDistributeReleaseBundleParams(bundleName, bundleVersion)
   346  	distributeBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
   347  	testsBundleDistributeService.Sync = true
   348  	testsBundleDistributeService.AutoCreateRepo = true
   349  	testsBundleDistributeService.DistributeParams = distributeBundleParams
   350  	err = testsBundleDistributeService.Distribute()
   351  	assert.NoError(t, err)
   352  
   353  	// Assert release bundle in "completed" status
   354  	distributionStatusParams := services.DistributionStatusParams{
   355  		Name:    bundleName,
   356  		Version: bundleVersion,
   357  	}
   358  	response, err := testsBundleDistributionStatusService.GetStatus(distributionStatusParams)
   359  	if assert.NoError(t, err) && assert.NotEmpty(t, *response) {
   360  		assert.Equal(t, distribution.Completed, (*response)[0].Status)
   361  	}
   362  }
   363  
   364  func createDistributeMapping(t *testing.T) {
   365  	bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())
   366  
   367  	// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
   368  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   369  	createBundleParams.SpecFiles = []*utils.CommonParams{
   370  		{
   371  			Aql: utils.Aql{
   372  				ItemsFind: "{\"$or\":[{\"$and\":[{\"repo\":{\"$match\":\"" + strings.TrimSuffix(getRtTargetRepo(), "/") + "\"},\"name\":{\"$match\":\"b.in\"}}]}]}",
   373  			},
   374  			PathMapping: utils.PathMapping{
   375  				Input:  getRtTargetRepo() + "b.in",
   376  				Output: getRtTargetRepo() + "b.out",
   377  			},
   378  		},
   379  	}
   380  	createBundleParams.SignImmediately = true
   381  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   382  	assert.NoError(t, err)
   383  
   384  	defer deleteRemoteAndLocalBundle(t, bundleName)
   385  	assert.NotNil(t, summary)
   386  	verifyValidSha256(t, summary.GetSha256())
   387  
   388  	// Distribute release bundle
   389  	distributeBundleParams := distribution.NewDistributeReleaseBundleParams(bundleName, bundleVersion)
   390  	distributeBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
   391  	testsBundleDistributeService.Sync = true
   392  	// On distribution with path mapping, the target repository cannot be auto-created
   393  	testsBundleDistributeService.AutoCreateRepo = false
   394  	testsBundleDistributeService.DistributeParams = distributeBundleParams
   395  	err = testsBundleDistributeService.Distribute()
   396  	assert.NoError(t, err)
   397  
   398  	// Distribute release bundle
   399  	assertReleaseBundleDistribution(t, bundleName)
   400  
   401  	// Make sure <RtTargetRepo>/b.out does exist in Artifactory
   402  	assertFileExistsInArtifactory(t, getRtTargetRepo()+"b.out")
   403  }
   404  
   405  func createDistributeMappingFromPatternAndTarget(t *testing.T) {
   406  	bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())
   407  
   408  	// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
   409  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   410  	createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: getRtTargetRepo() + "b.in", Target: getRtTargetRepo() + "b.out"}}
   411  	createBundleParams.SignImmediately = true
   412  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   413  	assert.NoError(t, err)
   414  
   415  	defer deleteRemoteAndLocalBundle(t, bundleName)
   416  	assert.NotNil(t, summary)
   417  	verifyValidSha256(t, summary.GetSha256())
   418  
   419  	// Distribute release bundle
   420  	assertReleaseBundleDistribution(t, bundleName)
   421  
   422  	// Make sure <RtTargetRepo>/b.out does exist in Artifactory
   423  	assertFileExistsInArtifactory(t, getRtTargetRepo()+"b.out")
   424  }
   425  
   426  func createDistributeMappingWithPlaceholder(t *testing.T) {
   427  	bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())
   428  
   429  	// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
   430  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   431  	createBundleParams.SpecFiles = []*utils.CommonParams{
   432  		{
   433  			Aql: utils.Aql{
   434  				ItemsFind: "{\"$or\":[{\"$and\":[{\"repo\":{\"$match\":\"" + strings.TrimSuffix(getRtTargetRepo(), "/") + "\"},\"name\":{\"$match\":\"*.in\"}}]}]}",
   435  			},
   436  			PathMapping: utils.PathMapping{
   437  				Input:  "(" + getRtTargetRepo() + ")" + "(.*).in",
   438  				Output: "$1$2.out",
   439  			},
   440  		},
   441  	}
   442  
   443  	createBundleParams.SignImmediately = true
   444  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   445  	assert.NoError(t, err)
   446  
   447  	defer deleteRemoteAndLocalBundle(t, bundleName)
   448  	assert.NotNil(t, summary)
   449  	verifyValidSha256(t, summary.GetSha256())
   450  
   451  	// Distribute release bundle
   452  	assertReleaseBundleDistribution(t, bundleName)
   453  
   454  	// Make sure <RtTargetRepo>/b.out does exist in Artifactory
   455  	assertFileExistsInArtifactory(t, getRtTargetRepo()+"b.out")
   456  }
   457  
   458  func createDistributeMappingFromPatternAndTargetWithPlaceholder(t *testing.T) {
   459  	bundleName := initRemoteDistributionTest(t, "client-test-bundle-"+getRunId())
   460  
   461  	// Create release bundle with path mapping from <RtTargetRepo>/b.in to <RtTargetRepo>/b.out
   462  	createBundleParams := services.NewCreateReleaseBundleParams(bundleName, bundleVersion)
   463  	createBundleParams.SpecFiles = []*utils.CommonParams{{Pattern: "(" + getRtTargetRepo() + ")" + "(*).in", Target: "{1}{2}.out"}}
   464  	createBundleParams.SignImmediately = true
   465  	summary, err := testsBundleCreateService.CreateReleaseBundle(createBundleParams)
   466  	assert.NoError(t, err)
   467  
   468  	defer deleteRemoteAndLocalBundle(t, bundleName)
   469  	assert.NotNil(t, summary)
   470  	verifyValidSha256(t, summary.GetSha256())
   471  
   472  	// Distribute release bundle
   473  	assertReleaseBundleDistribution(t, bundleName)
   474  
   475  	// Make sure <RtTargetRepo>/b.out does exist in Artifactory
   476  	assertFileExistsInArtifactory(t, getRtTargetRepo()+"b.out")
   477  }
   478  
   479  // Send GPG keys to Distribution and Artifactory to allow signing of release bundles
   480  func sendGpgKeys(t *testing.T) {
   481  	// Read gpg public and private key
   482  	publicKey, err := os.ReadFile(filepath.Join(getTestDataPath(), "public.key"))
   483  	assert.NoError(t, err)
   484  	privateKey, err := os.ReadFile(filepath.Join(getTestDataPath(), "private.key"))
   485  	assert.NoError(t, err)
   486  
   487  	err = testsBundleSetSigningKeyService.SetSigningKey(services.NewSetSigningKeyParams(string(publicKey), string(privateKey)))
   488  	assert.NoError(t, err)
   489  
   490  	// Send public key to Artifactory
   491  	content := fmt.Sprintf(artifactoryGpgKeyCreatePattern, publicKey)
   492  	resp, body, err := httpClient.SendPost(GetRtDetails().GetUrl()+"api/security/keys/trusted", []byte(content), distHttpDetails, "")
   493  	assert.NoError(t, err)
   494  	if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusConflict {
   495  		t.Error(resp.Status)
   496  		t.Error(string(body))
   497  	}
   498  }
   499  
   500  // Delete GPG key from Artifactory to clean up the test environment
   501  func deleteGpgKeys(t *testing.T) {
   502  	// Delete public key from Artifactory
   503  	gpgKeyId := getGpgKeyId(t)
   504  	if gpgKeyId == "" {
   505  		return
   506  	}
   507  	resp, body, err := httpClient.SendDelete(GetRtDetails().GetUrl()+"api/security/keys/trusted/"+gpgKeyId, nil, distHttpDetails, "")
   508  	assert.NoError(t, err)
   509  	if resp.StatusCode != http.StatusNoContent {
   510  		t.Error(resp.Status)
   511  		t.Error(string(body))
   512  	}
   513  }
   514  
   515  // Get GPG key ID created in the tests
   516  func getGpgKeyId(t *testing.T) string {
   517  	resp, body, _, err := httpClient.SendGet(GetRtDetails().GetUrl()+"api/security/keys/trusted", true, distHttpDetails, "")
   518  	assert.NoError(t, err)
   519  	if resp.StatusCode != http.StatusOK {
   520  		t.Error(resp.Status)
   521  		t.Error(string(body))
   522  		return ""
   523  	}
   524  	responses := &gpgKeysResponse{}
   525  	err = json.Unmarshal(body, &responses)
   526  	assert.NoError(t, err)
   527  	for _, gpgKeyResponse := range responses.Keys {
   528  		if gpgKeyResponse.Alias == gpgKeyAlias {
   529  			return gpgKeyResponse.Kid
   530  		}
   531  	}
   532  	return ""
   533  }
   534  
   535  func assertReleaseBundleSigned(t *testing.T, status distributableDistributionStatus) {
   536  	assert.Contains(t, []distributableDistributionStatus{signed, stored, readyForDistribution}, status)
   537  }
   538  
   539  // Wait for distribution of a release bundle
   540  func waitForDistribution(t *testing.T, bundleName string) {
   541  	distributionStatusParams := services.DistributionStatusParams{
   542  		Name:    bundleName,
   543  		Version: bundleVersion,
   544  	}
   545  	for i := 0; i < 120; i++ {
   546  		response, err := testsBundleDistributionStatusService.GetStatus(distributionStatusParams)
   547  		if assert.NoError(t, err) {
   548  			assert.Len(t, *response, 1)
   549  
   550  			switch (*response)[0].Status {
   551  			case distribution.Completed:
   552  				return
   553  			case distribution.Failed:
   554  				t.Error("Distribution failed for " + bundleName + "/" + bundleVersion)
   555  				return
   556  			case distribution.InProgress, distribution.NotDistributed:
   557  				// Wait
   558  			}
   559  			t.Log("Waiting for " + bundleName + "/" + bundleVersion + "...")
   560  			time.Sleep(time.Second)
   561  		}
   562  	}
   563  	t.Error("Timeout for release bundle distribution " + bundleName + "/" + bundleVersion)
   564  }
   565  
   566  type distributableResponse struct {
   567  	distributionServicesUtils.ReleaseBundleBody
   568  	Name    string                          `json:"name,omitempty"`
   569  	Version string                          `json:"version,omitempty"`
   570  	State   distributableDistributionStatus `json:"state,omitempty"`
   571  }
   572  
   573  type gpgKeysResponse struct {
   574  	Keys []gpgKeyResponse `json:"keys,omitempty"`
   575  }
   576  
   577  type gpgKeyResponse struct {
   578  	Kid   string `json:"kid,omitempty"`
   579  	Alias string `json:"alias,omitempty"`
   580  }
   581  
   582  func getLocalBundle(t *testing.T, bundleName string, expectExist bool) *distributableResponse {
   583  	resp, body, _, err := httpClient.SendGet(GetDistDetails().GetUrl()+"api/v1/release_bundle/"+bundleName+"/"+bundleVersion, true, distHttpDetails, "")
   584  	assert.NoError(t, err)
   585  	if !expectExist {
   586  		assert.Equal(t, http.StatusNotFound, resp.StatusCode)
   587  		return nil
   588  	}
   589  	if resp.StatusCode != http.StatusOK {
   590  		t.Error(resp.Status)
   591  		t.Error(string(body))
   592  		return nil
   593  	}
   594  	response := &distributableResponse{}
   595  	err = json.Unmarshal(body, &response)
   596  	assert.NoError(t, err)
   597  	return response
   598  }
   599  
   600  func deleteLocalBundle(t *testing.T, bundleName string, assertDeletion bool) {
   601  	deleteLocalBundleParams := services.NewDeleteReleaseBundleParams(bundleName, bundleVersion)
   602  	testsBundleDeleteLocalService.Sync = true
   603  	err := testsBundleDeleteLocalService.DeleteDistribution(deleteLocalBundleParams)
   604  	if !assertDeletion {
   605  		return
   606  	}
   607  	assert.NoError(t, err)
   608  	distributionResponse := getLocalBundle(t, bundleName, false)
   609  	assert.Nil(t, distributionResponse)
   610  }
   611  
   612  func deleteRemoteAndLocalBundle(t *testing.T, bundleName string) {
   613  	deleteBundleParams := services.NewDeleteReleaseBundleParams(bundleName, bundleVersion)
   614  	// Delete also local release bundle
   615  	deleteBundleParams.DeleteFromDistribution = true
   616  	deleteBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
   617  	deleteBundleParams.Sync = true
   618  	err := testsBundleDeleteRemoteService.DeleteDistribution(deleteBundleParams)
   619  	artifactoryCleanup(t)
   620  	assert.NoError(t, err)
   621  }
   622  
   623  func assertFileExistsInArtifactory(t *testing.T, filePath string) {
   624  	searchParams := artifactoryServices.NewSearchParams()
   625  	searchParams.Pattern = filePath
   626  	reader, err := testsSearchService.Search(searchParams)
   627  	assert.NoError(t, err)
   628  	readerCloseAndAssert(t, reader)
   629  	length, err := reader.Length()
   630  	assert.NoError(t, err)
   631  	assert.Equal(t, 1, length)
   632  }
   633  
   634  func assertReleaseBundleDistribution(t *testing.T, bundleName string) {
   635  	// Distribute release bundle
   636  	distributeBundleParams := distribution.NewDistributeReleaseBundleParams(bundleName, bundleVersion)
   637  	distributeBundleParams.DistributionRules = []*distribution.DistributionCommonParams{{SiteName: "*"}}
   638  	testsBundleDistributeService.Sync = true
   639  	// On distribution with path mapping, the target repository cannot be auto-created
   640  	testsBundleDistributeService.AutoCreateRepo = false
   641  	testsBundleDistributeService.DistributeParams = distributeBundleParams
   642  	err := testsBundleDistributeService.Distribute()
   643  	assert.NoError(t, err)
   644  }