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

Asset deployment and a sample web application #511

Merged
merged 37 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
3202e1b
Working on a way for rain to deploy bucket contents
ericzbeard Aug 29, 2024
604c9d6
Metadata EmptyOnDelete for buckets
ericzbeard Aug 30, 2024
31725e4
Asset deployment, bucket cleanup, Metadata module Refs
ericzbeard Sep 3, 2024
c82b1af
Add webapp to build command menu
ericzbeard Sep 4, 2024
1f06d59
Fix distribution and content types
ericzbeard Sep 5, 2024
1b8903f
Fixed web acl
ericzbeard Sep 5, 2024
1396313
Adding Run command to s3Options
ericzbeard Sep 10, 2024
3eae74a
Add lambda function
ericzbeard Sep 10, 2024
3a0bd18
Lambda handler and API method
ericzbeard Sep 12, 2024
78ad2c1
Move test go lambda
ericzbeard Sep 12, 2024
3eb7d58
Fixed apigw integration
ericzbeard Sep 13, 2024
2f3d978
Adding api and authorizer
ericzbeard Sep 17, 2024
f48bf9c
Moving web app to test folder
ericzbeard Sep 17, 2024
ffda2c6
Fix distro
ericzbeard Sep 18, 2024
9c40f7c
Add authorizer to gateway method
ericzbeard Sep 18, 2024
b2ae7d9
npm site
ericzbeard Sep 18, 2024
842a53d
Site build script
ericzbeard Sep 19, 2024
bb7ac43
Adding jwt resource
ericzbeard Sep 20, 2024
ce9febf
Run script before uploading content
ericzbeard Sep 20, 2024
d71e5a8
Added an error with modules for overriding parameters
ericzbeard Sep 23, 2024
aad4563
jwt token verification
ericzbeard Sep 23, 2024
2df620e
Fix CORS response headers
ericzbeard Sep 24, 2024
654d872
Fixed module overrides to do a merge instead of replacement
ericzbeard Sep 24, 2024
fa7671c
Invalidate CloudFront in bucket module
ericzbeard Sep 24, 2024
f977723
Invalidate cloudfront and Run before deploy
ericzbeard Sep 25, 2024
d1175cb
Index page
ericzbeard Sep 25, 2024
1c49a7a
Getting 401 from the test resource
ericzbeard Sep 25, 2024
e0073cd
Fixed idtoken
ericzbeard Sep 26, 2024
c744021
Site is fully functional
ericzbeard Sep 26, 2024
d8499e4
Cognito template
ericzbeard Sep 30, 2024
b588abc
Changing Run command to add args
ericzbeard Oct 1, 2024
9829bd0
Fresh deployment now works with automated web site config
ericzbeard Oct 1, 2024
554e274
Adding webapp to build recommended templates
ericzbeard Oct 1, 2024
c14fc71
Fix double spaces
ericzbeard Oct 1, 2024
6327ae5
Remove default MFA setting
ericzbeard Oct 1, 2024
b3242a8
Generate docs
ericzbeard Oct 2, 2024
563fef2
Dependency updates and template validation
ericzbeard Oct 2, 2024
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ cfn-lint, Guard and more:

* **Modules** (EXPERIMENTAL): `rain pkg` supports client-side module development with the `!Rain::Module` directive. Rain modules are partial templates that are inserted into the parent template, with some extra functionality added to enable extending existing resource types. This feature integrates with CodeArtifact to enable package publish and install.

* **Content Deployment** (EXPERIMENTAL): `rain deploy` and `rain rm` support metadata commands that can upload static assets to a bucket and then delete those assets when the bucket is deleted. Rain can also run build scripts before and after stack deployment to prepare content like web sites and lambda functions before uploading to S3.

_Note that in order to use experimental commands, you have to add `--experimental` or `-x` as an argument._

## Getting started
Expand Down
17 changes: 14 additions & 3 deletions cft/cft.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"fmt"
"slices"

