Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions clients/cfrestclient/cloud_foundry_operations_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ type CloudFoundryOperationsExtended interface {
GetApplicationRoutes(appGuid string) ([]models.ApplicationRoute, error)
GetServiceInstances(mtaId, mtaNamespace, spaceGuid string) ([]models.CloudFoundryServiceInstance, error)
GetServiceBindings(serviceName string) ([]models.ServiceBinding, error)
GetServiceInstanceByName(serviceName, spaceGuid string) (models.CloudFoundryServiceInstance, error)
CreateUserProvidedServiceInstance(serviceName string, spaceGuid string, credentials map[string]string) (models.CloudFoundryServiceInstance, error)
}
8 changes: 8 additions & 0 deletions clients/cfrestclient/fakes/fake_cloud_foundry_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ func (f FakeCloudFoundryClient) GetServiceInstances(mtaId, mtaNamespace, spaceGu
func (f FakeCloudFoundryClient) GetServiceBindings(serviceName string) ([]models.ServiceBinding, error) {
return f.ServiceBindings, f.ServiceBindingsErr
}

func (f FakeCloudFoundryClient) GetServiceInstanceByName(serviceName, spaceGuid string) (models.CloudFoundryServiceInstance, error) {
return f.Services[0], f.ServiceBindingsErr
}

func (f FakeCloudFoundryClient) CreateUserProvidedServiceInstance(serviceName string, spaceGuid string, credentials map[string]string) (models.CloudFoundryServiceInstance, error) {
return f.Services[0], f.ServicesErr
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package resilient

import (
"time"

"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/cfrestclient"
"github.com/cloudfoundry-incubator/multiapps-cli-plugin/clients/models"
"time"
)

type ResilientCloudFoundryRestClient struct {
Expand Down Expand Up @@ -46,6 +47,18 @@ func (c ResilientCloudFoundryRestClient) GetServiceBindings(serviceName string)
}, c.MaxRetriesCount, c.RetryInterval)
}

func (c ResilientCloudFoundryRestClient) GetServiceInstanceByName(serviceName, spaceGuid string) (models.CloudFoundryServiceInstance, error) {
return retryOnError(func() (models.CloudFoundryServiceInstance, error) {
return c.CloudFoundryRestClient.GetServiceInstanceByName(serviceName, spaceGuid)
}, c.MaxRetriesCount, c.RetryInterval)
}

func (c ResilientCloudFoundryRestClient) CreateUserProvidedServiceInstance(serviceName string, spaceGuid string, credentials map[string]string) (models.CloudFoundryServiceInstance, error) {
return retryOnError(func() (models.CloudFoundryServiceInstance, error) {
return c.CloudFoundryRestClient.CreateUserProvidedServiceInstance(serviceName, spaceGuid, credentials)
}, c.MaxRetriesCount, c.RetryInterval)
}

