Skip to content

Commit

Permalink
Artifactory Release Lifecycle Management - Support creation by artifa…
Browse files Browse the repository at this point in the history
…cts and sync for distribution (jfrog#903)
  • Loading branch information
ns-kbhat authored Feb 22, 2024
1 parent 3cea121 commit e55c7d7
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 25 deletions.
38 changes: 23 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2510,22 +2510,30 @@ resp, err := serviceManager.GetReleaseBundlePromotionStatus(rbDetails, projectKe
#### Distribute Release Bundle
```go
rules := &distribution.DistributionCommonParams{
SiteName: "*",
CityName: "*",
CountryCodes: []string{"*"},
rbDetails := ReleaseBundleDetails{"rbName", "rbVersion"}
pathMappings := []services.PathMapping{
{
Pattern: "(*)/(*)",
Target: "{1}/target/{2}",
},
}
params := distribution.NewDistributeReleaseBundleParams("rbName", "rbVersion")
params.DistributionRules = append(params.DistributionRules, rules)

autoCreateRepo := true

pathMapping := services.PathMapping{
Pattern: "(*)/(*)",
Target: "{1}/target/{2}",
rules := &distribution.DistributionCommonParams{
SiteName: "*",
CityName: "*",
CountryCodes: []string{"*"},
}
dsParams := DistributeReleaseBundleParams{
Sync: true,
AutoCreateRepo: true,
MaxWaitMinutes: 60,
PathMappings: pathMappings,
DistributionRules: []*dmUtils.DistributionCommonParams{
rules,
},
}

resp, err := serviceManager.DistributeReleaseBundle(params, autoCreateRepo, pathMapping)
resp, err := serviceManager.DistributeReleaseBundle(rbDetails, dsParams)
```
#### Delete Release Bundle
Expand All @@ -2543,9 +2551,9 @@ resp, err := serviceManager.DeleteReleaseBundle(rbDetails, queryParams)
```go
rules := &distribution.DistributionCommonParams{
SiteName: "*",
CityName: "*",
CountryCodes: []string{"*"},
SiteName: "*",
CityName: "*",
CountryCodes: []string{"*"},
}
params := distribution.NewDistributeReleaseBundleParams("rbName", "rbVersion")
params.DistributionRules = append(params.DistributionRules, rules)
Expand Down
26 changes: 22 additions & 4 deletions lifecycle/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ func (lcs *LifecycleServicesManager) Client() *jfroghttpclient.JfrogHttpClient {
return lcs.client
}

func (lcs *LifecycleServicesManager) CreateReleaseBundleFromArtifacts(rbDetails lifecycle.ReleaseBundleDetails,
queryParams lifecycle.CommonOptionalQueryParams, signingKeyName string, sourceArtifacts lifecycle.CreateFromArtifacts) error {
rbService := lifecycle.NewReleaseBundlesService(lcs.config.GetServiceDetails(), lcs.client)
return rbService.CreateFromArtifacts(rbDetails, queryParams, signingKeyName, sourceArtifacts)
}

func (lcs *LifecycleServicesManager) CreateReleaseBundleFromBuilds(rbDetails lifecycle.ReleaseBundleDetails,
queryParams lifecycle.CommonOptionalQueryParams, signingKeyName string, sourceBuilds lifecycle.CreateFromBuildsSource) error {
rbService := lifecycle.NewReleaseBundlesService(lcs.config.GetServiceDetails(), lcs.client)
Expand Down Expand Up @@ -68,13 +74,25 @@ func (lcs *LifecycleServicesManager) DeleteReleaseBundle(rbDetails lifecycle.Rel
return rbService.DeleteReleaseBundle(rbDetails, queryParams)
}

func (lcs *LifecycleServicesManager) DistributeReleaseBundle(params distribution.DistributionParams, autoCreateRepo bool, pathMapping lifecycle.PathMapping) error {
func (lcs *LifecycleServicesManager) DistributeReleaseBundle(rbDetails lifecycle.ReleaseBundleDetails, distributeParams lifecycle.DistributeReleaseBundleParams) error {
distributeBundleService := lifecycle.NewDistributeReleaseBundleService(lcs.client)
distributeBundleService.LcDetails = lcs.config.GetServiceDetails()
distributeBundleService.DryRun = lcs.config.IsDryRun()
distributeBundleService.AutoCreateRepo = autoCreateRepo
distributeBundleService.DistributeParams = params
distributeBundleService.PathMapping = pathMapping

distributeBundleService.DistributeParams = distribution.DistributionParams{
Name: rbDetails.ReleaseBundleName,
Version: rbDetails.ReleaseBundleVersion,
DistributionRules: distributeParams.DistributionRules,
}
distributeBundleService.AutoCreateRepo = distributeParams.AutoCreateRepo
distributeBundleService.Sync = distributeParams.Sync
distributeBundleService.MaxWaitMinutes = distributeParams.MaxWaitMinutes

m := &distributeBundleService.Modifications.PathMappings
for _, pathMapping := range distributeParams.PathMappings {
*m = append(*m,
distribution.CreatePathMappingsFromPatternAndTarget(pathMapping.Pattern, pathMapping.Target)...)
}
return distributeBundleService.Distribute()
}

Expand Down
23 changes: 23 additions & 0 deletions lifecycle/services/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const (
type sourceType string

const (
artifacts sourceType = "artifacts"
builds sourceType = "builds"
releaseBundles sourceType = "release_bundles"
)
Expand Down Expand Up @@ -37,6 +38,19 @@ func (c *createOperation) getSigningKeyName() string {
return c.signingKeyName
}

func (rbs *ReleaseBundlesService) CreateFromArtifacts(rbDetails ReleaseBundleDetails, params CommonOptionalQueryParams, signingKeyName string, sourceArtifacts CreateFromArtifacts) error {
operation := createOperation{
reqBody: RbCreationBody{
ReleaseBundleDetails: rbDetails,
SourceType: artifacts,
Source: sourceArtifacts},
params: params,
signingKeyName: signingKeyName,
}
_, err := rbs.doOperation(&operation)
return err
}

func (rbs *ReleaseBundlesService) CreateFromBuilds(rbDetails ReleaseBundleDetails, params CommonOptionalQueryParams, signingKeyName string, sourceBuilds CreateFromBuildsSource) error {
operation := createOperation{
reqBody: RbCreationBody{
Expand Down Expand Up @@ -69,10 +83,19 @@ type SourceBuildDetails struct {
ProjectKey string
}

type CreateFromArtifacts struct {
Artifacts []ArtifactSource `json:"artifacts,omitempty"`
}

type CreateFromBuildsSource struct {
Builds []BuildSource `json:"builds,omitempty"`
}

type ArtifactSource struct {
Path string `json:"path,omitempty"`
Sha256 string `json:"sha256,omitempty"`
}

type BuildSource struct {
BuildName string `json:"build_name,omitempty"`
BuildNumber string `json:"build_number,omitempty"`
Expand Down
33 changes: 27 additions & 6 deletions lifecycle/services/distribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,18 @@ type DistributeReleaseBundleService struct {
LcDetails auth.ServiceDetails
DryRun bool
AutoCreateRepo bool
Sync bool
MaxWaitMinutes int
DistributeParams distribution.DistributionParams
PathMapping
Modifications
}

type DistributeReleaseBundleParams struct {
Sync bool
AutoCreateRepo bool
MaxWaitMinutes int
DistributionRules []*distribution.DistributionCommonParams
PathMappings []PathMapping
}

func (dr *DistributeReleaseBundleService) GetHttpClient() *jfroghttpclient.JfrogHttpClient {
Expand All @@ -34,6 +44,14 @@ func (dr *DistributeReleaseBundleService) IsDryRun() bool {
return dr.DryRun
}

func (dr *DistributeReleaseBundleService) IsSync() bool {
return dr.Sync
}

func (dr *DistributeReleaseBundleService) GetMaxWaitMinutes() int {
return dr.MaxWaitMinutes
}

func (dr *DistributeReleaseBundleService) IsAutoCreateRepo() bool {
return dr.AutoCreateRepo
}
Expand All @@ -55,16 +73,19 @@ func NewDistributeReleaseBundleService(client *jfroghttpclient.JfrogHttpClient)
}

func (dr *DistributeReleaseBundleService) Distribute() error {
_, err := distribution.DoDistribute(dr)
return err
trackerId, err := distribution.DoDistribute(dr)
if err != nil || !dr.IsSync() || dr.IsDryRun() {
return err
}

// Sync distribution
return dr.waitForDistributionOperationCompletion(&dr.DistributeParams, trackerId)
}

func (dr *DistributeReleaseBundleService) createDistributeBody() ReleaseBundleDistributeBody {
return ReleaseBundleDistributeBody{
ReleaseBundleDistributeV1Body: distribution.CreateDistributeV1Body(dr.DistributeParams, dr.DryRun, dr.AutoCreateRepo),
Modifications: Modifications{
PathMappings: distribution.CreatePathMappingsFromPatternAndTarget(dr.Pattern, dr.Target),
},
Modifications: dr.Modifications,
}
}

Expand Down
71 changes: 71 additions & 0 deletions lifecycle/services/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ package services

import (
"encoding/json"
"errors"
"fmt"
"github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/distribution"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/io/httputils"
"github.com/jfrog/jfrog-client-go/utils/log"
"net/http"
"path"
"time"

dsServices "github.com/jfrog/jfrog-client-go/distribution/services"
)

const (
statusesApi = "statuses"
trackersApi = "trackers"
defaultMaxWait = 60 * time.Minute
DefaultSyncSleepInterval = 10 * time.Second
)
Expand Down Expand Up @@ -68,6 +74,24 @@ func (rbs *ReleaseBundlesService) getReleaseBundleStatus(restApi string, project
return
}

func (dbs *DistributeReleaseBundleService) getReleaseBundleDistributionStatus(distributeParams *distribution.DistributionParams, trackerId json.Number) (statusResp *dsServices.DistributionStatusResponse, body []byte, err error) {
restApi := path.Join(distributionBaseApi, trackersApi, distributeParams.Name, distributeParams.Version, trackerId.String())
requestFullUrl, err := utils.BuildUrl(dbs.LcDetails.GetUrl(), restApi, nil)
if err != nil {
return
}
httpClientsDetails := dbs.LcDetails.CreateHttpClientDetails()
resp, body, _, err := dbs.client.SendGet(requestFullUrl, true, &httpClientsDetails)
if err != nil {
return
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil {
return
}
err = errorutils.CheckError(json.Unmarshal(body, &statusResp))
return
}

func getStatusResponse(respBody []byte) (ReleaseBundleStatusResponse, error) {
var rbStatusResponse ReleaseBundleStatusResponse
err := json.Unmarshal(respBody, &rbStatusResponse)
Expand Down Expand Up @@ -103,6 +127,53 @@ func (rbs *ReleaseBundlesService) waitForRbOperationCompletion(restApi, projectK
return getStatusResponse(finalRespBody)
}

func (dbs *DistributeReleaseBundleService) waitForDistributionOperationCompletion(distributeParams *distribution.DistributionParams, trackerId json.Number) error {
maxWait := time.Duration(dbs.GetMaxWaitMinutes()) * time.Minute
if maxWait.Minutes() < 1 {
maxWait = defaultMaxWait
}

pollingAction := func() (shouldStop bool, responseBody []byte, err error) {
statusResponse, responseBody, err := dbs.getReleaseBundleDistributionStatus(distributeParams, trackerId)
if err != nil {
return true, nil, err
}

switch statusResponse.Status {
case dsServices.NotDistributed, dsServices.InProgress:
return false, nil, nil
case dsServices.Failed, dsServices.Completed:
return true, responseBody, nil
default:
return true, nil, errorutils.CheckErrorf("received unexpected status: '%s'", statusResponse.Status)
}
}
pollingExecutor := &httputils.PollingExecutor{
Timeout: maxWait,
PollingInterval: SyncSleepInterval,
PollingAction: pollingAction,
MsgPrefix: fmt.Sprintf("Sync: Distributing %s/%s...", distributeParams.Name, distributeParams.Version),
}
finalRespBody, err := pollingExecutor.Execute()
if err != nil {
return err
}

var dsStatusResponse dsServices.DistributionStatusResponse
if err = json.Unmarshal(finalRespBody, &dsStatusResponse); err != nil {
return errorutils.CheckError(err)
}

if dsStatusResponse.Status != dsServices.Completed {
for _, st := range dsStatusResponse.Sites {
err = errors.Join(err, fmt.Errorf("target %s name:%s error:%s", st.TargetArtifactory.Type, st.TargetArtifactory.Name, st.Error))
}
return errorutils.CheckError(err)
}
log.Info("Distribution Completed!")
return nil
}

type ReleaseBundleStatusResponse struct {
Status RbStatus `json:"status,omitempty"`
Messages []Message `json:"messages,omitempty"`
Expand Down

0 comments on commit e55c7d7

Please sign in to comment.