Skip to content

Commit

Permalink
feat: SQS standard queues (experimental) (#1503)
Browse files Browse the repository at this point in the history
* feat: SQS standard queues (experimental)

This is the initial steel thread, and should be considered experimental
at this stage.

[#186768674](https://www.pivotaltracker.com/story/show/186768674)

* feat: SQS resolve code review comments

[#186768674](https://www.pivotaltracker.com/story/show/186768674)
  • Loading branch information
blgm authored Jan 31, 2024
1 parent deaf198 commit c6d9928
Show file tree
Hide file tree
Showing 13 changed files with 329 additions and 3 deletions.
1 change: 1 addition & 0 deletions .envrc
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export GSB_SERVICE_CSB_AWS_AURORA_MYSQL_PLANS='[{"name":"default","id":"10b2bd92
export GSB_SERVICE_CSB_AWS_MYSQL_PLANS='[{"name":"default","id":"0f3522b2-f040-443b-bc53-4aed25284840","description":"Default MySQL plan","display_name":"default","instance_class":"db.t3.micro","mysql_version":"8.0","storage_gb":100}]'
export GSB_SERVICE_CSB_AWS_REDIS_PLANS='[{"name":"default", "id":"c7f64994-a1d9-4e1f-9491-9d8e56bbf146","description":"Default Redis plan","display_name":"default","node_type":"cache.t3.medium","redis_version": "6.0"},{"name" : "example-with-flexible-node-type","id" : "2deb6c13-7ea1-4bad-a519-0ac9600e9a29","description" : "An example of a Redis plan for which node_type can be specified at provision time. Replace with your own plan configuration.","redis_version" : "6.x","node_count" : 2}]'
export GSB_SERVICE_CSB_AWS_MSSQL_PLANS='[{"name":"default","id":"7400cd8f-5f98-4457-8de0-03232ec12f62","description":"Default MSSQL plan","display_name":"default","engine":"sqlserver-se","mssql_version":"15.00","storage_gb":100, "instance_class":"db.r5.large" }]'
export GSB_SERVICE_CSB_AWS_SQS_PLANS='[{"name":"standard","id":"c2fdfc84-bf86-11ee-a4f5-8b0d531ce7e2","description":"Default SQS standard queue plan","display_name":"standard"}]'
export GSB_BROKERPAK_CONFIG='{"global_labels":[{"key": "key1", "value": "value1"},{"key": "key2", "value": "value2"}]}'
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ ifeq ($(GO_OK), 0) # use local go binary
GO=go
GOFMT=gofmt
BROKER_GO_OPTS=PORT=8080 \
DB_TYPE=sqlite3 \
DB_PATH=/tmp/csb-db \
BROKERPAK_UPDATES_ENABLED=$(BROKERPAK_UPDATES_ENABLED) \
SECURITY_USER_NAME=$(SECURITY_USER_NAME) \
SECURITY_USER_PASSWORD=$(SECURITY_USER_PASSWORD) \
AWS_ACCESS_KEY_ID='$(AWS_ACCESS_KEY_ID)' \
AWS_SECRET_ACCESS_KEY=$(AWS_SECRET_ACCESS_KEY) \
DB_TYPE=sqlite3 \
DB_PATH=/tmp/csb-db \
BROKERPAK_UPDATES_ENABLED=$(BROKERPAK_UPDATES_ENABLED) \
PAK_BUILD_CACHE_PATH=$(PAK_BUILD_CACHE_PATH) \
GSB_PROVISION_DEFAULTS='$(GSB_PROVISION_DEFAULTS)' \
GSB_SERVICE_CSB_AWS_S3_BUCKET_PLANS='$(GSB_SERVICE_CSB_AWS_S3_BUCKET_PLANS)' \
Expand All @@ -40,6 +40,7 @@ BROKER_GO_OPTS=PORT=8080 \
GSB_SERVICE_CSB_AWS_AURORA_MYSQL_PLANS='$(GSB_SERVICE_CSB_AWS_AURORA_MYSQL_PLANS)' \
GSB_SERVICE_CSB_AWS_MYSQL_PLANS='$(GSB_SERVICE_CSB_AWS_MYSQL_PLANS)' \
GSB_SERVICE_CSB_AWS_REDIS_PLANS='$(GSB_SERVICE_CSB_AWS_REDIS_PLANS)' \
GSB_SERVICE_CSB_AWS_SQS_PLANS='$(GSB_SERVICE_CSB_AWS_SQS_PLANS)' \
GSB_COMPATIBILITY_ENABLE_BETA_SERVICES='$(GSB_COMPATIBILITY_ENABLE_BETA_SERVICES)'

PAK_PATH=$(PWD)
Expand All @@ -55,6 +56,7 @@ BROKER_DOCKER_OPTS=--rm -v $(PAK_BUILD_CACHE_PATH):$(PAK_BUILD_CACHE_PATH) -v $(
-e AWS_SECRET_ACCESS_KEY \
-e "DB_TYPE=sqlite3" \
-e "DB_PATH=/tmp/csb-db" \
-e BROKERPAK_UPDATES_ENABLED \
-e PAK_BUILD_CACHE_PATH=$(PAK_BUILD_CACHE_PATH) \
-e GSB_PROVISION_DEFAULTS \
-e GSB_SERVICE_CSB_AWS_S3_BUCKET_PLANS \
Expand All @@ -63,6 +65,7 @@ BROKER_DOCKER_OPTS=--rm -v $(PAK_BUILD_CACHE_PATH):$(PAK_BUILD_CACHE_PATH) -v $(
-e GSB_SERVICE_CSB_AWS_AURORA_MYSQL_PLANS \
-e GSB_SERVICE_CSB_AWS_MYSQL_PLANS \
-e GSB_SERVICE_CSB_AWS_REDIS_PLANS \
-e GSB_SERVICE_CSB_AWS_SQS_PLANS \
-e GSB_COMPATIBILITY_ENABLE_BETA_SERVICES

RUN_CSB=docker run $(BROKER_DOCKER_OPTS) $(CSB_DOCKER_IMAGE)
Expand Down
59 changes: 59 additions & 0 deletions aws-sqs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
version: 1
name: csb-aws-sqs
id: 2198d694-bf85-11ee-a918-a7bdfa69a96d
description: CSB AWS SQS
display_name: CSB AWS SQS
image_url: file://service-images/csb.png
documentation_url: https://docs.vmware.com/en/Cloud-Service-Broker-for-VMware-Tanzu/index.html
provider_display_name: VMware
support_url: https://aws.amazon.com/sqs/
tags: [aws, sqs, beta]
plan_updateable: true
provision:
user_inputs:
- field_name: region
type: string
details: The region of AWS.
default: us-west-2
constraints:
examples:
- us-west-2
- eu-west-1
pattern: ^[a-z][a-z0-9-]+$
prohibit_update: true
- field_name: aws_access_key_id
type: string
details: AWS access key
default: ${config("aws.access_key_id")}
- field_name: aws_secret_access_key
type: string
details: AWS secret key
default: ${config("aws.secret_access_key")}
computed_inputs:
- name: instance_name
default: csb-sqs-${request.instance_id}
overwrite: true
type: string
- name: labels
default: ${json.marshal(request.default_labels)}
overwrite: true
type: object
template_refs:
main: terraform/sqs/provision/main.tf
outputs: terraform/sqs/provision/outputs.tf
provider: terraform/sqs/provision/providers.tf
versions: terraform/sqs/provision/versions.tf
variables: terraform/sqs/provision/variables.tf
outputs:
- field_name: arn
type: string
details: ARN for the queue
- field_name: url
type: string
details: URL for the queue
- field_name: name
type: string
details: name for the queue
- field_name: region
type: string
details: AWS region for the queue
1 change: 1 addition & 0 deletions integration-tests/integration_test_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var _ = BeforeSuite(func() {
"GSB_SERVICE_CSB_AWS_MYSQL_PLANS=" + marshall(customMySQLPlans),
"GSB_SERVICE_CSB_AWS_REDIS_PLANS=" + marshall(customRedisPlans),
"GSB_SERVICE_CSB_AWS_MSSQL_PLANS=" + marshall(customMSSQLPlans),
"GSB_SERVICE_CSB_AWS_SQS_PLANS=" + marshall(customSQSPlans),
"AWS_ACCESS_KEY_ID=" + awsAccessKeyID,
"AWS_SECRET_ACCESS_KEY=" + awsSecretAccessKey,
"CSB_LISTENER_HOST=localhost",
Expand Down
156 changes: 156 additions & 0 deletions integration-tests/sqs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package integration_test

import (
"fmt"

testframework "github.com/cloudfoundry/cloud-service-broker/brokerpaktestframework"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
)

const (
sqsServiceID = "2198d694-bf85-11ee-a918-a7bdfa69a96d"
sqsServiceName = "csb-aws-sqs"
sqsServiceDescription = "CSB AWS SQS"
sqsServiceDisplayName = "CSB AWS SQS"
sqsServiceSupportURL = "https://aws.amazon.com/sqs/"
sqsServiceProviderDisplayName = "VMware"
sqsCustomStandardPlanName = "custom-standard"
sqsCustomStandardPlanID = "4c206ad6-bf89-11ee-8900-2f8e8940fc0d"
)

var customSQSPlans = []map[string]any{
customSQSPlan,
}

var customSQSPlan = map[string]any{
"name": sqsCustomStandardPlanName,
"id": sqsCustomStandardPlanID,
"description": "Custom SQS standard queue plan",
"metadata": map[string]any{
"displayName": "custom-standard",
},
}

var _ = Describe("SQS", Label("SQS"), func() {
BeforeEach(func() {
Expect(mockTerraform.SetTFState([]testframework.TFStateValue{})).To(Succeed())

DeferCleanup(func() {
Expect(mockTerraform.Reset()).To(Succeed())
})
})

It("should publish AWS SQS in the catalog", func() {
catalog, err := broker.Catalog()
Expect(err).NotTo(HaveOccurred())

service := testframework.FindService(catalog, sqsServiceName)
Expect(service.ID).To(Equal(sqsServiceID))
Expect(service.Description).To(Equal(sqsServiceDescription))
Expect(service.Tags).To(ConsistOf("aws", "sqs", "beta"))
Expect(service.Metadata.DisplayName).To(Equal(sqsServiceDisplayName))
Expect(service.Metadata.DocumentationUrl).To(Equal(documentationURL))
Expect(service.Metadata.ImageUrl).To(ContainSubstring("data:image/png;base64,"))
Expect(service.Metadata.SupportUrl).To(Equal(sqsServiceSupportURL))
Expect(service.Metadata.ProviderDisplayName).To(Equal(sqsServiceProviderDisplayName))
Expect(service.Plans).To(
ConsistOf(
MatchFields(IgnoreExtras, Fields{
Name: Equal(sqsCustomStandardPlanName),
ID: Equal(sqsCustomStandardPlanID),
}),
),
)
})

Describe("provisioning", func() {
DescribeTable("property constraints",
func(params map[string]any, expectedErrorMsg string) {
_, err := broker.Provision(sqsServiceName, sqsCustomStandardPlanName, params)

Expect(err).To(MatchError(ContainSubstring(expectedErrorMsg)))
},
Entry(
"invalid region",
map[string]any{"region": "-Asia-northeast1"},
"region: Does not match pattern '^[a-z][a-z0-9-]+$'",
),
)

It("should provision a plan", func() {
instanceID, err := broker.Provision(sqsServiceName, sqsCustomStandardPlanName, nil)
Expect(err).NotTo(HaveOccurred())

Expect(mockTerraform.FirstTerraformInvocationVars()).To(
SatisfyAll(
HaveKeyWithValue("labels", MatchKeys(IgnoreExtras, Keys{
"pcf-instance-id": Equal(instanceID),
"key1": Equal("value1"),
"key2": Equal("value2"),
})),
HaveKeyWithValue("instance_name", fmt.Sprintf("csb-sqs-%s", instanceID)),
HaveKeyWithValue("region", fakeRegion),
HaveKeyWithValue("aws_access_key_id", awsAccessKeyID),
HaveKeyWithValue("aws_secret_access_key", awsSecretAccessKey),
),
)
})

It("should allow properties to be set on provision", func() {
_, err := broker.Provision(sqsServiceName, sqsCustomStandardPlanName, map[string]any{
"region": "africa-north-4",
"aws_access_key_id": "fake-aws-access-key-id",
"aws_secret_access_key": "fake-aws-secret-access-key",
})
Expect(err).NotTo(HaveOccurred())

Expect(mockTerraform.FirstTerraformInvocationVars()).To(
SatisfyAll(
HaveKeyWithValue("region", "africa-north-4"),
HaveKeyWithValue("aws_access_key_id", "fake-aws-access-key-id"),
HaveKeyWithValue("aws_secret_access_key", "fake-aws-secret-access-key"),
),
)
})
})

Describe("updating instance", func() {
var instanceID string

BeforeEach(func() {
var err error
instanceID, err = broker.Provision(sqsServiceName, sqsCustomStandardPlanName, nil)

Expect(err).NotTo(HaveOccurred())
})

DescribeTable("should prevent updating properties flagged as `prohibit_update` because it can result in the recreation of the service instance",
func(prop string, value any) {
err := broker.Update(instanceID, sqsServiceName, sqsCustomStandardPlanName, map[string]any{prop: value})

Expect(err).To(MatchError(
ContainSubstring(
"attempt to update parameter that may result in service instance re-creation and data loss",
),
))

const initialProvisionInvocation = 1
Expect(mockTerraform.ApplyInvocations()).To(HaveLen(initialProvisionInvocation))
},
Entry("update region", "region", "no-matter-what-region"),
)

DescribeTable(
"some allowed updates",
func(prop string, value any) {
err := broker.Update(instanceID, sqsServiceName, sqsCustomStandardPlanName, map[string]any{prop: value})

Expect(err).NotTo(HaveOccurred())
},
Entry(nil, "aws_access_key_id", "fake-aws-access-key-id"),
Entry(nil, "aws_secret_access_key", "fake-aws-secret-access-key"),
)
})
})
1 change: 1 addition & 0 deletions manifest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ service_definitions:
- aws-aurora-postgresql.yml
- aws-aurora-mysql.yml
- aws-mssql.yml
- aws-sqs.yml



Expand Down
6 changes: 6 additions & 0 deletions scripts/push-broker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ if [[ -z "$GSB_SERVICE_CSB_AWS_MSSQL_PLANS" ]]; then
fi
echo " GSB_SERVICE_CSB_AWS_MSSQL_PLANS: $(echo "$GSB_SERVICE_CSB_AWS_MSSQL_PLANS" | jq @json)" >>$cfmf

if [[ -z "$GSB_SERVICE_CSB_AWS_SQS_PLANS" ]]; then
echo "Missing GSB_SERVICE_CSB_AWS_SQS_PLANS variable"
exit 1
fi
echo " GSB_SERVICE_CSB_AWS_SQS_PLANS: $(echo "$GSB_SERVICE_CSB_AWS_SQS_PLANS" | jq @json)" >>$cfmf

cf push --no-start -f "${cfmf}" --var app=${APP_NAME}

if [[ -z ${MSYQL_INSTANCE} ]]; then
Expand Down
65 changes: 65 additions & 0 deletions terraform-tests/sqs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package terraformtests

import (
. "csbbrokerpakaws/terraform-tests/helpers"
"fmt"
"path"
"time"

tfjson "github.com/hashicorp/terraform-json"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
)

var _ = Describe("SQS", Label("SQS-terraform"), Ordered, func() {
var (
name string
plan tfjson.Plan
terraformProvisionDir string
defaultVars map[string]any
)

BeforeAll(func() {
name = fmt.Sprintf("csb-tf-test-sqs-%d-%d", GinkgoRandomSeed(), time.Now().Unix())

terraformProvisionDir = path.Join(workingDir, "sqs/provision")
Init(terraformProvisionDir)
})

BeforeEach(func() {
defaultVars = map[string]any{
"instance_name": name,
"labels": map[string]string{"label1": "value1"},
"aws_access_key_id": awsAccessKeyID,
"aws_secret_access_key": awsSecretAccessKey,
"region": awsRegion,
}
})

Context("with default values", func() {
BeforeAll(func() {
plan = ShowPlan(terraformProvisionDir, buildVars(defaultVars, map[string]any{}))
})

It("should create the right resources", func() {
Expect(plan.ResourceChanges).To(HaveLen(1))

Expect(ResourceChangesTypes(plan)).To(ConsistOf(
"aws_sqs_queue",
))
})

It("should create an SQS queue with the correct properties", func() {
Expect(AfterValuesForType(plan, "aws_sqs_queue")).To(
MatchKeys(IgnoreExtras, Keys{
"name": Equal(name),
"fifo_queue": BeFalse(),
"tags": MatchAllKeys(Keys{
"label1": Equal("value1"),
}),
}),
)
})
})
})
5 changes: 5 additions & 0 deletions terraform/sqs/provision/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "aws_sqs_queue" "queue" {
name = var.instance_name
fifo_queue = false
tags = var.labels
}
4 changes: 4 additions & 0 deletions terraform/sqs/provision/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
output "arn" { value = aws_sqs_queue.queue.arn }
output "url" { value = aws_sqs_queue.queue.id }
output "name" { value = aws_sqs_queue.queue.name }
output "region" { value = var.region }
5 changes: 5 additions & 0 deletions terraform/sqs/provision/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
provider "aws" {
region = var.region
access_key = var.aws_access_key_id
secret_key = var.aws_secret_access_key
}
12 changes: 12 additions & 0 deletions terraform/sqs/provision/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
variable "aws_access_key_id" {
type = string
sensitive = true
}
variable "aws_secret_access_key" {
type = string
sensitive = true
}
variable "region" { type = string }

variable "instance_name" { type = string }
variable "labels" { type = map(any) }
Loading

0 comments on commit c6d9928

Please sign in to comment.