Skip to content

Commit a342a90

Browse files
authored
Merge pull request #76 from netlify/lambda_function_support_go
Upload go binaries as functions
2 parents ab8a201 + 253f7b8 commit a342a90

File tree

6 files changed

+139
-37
lines changed

6 files changed

+139
-37
lines changed

go/Gopkg.lock

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/Gopkg.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,7 @@
5656
[[constraint]]
5757
name = "github.com/stretchr/testify"
5858
version = "v1.1.4"
59+
60+
[[constraint]]
61+
name = "github.com/rsc/goversion"
62+
branch = "master"

go/plumbing/operations/upload_deploy_function_parameters.go

Lines changed: 29 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go/porcelain/deploy.go

Lines changed: 91 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"bytes"
66
"crypto/sha1"
77
"crypto/sha256"
8+
"debug/elf"
89
"encoding/hex"
910
"fmt"
1011
"io"
@@ -23,10 +24,13 @@ import (
2324
"github.com/netlify/open-api/go/porcelain/context"
2425

2526
"github.com/go-openapi/errors"
27+
"github.com/rsc/goversion/version"
2628
)
2729

2830
const (
2931
preProcessingTimeout = time.Minute * 5
32+
jsRuntime = "js"
33+
goRuntime = "go"
3034
)
3135

3236
type uploadType int
@@ -76,8 +80,9 @@ type uploadError struct {
7680
}
7781

