From e1c0012d04897a0c204e0dafb3ede689cf445f83 Mon Sep 17 00:00:00 2001 From: Arda Karaduman Date: Tue, 7 Nov 2017 22:49:54 +0900 Subject: [PATCH] refactor fn to seperate common and commands (#657) [skip ci] * refactor fn to seperate common and commands * remove refactored code * reorganize imports * move image commands in images folder --- fn/bump.go | 103 --------------------- fn/{ => commands}/apps.go | 15 +-- fn/commands/images.go | 27 ++++++ fn/{ => commands/images}/build.go | 25 ++--- fn/commands/images/bump.go | 70 ++++++++++++++ fn/{ => commands/images}/deploy.go | 19 ++-- fn/{ => commands/images}/push.go | 14 +-- fn/{ => commands/images}/run.go | 20 ++-- fn/{ => commands/images}/run_others.go | 4 +- fn/{ => commands/images}/run_windows.go | 4 +- fn/{ => commands}/init.go | 24 ++--- fn/{ => commands}/lambda.go | 7 +- fn/{ => commands}/lambda/node/Dockerfile | 0 fn/{ => commands}/lambda/node/bootstrap.js | 0 fn/{ => commands}/lambda/node/build.sh | 0 fn/{ => commands}/lambda/node/release.sh | 0 fn/{ => commands}/lambda/release.sh | 0 fn/{ => commands}/routes.go | 26 +++--- fn/{ => commands}/routes_test.go | 2 +- fn/{ => commands}/testfn.go | 14 +-- fn/{ => commands}/version.go | 7 +- fn/{ => common}/api.go | 8 +- fn/{ => common}/common.go | 62 +++++++++---- fn/common/errors.go | 13 +++ fn/{ => common}/funcfile.go | 72 ++++++++++---- fn/errors.go | 13 --- fn/images.go | 26 ------ fn/main.go | 57 ++++-------- 28 files changed, 322 insertions(+), 310 deletions(-) delete mode 100644 fn/bump.go rename fn/{ => commands}/apps.go (97%) create mode 100644 fn/commands/images.go rename fn/{ => commands/images}/build.go (59%) create mode 100644 fn/commands/images/bump.go rename fn/{ => commands/images}/deploy.go (91%) rename fn/{ => commands/images}/push.go (80%) rename fn/{ => commands/images}/run.go (85%) rename fn/{ => commands/images}/run_others.go (79%) rename fn/{ => commands/images}/run_windows.go (90%) rename fn/{ => commands}/init.go (90%) rename fn/{ => commands}/lambda.go (98%) rename fn/{ => commands}/lambda/node/Dockerfile (100%) rename fn/{ => commands}/lambda/node/bootstrap.js (100%) rename fn/{ => commands}/lambda/node/build.sh (100%) rename fn/{ => commands}/lambda/node/release.sh (100%) rename fn/{ => commands}/lambda/release.sh (100%) rename fn/{ => commands}/routes.go (96%) rename fn/{ => commands}/routes_test.go (96%) rename fn/{ => commands}/testfn.go (91%) rename fn/{ => commands}/version.go (80%) rename fn/{ => common}/api.go (92%) rename fn/{ => common}/common.go (77%) create mode 100644 fn/common/errors.go rename fn/{ => common}/funcfile.go (70%) delete mode 100644 fn/errors.go delete mode 100644 fn/images.go diff --git a/fn/bump.go b/fn/bump.go deleted file mode 100644 index f555daf1..00000000 --- a/fn/bump.go +++ /dev/null @@ -1,103 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" - - bumper "github.com/giantswarm/semver-bump/bump" - "github.com/giantswarm/semver-bump/storage" - "github.com/urfave/cli" -) - -var ( - initialVersion = "0.0.1" -) - -func bump() cli.Command { - cmd := bumpcmd{} - flags := append([]cli.Flag{}, cmd.flags()...) - return cli.Command{ - Name: "bump", - Usage: "bump function version", - Flags: flags, - Action: cmd.bump, - } -} - -type bumpcmd struct { - verbose bool -} - -func (b *bumpcmd) flags() []cli.Flag { - return []cli.Flag{ - cli.BoolFlag{ - Name: "v", - Usage: "verbose mode", - Destination: &b.verbose, - }, - } -} - -// bump will take the found valid function and bump its version -func (b *bumpcmd) bump(c *cli.Context) error { - verbwriter := verbwriter(b.verbose) - - path, err := os.Getwd() - if err != nil { - return err - } - fn, err := findFuncfile(path) - if err != nil { - return err - } - - fmt.Fprintln(verbwriter, "bumping version for", fn) - - funcfile, err := parsefuncfile(fn) - if err != nil { - return err - } - - funcfile, err = bumpversion(*funcfile) - if err != nil { - return err - } - - if err := storefuncfile(fn, funcfile); err != nil { - return err - } - - fmt.Println("Bumped to version", funcfile.Version) - return nil -} - -func bumpversion(funcfile funcfile) (*funcfile, error) { - funcfile.Name = cleanImageName(funcfile.Name) - if funcfile.Version == "" { - funcfile.Version = initialVersion - return &funcfile, nil - } - - s, err := storage.NewVersionStorage("local", funcfile.Version) - if err != nil { - return nil, err - } - - version := bumper.NewSemverBumper(s, "") - newver, err := version.BumpPatchVersion("", "") - if err != nil { - return nil, err - } - - funcfile.Version = newver.String() - return &funcfile, nil -} - -func cleanImageName(name string) string { - if i := strings.Index(name, ":"); i != -1 { - name = name[:i] - } - - return name -} diff --git a/fn/apps.go b/fn/commands/apps.go similarity index 97% rename from fn/apps.go rename to fn/commands/apps.go index 54617458..35319cb5 100644 --- a/fn/apps.go +++ b/fn/commands/apps.go @@ -1,27 +1,28 @@ -package main +package commands import ( + "context" "encoding/json" "errors" "fmt" "os" + "strings" - "context" + "github.com/iron-io/functions/fn/common" "github.com/iron-io/functions_go" fnclient "github.com/iron-io/functions_go/client" apiapps "github.com/iron-io/functions_go/client/apps" "github.com/iron-io/functions_go/models" "github.com/jmoiron/jsonq" "github.com/urfave/cli" - "strings" ) type appsCmd struct { client *fnclient.Functions } -func apps() cli.Command { - a := appsCmd{client: apiClient()} +func Apps() cli.Command { + a := appsCmd{client: common.ApiClient()} return cli.Command{ Name: "apps", @@ -125,7 +126,7 @@ func (a *appsCmd) list(c *cli.Context) error { func (a *appsCmd) create(c *cli.Context) error { body := &models.AppWrapper{App: &models.App{ Name: c.Args().Get(0), - Config: extractEnvConfig(c.StringSlice("config")), + Config: common.ExtractEnvConfig(c.StringSlice("config")), }} resp, err := a.client.Apps.PostApps(&apiapps.PostAppsParams{ @@ -153,7 +154,7 @@ func (a *appsCmd) update(c *cli.Context) error { appName := c.Args().First() patchedApp := &functions.App{ - Config: extractEnvConfig(c.StringSlice("config")), + Config: common.ExtractEnvConfig(c.StringSlice("config")), } err := a.patchApp(appName, patchedApp) diff --git a/fn/commands/images.go b/fn/commands/images.go new file mode 100644 index 00000000..69e7b73f --- /dev/null +++ b/fn/commands/images.go @@ -0,0 +1,27 @@ +package commands + +import ( + image_commands "github.com/iron-io/functions/fn/commands/images" + "github.com/iron-io/functions_go" + "github.com/urfave/cli" +) + +type imagesCmd struct { + *functions.AppsApi +} + +func Images() cli.Command { + return cli.Command{ + Name: "images", + Usage: "manage function images", + Subcommands: []cli.Command{ + image_commands.Build(), + image_commands.Deploy(), + image_commands.Bump(), + Call(), + image_commands.Push(), + image_commands.Run(), + testfn(), + }, + } +} diff --git a/fn/build.go b/fn/commands/images/build.go similarity index 59% rename from fn/build.go rename to fn/commands/images/build.go index 84b44eff..97858491 100644 --- a/fn/build.go +++ b/fn/commands/images/build.go @@ -1,52 +1,53 @@ -package main +package commands import ( "fmt" "os" + "github.com/iron-io/functions/fn/common" "github.com/urfave/cli" ) -func build() cli.Command { - cmd := buildcmd{} +func Build() cli.Command { + cmd := Buildcmd{} flags := append([]cli.Flag{}, cmd.flags()...) return cli.Command{ Name: "build", Usage: "build function version", Flags: flags, - Action: cmd.build, + Action: cmd.Build, } } -type buildcmd struct { - verbose bool +type Buildcmd struct { + Verbose bool } -func (b *buildcmd) flags() []cli.Flag { +func (b *Buildcmd) flags() []cli.Flag { return []cli.Flag{ cli.BoolFlag{ Name: "v", Usage: "verbose mode", - Destination: &b.verbose, + Destination: &b.Verbose, }, } } // build will take the found valid function and build it -func (b *buildcmd) build(c *cli.Context) error { - verbwriter := verbwriter(b.verbose) +func (b *Buildcmd) Build(c *cli.Context) error { + verbwriter := common.Verbwriter(b.Verbose) path, err := os.Getwd() if err != nil { return err } - fn, err := findFuncfile(path) + fn, err := common.FindFuncfile(path) if err != nil { return err } fmt.Fprintln(verbwriter, "building", fn) - ff, err := buildfunc(verbwriter, fn) + ff, err := common.Buildfunc(verbwriter, fn) if err != nil { return err } diff --git a/fn/commands/images/bump.go b/fn/commands/images/bump.go new file mode 100644 index 00000000..9f0a58db --- /dev/null +++ b/fn/commands/images/bump.go @@ -0,0 +1,70 @@ +package commands + +import ( + "fmt" + "github.com/iron-io/functions/fn/common" + "github.com/urfave/cli" + "os" +) + +var ( + initialVersion = common.INITIAL_VERSION +) + +func Bump() cli.Command { + cmd := bumpcmd{} + flags := append([]cli.Flag{}, cmd.flags()...) + return cli.Command{ + Name: "bump", + Usage: "bump function version", + Flags: flags, + Action: cmd.bump, + } +} + +type bumpcmd struct { + verbose bool +} + +func (b *bumpcmd) flags() []cli.Flag { + return []cli.Flag{ + cli.BoolFlag{ + Name: "v", + Usage: "verbose mode", + Destination: &b.verbose, + }, + } +} + +// bump will take the found valid function and bump its version +func (b *bumpcmd) bump(c *cli.Context) error { + verbwriter := common.Verbwriter(b.verbose) + + path, err := os.Getwd() + if err != nil { + return err + } + fn, err := common.FindFuncfile(path) + if err != nil { + return err + } + + fmt.Fprintln(verbwriter, "bumping version for", fn) + + funcfile, err := common.ParseFuncfile(fn) + if err != nil { + return err + } + + err = funcfile.Bumpversion() + if err != nil { + return err + } + + if err := common.StoreFuncfile(fn, funcfile); err != nil { + return err + } + + fmt.Println("Bumped to version", funcfile.Version) + return nil +} diff --git a/fn/deploy.go b/fn/commands/images/deploy.go similarity index 91% rename from fn/deploy.go rename to fn/commands/images/deploy.go index 509ad25f..3ae46763 100644 --- a/fn/deploy.go +++ b/fn/commands/images/deploy.go @@ -1,4 +1,4 @@ -package main +package commands import ( "errors" @@ -9,11 +9,12 @@ import ( "path/filepath" "time" + "github.com/iron-io/functions/fn/common" functions "github.com/iron-io/functions_go" "github.com/urfave/cli" ) -func deploy() cli.Command { +func Deploy() cli.Command { cmd := deploycmd{ RoutesApi: functions.NewRoutesApi(), } @@ -69,7 +70,7 @@ func (p *deploycmd) flags() []cli.Flag { func (p *deploycmd) scan(c *cli.Context) error { p.appName = c.Args().First() - p.verbwriter = verbwriter(p.verbose) + p.verbwriter = common.Verbwriter(p.verbose) var walked bool @@ -114,7 +115,7 @@ func (p *deploycmd) scan(c *cli.Context) error { func (p *deploycmd) deploy(path string) error { fmt.Fprintln(p.verbwriter, "deploying", path) - funcfile, err := buildfunc(p.verbwriter, path) + funcfile, err := common.Buildfunc(p.verbwriter, path) if err != nil { return err } @@ -123,20 +124,20 @@ func (p *deploycmd) deploy(path string) error { return nil } - if err := dockerpush(funcfile); err != nil { + if err := common.Dockerpush(funcfile); err != nil { return err } return p.route(path, funcfile) } -func (p *deploycmd) route(path string, ff *funcfile) error { - if err := resetBasePath(p.Configuration); err != nil { +func (p *deploycmd) route(path string, ff *common.Funcfile) error { + if err := common.ResetBasePath(p.Configuration); err != nil { return fmt.Errorf("error setting endpoint: %v", err) } if ff.Path == nil { - _, path := appNamePath(ff.FullName()) + _, path := common.AppNamePath(ff.FullName()) ff.Path = &path } @@ -201,7 +202,7 @@ func isFuncfile(path string, info os.FileInfo) bool { } basefn := filepath.Base(path) - for _, fn := range validfn { + for _, fn := range common.Validfn { if basefn == fn { return true } diff --git a/fn/push.go b/fn/commands/images/push.go similarity index 80% rename from fn/push.go rename to fn/commands/images/push.go index 92b33d98..5b023dcf 100644 --- a/fn/push.go +++ b/fn/commands/images/push.go @@ -1,13 +1,13 @@ -package main +package commands import ( "errors" "fmt" - + "github.com/iron-io/functions/fn/common" "github.com/urfave/cli" ) -func push() cli.Command { +func Push() cli.Command { cmd := pushcmd{} var flags []cli.Flag flags = append(flags, cmd.flags()...) @@ -38,11 +38,11 @@ func (p *pushcmd) flags() []cli.Flag { // push the container, and finally it will update function's route. Optionally, // the route can be overriden inside the functions file. func (p *pushcmd) push(c *cli.Context) error { - verbwriter := verbwriter(p.verbose) + verbwriter := common.Verbwriter(p.verbose) - ff, err := loadFuncfile() + ff, err := common.LoadFuncfile() if err != nil { - if _, ok := err.(*notFoundError); ok { + if _, ok := err.(*common.NotFoundError); ok { return errors.New("error: image name is missing or no function file found") } return err @@ -50,7 +50,7 @@ func (p *pushcmd) push(c *cli.Context) error { fmt.Fprintln(verbwriter, "pushing", ff.FullName()) - if err := dockerpush(ff); err != nil { + if err := common.Dockerpush(ff); err != nil { return err } diff --git a/fn/run.go b/fn/commands/images/run.go similarity index 85% rename from fn/run.go rename to fn/commands/images/run.go index 728792a0..a7057abe 100644 --- a/fn/run.go +++ b/fn/commands/images/run.go @@ -1,31 +1,31 @@ -package main +package commands import ( "errors" "fmt" + "github.com/iron-io/functions/fn/common" + "github.com/urfave/cli" "io" "os" "os/exec" "strings" - - "github.com/urfave/cli" ) -func run() cli.Command { +func Run() cli.Command { r := runCmd{} return cli.Command{ Name: "run", Usage: "run a function locally", ArgsUsage: "[username/image:tag]", - Flags: append(runflags(), []cli.Flag{}...), + Flags: append(Runflags(), []cli.Flag{}...), Action: r.run, } } type runCmd struct{} -func runflags() []cli.Flag { +func Runflags() []cli.Flag { return []cli.Flag{ cli.StringSliceFlag{ Name: "e", @@ -45,9 +45,9 @@ func runflags() []cli.Flag { func (r *runCmd) run(c *cli.Context) error { image := c.Args().First() if image == "" { - ff, err := loadFuncfile() + ff, err := common.LoadFuncfile() if err != nil { - if _, ok := err.(*notFoundError); ok { + if _, ok := err.(*common.NotFoundError); ok { return errors.New("error: image name is missing or no function file found") } return err @@ -55,10 +55,10 @@ func (r *runCmd) run(c *cli.Context) error { image = ff.FullName() } - return runff(image, stdin(), os.Stdout, os.Stderr, c.String("method"), c.StringSlice("e"), c.StringSlice("link")) + return Runff(image, Stdin(), os.Stdout, os.Stderr, c.String("method"), c.StringSlice("e"), c.StringSlice("link")) } -func runff(image string, stdin io.Reader, stdout, stderr io.Writer, method string, restrictedEnv []string, links []string) error { +func Runff(image string, stdin io.Reader, stdout, stderr io.Writer, method string, restrictedEnv []string, links []string) error { sh := []string{"docker", "run", "--rm", "-i"} var env []string diff --git a/fn/run_others.go b/fn/commands/images/run_others.go similarity index 79% rename from fn/run_others.go rename to fn/commands/images/run_others.go index ad42df35..0e0d9582 100644 --- a/fn/run_others.go +++ b/fn/commands/images/run_others.go @@ -1,13 +1,13 @@ // +build !windows -package main +package commands import ( "io" "os" ) -func stdin() io.Reader { +func Stdin() io.Reader { stat, err := os.Stdin.Stat() if err != nil || (stat.Mode()&os.ModeCharDevice) != 0 { return nil diff --git a/fn/run_windows.go b/fn/commands/images/run_windows.go similarity index 90% rename from fn/run_windows.go rename to fn/commands/images/run_windows.go index 1a7e2421..456517a2 100644 --- a/fn/run_windows.go +++ b/fn/commands/images/run_windows.go @@ -1,6 +1,6 @@ // +build windows -package main +package commands import ( "io" @@ -9,7 +9,7 @@ import ( "unsafe" ) -func stdin() io.Reader { +func Stdin() io.Reader { if isTerminal(int(os.Stdin.Fd())) { return nil } diff --git a/fn/init.go b/fn/commands/init.go similarity index 90% rename from fn/init.go rename to fn/commands/init.go index 4653027c..1e473444 100644 --- a/fn/init.go +++ b/fn/commands/init.go @@ -1,4 +1,4 @@ -package main +package commands /* usage: fn init @@ -14,9 +14,9 @@ import ( "fmt" "os" "path/filepath" - "strings" + "github.com/iron-io/functions/fn/common" "github.com/iron-io/functions/fn/langs" "github.com/urfave/cli" ) @@ -57,7 +57,7 @@ type initFnCmd struct { maxConcurrency int } -func initFn() cli.Command { +func InitFn() cli.Command { a := initFnCmd{} return cli.Command{ @@ -106,8 +106,8 @@ func initFn() cli.Command { func (a *initFnCmd) init(c *cli.Context) error { if !a.force { - ff, err := loadFuncfile() - if _, ok := err.(*notFoundError); !ok && err != nil { + ff, err := common.LoadFuncfile() + if _, ok := err.(*common.NotFoundError); !ok && err != nil { return err } if ff != nil { @@ -125,20 +125,20 @@ func (a *initFnCmd) init(c *cli.Context) error { ffmt = &a.format } - ff := &funcfile{ + ff := &common.Funcfile{ Name: a.name, Runtime: &a.runtime, - Version: initialVersion, + Version: common.INITIAL_VERSION, Entrypoint: a.entrypoint, Cmd: a.cmd, Format: ffmt, MaxConcurrency: &a.maxConcurrency, } - _, path := appNamePath(ff.FullName()) + _, path := common.AppNamePath(ff.FullName()) ff.Path = &path - if err := encodeFuncfileYAML("func.yaml", ff); err != nil { + if err := common.EncodeFuncfileYAML("func.yaml", ff); err != nil { return err } @@ -157,7 +157,7 @@ func (a *initFnCmd) buildFuncFile(c *cli.Context) error { return errors.New("Please specify a name for your function in the following format /.\nTry: fn init /") } - if exists("Dockerfile") { + if common.Exists("Dockerfile") { fmt.Println("Dockerfile found, will use that to build.") return nil } @@ -172,7 +172,7 @@ func (a *initFnCmd) buildFuncFile(c *cli.Context) error { fmt.Printf("assuming %v runtime\n", rt) } fmt.Println("runtime:", a.runtime) - if _, ok := acceptableFnRuntimes[a.runtime]; !ok { + if _, ok := common.AcceptableFnRuntimes[a.runtime]; !ok { return fmt.Errorf("init does not support the %s runtime, you'll have to create your own Dockerfile for this function", a.runtime) } @@ -202,7 +202,7 @@ func detectRuntime(path string) (runtime string, err error) { for ext, runtime := range fileExtToRuntime { for _, filename := range filenames { fn := filepath.Join(path, fmt.Sprintf("%s%s", filename, ext)) - if exists(fn) { + if common.Exists(fn) { return runtime, nil } } diff --git a/fn/lambda.go b/fn/commands/lambda.go similarity index 98% rename from fn/lambda.go rename to fn/commands/lambda.go index 26d74b93..495f84a2 100644 --- a/fn/lambda.go +++ b/fn/commands/lambda.go @@ -1,4 +1,4 @@ -package main +package commands import ( "archive/zip" @@ -16,6 +16,7 @@ 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" + "github.com/iron-io/functions/fn/common" "github.com/urfave/cli" yaml "gopkg.in/yaml.v2" ) @@ -24,7 +25,7 @@ var runtimes = map[string]string{ "nodejs4.3": "lambda-nodejs4.3", } -func lambda() cli.Command { +func Lambda() cli.Command { var flags []cli.Flag flags = append(flags, getFlags()...) @@ -178,7 +179,7 @@ func createFunctionYaml(opts createImageOptions, functionName string) error { strs := strings.Split(opts.Name, "/") path := fmt.Sprintf("/%s", strs[1]) - funcDesc := &funcfile{ + funcDesc := &common.Funcfile{ Name: opts.Name, Path: &path, Config: opts.Config, diff --git a/fn/lambda/node/Dockerfile b/fn/commands/lambda/node/Dockerfile similarity index 100% rename from fn/lambda/node/Dockerfile rename to fn/commands/lambda/node/Dockerfile diff --git a/fn/lambda/node/bootstrap.js b/fn/commands/lambda/node/bootstrap.js similarity index 100% rename from fn/lambda/node/bootstrap.js rename to fn/commands/lambda/node/bootstrap.js diff --git a/fn/lambda/node/build.sh b/fn/commands/lambda/node/build.sh similarity index 100% rename from fn/lambda/node/build.sh rename to fn/commands/lambda/node/build.sh diff --git a/fn/lambda/node/release.sh b/fn/commands/lambda/node/release.sh similarity index 100% rename from fn/lambda/node/release.sh rename to fn/commands/lambda/node/release.sh diff --git a/fn/lambda/release.sh b/fn/commands/lambda/release.sh similarity index 100% rename from fn/lambda/release.sh rename to fn/commands/lambda/release.sh diff --git a/fn/routes.go b/fn/commands/routes.go similarity index 96% rename from fn/routes.go rename to fn/commands/routes.go index 59c1e100..9ffd89ef 100644 --- a/fn/routes.go +++ b/fn/commands/routes.go @@ -1,4 +1,4 @@ -package main +package commands import ( "context" @@ -13,6 +13,8 @@ import ( "strings" "text/tabwriter" + image_commands "github.com/iron-io/functions/fn/commands/images" + "github.com/iron-io/functions/fn/common" fnclient "github.com/iron-io/functions_go/client" apiroutes "github.com/iron-io/functions_go/client/routes" "github.com/iron-io/functions_go/models" @@ -60,9 +62,9 @@ var routeFlags = []cli.Flag{ }, } -func routes() cli.Command { +func Routes() cli.Command { - r := routesCmd{client: apiClient()} + r := routesCmd{client: common.ApiClient()} return cli.Command{ Name: "routes", @@ -73,7 +75,7 @@ func routes() cli.Command { Usage: "call a route", ArgsUsage: " [image]", Action: r.call, - Flags: runflags(), + Flags: image_commands.Runflags(), }, { Name: "list", @@ -136,14 +138,14 @@ func routes() cli.Command { } } -func call() cli.Command { - r := routesCmd{client: apiClient()} +func Call() cli.Command { + r := routesCmd{client: common.ApiClient()} return cli.Command{ Name: "call", Usage: "call a remote function", ArgsUsage: " ", - Flags: runflags(), + Flags: image_commands.Runflags(), Action: r.call, } } @@ -195,11 +197,11 @@ func (a *routesCmd) call(c *cli.Context) error { route := cleanRoutePath(c.Args().Get(1)) u := url.URL{ - Scheme: SCHEME, - Host: HOST, + Scheme: common.SCHEME, + Host: common.HOST, } u.Path = path.Join(u.Path, "r", appName, route) - content := stdin() + content := image_commands.Stdin() return callfn(u.String(), content, os.Stdout, c.String("method"), c.StringSlice("e")) } @@ -283,12 +285,12 @@ func routeWithFlags(c *cli.Context, rt *models.Route) { } if len(c.StringSlice("config")) > 0 { - rt.Config = extractEnvConfig(c.StringSlice("config")) + rt.Config = common.ExtractEnvConfig(c.StringSlice("config")) } } func routeWithFuncFile(c *cli.Context, rt *models.Route) { - ff, err := loadFuncfile() + ff, err := common.LoadFuncfile() if err == nil { if ff.FullName() != "" { // args take precedence rt.Image = ff.FullName() diff --git a/fn/routes_test.go b/fn/commands/routes_test.go similarity index 96% rename from fn/routes_test.go rename to fn/commands/routes_test.go index f519ccba..12fe2afb 100644 --- a/fn/routes_test.go +++ b/fn/commands/routes_test.go @@ -1,4 +1,4 @@ -package main +package commands import ( "net/http" diff --git a/fn/testfn.go b/fn/commands/testfn.go similarity index 91% rename from fn/testfn.go rename to fn/commands/testfn.go index 0758eed6..39f83bd6 100644 --- a/fn/testfn.go +++ b/fn/commands/testfn.go @@ -1,4 +1,4 @@ -package main +package commands import ( "bufio" @@ -11,6 +11,8 @@ import ( "strings" "time" + image_commands "github.com/iron-io/functions/fn/commands/images" + "github.com/iron-io/functions/fn/common" functions "github.com/iron-io/functions_go" "github.com/urfave/cli" ) @@ -49,14 +51,14 @@ func (t *testcmd) flags() []cli.Flag { func (t *testcmd) test(c *cli.Context) error { if t.build { - b := &buildcmd{verbose: true} - if err := b.build(c); err != nil { + b := &image_commands.Buildcmd{Verbose: true} + if err := b.Build(c); err != nil { return err } fmt.Println() } - ff, err := loadFuncfile() + ff, err := common.LoadFuncfile() if err != nil { return err } @@ -71,7 +73,7 @@ func (t *testcmd) test(c *cli.Context) error { if ff.Path == nil || *ff.Path == "" { return errors.New("execution of tests on remote server demand that this function to have a `path`.") } - if err := resetBasePath(t.Configuration); err != nil { + if err := common.ResetBasePath(t.Configuration); err != nil { return fmt.Errorf("error setting endpoint: %v", err) } baseURL, err := url.Parse(t.Configuration.BasePath) @@ -134,7 +136,7 @@ func runlocaltest(target string, in, expectedOut, expectedErr *string, env map[s restrictedEnv = append(restrictedEnv, k) } - if err := runff(target, stdin, &stdout, &stderr, "", restrictedEnv, nil); err != nil { + if err := image_commands.Runff(target, stdin, &stdout, &stderr, "", restrictedEnv, nil); err != nil { return fmt.Errorf("%v\nstdout:%s\nstderr:%s\n", err, stdout.String(), stderr.String()) } diff --git a/fn/version.go b/fn/commands/version.go similarity index 80% rename from fn/version.go rename to fn/commands/version.go index ee49fd5a..63d992a5 100644 --- a/fn/version.go +++ b/fn/commands/version.go @@ -1,13 +1,14 @@ -package main +package commands import ( "fmt" vers "github.com/iron-io/functions/api/version" + "github.com/iron-io/functions/fn/common" functions "github.com/iron-io/functions_go" "github.com/urfave/cli" ) -func version() cli.Command { +func Version() cli.Command { r := versionCmd{VersionApi: functions.NewVersionApi()} return cli.Command{ Name: "version", @@ -21,7 +22,7 @@ type versionCmd struct { } func (r *versionCmd) version(c *cli.Context) error { - r.Configuration.BasePath = getBasePath("") + r.Configuration.BasePath = common.GetBasePath("") fmt.Println("Client version:", vers.Version) v, _, err := r.VersionGet() diff --git a/fn/api.go b/fn/common/api.go similarity index 92% rename from fn/api.go rename to fn/common/api.go index 2bfa0fa7..21f58b63 100644 --- a/fn/api.go +++ b/fn/common/api.go @@ -1,16 +1,16 @@ -package main +package common import ( + "crypto/tls" + "net/http" "os" - "crypto/tls" httptransport "github.com/go-openapi/runtime/client" "github.com/go-openapi/strfmt" fnclient "github.com/iron-io/functions_go/client" - "net/http" ) -func apiClient() *fnclient.Functions { +func ApiClient() *fnclient.Functions { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: SSL_SKIP_VERIFY}, } diff --git a/fn/common.go b/fn/common/common.go similarity index 77% rename from fn/common.go rename to fn/common/common.go index 0476da4c..95e25b31 100644 --- a/fn/common.go +++ b/fn/common/common.go @@ -1,21 +1,47 @@ -package main +package common import ( "bytes" "errors" "fmt" + "github.com/iron-io/functions/fn/langs" + functions "github.com/iron-io/functions_go" "io" "io/ioutil" + "log" + "net/url" "os" "os/exec" "path/filepath" "strings" "text/template" - - "github.com/iron-io/functions/fn/langs" ) -func verbwriter(verbose bool) io.Writer { +var API_VERSION = "/v1" +var SSL_SKIP_VERIFY = (os.Getenv("SSL_SKIP_VERIFY") == "true") +var API_URL = "http://localhost:8080" +var SCHEME = "http" +var INITIAL_VERSION = "0.0.1" +var HOST string +var BASE_PATH string + +func GetBasePath(version string) string { + u, err := url.Parse(API_URL) + if err != nil { + log.Fatalln("Couldn't parse API URL:", err) + } + HOST = u.Host + SCHEME = u.Scheme + u.Path = version + return u.String() +} + +func ResetBasePath(c *functions.Configuration) error { + c.BasePath = BASE_PATH + return nil +} + +func Verbwriter(verbose bool) io.Writer { verbwriter := ioutil.Discard if verbose { verbwriter = os.Stderr @@ -23,21 +49,21 @@ func verbwriter(verbose bool) io.Writer { return verbwriter } -func buildfunc(verbwriter io.Writer, fn string) (*funcfile, error) { - funcfile, err := parsefuncfile(fn) +func Buildfunc(verbwriter io.Writer, fn string) (*Funcfile, error) { + funcfile, err := ParseFuncfile(fn) if err != nil { return nil, err } if funcfile.Version == "" { - funcfile, err = bumpversion(*funcfile) + err = funcfile.Bumpversion() if err != nil { return nil, err } - if err := storefuncfile(fn, funcfile); err != nil { + if err := StoreFuncfile(fn, funcfile); err != nil { return nil, err } - funcfile, err = parsefuncfile(fn) + funcfile, err = ParseFuncfile(fn) if err != nil { return nil, err } @@ -68,12 +94,12 @@ func localbuild(verbwriter io.Writer, path string, steps []string) error { return nil } -func dockerbuild(verbwriter io.Writer, path string, ff *funcfile) error { +func dockerbuild(verbwriter io.Writer, path string, ff *Funcfile) error { dir := filepath.Dir(path) var helper langs.LangHelper dockerfile := filepath.Join(dir, "Dockerfile") - if !exists(dockerfile) { + if !Exists(dockerfile) { err := writeTmpDockerfile(dir, ff) defer os.Remove(filepath.Join(dir, "Dockerfile")) if err != nil { @@ -108,7 +134,7 @@ func dockerbuild(verbwriter io.Writer, path string, ff *funcfile) error { return nil } -func exists(name string) bool { +func Exists(name string) bool { if _, err := os.Stat(name); err != nil { if os.IsNotExist(err) { return false @@ -117,7 +143,7 @@ func exists(name string) bool { return true } -var acceptableFnRuntimes = map[string]string{ +var AcceptableFnRuntimes = map[string]string{ "elixir": "iron/elixir", "erlang": "iron/erlang", "gcc": "iron/gcc", @@ -143,13 +169,13 @@ ADD . /function/ {{ if ne .Cmd "" }} CMD [{{ .Cmd }}] {{ end }} ` -func writeTmpDockerfile(dir string, ff *funcfile) error { +func writeTmpDockerfile(dir string, ff *Funcfile) error { if ff.Entrypoint == "" && ff.Cmd == "" { return errors.New("entrypoint and cmd are missing, you must provide one or the other") } runtime, tag := ff.RuntimeTag() - rt, ok := acceptableFnRuntimes[runtime] + rt, ok := AcceptableFnRuntimes[runtime] if !ok { return fmt.Errorf("cannot use runtime %s", runtime) } @@ -190,7 +216,7 @@ func stringToSlice(in string) bytes.Buffer { return buffer } -func extractEnvConfig(configs []string) map[string]string { +func ExtractEnvConfig(configs []string) map[string]string { c := make(map[string]string) for _, v := range configs { kv := strings.SplitN(v, "=", 2) @@ -201,7 +227,7 @@ func extractEnvConfig(configs []string) map[string]string { return c } -func dockerpush(ff *funcfile) error { +func Dockerpush(ff *Funcfile) error { latestTag := ff.Name + ":latest" cmd := exec.Command("docker", "tag", ff.FullName(), latestTag) cmd.Stderr = os.Stderr @@ -216,7 +242,7 @@ func dockerpush(ff *funcfile) error { return nil } -func appNamePath(img string) (string, string) { +func AppNamePath(img string) (string, string) { sep := strings.Index(img, "/") if sep < 0 { return "", "" diff --git a/fn/common/errors.go b/fn/common/errors.go new file mode 100644 index 00000000..c05a96d3 --- /dev/null +++ b/fn/common/errors.go @@ -0,0 +1,13 @@ +package common + +type NotFoundError struct { + S string +} + +func (e *NotFoundError) Error() string { + return e.S +} + +func newNotFoundError(s string) *NotFoundError { + return &NotFoundError{S: s} +} diff --git a/fn/funcfile.go b/fn/common/funcfile.go similarity index 70% rename from fn/funcfile.go rename to fn/common/funcfile.go index 1545eb1f..41772fd9 100644 --- a/fn/funcfile.go +++ b/fn/common/funcfile.go @@ -1,4 +1,4 @@ -package main +package common import ( "encoding/json" @@ -10,11 +10,13 @@ import ( "strings" "time" + bumper "github.com/giantswarm/semver-bump/bump" + "github.com/giantswarm/semver-bump/storage" yaml "gopkg.in/yaml.v2" ) var ( - validfn = [...]string{ + Validfn = [...]string{ "func.yaml", "func.yml", "func.json", @@ -31,7 +33,7 @@ type fftest struct { Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"` } -type funcfile struct { +type Funcfile struct { Name string `yaml:"name,omitempty" json:"name,omitempty"` Version string `yaml:"version,omitempty" json:"version,omitempty"` Runtime *string `yaml:"runtime,omitempty" json:"runtime,omitempty"` @@ -49,7 +51,7 @@ type funcfile struct { MaxConcurrency *int `yaml:"max_concurrency,omitempty" json:"max_concurrency,omitempty"` } -func (ff *funcfile) FullName() string { +func (ff *Funcfile) FullName() string { fname := ff.Name if ff.Version != "" { fname = fmt.Sprintf("%s:%s", fname, ff.Version) @@ -57,7 +59,7 @@ func (ff *funcfile) FullName() string { return fname } -func (ff *funcfile) RuntimeTag() (runtime, tag string) { +func (ff *Funcfile) RuntimeTag() (runtime, tag string) { if ff.Runtime == nil { return "", "" } @@ -71,25 +73,55 @@ func (ff *funcfile) RuntimeTag() (runtime, tag string) { return rt[:tagpos], rt[tagpos+1:] } -func findFuncfile(path string) (string, error) { - for _, fn := range validfn { +func cleanImageName(name string) string { + if i := strings.Index(name, ":"); i != -1 { + name = name[:i] + } + + return name +} + +func (ff *Funcfile) Bumpversion() error { + ff.Name = cleanImageName(ff.Name) + if ff.Version == "" { + ff.Version = INITIAL_VERSION + return nil + } + + s, err := storage.NewVersionStorage("local", ff.Version) + if err != nil { + return err + } + + version := bumper.NewSemverBumper(s, "") + newver, err := version.BumpPatchVersion("", "") + if err != nil { + return err + } + + ff.Version = newver.String() + return nil +} + +func FindFuncfile(path string) (string, error) { + for _, fn := range Validfn { fullfn := filepath.Join(path, fn) - if exists(fullfn) { + if Exists(fullfn) { return fullfn, nil } } return "", newNotFoundError("could not find function file") } -func loadFuncfile() (*funcfile, error) { - fn, err := findFuncfile(".") +func LoadFuncfile() (*Funcfile, error) { + fn, err := FindFuncfile(".") if err != nil { return nil, err } - return parsefuncfile(fn) + return ParseFuncfile(fn) } -func parsefuncfile(path string) (*funcfile, error) { +func ParseFuncfile(path string) (*Funcfile, error) { ext := filepath.Ext(path) switch ext { case ".json": @@ -100,38 +132,38 @@ func parsefuncfile(path string) (*funcfile, error) { return nil, errUnexpectedFileFormat } -func storefuncfile(path string, ff *funcfile) error { +func StoreFuncfile(path string, ff *Funcfile) error { ext := filepath.Ext(path) switch ext { case ".json": return encodeFuncfileJSON(path, ff) case ".yaml", ".yml": - return encodeFuncfileYAML(path, ff) + return EncodeFuncfileYAML(path, ff) } return errUnexpectedFileFormat } -func decodeFuncfileJSON(path string) (*funcfile, error) { +func decodeFuncfileJSON(path string) (*Funcfile, error) { f, err := os.Open(path) if err != nil { return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err) } - ff := new(funcfile) + ff := new(Funcfile) err = json.NewDecoder(f).Decode(ff) return ff, err } -func decodeFuncfileYAML(path string) (*funcfile, error) { +func decodeFuncfileYAML(path string) (*Funcfile, error) { b, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("could not open %s for parsing. Error: %v", path, err) } - ff := new(funcfile) + ff := new(Funcfile) err = yaml.Unmarshal(b, ff) return ff, err } -func encodeFuncfileJSON(path string, ff *funcfile) error { +func encodeFuncfileJSON(path string, ff *Funcfile) error { f, err := os.Open(path) if err != nil { return fmt.Errorf("could not open %s for encoding. Error: %v", path, err) @@ -139,7 +171,7 @@ func encodeFuncfileJSON(path string, ff *funcfile) error { return json.NewEncoder(f).Encode(ff) } -func encodeFuncfileYAML(path string, ff *funcfile) error { +func EncodeFuncfileYAML(path string, ff *Funcfile) error { b, err := yaml.Marshal(ff) if err != nil { return fmt.Errorf("could not encode function file. Error: %v", err) diff --git a/fn/errors.go b/fn/errors.go deleted file mode 100644 index ab9072d5..00000000 --- a/fn/errors.go +++ /dev/null @@ -1,13 +0,0 @@ -package main - -type notFoundError struct { - S string -} - -func (e *notFoundError) Error() string { - return e.S -} - -func newNotFoundError(s string) *notFoundError { - return ¬FoundError{S: s} -} diff --git a/fn/images.go b/fn/images.go deleted file mode 100644 index bd36e556..00000000 --- a/fn/images.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "github.com/iron-io/functions_go" - "github.com/urfave/cli" -) - -type imagesCmd struct { - *functions.AppsApi -} - -func images() cli.Command { - return cli.Command{ - Name: "images", - Usage: "manage function images", - Subcommands: []cli.Command{ - build(), - deploy(), - bump(), - call(), - push(), - run(), - testfn(), - }, - } -} diff --git a/fn/main.go b/fn/main.go index 583b1e6f..40d1a9b2 100644 --- a/fn/main.go +++ b/fn/main.go @@ -3,48 +3,30 @@ package main import ( "bytes" "fmt" - "net/url" "os" "strings" vers "github.com/iron-io/functions/api/version" - functions "github.com/iron-io/functions_go" + "github.com/iron-io/functions/fn/commands" + image_commands "github.com/iron-io/functions/fn/commands/images" + "github.com/iron-io/functions/fn/common" "github.com/urfave/cli" - "log" ) var aliases = map[string]cli.Command{ - "build": build(), - "bump": bump(), - "deploy": deploy(), - "push": push(), - "run": run(), - "call": call(), -} - -var API_VERSION = "/v1" -var SSL_SKIP_VERIFY = (os.Getenv("SSL_SKIP_VERIFY") == "true") -var API_URL = "http://localhost:8080" -var SCHEME = "http" -var HOST string -var BASE_PATH string - -func getBasePath(version string) string { - u, err := url.Parse(API_URL) - if err != nil { - log.Fatalln("Couldn't parse API URL:", err) - } - HOST = u.Host - SCHEME = u.Scheme - u.Path = version - return u.String() + "build": image_commands.Build(), + "bump": image_commands.Bump(), + "deploy": image_commands.Deploy(), + "push": image_commands.Push(), + "run": image_commands.Run(), + "call": commands.Call(), } func init() { if os.Getenv("API_URL") != "" { - API_URL = os.Getenv("API_URL") + common.API_URL = os.Getenv("API_URL") } - BASE_PATH = getBasePath(API_VERSION) + common.BASE_PATH = common.GetBasePath(common.API_VERSION) } func main() { @@ -52,11 +34,6 @@ func main() { app.Run(os.Args) } -func resetBasePath(c *functions.Configuration) error { - c.BasePath = BASE_PATH - return nil -} - func aliasesFn() []cli.Command { cmds := []cli.Command{} for alias, cmd := range aliases { @@ -106,12 +83,12 @@ GLOBAL OPTIONS: fmt.Fprintf(os.Stderr, "command not found: %v\n", cmd) } app.Commands = []cli.Command{ - initFn(), - apps(), - routes(), - images(), - lambda(), - version(), + commands.InitFn(), + commands.Apps(), + commands.Routes(), + commands.Images(), + commands.Lambda(), + commands.Version(), } app.Commands = append(app.Commands, aliasesFn()...)