"github.com/aws-cloudformation/rain/internal/config"
"github.com/aws-cloudformation/rain/internal/node"
"github.com/aws-cloudformation/rain/internal/s11n"
"gopkg.in/yaml.v3"
Expand Down Expand Up @@ -118,6 +119,9 @@ func (t Template) AddMapSection(section Section) (*yaml.Node, error) {

// GetSection returns the yaml node for the section
func (t Template) GetSection(section Section) (*yaml.Node, error) {
if t.Node == nil {
return nil, fmt.Errorf("unable to get section because t.Node is nil")
}
_, s, _ := s11n.GetMapValue(t.Node.Content[0], string(section))
if s == nil {
return nil, fmt.Errorf("unable to locate the %s node", section)
Expand Down Expand Up @@ -148,20 +152,27 @@ func (t Template) GetTypes() ([]string, error) {
return retval, nil
}

func (t Template) GetResourcesOfType(typeName string) []*yaml.Node {
type Resource struct {
LogicalId string
Node *yaml.Node
}

func (t Template) GetResourcesOfType(typeName string) []*Resource {
resources, err := t.GetSection(Resources)
if err != nil {
config.Debugf("GetResourcesOfType error: %v", err)
return nil
}
retval := make([]*yaml.Node, 0)
retval := make([]*Resource, 0)
for i := 0; i < len(resources.Content); i += 2 {
logicalId := resources.Content[i].Value
resource := resources.Content[i+1]
_, typ, _ := s11n.GetMapValue(resource, "Type")
if typ == nil {
continue
}
if typ.Value == typeName {
retval = append(retval, resource)
retval = append(retval, &Resource{LogicalId: logicalId, Node: resource})
}
}
return retval
Expand Down
10 changes: 4 additions & 6 deletions cft/format/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ func formatNode(n *yaml.Node) *yaml.Node {
// Does it have just one key/value pair?
if len(n.Content) == 2 {

if n.Content[1].Kind == yaml.ScalarNode {
if NodeStyle == "quotescalars" {
n.Content[1].Style = yaml.DoubleQuotedStyle
}
}

// Is the key relevant?
for tag, funcName := range cft.Tags {
if n.Content[0].Value == funcName {
Expand Down Expand Up @@ -121,6 +115,10 @@ func formatNode(n *yaml.Node) *yaml.Node {
n.Style = yaml.FlowStyle
case "original":
// Do nothing, leave it alone
case "quotescalars":
if n.Kind == yaml.ScalarNode {
n.Style = yaml.DoubleQuotedStyle
}
case "":
// Default style for consistent formatting
n.Style = 0
Expand Down
20 changes: 20 additions & 0 deletions cft/parse/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,23 @@ Resources:
t.Error("Unexpected: resource is nil")
}
}

func TestGetResourcesOfType(t *testing.T) {

source := `
Resources:
Bucket:
Type: AWS::S3::Bucket
`

template, err := parse.String(source)
if err != nil {
t.Fatal(err)
}

resources := template.GetResourcesOfType("AWS::S3::Bucket")

if len(resources) != 1 {
t.Fatal("should have found 1 resource")
}
}
61 changes: 45 additions & 16 deletions cft/pkg/directives.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"

Expand All @@ -32,6 +33,7 @@ type s3Options struct {
KeyProperty string `yaml:"KeyProperty"`
Zip bool `yaml:"Zip"`
Format s3Format `yaml:"Format"`
Run string `yaml:"Run"`
}

type directiveContext struct {
Expand Down Expand Up @@ -109,7 +111,6 @@ func includeLiteral(ctx *directiveContext) (bool, error) {
}

func includeEnv(ctx *directiveContext) (bool, error) {
config.Debugf("includeEnv n: %v", node.ToSJson(ctx.n))
name, err := expectString(ctx.n)
if err != nil {
return false, err
Expand All @@ -131,6 +132,29 @@ func includeEnv(ctx *directiveContext) (bool, error) {
}

func handleS3(root string, options s3Options) (*yaml.Node, error) {

// Check to see if we need to run a build command first
if options.Run != "" {
relativePath := filepath.Join(".", root, options.Run)
absPath, absErr := filepath.Abs(relativePath)
if absErr != nil {
config.Debugf("filepath.Abs failed? %s", absErr)
return nil, absErr
}
cmd := exec.Command(absPath)
var stdout strings.Builder
var stderr strings.Builder
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.Dir = root
err := cmd.Run()
if err != nil {
config.Debugf("s3Option Run %s failed with %s: %s",
options.Run, err, stderr.String())
return nil, err
}
}

s, err := upload(root, options.Path, options.Zip)
if err != nil {
return nil, err
Expand Down Expand Up @@ -179,31 +203,36 @@ func handleS3(root string, options s3Options) (*yaml.Node, error) {
}

func includeS3Object(ctx *directiveContext) (bool, error) {

n := ctx.n
parent := ctx.parent
if n.Kind != yaml.MappingNode || len(n.Content) != 2 {
return false, errors.New("expected a map")
}

// Check to see if the Path is a Ref.
// Check to see if any of the properties is a Ref.
// The only valid use case is if the !Rain::S3 directive is inside a module,
// and the Ref points to one of the properties set in the parent template
_, pathOption, _ := s11n.GetMapValue(n.Content[1], "Path")
if pathOption != nil && pathOption.Kind == yaml.MappingNode {
if pathOption.Content[0].Value == "Ref" {
if parent.Parent != nil {
moduleParentMap := parent.Parent.Value
_, moduleParentProps, _ := s11n.GetMapValue(moduleParentMap, "Properties")
if moduleParentProps != nil {
_, pathProp, _ := s11n.GetMapValue(moduleParentProps, pathOption.Content[1].Value)
if pathProp != nil {
// Replace the Ref with the value
node.SetMapValue(n.Content[1], "Path", node.Clone(pathProp))
for i := 0; i < len(n.Content[1].Content); i += 2 {
s3opt := n.Content[1].Content[i+1]
name := n.Content[1].Content[i].Value
if s3opt.Kind == yaml.MappingNode {
if s3opt.Content[0].Value == "Ref" {
if parent.Parent != nil {
moduleParentMap := parent.Parent.Value
_, moduleParentProps, _ := s11n.GetMapValue(moduleParentMap, "Properties")
if moduleParentProps != nil {
_, parentProp, _ := s11n.GetMapValue(moduleParentProps, s3opt.Content[1].Value)
if parentProp != nil {
// Replace the Ref with the value
node.SetMapValue(n.Content[1], name, node.Clone(parentProp))
} else {
config.Debugf("expected Properties to have Path")
}
} else {
config.Debugf("expected Properties to have Path")
config.Debugf("expected parent resource to have Properties")
config.Debugf("moduleParentMap: %s", node.ToSJson(moduleParentMap))
}
} else {
config.Debugf("expected parent resource to have Properties")
}
}
}
Expand Down
Loading
Loading