Compare commits

...

7 Commits

Author SHA1 Message Date
Minghe Huang
422035b5b7 fix test 2020-02-11 20:02:58 +08:00
Minghe Huang
4547c0b1eb refactor hook manager 2020-02-11 19:41:54 +08:00
Minghe
bb5173deb2 more clear comment message 2020-02-11 18:30:46 +08:00
Minghe Huang
5b4eaf5ff2 update base image 2020-02-11 18:17:11 +08:00
Minghe Huang
f049ebd383 trigger before hook 2020-02-10 23:06:09 +08:00
Minghe Huang
9904a979e2 longer timeout 2020-02-10 22:33:10 +08:00
Minghe Huang
001712f9ad support before_build hook 2020-02-10 22:20:32 +08:00
13 changed files with 237 additions and 4 deletions

View File

@@ -1,6 +1,6 @@
run:
deadline: 10m
timeout: 10m
deadline: 20m
timeout: 20m
issues-exit-code: 1
tests: true
skip-dirs:

View File

@@ -1,13 +1,26 @@
{
"name": "aok",
"name": "fx-node-base",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@koa/cors": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@koa/cors/-/cors-2.2.3.tgz",
"integrity": "sha512-tCVVXa39ETsit5kGBtEWWimjLn1sDaeu8+0phgb8kT3GmBDZOykkI3ZO8nMjV2p3MGkJI4K5P+bxR8Ztq0bwsA==",
"requires": {
"vary": "^1.1.2"
}
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
}
}
}

View File

@@ -10,6 +10,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"@koa/cors": "^2.2.3",
"get-port": "^3.2.0",
"is-generator-function": "^1.0.6",
"koa": "^2.3.0",

View File

@@ -9,6 +9,7 @@ import (
"github.com/metrue/fx/constants"
containerruntimes "github.com/metrue/fx/container_runtimes"
"github.com/metrue/fx/context"
"github.com/metrue/fx/hook"
"github.com/metrue/fx/packer"
"github.com/metrue/fx/pkg/spinner"
"github.com/metrue/fx/utils"
@@ -39,6 +40,9 @@ func BuildImage(ctx context.Contexter) (err error) {
if err := packer.Pack(workdir, sources...); err != nil {
return err
}
if err := hook.RunBeforeBuildHook(workdir); err != nil {
return err
}
}
docker := ctx.Get("docker").(containerruntimes.ContainerRuntime)
@@ -68,6 +72,9 @@ func ExportImage(ctx context.Contexter) (err error) {
if err := packer.Pack(outputDir, sources...); err != nil {
return err
}
if err := hook.RunBeforeBuildHook(outputDir); err != nil {
return err
}
}
log.Infof("exported to %v: %v", outputDir, constants.CheckedSymbol)

1
hook/.hooks/before_build Normal file
View File

@@ -0,0 +1 @@
npm install

15
hook/fixture/package.json Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "fixture",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"leftpad": "0.0.1"
}
}

73
hook/hook.go Normal file
View File

@@ -0,0 +1,73 @@
package hook
import (
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"github.com/metrue/fx/utils"
)
// Hooker defines hook interface
type Hooker interface {
Run() error
}
// Hook to run
type Hook struct {
name string
script string
}
// New a hook
func New(name string, script string, workdir string) *Hook {
return &Hook{
name: name,
script: script,
}
}
// Run execute a hook
func (h *Hook) Run(workdir string) error {
var script string
if !utils.IsRegularFile(h.script) {
hookScript, err := ioutil.TempFile(os.TempDir(), "fx-hook-script-")
if err != nil {
return err
}
defer os.Remove(hookScript.Name())
content := []byte(h.script)
if _, err = hookScript.Write(content); err != nil {
return err
}
if err := hookScript.Close(); err != nil {
return err
}
script = hookScript.Name()
} else {
absScript, err := filepath.Abs(h.script)
if err != nil {
return err
}
script = absScript
}
cmd := exec.Command("/bin/sh", script)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if workdir != "" {
cmd.Dir = workdir
}
if err := cmd.Run(); err != nil {
return err
}
return nil
}
// Name hook name
func (h *Hook) Name() string {
return h.name
}

