Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions clients/cfrestclient/cloud_foundry_operations_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ 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)
}
4 changes: 4 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,7 @@ 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
}
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,12 @@ 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 retryOnError[T any](operation func() (T, error), retries int, retryInterval time.Duration) (T, error) {
result, err := operation()
for shouldRetry(retries, err) {
Expand Down
21 changes: 21 additions & 0 deletions clients/cfrestclient/rest_cloud_foundry_client_extended.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,27 @@ 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 getPaginatedResources[T any](url, token string, isSslDisabled bool) ([]T, error) {
var result []T
for url != "" {
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
239 changes: 212 additions & 27 deletions commands/deploy_command.go

Large diffs are not rendered by default.

117 changes: 111 additions & 6 deletions commands/deploy_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ var _ = Describe("DeployCommand", func() {
const testArchive = "mtaArchive.mtar"
const mtaArchivePath = testFilesLocation + testArchive
const extDescriptorPath = testFilesLocation + "extDescriptor.mtaext"
const userProvidedServiceSecurityRelated = "__mta-secure-anatz"

var name string
var cliConnection *plugin_fakes.FakeCliConnection
Expand Down Expand Up @@ -105,7 +106,7 @@ var _ = Describe("DeployCommand", func() {
}
}

var getOutputLines = func(extDescriptor, processAborted, fromUrl bool) []string {
var getOutputLines = func(extDescriptor, processAborted, fromUrl, existentUserProvidedServiceSecurity, createdUserProvidedServiceSecurity bool) []string {
var lines []string
mtaNameToPrint := mtaArchivePath
if fromUrl {
Expand Down Expand Up @@ -134,6 +135,14 @@ var _ = Describe("DeployCommand", func() {
" "+fullExtDescriptorPath,
"OK")
}
if existentUserProvidedServiceSecurity {
lines = append(lines,
"Using existing user-provided service "+userProvidedServiceSecurityRelated+" for secure parameters.")
}
if createdUserProvidedServiceSecurity {
lines = append(lines,
"Created user-provided service "+userProvidedServiceSecurityRelated+" for secure parameters.")
}
lines = append(lines,
"Test message",
"Process finished.",
Expand Down Expand Up @@ -246,7 +255,7 @@ var _ = Describe("DeployCommand", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, true))
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, true, false, false))
})
})

Expand Down Expand Up @@ -348,7 +357,7 @@ var _ = Describe("DeployCommand", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false))
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(false), operation.Parameters)
})
Expand All @@ -360,7 +369,7 @@ var _ = Describe("DeployCommand", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "-e", extDescriptorPath}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(true, false, false))
ex.ExpectSuccessWithOutput(status, output, getOutputLines(true, false, false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(false), operation.Parameters)
})
Expand All @@ -372,7 +381,7 @@ var _ = Describe("DeployCommand", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath, "-f", "-delete-services", "-no-start", "-keep-files", "-do-not-fail-on-missing-permissions"}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false))
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(true), operation.Parameters)
})
Expand Down Expand Up @@ -412,7 +421,7 @@ var _ = Describe("DeployCommand", func() {
output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{mtaArchivePath}).ToInt()
})
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false))
ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, false, false, false))
// operation := mtaClient.StartMtaOperationArgsForCall(1)
// expectProcessParameters(getProcessParameters(false), operation.Parameters)
})
Expand Down Expand Up @@ -494,5 +503,101 @@ var _ = Describe("DeployCommand", func() {
ex.ExpectSuccessWithOutput(status, output, getLinesForAbortingProcess())
})
})

Context("with --require-secure-parameters flag and a user-provided service instance which already exists", func() {
It("should not create a new user-provided service", func() {
os.Setenv("__MTA___fake-variable", "fakeSecret")
defer os.Unsetenv("__MTA___fake-variable")
command.FileUrlReader = newMockFileReader(correctMtaUrl)

upsName := "__mta-secure-anatz"
cliConnection.CliCommandWithoutTerminalOutputStub = func(args ...string) ([]string, error) {
if len(args) > 0 && args[0] == "services" {
table := fmt.Sprintf("%s user-provided fake-plan\nanother-service-instance managed fake-plan\n", upsName)
return []string{table}, nil
}
return []string{}, nil
}

output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"--require-secure-parameters"}).ToInt()
})

ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, true, true, false))
Expect(output).To(ContainElement(ContainSubstring("Using existing user-provided service")))
Expect(output).To(ContainElement(ContainSubstring(upsName)))

callCount := mtaClient.StartMtaOperationCallCount()
Expect(callCount).To(BeNumerically(">", 0))
operation := mtaClient.StartMtaOperationArgsForCall(callCount - 1)
Expect(operation.Parameters["isSecurityEnabled"]).To(Equal("true"))
})
})

