Skip to content
Merged
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
6 changes: 3 additions & 3 deletions internal/pkg/addon/addon_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestAddons(t *testing.T) {
outFileName string
}{
"aurora": {
addonMarshaler: addon.NewRDS(addon.RDSProps{
addonMarshaler: addon.NewRDSTemplate(addon.RDSProps{
ClusterName: "aurora",
Engine: "MySQL",
InitialDBName: "main",
Expand All @@ -33,7 +33,7 @@ func TestAddons(t *testing.T) {
outFileName: "aurora.yml",
},
"ddb": {
addonMarshaler: addon.NewDynamoDB(&addon.DynamoDBProps{
addonMarshaler: addon.NewDDBTemplate(&addon.DynamoDBProps{
StorageProps: &addon.StorageProps{
Name: "ddb",
},
Expand Down Expand Up @@ -65,7 +65,7 @@ func TestAddons(t *testing.T) {
outFileName: "ddb.yml",
},
"s3": {
addonMarshaler: addon.NewS3(&addon.S3Props{
addonMarshaler: addon.NewS3Template(&addon.S3Props{
StorageProps: &addon.StorageProps{
Name: "bucket",
},
Expand Down
180 changes: 101 additions & 79 deletions internal/pkg/addon/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"regexp"
"strings"

"github.com/aws/copilot-cli/internal/pkg/manifest"

"github.com/aws/copilot-cli/internal/pkg/template"
)

const (
dynamoDbAddonPath = "addons/ddb/cf.yml"
s3AddonPath = "addons/s3/cf.yml"
rdsAddonPath = "addons/aurora/cf.yml"
dynamoDbTemplatePath = "addons/ddb/cf.yml"
s3TemplatePath = "addons/s3/cf.yml"
rdsTemplatePath = "addons/aurora/cf.yml"
rdsRDWSTemplatePath = "addons/aurora/rdws/cf.yml"
rdsRDWSParamsPath = "addons/aurora/rdws/addons.parameters.yml"
)

const (
Expand All @@ -33,41 +37,95 @@ var storageTemplateFunctions = map[string]interface{}{
"toSnakeCase": template.ToSnakeCaseFunc,
}

// DynamoDB contains configuration options which fully describe a DynamoDB table.
// DynamoDBTemplate contains configuration options which fully describe a DynamoDB table.
// Implements the encoding.BinaryMarshaler interface.
type DynamoDB struct {
type DynamoDBTemplate struct {
DynamoDBProps

parser template.Parser
}

// S3 contains configuration options which fully describe an S3 bucket.
// MarshalBinary serializes the content of the template into binary.
func (d *DynamoDBTemplate) MarshalBinary() ([]byte, error) {
content, err := d.parser.Parse(dynamoDbTemplatePath, *d, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
}

// S3Template contains configuration options which fully describe an S3 bucket.
// Implements the encoding.BinaryMarshaler interface.
type S3 struct {
type S3Template struct {
S3Props

parser template.Parser
}

// RDS contains configuration options which fully describe a RDS Aurora Serverless cluster.
// MarshalBinary serializes the content of the template into binary.
func (s *S3Template) MarshalBinary() ([]byte, error) {
content, err := s.parser.Parse(s3TemplatePath, *s, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
}

// RDSTemplate contains configuration options which fully describe a RDS Aurora Serverless cluster.
// Implements the encoding.BinaryMarshaler interface.
type RDS struct {
type RDSTemplate struct {
RDSProps

parser template.Parser
}

// StorageProps holds basic input properties for addon.NewDynamoDB() or addon.NewS3().
// MarshalBinary serializes the content of the template into binary.
func (r *RDSTemplate) MarshalBinary() ([]byte, error) {
path := rdsTemplatePath
if r.WorkloadType == manifest.RequestDrivenWebServiceType {
path = rdsRDWSTemplatePath
}
content, err := r.parser.Parse(path, *r, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
}

// RDSParams represents the addons.parameters.yml file for a RDS Aurora Serverless cluster.
type RDSParams struct {
parser template.Parser
}

// MarshalBinary serializes the content of the params file into binary.
func (r *RDSParams) MarshalBinary() ([]byte, error) {
content, err := r.parser.Parse(rdsRDWSParamsPath, *r, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
}

// StorageProps holds basic input properties for addon.NewDDBTemplate() or addon.NewS3Template().
type StorageProps struct {
Name string
}

// S3Props contains S3-specific properties for addon.NewS3().
// S3Props contains S3-specific properties for addon.NewS3Template().
type S3Props struct {
*StorageProps
}

// DynamoDBProps contains DynamoDB-specific properties for addon.NewDynamoDB().
// NewS3Template creates a new S3 marshaler which can be used to write CF via addonWriter.
func NewS3Template(input *S3Props) *S3Template {
return &S3Template{
S3Props: *input,

parser: template.New(),
}
}

// DynamoDBProps contains DynamoDB-specific properties for addon.NewDDBTemplate().
type DynamoDBProps struct {
*StorageProps
Attributes []DDBAttribute
Expand All @@ -77,87 +135,38 @@ type DynamoDBProps struct {
HasLSI bool
}

// DDBAttribute holds the attribute definition of a DynamoDB attribute (keys, local secondary indices).
type DDBAttribute struct {
Name *string
DataType *string // Must be one of "N", "S", "B"
}

// DDBLocalSecondaryIndex holds a representation of an LSI.
type DDBLocalSecondaryIndex struct {
PartitionKey *string
SortKey *string
Name *string
}

// RDSProps holds RDS-specific properties for addon.NewRDS().
type RDSProps struct {
// The name of the cluster.
ClusterName string
// The engine type of the RDS Aurora Serverless cluster.
Engine string
// The name of the initial database created inside the cluster.
InitialDBName string
// The parameter group to use for the cluster.
ParameterGroup string
// The copilot environments found inside the current app.
Envs []string
}

// MarshalBinary serializes the DynamoDB object into a binary YAML CF template.
// Implements the encoding.BinaryMarshaler interface.
func (d *DynamoDB) MarshalBinary() ([]byte, error) {
content, err := d.parser.Parse(dynamoDbAddonPath, *d, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
}

// NewDynamoDB creates a DynamoDB cloudformation template specifying attributes,
// NewDDBTemplate creates a DynamoDB cloudformation template specifying attributes,
// primary key schema, and local secondary index configuration.
func NewDynamoDB(input *DynamoDBProps) *DynamoDB {
return &DynamoDB{
func NewDDBTemplate(input *DynamoDBProps) *DynamoDBTemplate {
return &DynamoDBTemplate{
DynamoDBProps: *input,

parser: template.New(),
}
}

// MarshalBinary serializes the S3 object into a binary YAML CF template.
// Implements the encoding.BinaryMarshaler interface.
func (s *S3) MarshalBinary() ([]byte, error) {
content, err := s.parser.Parse(s3AddonPath, *s, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
// RDSProps holds RDS-specific properties for addon.NewRDSTemplate().
type RDSProps struct {
WorkloadType string // The type of the workload associated with the RDS addon.
ClusterName string // The name of the cluster.
Engine string // The engine type of the RDS Aurora Serverless cluster.
InitialDBName string // The name of the initial database created inside the cluster.
ParameterGroup string // The parameter group to use for the cluster.
Envs []string // The copilot environments found inside the current app.
}

// NewS3 creates a new S3 marshaler which can be used to write CF via addonWriter.
func NewS3(input *S3Props) *S3 {
return &S3{
S3Props: *input,
// NewRDSTemplate creates a new RDS marshaler which can be used to write a RDS CloudFormation template.
func NewRDSTemplate(input RDSProps) *RDSTemplate {
return &RDSTemplate{
RDSProps: input,

parser: template.New(),
}
}

// MarshalBinary serializes the RDS object into a binary YAML CF template.
// Implements the encoding.BinaryMarshaler interface.
func (r *RDS) MarshalBinary() ([]byte, error) {
content, err := r.parser.Parse(rdsAddonPath, *r, template.WithFuncs(storageTemplateFunctions))
if err != nil {
return nil, err
}
return content.Bytes(), nil
}

// NewRDS creates a new RDS marshaler which can be used to write CF via addonWriter.
func NewRDS(input RDSProps) *RDS {
return &RDS{
RDSProps: input,

// NewRDSParams creates a new RDS parameters marshaler.
func NewRDSParams() *RDSParams {
return &RDSParams{
parser: template.New(),
}
}
Expand Down Expand Up @@ -218,6 +227,12 @@ func (p *DynamoDBProps) BuildLocalSecondaryIndex(noLSI bool, lsiSorts []string)
return true, nil
}

// DDBAttribute holds the attribute definition of a DynamoDB attribute (keys, local secondary indices).
type DDBAttribute struct {
Name *string
DataType *string // Must be one of "N", "S", "B"
}

// DDBAttributeFromKey parses the DDB type and name out of keys specified in the form "Email:S"
func DDBAttributeFromKey(input string) (DDBAttribute, error) {
attrs := regexpMatchAttribute.FindStringSubmatch(input)
Expand All @@ -231,6 +246,13 @@ func DDBAttributeFromKey(input string) (DDBAttribute, error) {
}, nil
}

// DDBLocalSecondaryIndex holds a representation of an LSI.
type DDBLocalSecondaryIndex struct {
PartitionKey *string
SortKey *string
Name *string
}

func newLSI(partitionKey string, lsis []string) ([]DDBLocalSecondaryIndex, error) {
var output []DDBLocalSecondaryIndex
for _, lsi := range lsis {
Expand Down
Loading