54
hook/hook_manager.go Normal file
View File

@@ -0,0 +1,54 @@
package hook
import (
"os"
"path/filepath"
"github.com/metrue/fx/utils"
)
// HookNameBeforeBuild before build hook
const HookNameBeforeBuild = "before_build"
// RunBeforeBuildHook trigger before_build hook
func RunBeforeBuildHook(workdir string) error {
hooks, err := descovery("")
if err != nil {
return err
}
for _, h := range hooks {
if h.Name() == HookNameBeforeBuild {
if err := h.Run(workdir); err != nil {
return err
}
}
}
return nil
}
func descovery(hookdir string) ([]*Hook, error) {
if hookdir == "" {
dir, err := os.Getwd()
if err != nil {
return nil, err
}
hookdir = filepath.Join(dir, ".hooks")
}
hooks := []*Hook{}
if !utils.IsDir(hookdir) {
return hooks, nil
}
if err := filepath.Walk(hookdir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Name() == HookNameBeforeBuild {
hooks = append(hooks, New("before_build", path, ""))
}
return nil
}); err != nil {
return nil, err
}
return hooks, nil
}

40
hook/hook_manager_test.go Normal file
View File

@@ -0,0 +1,40 @@
package hook
import (
"os"
"path/filepath"
"testing"
)
func TestHookManager(t *testing.T) {
t.Run("descovery in default hookdir .hooks", func(t *testing.T) {
hooks, err := descovery("")
if err != nil {
t.Fatal(err)
}
if len(hooks) != 1 {
t.Fatalf("should have one hook, but got %d", len(hooks))
}
if hooks[0].Name() != HookNameBeforeBuild {
t.Fatalf("should be before_build hook, but got %s", hooks[0].Name())
}
})
t.Run("descovery in empty hookdir", func(t *testing.T) {
hooks, err := descovery(filepath.Join(os.TempDir(), ".hooks"))
if err != nil {
t.Fatal(err)
}
if len(hooks) != 0 {
t.Fatalf("should get 0 hooks, but got %d", len(hooks))
}
})
t.Run("run before_build hook", func(t *testing.T) {
if err := RunBeforeBuildHook("fixture"); err != nil {
t.Fatal(err)
}
})
}

21
hook/hook_test.go Normal file
View File

@@ -0,0 +1,21 @@
package hook
import (
"testing"
)
func TestHook(t *testing.T) {
t.Run("text", func(t *testing.T) {
h := New("before_build", "npm install leftpad", "fixture")
if err := h.Run("fixture"); err != nil {
t.Fatal(err)
}
})
t.Run("script", func(t *testing.T) {
h := New("before_build", ".hooks/before_build", "fixture")
if err := h.Run("fixture"); err != nil {
t.Fatal(err)
}
})
}

View File

