fnctl: add run command and fix env var handling (#211)

* fnctl: add run command and fix env var handling
* fnctl: fix help screens
* fnctl: address code review and fix logic mistake
This commit is contained in:
C Cirello
2016-11-03 08:37:29 -07:00
committed by Seif Lotfy سيف لطفي
parent 5b5422148b
commit 1025caeb04
6 changed files with 123 additions and 16 deletions

View File

@@ -15,6 +15,7 @@ build-docker:
test: test:
go test -v $(shell glide nv | grep -v examples | grep -v tool | grep -v fnctl) go test -v $(shell glide nv | grep -v examples | grep -v tool | grep -v fnctl)
cd fnctl && $(MAKE) test
test-docker: test-docker:
docker run -ti --privileged --rm -e LOG_LEVEL=debug \ docker run -ti --privileged --rm -e LOG_LEVEL=debug \

View File

@@ -30,7 +30,7 @@ func handleSpecial(c *gin.Context) {
} }
} }
func toEnvName(envtype, name string) string { func ToEnvName(envtype, name string) string {
name = strings.ToUpper(strings.Replace(name, "-", "_", -1)) name = strings.ToUpper(strings.Replace(name, "-", "_", -1))
return fmt.Sprintf("%s_%s", envtype, name) return fmt.Sprintf("%s_%s", envtype, name)
} }
@@ -128,22 +128,22 @@ func handleRequest(c *gin.Context, enqueue models.Enqueue) {
// app config // app config
for k, v := range app.Config { for k, v := range app.Config {
envVars[toEnvName("CONFIG", k)] = v envVars[ToEnvName("CONFIG", k)] = v
} }
// route config // route config
for k, v := range found.Config { for k, v := range found.Config {
envVars[toEnvName("CONFIG", k)] = v envVars[ToEnvName("CONFIG", k)] = v
} }
// params // params
for _, param := range params { for _, param := range params {
envVars[toEnvName("PARAM", param.Key)] = param.Value envVars[ToEnvName("PARAM", param.Key)] = param.Value
} }
// headers // headers
for header, value := range c.Request.Header { for header, value := range c.Request.Header {
envVars[toEnvName("HEADER", header)] = strings.Join(value, " ") envVars[ToEnvName("HEADER", header)] = strings.Join(value, " ")
} }
cfg := &runner.Config{ cfg := &runner.Config{

View File

@@ -9,3 +9,6 @@ docker: vendor
vendor: vendor:
go get -u . go get -u .
test:
go test -v $(shell glide nv)

View File

@@ -21,6 +21,7 @@ func main() {
apps(), apps(),
build(), build(),
bump(), bump(),
call(),
lambda(), lambda(),
publish(), publish(),
routes(), routes(),

View File

@@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"os" "os"
"path" "path"
"strings"
"text/tabwriter" "text/tabwriter"
"github.com/iron-io/functions_go" "github.com/iron-io/functions_go"
@@ -22,18 +23,20 @@ type routesCmd struct {
func routes() cli.Command { func routes() cli.Command {
r := routesCmd{RoutesApi: functions.NewRoutesApi()} r := routesCmd{RoutesApi: functions.NewRoutesApi()}
flags := append(confFlags(&r.Configuration), []cli.Flag{}...)
return cli.Command{ return cli.Command{
Name: "routes", Name: "routes",
Usage: "list routes", Usage: "list routes",
ArgsUsage: "fnclt routes", ArgsUsage: "fnclt routes",
Flags: append(confFlags(&r.Configuration), []cli.Flag{}...), Flags: flags,
Action: r.list, Action: r.list,
Subcommands: []cli.Command{ Subcommands: []cli.Command{
{ {
Name: "run", Name: "call",
Usage: "run a route", Usage: "call a route",
ArgsUsage: "appName /path", ArgsUsage: "appName /path",
Action: r.run, Action: r.call,
Flags: append(flags, runflags()...),
}, },
{ {
Name: "create", Name: "create",
@@ -51,6 +54,20 @@ func routes() cli.Command {
} }
} }
func call() cli.Command {
r := routesCmd{RoutesApi: functions.NewRoutesApi()}
flags := append([]cli.Flag{}, confFlags(&r.Configuration)...)
flags = append(flags, runflags()...)
return cli.Command{
Name: "call",
Usage: "call a remote function",
ArgsUsage: "appName /path",
Flags: flags,
Action: r.call,
}
}
func (a *routesCmd) list(c *cli.Context) error { func (a *routesCmd) list(c *cli.Context) error {
if c.Args().First() == "" { if c.Args().First() == "" {
return errors.New("error: routes listing takes one argument, an app name") return errors.New("error: routes listing takes one argument, an app name")
@@ -85,7 +102,7 @@ func (a *routesCmd) list(c *cli.Context) error {
return nil return nil
} }
func (a *routesCmd) run(c *cli.Context) error { func (a *routesCmd) call(c *cli.Context) error {
if c.Args().Get(0) == "" || c.Args().Get(1) == "" { if c.Args().Get(0) == "" || c.Args().Get(1) == "" {
return errors.New("error: routes listing takes three arguments: an app name and a route") return errors.New("error: routes listing takes three arguments: an app name and a route")
} }
@@ -108,7 +125,15 @@ func (a *routesCmd) run(c *cli.Context) error {
content = os.Stdin content = os.Stdin
} }
resp, err := http.Post(baseURL.ResolveReference(u).String(), "application/json", content) req, err := http.NewRequest("POST", baseURL.ResolveReference(u).String(), content)
if err != nil {
return fmt.Errorf("error running route: %v", err)
}
req.Header.Set("Content-Type", "application/json")
envAsHeader(req, c.StringSlice("e"))
resp, err := http.DefaultClient.Do(req)
if err != nil { if err != nil {
return fmt.Errorf("error running route: %v", err) return fmt.Errorf("error running route: %v", err)
} }
@@ -117,6 +142,19 @@ func (a *routesCmd) run(c *cli.Context) error {
return nil return nil
} }
func envAsHeader(req *http.Request, selectedEnv []string) {
detectedEnv := os.Environ()
if len(selectedEnv) > 0 {
detectedEnv = selectedEnv
}
for _, e := range detectedEnv {
kv := strings.Split(e, "=")
name := kv[0]
req.Header.Set(name, os.Getenv(name))
}
}
func (a *routesCmd) create(c *cli.Context) error { func (a *routesCmd) create(c *cli.Context) error {
if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" { if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" {
return errors.New("error: routes listing takes three arguments: an app name, a route path and an image") return errors.New("error: routes listing takes three arguments: an app name, a route path and an image")

View File

@@ -1,18 +1,82 @@
package main package main
import ( import (
"github.com/iron-io/functions_go" "errors"
"fmt"
"os"
"os/exec"
"strings"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func run() cli.Command { func run() cli.Command {
r := routesCmd{RoutesApi: functions.NewRoutesApi()} r := runCmd{}
return cli.Command{ return cli.Command{
Name: "run", Name: "run",
Usage: "run function", Usage: "run a function locally",
ArgsUsage: "fnclt run appName /path", ArgsUsage: "USERNAME/image:tag",
Flags: append(confFlags(&r.Configuration), []cli.Flag{}...), Flags: append(runflags(), []cli.Flag{}...),
Action: r.run, Action: r.run,
} }
} }
type runCmd struct{}
func runflags() []cli.Flag {
return []cli.Flag{
cli.StringSliceFlag{
Name: "e",
Usage: "limit the environment variables sent to function, if ommited then all are sent.",
},
}
}
func (r *runCmd) run(c *cli.Context) error {
image := c.Args().First()
if image == "" {
return errors.New("error: image name is missing")
}
sh := []string{"docker", "run", "--rm", "-i"}
var env []string
detectedEnv := os.Environ()
if se := c.StringSlice("e"); len(se) > 0 {
detectedEnv = se
}
for _, e := range detectedEnv {
shellvar, envvar := extractEnvVar(e)
sh = append(sh, shellvar...)
env = append(env, envvar)
}
dockerenv := []string{"DOCKER_TLS_VERIFY", "DOCKER_HOST", "DOCKER_CERT_PATH", "DOCKER_MACHINE_NAME"}
for _, e := range dockerenv {
env = append(env, fmt.Sprint(e, "=", os.Getenv(e)))
}
sh = append(sh, image)
cmd := exec.Command(sh[0], sh[1:]...)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Env = env
return cmd.Run()
}
func extractEnvVar(e string) ([]string, string) {
kv := strings.Split(e, "=")
name := toEnvName("HEADER", kv[0])
sh := []string{"-e", name}
env := fmt.Sprintf("%s=%s", name, os.Getenv(kv[0]))
return sh, env
}
// From server.toEnvName()
func toEnvName(envtype, name string) string {
name = strings.ToUpper(strings.Replace(name, "-", "_", -1))
return fmt.Sprintf("%s_%s", envtype, name)
}