Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CloudFormation resource type configuration support #199

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,6 @@ pre-commit run --all-files
pre-commit run pytest-local
```

Use `./generate-examples.sh` to run install `cloudformation-cli-go-plugin` locally and run `cfn generate` in each example.

Getting started
---------------

Expand Down
1 change: 1 addition & 0 deletions cfn/cfn.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ func makeEventFunc(h Handler) eventFunc {
credentials.SessionFromCredentialsProvider(&event.RequestData.CallerCredentials),
event.RequestData.PreviousResourceProperties,
event.RequestData.ResourceProperties,
event.RequestData.TypeConfiguration,
)
p := invoke(handlerFn, request, m, event.Action)
r, err := newResponse(&p, event.BearerToken)
Expand Down
1 change: 1 addition & 0 deletions cfn/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type requestData struct {
ProviderLogGroupName string `json:"providerLogGroupName"`
StackTags tags `json:"stackTags"`
SystemTags tags `json:"systemTags"`
TypeConfiguration json.RawMessage `json:"typeConfiguration"`
}

// validateEvent ensures the event struct generated from the Lambda SDK is correct
Expand Down
7 changes: 2 additions & 5 deletions cfn/handler/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,8 @@ type ProgressEvent struct {
// and by CREATE/UPDATE/DELETE for final response validation/confirmation
ResourceModel interface{} `json:"resourceModel,omitempty"`

// ResourceModels is the output resource instances populated by a LIST for
// synchronous results. ResourceModels must be returned by LIST so it's
// always included in the response. When ResourceModels is not set, null is
// returned.
ResourceModels []interface{} `json:"resourceModels"`
// ResourceModels is the output resource instances populated by a LIST for synchronous results
ResourceModels []interface{} `json:"resourceModels,omitempty"`

// NextToken is the token used to request additional pages of resources for a LIST operation
NextToken string `json:"nextToken,omitempty"`
Expand Down
73 changes: 0 additions & 73 deletions cfn/handler/event_test.go

This file was deleted.

8 changes: 4 additions & 4 deletions cfn/handler/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func TestNewRequest(t *testing.T) {
prev := Props{}
curr := Props{}

req := NewRequest("foo", nil, rctx, nil, []byte(`{"color": "red"}`), []byte(`{"color": "green"}`))
req := NewRequest("foo", nil, rctx, nil, []byte(`{"color": "red"}`), []byte(`{"color": "green"}`), []byte(``))

if err := req.UnmarshalPrevious(&prev); err != nil {
t.Fatalf("Unable to unmarshal props: %v", err)
Expand All @@ -43,7 +43,7 @@ func TestNewRequest(t *testing.T) {

t.Run("ResourceProps", func(t *testing.T) {
t.Run("Invalid Body", func(t *testing.T) {
req := NewRequest("foo", nil, rctx, nil, []byte(``), []byte(``))
req := NewRequest("foo", nil, rctx, nil, []byte(``), []byte(``), []byte(``))

invalid := struct {
Color *int `json:"color"`
Expand All @@ -61,7 +61,7 @@ func TestNewRequest(t *testing.T) {
})

t.Run("Invalid Marshal", func(t *testing.T) {
req := NewRequest("foo", nil, rctx, nil, []byte(`{"color": "ref"}`), []byte(`---BAD JSON---`))
req := NewRequest("foo", nil, rctx, nil, []byte(`{"color": "ref"}`), []byte(`---BAD JSON---`), []byte(``))

var invalid Props

Expand All @@ -79,7 +79,7 @@ func TestNewRequest(t *testing.T) {

t.Run("PreviousResourceProps", func(t *testing.T) {
t.Run("Invalid Marshal", func(t *testing.T) {
req := NewRequest("foo", nil, rctx, nil, []byte(`---BAD JSON---`), []byte(`{"color": "green"}`))
req := NewRequest("foo", nil, rctx, nil, []byte(`---BAD JSON---`), []byte(`{"color": "green"}`), []byte(``))

var invalid Props

Expand Down
18 changes: 17 additions & 1 deletion cfn/handler/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type Request struct {

previousResourcePropertiesBody []byte
resourcePropertiesBody []byte
typeConfiguration []byte
}

// RequestContext represents information about the current
Expand All @@ -61,14 +62,15 @@ type RequestContext struct {
}

// NewRequest returns a new Request based on the provided parameters
func NewRequest(id string, ctx map[string]interface{}, requestCTX RequestContext, sess *session.Session, previousBody, body []byte) Request {
func NewRequest(id string, ctx map[string]interface{}, requestCTX RequestContext, sess *session.Session, previousBody, body []byte, config []byte) Request {
return Request{
LogicalResourceID: id,
CallbackContext: ctx,
Session: sess,
previousResourcePropertiesBody: previousBody,
resourcePropertiesBody: body,
RequestContext: requestCTX,
typeConfiguration: config,
}
}

Expand Down Expand Up @@ -99,3 +101,17 @@ func (r *Request) Unmarshal(v interface{}) error {

return nil
}

// UnmarshalType populates the provided interface
// with the current resource type configuration
func (r *Request) UnmarshalType(v interface{}) error {
if len(r.resourcePropertiesBody) == 0 {
return cfnerr.New(bodyEmptyError, "Body is empty", nil)
}

if err := encoding.Unmarshal(r.typeConfiguration, v); err != nil {
return cfnerr.New(marshalingError, "Unable to convert type", err)
}

return nil
}
7 changes: 2 additions & 5 deletions cfn/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ type response struct {
//passed back to CloudFormation
BearerToken string `json:"bearerToken,omitempty"`

// ResourceModels is the output resource instances populated by a LIST for
// synchronous results. ResourceModels must be returned by LIST so it's
// always included in the response. When ResourceModels is not set, null is
// returned.
ResourceModels []interface{} `json:"resourceModels"`
// ResourceModels is the output resource instances populated by a LIST for synchronous results
ResourceModels []interface{} `json:"resourceModels,omitempty"`

// NextToken the token used to request additional pages of resources for a LIST operation
NextToken string `json:"nextToken,omitempty"`
Expand Down
69 changes: 17 additions & 52 deletions cfn/response_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,66 +12,31 @@ import (
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
)

func TestResponseMarshalJSON(t *testing.T) {
func TestMarshalJSON(t *testing.T) {
type Model struct {
Name *encoding.String
Version *encoding.Float
}

for _, tt := range []struct {
name string
response response
expected string
}{
{
name: "updated failed",
response: response{
Message: "foo",
OperationStatus: handler.Failed,
ResourceModel: Model{
Name: encoding.NewString("Douglas"),
Version: encoding.NewFloat(42.1),
},
ErrorCode: cloudformation.HandlerErrorCodeNotUpdatable,
BearerToken: "xyzzy",
},
expected: `{"message":"foo","status":"FAILED","resourceModel":{"Name":"Douglas","Version":"42.1"},"errorCode":"NotUpdatable","bearerToken":"xyzzy","resourceModels":null}`,
r := response{
Message: "foo",
OperationStatus: handler.Success,
ResourceModel: Model{
Name: encoding.NewString("Douglas"),
Version: encoding.NewFloat(42.1),
},
{
name: "list with 1 result",
response: response{
OperationStatus: handler.Success,
ResourceModels: []interface{}{
Model{
Name: encoding.NewString("Douglas"),
Version: encoding.NewFloat(42.1),
},
},
BearerToken: "xyzzy",
},
expected: `{"status":"SUCCESS","bearerToken":"xyzzy","resourceModels":[{"Name":"Douglas","Version":"42.1"}]}`,
},
{
name: "list with empty array",
response: response{
OperationStatus: handler.Success,
ResourceModels: []interface{}{},
BearerToken: "xyzzy",
},
expected: `{"status":"SUCCESS","bearerToken":"xyzzy","resourceModels":[]}`,
},
} {
t.Run(tt.name, func(t *testing.T) {
ErrorCode: cloudformation.HandlerErrorCodeNotUpdatable,
BearerToken: "xyzzy",
}

actual, err := json.Marshal(tt.response)
if err != nil {
t.Errorf("Unexpected error marshaling response JSON: %s", err)
}
expected := `{"message":"foo","status":"SUCCESS","resourceModel":{"Name":"Douglas","Version":"42.1"},"errorCode":"NotUpdatable","bearerToken":"xyzzy"}`

if diff := cmp.Diff(string(actual), tt.expected); diff != "" {
t.Errorf(diff)
}
})
actual, err := json.Marshal(r)
if err != nil {
t.Errorf("Unexpected error marshaling response JSON: %s", err)
}

if diff := cmp.Diff(string(actual), expected); diff != "" {
t.Errorf(diff)
}
}
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ module github.com/aws-cloudformation/cloudformation-cli-go-plugin
go 1.13

require (
github.com/avast/retry-go v2.6.0+incompatible
github.com/aws/aws-lambda-go v1.13.3
github.com/aws/aws-sdk-go v1.25.37
github.com/google/go-cmp v0.3.1
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/avast/retry-go v2.6.0+incompatible h1:FelcMrm7Bxacr1/RM8+/eqkDkmVN7tjlsy51dOzB3LI=
github.com/avast/retry-go v2.6.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.25.37 h1:gBtB/F3dophWpsUQKN/Kni+JzYEH2mGHF4hWNtfED1w=
github.com/aws/aws-sdk-go v1.25.37/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
Expand Down Expand Up @@ -38,7 +35,6 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BG
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/validator.v2 v2.0.0-20191107172027-c3144fdedc21 h1:2QQcyaEBdpfjjYkF0MXc69jZbHb4IOYuXz2UwsmVM8k=
Expand Down
Loading