func retryOnError[T any](operation func() (T, error), retries int, retryInterval time.Duration) (T, error) {
result, err := operation()
for shouldRetry(retries, err) {
Expand Down
100 changes: 93 additions & 7 deletions clients/cfrestclient/rest_cloud_foundry_client_extended.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cfrestclient

import (
"bytes"
"crypto/md5"
"crypto/tls"
"encoding/hex"
Expand Down Expand Up @@ -60,7 +61,7 @@ func (c CloudFoundryRestClient) GetAppProcessStatistics(appGuid string) ([]model
apiEndpoint, _ := c.cliConn.ApiEndpoint()

getAppProcessStatsUrl := fmt.Sprintf("%s/%sapps/%s/processes/web/stats", apiEndpoint, cfBaseUrl, appGuid)
body, err := executeRequest(getAppProcessStatsUrl, token, c.isSslDisabled)
body, err := executeRequest("GET", getAppProcessStatsUrl, token, c.isSslDisabled, nil)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use http.MethodGet instead of raw strings.

if err != nil {
return nil, err
}
Expand Down Expand Up @@ -114,10 +115,82 @@ func (c CloudFoundryRestClient) GetServiceBindings(serviceName string) ([]models
return getPaginatedResourcesWithIncluded(getServiceBindingsUrl, token, c.isSslDisabled, buildServiceBinding)
}

func (c CloudFoundryRestClient) GetServiceInstanceByName(serviceName, spaceGuid string) (models.CloudFoundryServiceInstance, error) {
token, err := c.cliConn.AccessToken()
if err != nil {
return models.CloudFoundryServiceInstance{}, fmt.Errorf("failed to retrieve access token: %s", err)
}
apiEndpoint, _ := c.cliConn.ApiEndpoint()

getServicesUrl := fmt.Sprintf("%s/%sservice_instances?names=%s&space_guids=%s",
apiEndpoint, cfBaseUrl, serviceName, spaceGuid)
services, err := getPaginatedResourcesWithIncluded(getServicesUrl, token, c.isSslDisabled, buildServiceInstance)
if err != nil {
return models.CloudFoundryServiceInstance{}, err
}
if len(services) == 0 {
return models.CloudFoundryServiceInstance{}, fmt.Errorf("service instance not found")
}

resultService := services[0]
return resultService, nil
}

func (c CloudFoundryRestClient) CreateUserProvidedServiceInstance(serviceName string, spaceGuid string, credentials map[string]string) (models.CloudFoundryServiceInstance, error) {
token, err := c.cliConn.AccessToken()
if err != nil {
return models.CloudFoundryServiceInstance{}, fmt.Errorf("failed to retrieve access token: %s", err)
}

apiEndpoint, _ := c.cliConn.ApiEndpoint()

createServiceURL := fmt.Sprintf("%s/%sservice_instances", apiEndpoint, cfBaseUrl)

payload := map[string]any{
"type": "user-provided",
"name": serviceName,
"relationships": map[string]any{
"space": map[string]any{
"data": map[string]any{
"guid": spaceGuid,
},
},
},
}

if credentials != nil {
payload["credentials"] = credentials
}

jsonBody, err := json.Marshal(payload)
if err != nil {
return models.CloudFoundryServiceInstance{}, fmt.Errorf("failed to marshal create UPS request: %w", err)
}

body, err := executeRequest(http.MethodPost, createServiceURL, token, c.isSslDisabled, jsonBody)
if err != nil {
return models.CloudFoundryServiceInstance{}, err
}

response, err := parseBody[models.CloudFoundryUserProvidedServiceInstance](body)
if err != nil {
return models.CloudFoundryServiceInstance{}, err
}

return models.CloudFoundryServiceInstance{
Guid: response.Guid,
Name: response.Name,
Type: response.Type,
LastOperation: response.LastOperation,
SpaceGuid: response.SpaceGuid,
Metadata: response.Metadata,
}, nil
}

func getPaginatedResources[T any](url, token string, isSslDisabled bool) ([]T, error) {
var result []T
for url != "" {
body, err := executeRequest(url, token, isSslDisabled)
body, err := executeRequest("GET", url, token, isSslDisabled, nil)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use http.MethodGet instead of raw strings.

if err != nil {
return nil, err
}
Expand All @@ -137,7 +210,7 @@ func getPaginatedResources[T any](url, token string, isSslDisabled bool) ([]T, e
func getPaginatedResourcesWithIncluded[T any, Auxiliary any](url, token string, isSslDisabled bool, auxiliaryContentHandler func(T, Auxiliary) T) ([]T, error) {
var result []T
for url != "" {
body, err := executeRequest(url, token, isSslDisabled)
body, err := executeRequest("GET", url, token, isSslDisabled, nil)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use http.MethodGet instead of raw strings.

if err != nil {
return nil, err
}
Expand All @@ -154,9 +227,22 @@ func getPaginatedResourcesWithIncluded[T any, Auxiliary any](url, token string,
return result, nil
}

func executeRequest(url, token string, isSslDisabled bool) ([]byte, error) {
req, _ := http.NewRequest(http.MethodGet, url, nil)
req.Header.Add("Authorization", token)
func executeRequest(methodType, url, token string, isSslDisabled bool, body []byte) ([]byte, error) {
var reader io.Reader

if body != nil {
reader = bytes.NewReader(body)
}
request, err := http.NewRequest(methodType, url, reader)
if err != nil {
return nil, err
}

request.Header.Add("Authorization", token)
request.Header.Set("Accept", "application/json")
if body != nil {
request.Header.Set("Content-Type", "application/json")
}

// Create transport with TLS configuration
httpTransport := http.DefaultTransport.(*http.Transport).Clone()
Expand All @@ -166,7 +252,7 @@ func executeRequest(url, token string, isSslDisabled bool) ([]byte, error) {
userAgentTransport := baseclient.NewUserAgentTransport(httpTransport)

client := &http.Client{Transport: userAgentTransport}
resp, err := client.Do(req)
resp, err := client.Do(request)
if err != nil {
return nil, err
}
Expand Down
11 changes: 10 additions & 1 deletion clients/models/cf_services_response.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ type CloudFoundryServiceInstance struct {
Offering ServiceOffering `json:"-"`
}

type CloudFoundryUserProvidedServiceInstance struct {
Guid string `json:"guid"`
Name string `json:"name"`
Type string `json:"type"`
LastOperation LastOperation `json:"last_operation,omitempty"`
SpaceGuid string `jsonry:"relationships.space.data.guid"`
Metadata Metadata `json:"metadata"`
}

type LastOperation struct {
Type string `json:"type"`
State string `json:"state"`
Expand All @@ -25,7 +34,7 @@ type LastOperation struct {
type ServicePlan struct {
Guid string `json:"guid"`
Name string `json:"name"`
OfferingGuid string `jsonry:"relationships.service_offering.data.guid,omitempty"`
OfferingGuid string `jsonry:"rela tionships.service_offering.data.guid,omitempty"`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this space typo.

}

type ServiceOffering struct {
Expand Down
1 change: 1 addition & 0 deletions clients/mtaclient/mta_rest_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type AsyncUploadJobResult struct {
Error string `json:"error,omitempty"`
File *models.FileMetadata `json:"file,omitempty"`
MtaId string `json:"mta_id,omitempty"`
SchemaVersion string `json:"schema_version,omitempty"`
BytesProcessed int64 `json:"bytes_processed,omitempty"`
ClientActions []string `json:"client_actions,omitempty"`
}
Expand Down
2 changes: 1 addition & 1 deletion commands/blue_green_deploy_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ type BlueGreenDeployCommand struct {
// NewBlueGreenDeployCommand creates a new BlueGreenDeployCommand.
func NewBlueGreenDeployCommand() *BlueGreenDeployCommand {
baseCmd := &BaseCommand{flagsParser: deployCommandLineArgumentsParser{}, flagsValidator: deployCommandFlagsValidator{}}
deployCmd := &DeployCommand{baseCmd, blueGreenDeployProcessParametersSetter(), &blueGreenDeployCommandProcessTypeProvider{}, os.Stdin, 30 * time.Second}
deployCmd := &DeployCommand{baseCmd, blueGreenDeployProcessParametersSetter(), &blueGreenDeployCommandProcessTypeProvider{}, os.Stdin, 30 * time.Second, nil}
bgDeployCmd := &BlueGreenDeployCommand{deployCmd}
baseCmd.Command = bgDeployCmd
return bgDeployCmd
Expand Down
Loading
Loading