7882
type FileBundle struct {
79-
Name string
80-
Sum string
83+
Name string
84+
Sum string
85+
Runtime string
8186

8287
// Path OR Buffer should be populated
8388
Path string
@@ -398,7 +403,7 @@ func (n *Netlify) uploadFile(ctx context.Context, d *models.Deploy, f *FileBundl
398403
_, operationError = n.Operations.UploadDeployFile(params, authInfo)
399404
}
400405
case functionUpload:
401-
params := operations.NewUploadDeployFunctionParams().WithDeployID(d.ID).WithName(f.Name).WithFileBody(f)
406+
params := operations.NewUploadDeployFunctionParams().WithDeployID(d.ID).WithName(f.Name).WithFileBody(f).WithRuntime(&f.Runtime)
402407
if timeout != 0 {
403408
params.SetTimeout(timeout)
404409
}
@@ -500,54 +505,92 @@ func bundle(functionDir string, observer DeployObserver) (*deployFiles, error) {
500505
if err != nil {
501506
return nil, err
502507
}
508+
503509
for _, i := range info {
504-
switch filepath.Ext(i.Name()) {
505-
case ".js":
506-
file := &FileBundle{
507-
Name: strings.TrimSuffix(i.Name(), filepath.Ext(i.Name())),
508-
}
510+
filePath := filepath.Join(functionDir, i.Name())
509511

510-
s := sha256.New()
511-
buf := new(bytes.Buffer)
512-
archive := zip.NewWriter(buf)
513-
fileHeader, err := archive.Create(i.Name())
512+
switch {
513+
case jsFile(i):
514+
file, err := newFunctionFile(filePath, i, jsRuntime, observer)
514515
if err != nil {
515516
return nil, err
516517
}
517-
fileEntry, err := os.Open(filepath.Join(functionDir, i.Name()))
518+
functions.Add(file.Name, file)
519+
case goFile(filePath, i):
520+
file, err := newFunctionFile(filePath, i, goRuntime, observer)
518521
if err != nil {
519522
return nil, err
520523
}
521-
defer fileEntry.Close()
522-
if _, err = io.Copy(fileHeader, fileEntry); err != nil {
523-
return nil, err
524-
}
524+
functions.Add(file.Name, file)
525+
}
526+
}
525527

526-
if err := archive.Close(); err != nil {
527-
return nil, err
528-
}
528+
return functions, nil
529+
}
529530

530-
fileBuffer := new(bytes.Buffer)
531-
m := io.MultiWriter(s, fileBuffer)
531+
func newFunctionFile(filePath string, i os.FileInfo, runtime string, observer DeployObserver) (*FileBundle, error) {
532+
file := &FileBundle{
533+
Name: strings.TrimSuffix(i.Name(), filepath.Ext(i.Name())),
534+
Runtime: runtime,
535+
}
532536

533-
if _, err := io.Copy(m, buf); err != nil {
534-
return nil, err
535-
}
536-
file.Sum = hex.EncodeToString(s.Sum(nil))
537-
file.Buffer = bytes.NewReader(fileBuffer.Bytes())
538-
functions.Add(file.Name, file)
537+
s := sha256.New()
539538

540-
if observer != nil {
541-
if err := observer.OnSuccessfulStep(file); err != nil {
542-
return nil, err
543-
}
544-
}
545-
default:
546-
// Ignore this file
539+
fileEntry, err := os.Open(filePath)
540+
if err != nil {
541+
return nil, err
542+
}
543+
defer fileEntry.Close()
544+
545+
buf := new(bytes.Buffer)
546+
archive := zip.NewWriter(buf)
547+
548+
fileHeader, err := createHeader(archive, i, runtime)
549+
if err != nil {
550+
return nil, err
551+
}
552+
553+
if _, err = io.Copy(fileHeader, fileEntry); err != nil {
554+
return nil, err
555+
}
556+
557+
if err := archive.Close(); err != nil {
558+
return nil, err
559+
}
560+
561+
fileBuffer := new(bytes.Buffer)
562+
m := io.MultiWriter(s, fileBuffer)
563+
564+
if _, err := io.Copy(m, buf); err != nil {
565+
return nil, err
566+
}
567+
file.Sum = hex.EncodeToString(s.Sum(nil))
568+
file.Buffer = bytes.NewReader(fileBuffer.Bytes())
569+
570+
if observer != nil {
571+
if err := observer.OnSuccessfulStep(file); err != nil {
572+
return nil, err
547573
}
548574
}
549575

550-
return functions, nil
576+
return file, nil
577+
}
578+
579+
func jsFile(i os.FileInfo) bool {
580+
return filepath.Ext(i.Name()) == ".js"
581+
}
582+
583+
func goFile(filePath string, i os.FileInfo) bool {
584+
if m := i.Mode(); m&0111 == 0 { // check if it's an executable file
585+
return false
586+
}
587+
588+
if _, err := elf.Open(filePath); err != nil { // check if it's a linux executable
589+
return false
590+
}
591+
592+
v, err := version.ReadExe(filePath)
593+
return err == nil && strings.HasPrefix(v.Release, "go1.")
551594
}
552595

553596
func ignoreFile(rel string) bool {
@@ -556,3 +599,15 @@ func ignoreFile(rel string) bool {
556599
}
557600
return false
558601
}
602+
603+
func createHeader(archive *zip.Writer, i os.FileInfo, runtime string) (io.Writer, error) {
604+
if runtime == goRuntime {
605+
return archive.CreateHeader(&zip.FileHeader{
606+
CreatorVersion: 3 << 8, // indicates Unix
607+
ExternalAttrs: 0777 << 16, // -rwxrwxrwx file permissions
608+
Name: i.Name(),
609+
Method: zip.Deflate,
610+
})
611+
}
612+
return archive.Create(i.Name())
613+
}

swagger.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,9 @@ paths:
813813
type: string
814814
in: path
815815
required: true
816+
- name: runtime
817+
type: string
818+
in: query
816819
- name: file_body
817820
in: body
818821
schema:

ui/swagger.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,11 @@
389389
"in": "path",
390390
"required": true
391391
},
392+
{
393+
"type": "string",
394+
"name": "runtime",
395+
"in": "query"
396+
},
392397
{
393398
"name": "file_body",
394399
"in": "body",

0 commit comments

Comments
 (0)