From fedf08964ea0acf921e839308cefef8dd1331bac Mon Sep 17 00:00:00 2001 From: Travis Reeder Date: Wed, 17 May 2017 15:11:44 -0700 Subject: [PATCH] Deploy will automatically create a route if it doesn't exist. --- CONTRIBUTING.md | 14 ++------ Makefile | 4 +-- README.md | 1 + api/datastore/bolt/bolt.go | 2 +- api/server/routes_update.go | 5 +++ fn/deploy.go | 34 +++++++++++++------ fn/routes.go | 68 ++++++++++++++++++++++--------------- 7 files changed, 75 insertions(+), 53 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index eec413700..a495f8c2a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -32,21 +32,13 @@ The first time after you fork or after dependencies get updated, run: make dep ``` -Then after every change, run: +Then after every change, run ```sh -make build +make run ``` -to build the `functions` binary. - -### Run - -```sh -./functions -``` - -will start IronFunctions using an embedded `Bolt` database running on port `8080`. +to build and run the `functions` binary. It will start Functions using an embedded `Bolt` database running on port `8080`. ### Test diff --git a/Makefile b/Makefile index 9ba357779..f0c581cb5 100644 --- a/Makefile +++ b/Makefile @@ -20,8 +20,8 @@ test-build-arm: GOARCH=arm GOARM=7 $(MAKE) build GOARCH=arm64 $(MAKE) build -run: - ./functions +run: build + GIN_MODE=debug ./functions docker-dep: # todo: need to create a dep tool image for this (or just ditch this) diff --git a/README.md b/README.md index 91447378d..b730b76e4 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ fn init $USERNAME/hello # Test it - you can pass data into it too by piping it in, eg: `cat hello.payload.json | fn run` fn run # Once it's ready, deploy it to your functions server (default localhost:8080) +fn apps create myapp fn deploy myapp ``` diff --git a/api/datastore/bolt/bolt.go b/api/datastore/bolt/bolt.go index e8329e84f..82c4c76e8 100644 --- a/api/datastore/bolt/bolt.go +++ b/api/datastore/bolt/bolt.go @@ -14,8 +14,8 @@ import ( "github.com/Sirupsen/logrus" "github.com/boltdb/bolt" - "gitlab.oracledx.com/odx/functions/api/models" "gitlab.oracledx.com/odx/functions/api/datastore/internal/datastoreutil" + "gitlab.oracledx.com/odx/functions/api/models" ) type BoltDatastore struct { diff --git a/api/server/routes_update.go b/api/server/routes_update.go index f2aa767be..cf6cc1be0 100644 --- a/api/server/routes_update.go +++ b/api/server/routes_update.go @@ -46,6 +46,7 @@ func (s *Server) handleRouteUpdate(c *gin.Context) { } if wroute.Route.Image != "" { + // This was checking that an image exists, but it's too slow of an operation. Checks at runtime now. // err = s.Runner.EnsureImageExists(ctx, &task.Config{ // Image: wroute.Route.Image, // }) @@ -57,6 +58,10 @@ func (s *Server) handleRouteUpdate(c *gin.Context) { } route, err := s.Datastore.UpdateRoute(ctx, wroute.Route) + if err == models.ErrRoutesNotFound { + // try insert then + route, err = s.Datastore.InsertRoute(ctx, wroute.Route) + } if err != nil { handleErrorResponse(c, err) return diff --git a/fn/deploy.go b/fn/deploy.go index d361cbd52..ec3a4daa9 100644 --- a/fn/deploy.go +++ b/fn/deploy.go @@ -5,9 +5,11 @@ import ( "fmt" "io" "os" + "path" "path/filepath" "time" + "github.com/Sirupsen/logrus" functions "github.com/iron-io/functions_go" "github.com/iron-io/functions_go/models" "github.com/urfave/cli" @@ -52,7 +54,7 @@ func (p *deploycmd) flags() []cli.Flag { Usage: "working directory", Destination: &p.wd, EnvVar: "WORK_DIR", - Value: "./", + // Value: "./", }, cli.BoolFlag{ Name: "i", @@ -72,9 +74,14 @@ func (p *deploycmd) scan(c *cli.Context) error { p.verbwriter = verbwriter(p.verbose) var walked bool + wd, err := os.Getwd() + if err != nil { + logrus.Fatalln("Couldn't get current directory:", err) + } + // logrus.Infoln("wd:", wd) - err := filepath.Walk(p.wd, func(path string, info os.FileInfo, err error) error { - if path != p.wd && info.IsDir() { + err = filepath.Walk(wd, func(path string, info os.FileInfo, err error) error { + if path != wd && info.IsDir() { return filepath.SkipDir } @@ -97,7 +104,7 @@ func (p *deploycmd) scan(c *cli.Context) error { return e }) if err != nil { - fmt.Fprintf(p.verbwriter, "file walk error: %s\n", err) + fmt.Fprintf(p.verbwriter, "error: %s\n", err) } if !walked { @@ -111,18 +118,23 @@ func (p *deploycmd) scan(c *cli.Context) error { // Parse functions file, bump version, build image, push to registry, and // finally it will update function's route. Optionally, // the route can be overriden inside the functions file. -func (p *deploycmd) deploy(c *cli.Context, path string) error { - fmt.Fprintln(p.verbwriter, "deploying", path) +func (p *deploycmd) deploy(c *cli.Context, funcFilePath string) error { + funcFileName := path.Base(funcFilePath) err := c.App.Command("bump").Run(c) if err != nil { return err } - funcfile, err := buildfunc(p.verbwriter, path) + funcfile, err := buildfunc(p.verbwriter, funcFileName) if err != nil { return err } + if funcfile.Path == nil || *funcfile.Path == "" { + dirName := "/" + path.Base(path.Dir(funcFilePath)) + funcfile.Path = &dirName + } + logrus.Infof("funcfile %+v", funcfile) if p.skippush { return nil @@ -132,18 +144,18 @@ func (p *deploycmd) deploy(c *cli.Context, path string) error { return err } - return p.route(c, path, funcfile) + return p.route(c, funcfile) } -func (p *deploycmd) route(c *cli.Context, path string, ff *funcfile) error { +func (p *deploycmd) route(c *cli.Context, ff *funcfile) error { if err := resetBasePath(p.Configuration); err != nil { return fmt.Errorf("error setting endpoint: %v", err) } routesCmd := routesCmd{client: apiClient()} rt := &models.Route{} - routeWithFuncFile(c, rt) - return routesCmd.patchRoute(p.appName, *ff.Path, rt) + routeWithFuncFile(c, ff, rt) + return routesCmd.patchRoute(c, p.appName, *ff.Path, rt) } func expandEnvConfig(configs map[string]string) map[string]string { diff --git a/fn/routes.go b/fn/routes.go index 08f814306..cb17ae7d9 100644 --- a/fn/routes.go +++ b/fn/routes.go @@ -287,26 +287,31 @@ func routeWithFlags(c *cli.Context, rt *models.Route) { } } -func routeWithFuncFile(c *cli.Context, rt *models.Route) { - ff, err := loadFuncfile() - if err == nil { - if ff.FullName() != "" { // args take precedence - rt.Image = ff.FullName() - } - if ff.Format != nil { - rt.Format = *ff.Format - } - if ff.MaxConcurrency != nil { - rt.MaxConcurrency = int32(*ff.MaxConcurrency) - } - if ff.Timeout != nil { - to := int64(ff.Timeout.Seconds()) - rt.Timeout = &to - } - if rt.Path == "" && ff.Path != nil { - rt.Path = *ff.Path +func routeWithFuncFile(c *cli.Context, ff *funcfile, rt *models.Route) error { + var err error + if ff == nil { + ff, err = loadFuncfile() + if err != nil { + return err } } + if ff.FullName() != "" { // args take precedence + rt.Image = ff.FullName() + } + if ff.Format != nil { + rt.Format = *ff.Format + } + if ff.MaxConcurrency != nil { + rt.MaxConcurrency = int32(*ff.MaxConcurrency) + } + if ff.Timeout != nil { + to := int64(ff.Timeout.Seconds()) + rt.Timeout = &to + } + if rt.Path == "" && ff.Path != nil { + rt.Path = *ff.Path + } + return nil } func (a *routesCmd) create(c *cli.Context) error { @@ -317,17 +322,21 @@ func (a *routesCmd) create(c *cli.Context) error { rt.Path = route rt.Image = c.Args().Get(2) - routeWithFuncFile(c, rt) + routeWithFuncFile(c, nil, rt) routeWithFlags(c, rt) if rt.Path == "" { - return errors.New("error: route path is missing") + return errors.New("route path is missing") } if rt.Image == "" { - fmt.Println("No image specified, using `iron/hello`") - rt.Image = "iron/hello" + return errors.New("no image specified") } + return a.postRoute(c, appName, rt) +} + +func (a *routesCmd) postRoute(c *cli.Context, appName string, rt *fnmodels.Route) error { + body := &models.RouteWrapper{ Route: rt, } @@ -354,7 +363,8 @@ func (a *routesCmd) create(c *cli.Context) error { return nil } -func (a *routesCmd) patchRoute(appName, routePath string, r *fnmodels.Route) error { +func (a *routesCmd) patchRoute(c *cli.Context, appName, routePath string, r *fnmodels.Route) error { + // TODO: this getting the old version and merging should be on the server side, not here on the client. resp, err := a.client.Routes.GetAppsAppRoutesRoute(&apiroutes.GetAppsAppRoutesRouteParams{ Context: context.Background(), App: appName, @@ -364,7 +374,9 @@ func (a *routesCmd) patchRoute(appName, routePath string, r *fnmodels.Route) err if err != nil { switch err.(type) { case *apiroutes.GetAppsAppRoutesRouteNotFound: - return fmt.Errorf("error: %s", err.(*apiroutes.GetAppsAppRoutesRouteNotFound).Payload.Error.Message) + // return fmt.Errorf("error: %s", err.(*apiroutes.GetAppsAppRoutesRouteNotFound).Payload.Error.Message) + // then insert it + return a.postRoute(c, appName, r) case *apiroutes.GetAppsAppRoutesDefault: return fmt.Errorf("unexpected error: %s", err.(*apiroutes.GetAppsAppRoutesDefault).Payload.Error.Message) } @@ -446,10 +458,10 @@ func (a *routesCmd) update(c *cli.Context) error { route := cleanRoutePath(c.Args().Get(1)) rt := &models.Route{} - routeWithFuncFile(c, rt) + routeWithFuncFile(c, nil, rt) routeWithFlags(c, rt) - err := a.patchRoute(appName, route, rt) + err := a.patchRoute(c, appName, route, rt) if err != nil { return err } @@ -470,7 +482,7 @@ func (a *routesCmd) configSet(c *cli.Context) error { patchRoute.Config[key] = value - err := a.patchRoute(appName, route, &patchRoute) + err := a.patchRoute(c, appName, route, &patchRoute) if err != nil { return err } @@ -490,7 +502,7 @@ func (a *routesCmd) configUnset(c *cli.Context) error { patchRoute.Config["-"+key] = "" - err := a.patchRoute(appName, route, &patchRoute) + err := a.patchRoute(c, appName, route, &patchRoute) if err != nil { return err }