Skip to content

Commit

Permalink
Support Lifecycle Distribute Release Bundle
Browse files Browse the repository at this point in the history
  • Loading branch information
RobiNino committed Jul 12, 2023
1 parent d73db17 commit e1ed6fc
Show file tree
Hide file tree
Showing 15 changed files with 372 additions and 206 deletions.
15 changes: 9 additions & 6 deletions distribution/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/jfrog/jfrog-client-go/distribution/services"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
clientutils "github.com/jfrog/jfrog-client-go/utils"
"github.com/jfrog/jfrog-client-go/utils/distribution"
)

type DistributionServicesManager struct {
Expand Down Expand Up @@ -57,22 +58,24 @@ func (sm *DistributionServicesManager) SignReleaseBundle(params services.SignBun
return signBundleService.SignReleaseBundle(params)
}

func (sm *DistributionServicesManager) DistributeReleaseBundle(params services.DistributionParams, autoCreateRepo bool) error {
distributeBundleService := services.NewDistributeReleaseBundleService(sm.client)
func (sm *DistributionServicesManager) DistributeReleaseBundle(params distribution.DistributionParams, autoCreateRepo bool) error {
distributeBundleService := services.NewDistributeReleaseBundleV1Service(sm.client)
distributeBundleService.DistDetails = sm.config.GetServiceDetails()
distributeBundleService.DryRun = sm.config.IsDryRun()
distributeBundleService.AutoCreateRepo = autoCreateRepo
return distributeBundleService.Distribute(params)
distributeBundleService.DistributeParams = params
return distributeBundleService.Distribute()
}

func (sm *DistributionServicesManager) DistributeReleaseBundleSync(params services.DistributionParams, maxWaitMinutes int, autoCreateRepo bool) error {
distributeBundleService := services.NewDistributeReleaseBundleService(sm.client)
func (sm *DistributionServicesManager) DistributeReleaseBundleSync(params distribution.DistributionParams, maxWaitMinutes int, autoCreateRepo bool) error {
distributeBundleService := services.NewDistributeReleaseBundleV1Service(sm.client)
distributeBundleService.DistDetails = sm.config.GetServiceDetails()
distributeBundleService.DryRun = sm.config.IsDryRun()
distributeBundleService.MaxWaitMinutes = maxWaitMinutes
distributeBundleService.Sync = true
distributeBundleService.AutoCreateRepo = autoCreateRepo
return distributeBundleService.Distribute(params)
distributeBundleService.DistributeParams = params
return distributeBundleService.Distribute()
}

func (sm *DistributionServicesManager) GetDistributionStatus(params services.DistributionStatusParams) (*[]services.DistributionStatusResponse, error) {
Expand Down
13 changes: 7 additions & 6 deletions distribution/services/deleteremote.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/jfrog/jfrog-client-go/auth"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"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/log"
"net/http"
Expand Down Expand Up @@ -43,9 +44,9 @@ func (dr *DeleteReleaseBundleService) IsDryRun() bool {
}

func (dr *DeleteReleaseBundleService) DeleteDistribution(deleteDistributionParams DeleteDistributionParams) error {
var distributionRules []DistributionRulesBody
var distributionRules []distribution.DistributionRulesBody
for _, rule := range deleteDistributionParams.DistributionRules {
distributionRule := DistributionRulesBody{
distributionRule := distribution.DistributionRulesBody{
SiteName: rule.GetSiteName(),
CityName: rule.GetCityName(),
CountryCodes: rule.GetCountryCodes(),
Expand All @@ -61,7 +62,7 @@ func (dr *DeleteReleaseBundleService) DeleteDistribution(deleteDistributionParam
}

deleteDistribution := DeleteRemoteDistributionBody{
DistributionBody: DistributionBody{
ReleaseBundleDistributeV1Body: distribution.ReleaseBundleDistributeV1Body{
DryRun: dr.DryRun,
DistributionRules: distributionRules,
},
Expand Down Expand Up @@ -133,12 +134,12 @@ func (dr *DeleteReleaseBundleService) waitForDeletion(name, version string) erro
}

type DeleteRemoteDistributionBody struct {
DistributionBody
distribution.ReleaseBundleDistributeV1Body
OnSuccess OnSuccess `json:"on_success"`
}

type DeleteDistributionParams struct {
DistributionParams
distribution.DistributionParams
DeleteFromDistribution bool
Sync bool
// Max time in minutes to wait for sync distribution to finish.
Expand All @@ -147,7 +148,7 @@ type DeleteDistributionParams struct {

func NewDeleteReleaseBundleParams(name, version string) DeleteDistributionParams {
return DeleteDistributionParams{
DistributionParams: DistributionParams{
DistributionParams: distribution.DistributionParams{
Name: name,
Version: version,
},
Expand Down
138 changes: 44 additions & 94 deletions distribution/services/distribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,109 +3,88 @@ package services
import (
"encoding/json"
"fmt"
"net/http"

artifactoryUtils "github.com/jfrog/jfrog-client-go/artifactory/services/utils"
"github.com/jfrog/jfrog-client-go/auth"
distributionUtils "github.com/jfrog/jfrog-client-go/distribution/services/utils"
"github.com/jfrog/jfrog-client-go/http/jfroghttpclient"
"github.com/jfrog/jfrog-client-go/utils"
clientUtils "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/log"
)

const defaultMaxWaitMinutes = 60 // 1 hour
const DefaultDistributeSyncSleepIntervalSeconds = 10 // 10 seconds

type DistributeReleaseBundleService struct {
type DistributeReleaseBundleV1Service struct {
client *jfroghttpclient.JfrogHttpClient
DistDetails auth.ServiceDetails
DryRun bool
Sync bool
AutoCreateRepo bool
// Max time in minutes to wait for sync distribution to finish.
MaxWaitMinutes int
MaxWaitMinutes int
DistributeParams distribution.DistributionParams
}

func NewDistributeReleaseBundleService(client *jfroghttpclient.JfrogHttpClient) *DistributeReleaseBundleService {
return &DistributeReleaseBundleService{client: client}
func (dr *DistributeReleaseBundleV1Service) GetHttpClient() *jfroghttpclient.JfrogHttpClient {
return dr.client
}

func (dr *DistributeReleaseBundleService) GetDistDetails() auth.ServiceDetails {
func (dr *DistributeReleaseBundleV1Service) ServiceDetails() auth.ServiceDetails {
return dr.DistDetails
}

func (dr *DistributeReleaseBundleService) Distribute(distributeParams DistributionParams) error {
var distributionRules []DistributionRulesBody
for _, spec := range distributeParams.DistributionRules {
distributionRule := DistributionRulesBody{
SiteName: spec.GetSiteName(),
CityName: spec.GetCityName(),
CountryCodes: spec.GetCountryCodes(),
}
distributionRules = append(distributionRules, distributionRule)
}
distribution := &DistributionBody{
DryRun: dr.DryRun,
DistributionRules: distributionRules,
AutoCreateRepo: dr.AutoCreateRepo,
}
func (dr *DistributeReleaseBundleV1Service) IsDryRun() bool {
return dr.DryRun
}

trackerId, err := dr.execDistribute(distributeParams.Name, distributeParams.Version, distribution)
if err != nil || !dr.Sync || dr.DryRun {
return err
}
func (dr *DistributeReleaseBundleV1Service) IsSync() bool {
return dr.Sync
}

// Sync distribution
return dr.waitForDistribution(&distributeParams, trackerId)
func (dr *DistributeReleaseBundleV1Service) GetMaxWaitMinutes() int {
return dr.MaxWaitMinutes
}

func (dr *DistributeReleaseBundleService) execDistribute(name, version string, distribution *DistributionBody) (json.Number, error) {
httpClientsDetails := dr.DistDetails.CreateHttpClientDetails()
content, err := json.Marshal(distribution)
if err != nil {
return "", errorutils.CheckError(err)
}
dryRunStr := ""
if distribution.DryRun {
dryRunStr = "[Dry run] "
}
log.Info(dryRunStr + "Distributing: " + name + "/" + version)
func (dr *DistributeReleaseBundleV1Service) GetRestApi(name, version string) string {
return "api/v1/distribution/" + name + "/" + version
}

url := dr.DistDetails.GetUrl() + "api/v1/distribution/" + name + "/" + version
artifactoryUtils.SetContentType("application/json", &httpClientsDetails.Headers)
resp, body, err := dr.client.SendPost(url, content, &httpClientsDetails)
if err != nil {
return "", err
}
if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK, http.StatusAccepted); err != nil {
return "", err
}
response := DistributionResponseBody{}
err = json.Unmarshal(body, &response)
if err != nil {
return "", errorutils.CheckError(err)
func (dr *DistributeReleaseBundleV1Service) GetDistributeBody() any {
return distribution.CreateDistributeV1Body(dr.DistributeParams, dr.DryRun, dr.AutoCreateRepo)
}

func (dr *DistributeReleaseBundleV1Service) GetDistributionParams() distribution.DistributionParams {
return dr.DistributeParams
}

func NewDistributeReleaseBundleV1Service(client *jfroghttpclient.JfrogHttpClient) *DistributeReleaseBundleV1Service {
return &DistributeReleaseBundleV1Service{client: client}
}

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

log.Debug("Distribution response:", resp.Status)
log.Debug(utils.IndentJson(body))
return response.TrackerId, nil
// Sync distribution
return dr.waitForDistribution(&dr.DistributeParams, trackerId)
}

func (dr *DistributeReleaseBundleService) waitForDistribution(distributeParams *DistributionParams, trackerId json.Number) error {
distributeBundleService := NewDistributionStatusService(dr.client)
distributeBundleService.DistDetails = dr.GetDistDetails()
func (dr *DistributeReleaseBundleV1Service) waitForDistribution(distributeParams *distribution.DistributionParams, trackerId json.Number) error {
distributeBundleService := NewDistributionStatusService(dr.GetHttpClient())
distributeBundleService.DistDetails = dr.ServiceDetails()
distributionStatusParams := DistributionStatusParams{
Name: distributeParams.Name,
Version: distributeParams.Version,
TrackerId: trackerId.String(),
}
maxWaitMinutes := defaultMaxWaitMinutes
if dr.MaxWaitMinutes >= 1 {
maxWaitMinutes = dr.MaxWaitMinutes
if dr.GetMaxWaitMinutes() >= 1 {
maxWaitMinutes = dr.GetMaxWaitMinutes()
}
distributingMessage := fmt.Sprintf("Sync: Distributing %s/%s...", distributeParams.Name, distributeParams.Version)
retryExecutor := &utils.RetryExecutor{
retryExecutor := &clientUtils.RetryExecutor{
MaxRetries: maxWaitMinutes * 60 / DefaultDistributeSyncSleepIntervalSeconds,
RetriesIntervalMilliSecs: DefaultDistributeSyncSleepIntervalSeconds * 1000,
ErrorMessage: "",
Expand All @@ -120,7 +99,7 @@ func (dr *DistributeReleaseBundleService) waitForDistribution(distributeParams *
if err != nil {
return false, errorutils.CheckError(err)
}
return false, errorutils.CheckErrorf("Distribution failed: " + utils.IndentJson(bytes))
return false, errorutils.CheckErrorf("Distribution failed: " + clientUtils.IndentJson(bytes))
}
if (*response)[0].Status == Completed {
log.Info("Distribution Completed!")
Expand All @@ -133,32 +112,3 @@ func (dr *DistributeReleaseBundleService) waitForDistribution(distributeParams *
}
return retryExecutor.Execute()
}

type DistributionBody struct {
DryRun bool `json:"dry_run"`
DistributionRules []DistributionRulesBody `json:"distribution_rules"`
AutoCreateRepo bool `json:"auto_create_missing_repositories,omitempty"`
}

type DistributionRulesBody struct {
SiteName string `json:"site_name,omitempty"`
CityName string `json:"city_name,omitempty"`
CountryCodes []string `json:"country_codes,omitempty"`
}

type DistributionResponseBody struct {
TrackerId json.Number `json:"id"`
}

type DistributionParams struct {
DistributionRules []*distributionUtils.DistributionCommonParams
Name string
Version string
}

func NewDistributeReleaseBundleParams(name, version string) DistributionParams {
return DistributionParams{
Name: name,
Version: version,
}
}
17 changes: 9 additions & 8 deletions distribution/services/getstatus.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package services
import (
"encoding/json"
"errors"
"github.com/jfrog/jfrog-client-go/utils/distribution"
"net/http"
"strings"

Expand Down Expand Up @@ -109,14 +110,14 @@ const (
)

type DistributionStatusResponse struct {
Id json.Number `json:"distribution_id"`
FriendlyId json.Number `json:"distribution_friendly_id,omitempty"`
Type DistributionType `json:"type,omitempty"`
Name string `json:"release_bundle_name,omitempty"`
Version string `json:"release_bundle_version,omitempty"`
Status DistributionStatus `json:"status,omitempty"`
DistributionRules []DistributionRulesBody `json:"distribution_rules,omitempty"`
Sites []DistributionSiteStatus `json:"sites,omitempty"`
Id json.Number `json:"distribution_id"`
FriendlyId json.Number `json:"distribution_friendly_id,omitempty"`
Type DistributionType `json:"type,omitempty"`
Name string `json:"release_bundle_name,omitempty"`
Version string `json:"release_bundle_version,omitempty"`
Status DistributionStatus `json:"status,omitempty"`
DistributionRules []distribution.DistributionRulesBody `json:"distribution_rules,omitempty"`
Sites []DistributionSiteStatus `json:"sites,omitempty"`
}

type DistributionSiteStatus struct {
Expand Down
27 changes: 2 additions & 25 deletions distribution/services/utils/distributionutils.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package utils

import (
"github.com/jfrog/gofrog/stringutils"
"regexp"

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

type ReleaseNotesSyntax string
Expand All @@ -15,8 +13,6 @@ const (
PlainText ReleaseNotesSyntax = "plain_text"
)

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

type ReleaseBundleParams struct {
SpecFiles []*rtUtils.CommonParams
Name string
Expand Down Expand Up @@ -47,7 +43,7 @@ func CreateBundleBody(releaseBundleParams ReleaseBundleParams, dryRun bool) (*Re
}

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

// Create added properties
addedProps := createAddedProps(specFile)
Expand Down Expand Up @@ -89,25 +85,6 @@ func createAql(specFile *rtUtils.CommonParams) (string, error) {
return rtUtils.BuildQueryFromSpecFile(specFile, rtUtils.NONE), nil
}

// Creat the path mapping from the input spec
func createPathMappings(specFile *rtUtils.CommonParams) []PathMapping {
if len(specFile.Target) == 0 {
return []PathMapping{}
}

// Convert the file spec pattern and target to match the path mapping input and output specifications, respectfully.
return []PathMapping{{
// The file spec pattern is wildcard based. Convert it to Regex:
Input: stringutils.WildcardPatternToRegExp(specFile.Pattern),
// The file spec target contain placeholders-style matching groups, like {1}.
// Convert it to REST API's matching groups style, like $1.
Output: fileSpecCaptureGroup.ReplaceAllStringFunc(specFile.Target, func(s string) string {
// Remove curly parenthesis and prepend $
return "$" + s[1:2]
}),
}}
}

// Create the AddedProps array from the input TargetProps string
func createAddedProps(specFile *rtUtils.CommonParams) []AddedProps {
props := specFile.TargetProps
Expand Down
Loading

0 comments on commit e1ed6fc

Please sign in to comment.