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

Support more runtime auto build #12

Merged
merged 37 commits into from
Sep 29, 2023
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
9d99ad6
refactor: add function fro getting shell output with env
spatocode Sep 21, 2023
667ee03
fix: return stderr only if the command returns an error
spatocode Sep 21, 2023
29bc56c
refactor: pass a string slice of command env instead
spatocode Sep 21, 2023
7b7b5ab
refactor: support building projects with standalone executable
spatocode Sep 21, 2023
798133c
fix: return buffered stderr only when there are available bytes
spatocode Sep 22, 2023
72706c2
style: enforce go code formatting
spatocode Sep 22, 2023
4f1e9b7
refactor: hardcode a default serverless function across config
spatocode Sep 22, 2023
d97cc09
fix: return default functions and handlers for generic runtime
spatocode Sep 22, 2023
48af132
feature: detect static web page project
spatocode Sep 22, 2023
6b6155f
refactor: implement a base build function as default for all runtimes
spatocode Sep 22, 2023
27d9b6d
fix: fix undefined name; change name to correct name
spatocode Sep 22, 2023
88add12
refactor: add default lambda runtime id
spatocode Sep 22, 2023
54c6d39
Merge branch 'main' into more-runtime-support
spatocode Sep 26, 2023
7ddf883
Merge branch 'main' into more-runtime-support
spatocode Sep 27, 2023
67db8d5
fix: fix backward incompatibility error
spatocode Sep 27, 2023
467fc84
style: add more context to comments
spatocode Sep 27, 2023
c4c6de4
fix: trim returned version of spaces
spatocode Sep 27, 2023
9312737
fix: handle cmd exec error immediately
spatocode Sep 27, 2023
db0acbb
refactor: use dependency injection to decouple cmd usage
spatocode Sep 27, 2023
f66bac4
test: add test to config runtime functionalities
spatocode Sep 27, 2023
c6e520f
Merge branch 'main' into more-runtime-support
spatocode Sep 27, 2023
d26fb72
style: add more context to comment
spatocode Sep 27, 2023
c23502b
test: add test to go build and entry methods
spatocode Sep 27, 2023
c0df568
Merge branch 'more-runtime-support' of https://github.com/spatocode/j…
spatocode Sep 27, 2023
57f5372
style: add better context to comment
spatocode Sep 28, 2023
95d1495
test: explicitly specify expected test value
spatocode Sep 28, 2023
749bc1b
fix: ensure we're on django project before creating function entry
spatocode Sep 28, 2023
1818b12
fix: fix typo on variable name
spatocode Sep 28, 2023
546b72e
tests: include tests for config runtime functionalities
spatocode Sep 28, 2023
41bfb3c
fix: remove redundant errr handling code
spatocode Sep 28, 2023
0ed27d8
feat: support buld for static web pages
spatocode Sep 28, 2023
1f22f3f
Merge branch 'main' into more-runtime-support
spatocode Sep 28, 2023
6895fae
Merge branch 'main' into more-runtime-support
spatocode Sep 28, 2023
9adec52
refactor: better creation of function handler
spatocode Sep 28, 2023
5f4e96c
test: add more tests to platform default workings
spatocode Sep 29, 2023
f03305e
remove entry from config and include more tests
spatocode Sep 29, 2023
621e155
tests: finalize config tests
spatocode Sep 29, 2023
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
3 changes: 1 addition & 2 deletions assets/tests/jerm.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@
"memory": 512,
"keep_warm": false
},
"dir": "/home/ubuntu/bodystats",
"entry": "bodyie"
"dir": "/home/ubuntu/bodystats"
}
44 changes: 10 additions & 34 deletions cloud/aws/lambda.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"path/filepath"
"sort"
"strconv"
"strings"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
Expand Down Expand Up @@ -56,13 +55,15 @@ func NewLambda(cfg *config.Config) (*Lambda, error) {
timeout: DefaultTimeout,
}