Context("with --require-secure-parameters flag and a user-provided service instance missing", func() {
It("should create a new user-provided service using the appropriate cf command", func() {
os.Setenv("__MTA___fake-variable", "fakeSecret")
defer os.Unsetenv("__MTA___fake-variable")
command.FileUrlReader = newMockFileReader(correctMtaUrl)

cliConnection.CliCommandWithoutTerminalOutputStub = func(args ...string) ([]string, error) {
if len(args) > 0 && args[0] == "services" {
return []string{"another-service-instance managed fake-plan\n"}, nil
}
return []string{}, nil
}

cliConnection.CliCommandStub = func(args ...string) ([]string, error) {
if len(args) > 0 && args[0] == "create-user-provided-service" {
return []string{}, nil
}
return []string{}, nil
}

output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"--require-secure-parameters"}).ToInt()
})

ex.ExpectSuccessWithOutput(status, output, getOutputLines(false, false, true, false, true))
Expect(output).To(ContainElement(ContainSubstring("Created user-provided service")))
Expect(output).To(ContainElement(ContainSubstring("__mta-secure-anatz")))

callCount := mtaClient.StartMtaOperationCallCount()
Expect(callCount).To(BeNumerically(">", 0))
operation := mtaClient.StartMtaOperationArgsForCall(callCount - 1)
Expect(operation.Parameters["isSecurityEnabled"]).To(Equal("true"))
})
})

Context("with --require-secure-parameters and `cf services` fails", func() {
It("should return an error from the UPS existence check", func() {
os.Setenv("__MTA___fake-variable", "fakeSecret")
defer os.Unsetenv("__MTA___fake-variable")
command.FileUrlReader = newMockFileReader(correctMtaUrl)

cliConnection.CliCommandWithoutTerminalOutputStub = func(args ...string) ([]string, error) {
if len(args) > 0 && args[0] == "services" {
return []string{"another-service-instance managed fake-plan\n"}, nil
}
return []string{}, nil
}

cliConnection.CliCommandStub = func(args ...string) ([]string, error) {
if len(args) > 0 && args[0] == "create-user-provided-service" {
return nil, fmt.Errorf("error - could not be created")
}
return []string{}, nil
}

output, status := oc.CaptureOutputAndStatus(func() int {
return command.Execute([]string{"--require-secure-parameters"}).ToInt()
})

ex.ExpectFailure(status, output, "")
Expect(output).To(ContainElement(ContainSubstring("Could not ensure user-provided service")))
Expect(mtaClient.StartMtaOperationCallCount()).To(Equal(0))
})
})

})
})
27 changes: 27 additions & 0 deletions commands/file_uploader.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package commands

import (
"bytes"
"fmt"
"io"
"os"
Expand Down Expand Up @@ -63,6 +64,23 @@ func (r *progressBarReader) Close() error {
return nil
}

type namedBytesReader struct {
r *bytes.Reader
fileName string
}

func (n *namedBytesReader) Read(p []byte) (int, error) {
return n.r.Read(p)
}

func (n *namedBytesReader) Seek(o int64, w int) (int64, error) {
return n.r.Seek(o, w)
}

func (n *namedBytesReader) Name() string {
return n.fileName
}

// NewFileUploader creates a new file uploader for the specified namespace
func NewFileUploader(mtaClient mtaclient.MtaClientOperations, namespace string, uploadChunkSizeInMB uint64,
sequentialUpload, shouldDisableProgressBar bool) *FileUploader {
Expand Down Expand Up @@ -249,3 +267,12 @@ func (f *FileUploader) isFileAlreadyUploaded(newFilePath string, fileInfo os.Fil
}
return false
}

func (f *FileUploader) UploadBytes(filename string, content []byte) (string, error) {
nb := &namedBytesReader{r: bytes.NewReader(content), fileName: filename}
uploadedFile, err := f.mtaClient.UploadMtaFile(nb, int64(len(content)), &f.namespace)
if err != nil {
return "", fmt.Errorf("Could not upload in-memory file %s: %w", filename, err)
}
return uploadedFile.ID, nil
}
35 changes: 35 additions & 0 deletions secure_parameters/secure_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package secure_parameters

import (
"errors"

"gopkg.in/yaml.v3"
)

func BuildSecureExtension(parameters map[string]ParameterValue, mtaID string, schemaVersion string) ([]byte, error) {
if len(parameters) == 0 {
return nil, errors.New("no secure parameters collected")
}

if mtaID == "" {
return nil, errors.New("mtaID is required for the secure extension descriptor's field 'extends'")
}

if schemaVersion == "" {
return nil, errors.New("schemaVersion is required for the secure extension descriptor")
}

secureExtensionDescriptor := map[string]interface{}{
"_schema-version": schemaVersion,
"ID": "__mta.secure",
"extends": mtaID,
"parameters": map[string]interface{}{},
}

parametersDescriptor := secureExtensionDescriptor["parameters"].(map[string]interface{})
for name, currentParameterValue := range parameters {
parametersDescriptor[name] = getValue(&currentParameterValue)
}

return yaml.Marshal(secureExtensionDescriptor)
}
Loading
Loading