Skip to content

Commit

Permalink
Goformation 1.0.0 (#79)
Browse files Browse the repository at this point in the history
This is a complete rewrite of GoFormation. It should fix a lot of old GoFormation bugs.
  • Loading branch information
PaulMaddox authored and sanathkr committed Aug 20, 2017
1 parent 269b612 commit f17d0ff
Show file tree
Hide file tree
Showing 1,157 changed files with 400,328 additions and 4,155 deletions.
53 changes: 22 additions & 31 deletions invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"encoding/json"

"github.com/awslabs/goformation"
"github.com/awslabs/goformation/resources"
"github.com/codegangsta/cli"
)

Expand All @@ -36,48 +35,40 @@ func invoke(c *cli.Context) {
}

filename := getTemplateFilename(c.String("template"))
template, _, errs := goformation.Open(filename)
if len(errs) > 0 {
for _, err := range errs {
log.Printf("%s\n", err)
}
os.Exit(1)
template, err := goformation.Open(filename)
if err != nil {
log.Fatalf("Failed to parse template: %s\n", err)
}

log.Printf("Successfully parsed %s (version %s)\n", filename, template.Version())
log.Printf("Successfully parsed %s\n", filename)

name := c.Args().First()

// Find the specified function in the SAM template. Either check for a function whose
// logical ID matches the first CLI argument, or if they only have a single function
// defined, and don't specify a name, then just use that function.
var function resources.AWSServerlessFunction
functions := template.GetResourcesByType("AWS::Serverless::Function")
for resourceName, resource := range functions {
if resourceName == name || (len(functions) == 1 && name == "") {
if f, ok := resource.(resources.AWSServerlessFunction); ok {
functions := template.GetAllAWSServerlessFunctionResources()
function, found := functions[name]
if !found {
if len(functions) == 1 && name == "" {
for _, f := range functions {
function = f
}
}
}

if function == nil {

if name == "" {
fmt.Fprintf(os.Stderr, "ERROR: You must provide a function identifier (function's Logical ID in the SAM template) as the first argument.\n")
} else {
fmt.Fprintf(os.Stderr, "ERROR: Could not find a AWS::Serverless::Function with logical ID '%s'\n", name)
}

// If have functions defined in the template, be helpful and suggest them
if len(functions) > 0 {
fmt.Fprintf(os.Stderr, "Possible options in your template:\n")
for resourceName := range functions {
fmt.Fprintf(os.Stderr, " * %s\n", resourceName)
if name == "" {
fmt.Fprintf(os.Stderr, "ERROR: You must provide a function identifier (function's Logical ID in the SAM template) as the first argument.\n")
} else {
fmt.Fprintf(os.Stderr, "ERROR: Could not find a AWS::Serverless::Function with logical ID '%s'\n", name)
}
// If have functions defined in the template, be helpful and suggest them
if len(functions) > 0 {
fmt.Fprintf(os.Stderr, "Possible options in your template:\n")
for resourceName := range functions {
fmt.Fprintf(os.Stderr, " * %s\n", resourceName)
}
}
os.Exit(1)
}
os.Exit(1)

}

// Check connectivity to docker
Expand Down Expand Up @@ -132,7 +123,7 @@ func invoke(c *cli.Context) {
DebugPort: c.String("debug-port"),
})
if err != nil {
log.Fatalf("Could not initiate %s runtime: %s\n", function.Runtime(), err)
log.Fatalf("Could not initiate %s runtime: %s\n", function.Runtime, err)
}

eventFile := c.String("event")
Expand Down
72 changes: 40 additions & 32 deletions runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"syscall"

"github.com/aws/aws-sdk-go/aws/session"
"github.com/awslabs/goformation/resources"
"github.com/awslabs/goformation/cloudformation"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
Expand Down Expand Up @@ -49,7 +49,7 @@ type Runtime struct {
Image string
Cwd string
DecompressedCwd string
Function resources.AWSServerlessFunction
Function cloudformation.AWSServerlessFunction
EnvVarOverrides map[string]string
DebugPort string
Context context.Context
Expand Down Expand Up @@ -93,7 +93,7 @@ var runtimeImageFor = map[string]string{

// NewRuntimeOpt contains parameters that are passed to the NewRuntime method
type NewRuntimeOpt struct {
Function resources.AWSServerlessFunction
Function cloudformation.AWSServerlessFunction
EnvVarsOverrides map[string]string
Basedir string
CheckWorkingDirExist bool
Expand All @@ -103,7 +103,7 @@ type NewRuntimeOpt struct {
// NewRuntime instantiates a Lambda runtime container
func NewRuntime(opt NewRuntimeOpt) (Invoker, error) {
// Determine which docker image to use for the provided runtime
image, found := runtimeImageFor[opt.Function.Runtime()]
image, found := runtimeImageFor[opt.Function.Runtime]
if !found {
return nil, ErrRuntimeNotSupported
}
Expand All @@ -113,13 +113,18 @@ func NewRuntime(opt NewRuntimeOpt) (Invoker, error) {
return nil, err
}

cwd, err := getWorkingDir(opt.Basedir, opt.Function.CodeURI().String(), opt.CheckWorkingDirExist)
codeuri := ""
if opt.Function.CodeUri != nil && opt.Function.CodeUri.String != nil {
codeuri = *opt.Function.CodeUri.String
}

cwd, err := getWorkingDir(opt.Basedir, codeuri, opt.CheckWorkingDirExist)
if err != nil {
return nil, err
}

r := &Runtime{
Name: opt.Function.Runtime(),
Name: opt.Function.Runtime,
Cwd: cwd,
Image: image,
Function: opt.Function,
Expand All @@ -139,7 +144,7 @@ func NewRuntime(opt NewRuntimeOpt) (Invoker, error) {
return nil, err
}

log.Printf("Fetching %s image for %s runtime...\n", r.Image, opt.Function.Runtime())
log.Printf("Fetching %s image for %s runtime...\n", r.Image, opt.Function.Runtime)
progress, err := cli.ImagePull(r.Context, r.Image, types.ImagePullOptions{})
if len(images) < 0 && err != nil {
log.Fatalf("Could not fetch %s Docker image\n%s", r.Image, err)
Expand Down Expand Up @@ -212,7 +217,7 @@ func (r *Runtime) getHostConfig() (*container.HostConfig, error) {

host := &container.HostConfig{
Resources: container.Resources{
Memory: int64(r.Function.MemorySize() * 1024 * 1024),
Memory: int64(r.Function.MemorySize * 1024 * 1024),
},
Binds: []string{
fmt.Sprintf("%s:/var/task:ro", mount),
Expand All @@ -232,7 +237,7 @@ func (r *Runtime) getHostConfig() (*container.HostConfig, error) {
// and stderr (runtime logs).
func (r *Runtime) Invoke(event string) (io.Reader, io.Reader, error) {

log.Printf("Invoking %s (%s)\n", r.Function.Handler(), r.Name)
log.Printf("Invoking %s (%s)\n", r.Function.Handler, r.Name)

// If the CodeUri has been specified as a .jar or .zip file, unzip it on the fly
if strings.HasSuffix(r.Cwd, ".jar") || strings.HasSuffix(r.Cwd, ".zip") {
Expand All @@ -255,7 +260,7 @@ func (r *Runtime) Invoke(event string) (io.Reader, io.Reader, error) {
Tty: false,
ExposedPorts: r.getDebugExposedPorts(),
Entrypoint: r.getDebugEntrypoint(),
Cmd: []string{r.Function.Handler(), event},
Cmd: []string{r.Function.Handler, event},
Env: func() []string {
result := []string{}
for k, v := range env {
Expand Down Expand Up @@ -314,13 +319,13 @@ func (r *Runtime) Invoke(event string) (io.Reader, io.Reader, error) {
func (r *Runtime) setupTimeoutTimer(stdout, stderr io.ReadCloser) {
// Start a timer, we'll use this to abort the function if it runs beyond the specified timeout
timeout := time.Duration(3) * time.Second
if r.Function.Timeout() > 0 {
timeout = time.Duration(r.Function.Timeout()) * time.Second
if r.Function.Timeout > 0 {
timeout = time.Duration(r.Function.Timeout) * time.Second
}
r.TimeoutTimer = time.NewTimer(timeout)
go func() {
<-r.TimeoutTimer.C
log.Printf("Function %s timed out after %d seconds", r.Function.Handler(), timeout/time.Second)
log.Printf("Function %s timed out after %d seconds", r.Function.Handler, timeout/time.Second)
stderr.Close()
stdout.Close()
r.CleanUp()
Expand All @@ -332,7 +337,7 @@ func (r *Runtime) setupInterruptHandler(stdout, stderr io.ReadCloser) {
signal.Notify(iChan, os.Interrupt, syscall.SIGTERM)
go func() {
<-iChan
log.Printf("Execution of function %q was interrupted", r.Function.Handler())
log.Printf("Execution of function %q was interrupted", r.Function.Handler)
stderr.Close()
stdout.Close()
r.CleanUp()
Expand Down Expand Up @@ -475,7 +480,7 @@ func (r *Runtime) getDebugEntrypoint() (overrides []string) {
This priority also applies to AWS_* system variables
*/
func getEnvironmentVariables(function resources.AWSServerlessFunction, overrides map[string]string) map[string]string {
func getEnvironmentVariables(function cloudformation.AWSServerlessFunction, overrides map[string]string) map[string]string {

creds := getSessionOrDefaultCreds()

Expand All @@ -487,9 +492,9 @@ func getEnvironmentVariables(function resources.AWSServerlessFunction, overrides
"AWS_ACCESS_KEY_ID": creds["key"],
"AWS_SECRET_ACCESS_KEY": creds["secret"],
"AWS_SESSION_TOKEN": creds["sessiontoken"],
"AWS_LAMBDA_FUNCTION_MEMORY_SIZE": strconv.Itoa(int(function.MemorySize())),
"AWS_LAMBDA_FUNCTION_TIMEOUT": strconv.Itoa(int(function.Timeout())),
"AWS_LAMBDA_FUNCTION_HANDLER": function.Handler(),
"AWS_LAMBDA_FUNCTION_MEMORY_SIZE": strconv.Itoa(int(function.MemorySize)),
"AWS_LAMBDA_FUNCTION_TIMEOUT": strconv.Itoa(int(function.Timeout)),
"AWS_LAMBDA_FUNCTION_HANDLER": function.Handler,
// "AWS_ACCOUNT_ID=",
// "AWS_LAMBDA_EVENT_BODY=",
// "AWS_REGION=",
Expand All @@ -499,27 +504,30 @@ func getEnvironmentVariables(function resources.AWSServerlessFunction, overrides

// Get all env vars from SAM file. Use values if it was hard-coded
osEnviron := getOsEnviron()
for name, value := range function.EnvironmentVariables() {
// hard-coded values, lowest priority
if stringedValue, ok := toStringMaybe(value); ok {
// Get only hard-coded values from the template
env[name] = stringedValue
}

// Shell's environment, second priority
if value, ok := osEnviron[name]; ok {
env[name] = value
}
if function.Environment != nil {
for name, value := range function.Environment.Variables {
// hard-coded values, lowest priority
if stringedValue, ok := toStringMaybe(value); ok {
// Get only hard-coded values from the template
env[name] = stringedValue
}

// EnvVars overrides provided by customer, highest priority
if len(overrides) > 0 {
if value, ok := overrides[name]; ok {
// Shell's environment, second priority
if value, ok := osEnviron[name]; ok {
env[name] = value
}

// EnvVars overrides provided by customer, highest priority
if len(overrides) > 0 {
if value, ok := overrides[name]; ok {
env[name] = value
}
}
}
}

return env

}

// Converts the input to string if it is a primitive type, Otherwise returns nil
Expand Down
17 changes: 7 additions & 10 deletions runtime_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"strings"

"github.com/awslabs/goformation"
"github.com/awslabs/goformation/resources"
"github.com/awslabs/goformation/cloudformation"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
Expand Down Expand Up @@ -82,16 +82,15 @@ var _ = Describe("sam", func() {

Context("environment variables", func() {

var functions map[string]resources.Resource
var functions map[string]cloudformation.AWSServerlessFunction
BeforeEach(func() {
template, _, _ := goformation.Open("test/templates/sam-official-samples/iot_backend/template.yaml")
functions = template.GetResourcesByType("AWS::Serverless::Function")
template, _ := goformation.Open("test/templates/sam-official-samples/iot_backend/template.yaml")
functions = template.GetAllAWSServerlessFunctionResources()
})

It("return defaults with those defined in the template", func() {

for _, resource := range functions {
function := resource.(resources.AWSServerlessFunction)
for _, function := range functions {
variables := getEnvironmentVariables(function, map[string]string{})
Expect(variables).To(HaveLen(10))
Expect(variables).To(HaveKey("AWS_SAM_LOCAL"))
Expand All @@ -108,8 +107,7 @@ var _ = Describe("sam", func() {
})

It("overides template with environment variables", func() {
for _, resource := range functions {
function := resource.(resources.AWSServerlessFunction)
for _, function := range functions {
variables := getEnvironmentVariables(function, map[string]string{})
Expect(variables["TABLE_NAME"]).To(Equal(""))

Expand All @@ -126,8 +124,7 @@ var _ = Describe("sam", func() {
"TABLE_NAME": "OVERRIDE_TABLE",
}

for _, resource := range functions {
function := resource.(resources.AWSServerlessFunction)
for _, function := range functions {
variables := getEnvironmentVariables(function, overrides)
Expect(variables["TABLE_NAME"]).To(Equal("OVERRIDE_TABLE"))
}
Expand Down
Loading

0 comments on commit f17d0ff

Please sign in to comment.