@@ -7,6 +7,7 @@ import (
containerruntimes "github.com/metrue/fx/container_runtimes"
"github.com/metrue/fx/context"
"github.com/metrue/fx/hook"
"github.com/metrue/fx/packer"
"github.com/metrue/fx/pkg/spinner"
"github.com/metrue/fx/types"
@@ -54,6 +55,9 @@ func Build(ctx context.Contexter) (err error) {
if err := packer.Pack(workdir, sources...); err != nil {
return err
}
if err := hook.RunBeforeBuildHook(workdir); err != nil {
return err
}
}
cloudType := ctx.Get("cloud_type").(string)

View File

@@ -24,7 +24,7 @@ func init() {
packr.PackJSONBytes("./images", "julia/deps.jl", "\"b3BlbigiL2FwcC9SRVFVSVJFIikgZG8gZgoJZGVwcyA9IHJlYWRsaW5lcyhmKQoJZm9yIGQgaW4gZGVwcwoJCVBrZy5hZGQoZCkKCWVuZAplbmQKUGtnLmJ1aWxkKCJIdHRwUGFyc2VyIikK\"")
packr.PackJSONBytes("./images", "julia/fx.jl", "\"CnN0cnVjdCBJbnB1dAogICAgYTo6TnVtYmVyCiAgICBiOjpOdW1iZXIKZW5kCgpmeCA9IGZ1bmN0aW9uKGlucHV0OjpJbnB1dCkKICAgIHJldHVybiBpbnB1dC5hICsgaW5wdXQuYgplbmQK\"")
packr.PackJSONBytes("./images", "node/Dockerfile", "\"RlJPTSBtZXRydWUvZngtbm9kZS1iYXNlCgpDT1BZIC4gLgpFWFBPU0UgMzAwMApDTUQgWyJub2RlIiwgImFwcC5qcyJdCg==\"")
packr.PackJSONBytes("./images", "node/app.js", "\"Y29uc3QgS29hID0gcmVxdWlyZSgna29hJyk7CmNvbnN0IGJvZHlQYXJzZXIgPSByZXF1aXJlKCdrb2EtYm9keXBhcnNlcicpOwpjb25zdCBmeCA9IHJlcXVpcmUoJy4vZngnKTsKCmNvbnN0IGFwcCA9IG5ldyBLb2EoKTsKYXBwLnVzZShib2R5UGFyc2VyKCkpOwphcHAudXNlKGZ4KTsKCmFwcC5saXN0ZW4oMzAwMCk7Cg==\"")
packr.PackJSONBytes("./images", "node/app.js", "\"Y29uc3QgS29hID0gcmVxdWlyZSgna29hJyk7CmNvbnN0IGJvZHlQYXJzZXIgPSByZXF1aXJlKCdrb2EtYm9keXBhcnNlcicpOwpjb25zdCBjb3JzID0gcmVxdWlyZSgnQGtvYS9jb3JzJyk7CmNvbnN0IGZ4ID0gcmVxdWlyZSgnLi9meCcpOwoKY29uc3QgYXBwID0gbmV3IEtvYSgpOwphcHAudXNlKGNvcnMoewogIG9yaWdpbjogJyonLAp9KSk7CmFwcC51c2UoYm9keVBhcnNlcigpKTsKYXBwLnVzZShmeCk7CgphcHAubGlzdGVuKDMwMDApOwo=\"")
packr.PackJSONBytes("./images", "node/fx.js", "\"bW9kdWxlLmV4cG9ydHMgPSAoY3R4KSA9PiB7CiAgY3R4LmJvZHkgPSAnaGVsbG8gd29ybGQnCn0K\"")
packr.PackJSONBytes("./images", "perl/Dockerfile", "\"RlJPTSBtZXRydWUvZngtcGVybC1iYXNlCgpBREQgLiAuCgpFWFBPU0UgMzAwMApDTUQgWyJwZXJsIiwgImFwcC5wbCIsICJkYWVtb24iXQo=\"")
packr.PackJSONBytes("./images", "perl/app.pl", "\"dXNlIE1vam9saWNpb3VzOjpMaXRlOwoKcmVxdWlyZSAiLi9meC5wbCI7CgpnZXQgJy8nID0+IHN1YiB7CiAgbXkgJGN0eCA9IHNoaWZ0OwogIG15ICRyZXMgPSBmeCgkY3R4KTsKICAkY3R4LT5yZW5kZXIoanNvbiA9PiAkcmVzKTsKfTsKCnBvc3QgJy8nID0+IHN1YiB7CiAgbXkgJGN0eCA9IHNoaWZ0OwogIG15ICRyZXMgPSBmeCgkY3R4KTsKICAkY3R4LT5yZW5kZXIoanNvbiA9PiAkcmVzKTsKfTsKCmFwcC0+c3RhcnQ7Cg==\"")

View File

@@ -1,8 +1,12 @@
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const cors = require('@koa/cors');
const fx = require('./fx');
const app = new Koa();
app.use(cors({
origin: '*',
}));
app.use(bodyParser());
app.use(fx);