diff --git a/examples/lambda/node/README.md b/examples/lambda/node/README.md index 640a0a91..9480dc46 100644 --- a/examples/lambda/node/README.md +++ b/examples/lambda/node/README.md @@ -2,12 +2,12 @@ This is the exact same function that is in the AWS Lambda tutorial. -Other than a different runtime, this is no different than any other node example. +Other than a different runtime, this is no different than any other node example. -To use the lambda-node runtime, use this `fn init` command: +To use the lambda-nodejs4.3 runtime, use this `fn init` command: ```sh -fn init --runtime lambda-node /lambda-node +fn init --runtime lambda-nodejs4.3 /lambda-node fn build cat payload.json | fn run ``` diff --git a/fn/common.go b/fn/common.go index 8e909d67..582a8bee 100644 --- a/fn/common.go +++ b/fn/common.go @@ -118,22 +118,22 @@ func exists(name string) bool { } var acceptableFnRuntimes = map[string]string{ - "elixir": "iron/elixir", - "erlang": "iron/erlang", - "gcc": "iron/gcc", - "go": "iron/go", - "java": "iron/java", - "leiningen": "iron/leiningen", - "mono": "iron/mono", - "node": "iron/node", - "perl": "iron/perl", - "php": "iron/php", - "python": "iron/python:2", - "ruby": "iron/ruby", - "scala": "iron/scala", - "rust": "corey/rust-alpine", - "dotnet": "microsoft/dotnet:runtime", - "lambda-node": "iron/functions-lambda:node", + "elixir": "iron/elixir", + "erlang": "iron/erlang", + "gcc": "iron/gcc", + "go": "iron/go", + "java": "iron/java", + "leiningen": "iron/leiningen", + "mono": "iron/mono", + "node": "iron/node", + "perl": "iron/perl", + "php": "iron/php", + "python": "iron/python:2", + "ruby": "iron/ruby", + "scala": "iron/scala", + "rust": "corey/rust-alpine", + "dotnet": "microsoft/dotnet:runtime", + "lambda-nodejs4.3": "iron/functions-lambda:nodejs4.3", } const tplDockerfile = `FROM {{ .BaseImage }} diff --git a/fn/lambda.go b/fn/lambda.go index 6376f654..26d74b93 100644 --- a/fn/lambda.go +++ b/fn/lambda.go @@ -2,7 +2,6 @@ package main import ( "archive/zip" - "bytes" "errors" "fmt" "io" @@ -17,16 +16,12 @@ import ( "github.com/aws/aws-sdk-go/aws/session" aws_lambda "github.com/aws/aws-sdk-go/service/lambda" "github.com/docker/docker/pkg/jsonmessage" - lambdaImpl "github.com/iron-io/lambda/lambda" "github.com/urfave/cli" yaml "gopkg.in/yaml.v2" ) -func init() { - if len(runtimeCreateHandlers) != len(runtimeImportHandlers) { - panic("incomplete implementation of runtime support") - } - +var runtimes = map[string]string{ + "nodejs4.3": "lambda-nodejs4.3", } func lambda() cli.Command { @@ -38,20 +33,6 @@ func lambda() cli.Command { Name: "lambda", Usage: "create and publish lambda functions", Subcommands: []cli.Command{ - { - Name: "create-function", - Usage: `create Docker image that can run your Lambda function, where files are the contents of the zip file to be uploaded to AWS Lambda.`, - ArgsUsage: " [/paths...]", - Action: create, - Flags: flags, - }, - { - Name: "test-function", - Usage: `runs local dockerized Lambda function and writes output to stdout.`, - ArgsUsage: "", - Action: test, - Flags: flags, - }, { Name: "aws-import", Usage: `converts an existing Lambda function to an image, where the function code is downloaded to a directory in the current working directory that has the same name as the Lambda function.`, @@ -100,87 +81,6 @@ func transcribeEnvConfig(configs []string) map[string]string { return c } -// create creates the Docker image for the Lambda function -func create(c *cli.Context) error { - args := c.Args() - functionName := args[0] - runtime := args[1] - handler := args[2] - fileNames := args[3:] - - files := make([]fileLike, 0, len(fileNames)) - opts := createImageOptions{ - Name: functionName, - Base: fmt.Sprintf("iron/lambda-%s", runtime), - Package: "", - Handler: handler, - OutputStream: newdockerJSONWriter(os.Stdout), - RawJSONStream: true, - Config: transcribeEnvConfig(c.StringSlice("config")), - } - - if handler == "" { - return errors.New("No handler specified.") - } - - rh, ok := runtimeCreateHandlers[runtime] - if !ok { - return fmt.Errorf("unsupported runtime %v", runtime) - } - - if err := rh(fileNames, &opts); err != nil { - return err - } - - for _, fileName := range fileNames { - file, err := os.Open(fileName) - if err != nil { - return err - } - defer file.Close() - files = append(files, file) - } - - return createDockerfile(opts, files...) -} - -var runtimeCreateHandlers = map[string]func(filenames []string, opts *createImageOptions) error{ - "nodejs": func(filenames []string, opts *createImageOptions) error { return nil }, - "python2.7": func(filenames []string, opts *createImageOptions) error { return nil }, - "java8": func(filenames []string, opts *createImageOptions) error { - if len(filenames) != 1 { - return errors.New("Java Lambda functions can only include 1 file and it must be a JAR file.") - } - - if filepath.Ext(filenames[0]) != ".jar" { - return errors.New("Java Lambda function package must be a JAR file.") - } - - opts.Package = filepath.Base(filenames[0]) - return nil - }, -} - -func test(c *cli.Context) error { - args := c.Args() - if len(args) < 1 { - return fmt.Errorf("Missing NAME argument") - } - functionName := args[0] - - exists, err := lambdaImpl.ImageExists(functionName) - if err != nil { - return err - } - if !exists { - return fmt.Errorf("Function %s does not exist.", functionName) - } - - payload := c.String("payload") - // Redirect output to stdout. - return lambdaImpl.RunImageWithPayload(functionName, payload) -} - func awsImport(c *cli.Context) error { args := c.Args() @@ -216,7 +116,7 @@ func awsImport(c *cli.Context) error { opts := createImageOptions{ Name: functionName, - Base: fmt.Sprintf("iron/lambda-%s", *function.Configuration.Runtime), + Base: runtimes[(*function.Configuration.Runtime)], Package: "", Handler: *function.Configuration.Handler, OutputStream: newdockerJSONWriter(os.Stdout), @@ -230,7 +130,7 @@ func awsImport(c *cli.Context) error { return fmt.Errorf("unsupported runtime %v", runtime) } - files, err := rh(functionName, tmpFileName, &opts) + _, err = rh(functionName, tmpFileName, &opts) if err != nil { return nil } @@ -239,12 +139,18 @@ func awsImport(c *cli.Context) error { opts.Name = image } - return createDockerfile(opts, files...) + fmt.Print("Creating func.yaml ... ") + if err := createFunctionYaml(opts, functionName); err != nil { + return err + } + fmt.Println("OK") + + return nil } var ( runtimeImportHandlers = map[string]func(functionName, tmpFileName string, opts *createImageOptions) ([]fileLike, error){ - "nodejs": basicImportHandler, + "nodejs4.3": basicImportHandler, "python2.7": basicImportHandler, "java8": func(functionName, tmpFileName string, opts *createImageOptions) ([]fileLike, error) { fmt.Println("Found Java Lambda function. Going to assume code is a single JAR file.") @@ -268,20 +174,25 @@ func basicImportHandler(functionName, tmpFileName string, opts *createImageOptio return unzipAndGetTopLevelFiles(functionName, tmpFileName) } -func createFunctionYaml(opts createImageOptions) error { +func createFunctionYaml(opts createImageOptions, functionName string) error { strs := strings.Split(opts.Name, "/") path := fmt.Sprintf("/%s", strs[1]) + funcDesc := &funcfile{ - Name: opts.Name, - Path: &path, - Config: opts.Config, + Name: opts.Name, + Path: &path, + Config: opts.Config, + Version: "0.0.1", + Runtime: &opts.Base, + Cmd: opts.Handler, } out, err := yaml.Marshal(funcDesc) if err != nil { return err } - return ioutil.WriteFile(filepath.Join(opts.Name, "func.yaml"), out, 0644) + + return ioutil.WriteFile(filepath.Join(functionName, "func.yaml"), out, 0644) } type createImageOptions struct { @@ -301,90 +212,6 @@ type fileLike interface { var errNoFiles = errors.New("No files to add to image") -// Create a Dockerfile that adds each of the files to the base image. The -// expectation is that the base image sets up the current working directory -// inside the image correctly. `handler` is set to be passed to node-lambda -// for now, but we may have to change this to accomodate other stacks. -func makeDockerfile(base string, pkg string, handler string, files ...fileLike) ([]byte, error) { - var buf bytes.Buffer - fmt.Fprintf(&buf, "FROM %s\n", base) - - for _, file := range files { - // FIXME(nikhil): Validate path, no parent paths etc. - info, err := file.Stat() - if err != nil { - return buf.Bytes(), err - } - - fmt.Fprintf(&buf, "ADD [\"%s\", \"./%s\"]\n", info.Name(), info.Name()) - } - - fmt.Fprint(&buf, "CMD [") - if pkg != "" { - fmt.Fprintf(&buf, "\"%s\", ", pkg) - } - // FIXME(nikhil): Validate handler. - fmt.Fprintf(&buf, `"%s"`, handler) - fmt.Fprint(&buf, "]\n") - - return buf.Bytes(), nil -} - -// Creates a docker image called `name`, using `base` as the base image. -// `handler` is the runtime-specific name to use for a lambda invocation (i.e. -// . for nodejs). `files` should be a list of files+dirs -// *relative to the current directory* that are to be included in the image. -func createDockerfile(opts createImageOptions, files ...fileLike) error { - if len(files) == 0 { - return errNoFiles - } - - df, err := makeDockerfile(opts.Base, opts.Package, opts.Handler, files...) - if err != nil { - return err - } - - fmt.Printf("Creating directory: %s ... ", opts.Name) - if err := os.MkdirAll(opts.Name, os.ModePerm); err != nil { - return err - } - fmt.Println("OK") - - fmt.Printf("Creating Dockerfile: %s ... ", filepath.Join(opts.Name, "Dockerfile")) - outputFile, err := os.Create(filepath.Join(opts.Name, "Dockerfile")) - if err != nil { - return err - } - fmt.Println("OK") - - for _, f := range files { - fstat, err := f.Stat() - if err != nil { - return err - } - fmt.Printf("Copying file: %s ... ", filepath.Join(opts.Name, fstat.Name())) - src, err := os.Create(filepath.Join(opts.Name, fstat.Name())) - if err != nil { - return err - } - if _, err := io.Copy(src, f); err != nil { - return err - } - fmt.Println("OK") - } - - if _, err = outputFile.Write(df); err != nil { - return err - } - - fmt.Print("Creating func.yaml ... ") - if err := createFunctionYaml(opts); err != nil { - return err - } - fmt.Println("OK") - return nil -} - type dockerJSONWriter struct { under io.Writer w io.Writer diff --git a/fn/lambda/node/build.sh b/fn/lambda/node/build.sh index d6acaef0..140faa7a 100755 --- a/fn/lambda/node/build.sh +++ b/fn/lambda/node/build.sh @@ -1,3 +1,3 @@ set -ex -docker build -t iron/functions-lambda:node4.3 . +docker build -t iron/functions-lambda:nodejs4.3 . diff --git a/fn/lambda/node/release.sh b/fn/lambda/node/release.sh index 3ba1107e..313898f5 100755 --- a/fn/lambda/node/release.sh +++ b/fn/lambda/node/release.sh @@ -1,4 +1,4 @@ set -ex ./build.sh -docker push iron/functions-lambda:node4.3 +docker push iron/functions-lambda:nodejs4.3 diff --git a/fn/langs/base.go b/fn/langs/base.go index 16585b74..606402f7 100644 --- a/fn/langs/base.go +++ b/fn/langs/base.go @@ -15,7 +15,7 @@ func GetLangHelper(lang string) LangHelper { return &RustLangHelper{} case "dotnet": return &DotNetLangHelper{} - case "lambda-node": + case "lambda-nodejs4.3": return &LambdaNodeHelper{} } return nil