lambdaConfig := config.Platform{Name: config.Lambda}
err := lambdaConfig.Defaults()
if err != nil {
return nil, err
if l.config.Platform.Name == "" {
lambdaConfig := config.Platform{Name: config.Lambda}
err := lambdaConfig.Defaults()
if err != nil {
return nil, err
}
l.config.Platform = lambdaConfig
}

l.config.Platform = lambdaConfig
awsConfig, err := l.getAwsConfig()
if err != nil {
return nil, err
Expand Down Expand Up @@ -98,9 +99,6 @@ func (l *Lambda) Build() (string, error) {
log.Debug("building Jerm project for Lambda...")

r := config.NewRuntime()
if l.config.Entry == "" {
l.config.Entry = r.Entry()
}

go func() {
err := l.config.ToJson(jerm.DefaultConfigFile)
Expand All @@ -109,18 +107,14 @@ func (l *Lambda) Build() (string, error) {
}
}()

handler, err := r.Build(l.config)
packageDir, function, err := r.Build(l.config)
if err != nil {
return "", err
}

dir := filepath.Dir(handler)
l.functionHandler = function

if l.config.Platform.Handler == "" {
err := l.CreateFunctionEntry(handler)
return dir, err
}
return dir, err
return packageDir, nil
}

func (l *Lambda) Invoke(command string) error {
Expand Down Expand Up @@ -433,24 +427,6 @@ func (l *Lambda) listLambdaVersions() ([]lambdaTypes.FunctionConfiguration, erro
return response.Versions, err
}

// CreateFunctionEntry creates a Lambda function handler file
func (l *Lambda) CreateFunctionEntry(file string) error {
log.Debug("creating lambda handler...")
f, err := os.Create(file)
if err != nil {
return err
}
defer f.Close()

handler := strings.ReplaceAll(awsLambdaHandler, ".wsgi", l.config.Entry+".wsgi")
_, err = f.Write([]byte(handler))
if err != nil {
return err
}
l.functionHandler = "handler.lambda_handler"
return nil
}

func (l *Lambda) isAlreadyDeployed() (bool, error) {
log.Debug("fetching function code location...")
versions, err := l.listLambdaVersions()
Expand Down
3 changes: 2 additions & 1 deletion cmd/manage.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/spatocode/jerm/cloud/aws"
"github.com/spatocode/jerm/config"
"github.com/spatocode/jerm/internal/log"
"github.com/spatocode/jerm/internal/utils"
)

// manageCmd represents the manage command
Expand All @@ -37,7 +38,7 @@ var manageCmd = &cobra.Command{
return
}

runtime := config.NewPythonRuntime()
runtime := config.NewPythonRuntime(utils.Command())
python, ok := runtime.(*config.Python)
if !ok || !python.IsDjango() {
log.PrintError("manage command is for Django projects only")
Expand Down
1 change: 0 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ type Config struct {
Region string `json:"region"`
Platform Platform `json:"platform"`
Dir string `json:"dir"`
Entry string `json:"entry"`
}

func (c *Config) GetFunctionName() string {
Expand Down
14 changes: 13 additions & 1 deletion config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ func TestConfigDefaults(t *testing.T) {
assert.NotNil(cfg.Region)
}

func TestConfigToJson(t *testing.T) {
assert := assert.New(t)
testfile := "../assets/test.json"
cfg := &Config{}

assert.False(utils.FileExists(testfile))
err := cfg.ToJson(testfile)
assert.Nil(err)
assert.True(utils.FileExists(testfile))

helperCleanup(t, []string{testfile})
}

func TestReadConfig(t *testing.T) {
assert := assert.New(t)
c, err := ReadConfig("../assets/tests/jerm.json")
Expand All @@ -44,7 +57,6 @@ func TestReadConfig(t *testing.T) {
assert.Equal(512, c.Platform.Memory)
assert.Equal(false, c.Platform.KeepWarm)
assert.Equal("/home/ubuntu/bodystats", c.Dir)
assert.Equal("bodyie", c.Entry)
}

func TestIgnoredFiles(t *testing.T) {
Expand Down
32 changes: 18 additions & 14 deletions config/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ type Go struct {
}

// NewGoConfig instantiates a new Go runtime
func NewGoRuntime() RuntimeInterface {
runtime := &Runtime{}
func NewGoRuntime(cmd utils.ShellCommand) RuntimeInterface {
runtime := &Runtime{cmd, RuntimeGo, DefaultGoVersion, ""}
g := &Go{runtime}
g.Name = RuntimeGo
version, err := g.getVersion()
if err != nil {
log.Debug(fmt.Sprintf("encountered an error while getting go version. Default to %s", DefaultGoVersion))
g.Version = DefaultGoVersion
return g
}
g.Version = version
Expand All @@ -32,27 +30,33 @@ func NewGoRuntime() RuntimeInterface {
// Gets the go version
func (g *Go) getVersion() (string, error) {
log.Debug("getting go version...")
goVersion, err := utils.GetShellCommandOutput("go", "version")
goVersion, err := g.RunCommand("go", "version")
if err != nil {
return "", err
}
s := strings.Split(goVersion, " ")
if len(s) > 1 {
version := strings.Split(s[2], "go")
return version[1], nil
return strings.TrimSpace(version[1]), nil
}
return "", errors.New("encountered error on go version")
}

// Builds the go deployment package
func (g *Go) Build(config *Config) (string, error) {
return "", nil
}
// Build builds the go deployment package
// It returns the executable path, the function name and error if any
func (g *Go) Build(config *Config) (string, string, error) {
_, err := g.RunCommand("go", "mod", "tidy")
if err != nil {
return "", "", err
}

env := []string{"GOOS=linux", "GOARCH=amd64", "CGO_ENABLED=0"}
_, err = g.RunCommandWithEnv(env, "go", "build", "main.go")
if err != nil {
return "", "", err
}

// Entry is the directory where the cloud function handler resides.
// The directory can be a file.
func (g *Go) Entry() string {
return "main.go"
return "main", "main", nil
}

// lambdaRuntime is the name of the go runtime as specified by AWS Lambda
Expand Down
79 changes: 79 additions & 0 deletions config/golang_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package config

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestNewGolangRuntime(t *testing.T) {
assert := assert.New(t)
fakeOutput = "go version go1.21.0 linux/amd64"
r := NewGoRuntime(fakeCommandExecutor{})
g := r.(*Go)
assert.Equal(RuntimeGo, g.Name)
assert.Equal("1.21.0", g.Version)
}

func TestNewGolangRuntimeDefaultVersion(t *testing.T) {
assert := assert.New(t)
fakeOutput = ""
r := NewGoRuntime(fakeCommandExecutor{})
g := r.(*Go)
assert.Equal(RuntimeGo, g.Name)
assert.Equal(DefaultGoVersion, g.Version)
}

func TestGoGetVersion(t *testing.T) {
assert := assert.New(t)
fakeOutput = "go version go1.21.0 linux/amd64"
r := NewGoRuntime(fakeCommandExecutor{})
g := r.(*Go)
v, err := g.getVersion()
assert.Nil(err)
assert.Equal(RuntimeGo, g.Name)
assert.Equal("1.21.0", v)
}

func TestGoGetVersionError(t *testing.T) {
assert := assert.New(t)
fakeOutput = ""
r := NewGoRuntime(fakeCommandExecutor{})
g := r.(*Go)
v, err := g.getVersion()
assert.NotNil(err)
assert.Equal(RuntimeGo, g.Name)
assert.Equal("", v)
}

func TestGoLambdaRuntime(t *testing.T) {
assert := assert.New(t)
fakeOutput = "go version go1.21.0 linux/amd64"
r := NewGoRuntime(fakeCommandExecutor{})
g := r.(*Go)
v, err := g.lambdaRuntime()
assert.Nil(err)
assert.Equal("go1.x", v)
}

func TestGoBuild(t *testing.T) {
assert := assert.New(t)
fakeOutput = "go version go1.21.0 linux/amd64"
r := NewGoRuntime(fakeCommandExecutor{})
cfg := &Config{Name: "test", Stage: "env"}
p, f, err := r.Build(cfg)
assert.Nil(err)
assert.Equal("main", p)
assert.Equal("main", f)
}

func TestGoBuildError(t *testing.T) {
assert := assert.New(t)
fakeOutput = ""
r := NewGoRuntime(fakeCommandExecutor{})
cfg := &Config{Name: "test", Stage: "env"}
p, f, err := r.Build(cfg)
assert.NotNil(err)
assert.Equal("", p)
assert.Equal("", f)
}
6 changes: 3 additions & 3 deletions cloud/aws/handler.go → config/handlers/django.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package aws
package handlers

const (
awsLambdaHandler = `
AwsLambdaHandlerDjango = `
import sys
import json
import io
Expand All @@ -20,7 +20,7 @@ logger = logging.getLogger()
logger.setLevel(logging.INFO)


def lambda_handler(event, context):
def handler(event, context):
if settings.DEBUG:
logger.debug("Jerm Event: {}".format(event))

Expand Down
19 changes: 19 additions & 0 deletions config/handlers/statichtml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package handlers

const (
AwsLambdaHandlerStaticPage = `
const fs = require('fs');
const html = fs.readFileSync('index.html', { encoding:'utf8' });

exports.handler = async (event) => {
const response = {
statusCode: 200,
headers: {
'Content-Type': 'text/html',
},
body: html,
};
return response;
};
`
)
17 changes: 5 additions & 12 deletions config/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ type Node struct {
}

// NewNodeConfig instantiates a new NodeJS runtime
func NewNodeRuntime() RuntimeInterface {
runtime := &Runtime{}
func NewNodeRuntime(cmd utils.ShellCommand) RuntimeInterface {
runtime := &Runtime{cmd, RuntimeNode, DefaultNodeVersion, ""}
n := &Node{runtime}
n.Name = RuntimeNode
version, err := n.getVersion()
if err != nil {
log.Debug(fmt.Sprintf("encountered an error while getting nodejs version. Default to %s", DefaultNodeVersion))
n.Version = DefaultNodeVersion
log.Debug(fmt.Sprintf("encountered an error while getting nodejs version. Default to v%s", DefaultNodeVersion))
return n
}
n.Version = version
Expand All @@ -31,19 +29,14 @@ func NewNodeRuntime() RuntimeInterface {
// Gets the nodejs version
func (n *Node) getVersion() (string, error) {
log.Debug("getting nodejs version...")
nodeVersion, err := utils.GetShellCommandOutput("node", "-v")
nodeVersion, err := n.RunCommand("node", "-v")
if err != nil {
return "", err
}
nodeVersion = nodeVersion[1:]
nodeVersion = strings.TrimSpace(nodeVersion[1:])
return nodeVersion, nil
}

// Builds the nodejs deployment package
func (n *Node) Build(config *Config) (string, error) {
return "", nil
}

// lambdaRuntime is the name of the nodejs runtime as specified by AWS Lambda
func (n *Node) lambdaRuntime() (string, error) {
v := strings.Split(n.Version, ".")
Expand Down
Loading
Loading