Fix lambda import (#605)

* Fix lambda import

* more updates

* more changes
This commit is contained in:
Seif Lotfy سيف لطفي
2017-03-24 18:44:38 +01:00
committed by Travis Reeder
parent cc4ff197e0
commit d8871d1562
6 changed files with 44 additions and 217 deletions

View File

@@ -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 <DOCKER_HUB_USERNAME>/lambda-node
fn init --runtime lambda-nodejs4.3 <DOCKER_HUB_USERNAME>/lambda-node
fn build
cat payload.json | fn run
```

View File

@@ -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 }}

View File

@@ -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: "<name> <runtime> <handler> </path> [/paths...]",
Action: create,
Flags: flags,
},
{
Name: "test-function",
Usage: `runs local dockerized Lambda function and writes output to stdout.`,
ArgsUsage: "<name>",
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.
// <module>.<function> 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

View File

@@ -1,3 +1,3 @@
set -ex
docker build -t iron/functions-lambda:node4.3 .
docker build -t iron/functions-lambda:nodejs4.3 .

View File

@@ -1,4 +1,4 @@
set -ex
./build.sh
docker push iron/functions-lambda:node4.3
docker push iron/functions-lambda:nodejs4.3

View File

@@ -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