Include missing update commands (#461)

* include missing update commands

* merge all update commands into one single command

* added route inspect command

* fn routes inspect improvement

* update fn docs
This commit is contained in:
Pedro Nasser
2017-01-03 18:31:26 -02:00
committed by GitHub
parent 384db0ac11
commit bf048c9222
4 changed files with 170 additions and 246 deletions

View File

@@ -114,37 +114,22 @@ Thus a more complete example of route creation will look like:
fn routes create --memory 256 --type async --config DB_URL=http://example.org/ otherapp /hello iron/hello fn routes create --memory 256 --type async --config DB_URL=http://example.org/ otherapp /hello iron/hello
``` ```
`--memory` is number of usable MiB for this function. If during the execution it You can also update existent routes configurations using the command `fn routes update`
exceeds this maximum threshold, it will halt and return an error in the logs.
`--type` is the type of the function. Either `sync`, in which the client waits For example:
until the request is successfully completed, or `async`, in which the clients
dispatches a new request, gets a task ID back and closes the HTTP connection.
`--config` is a map of values passed to the route runtime in the form of
environment variables.
Repeated calls to `fn route create` will trigger an update of the given
route, thus you will be able to change any of these attributes later in time
if necessary.
## Route headers
You can configure a route's HTTP response to return specific headers.
A header configuration workflow example:
```sh ```sh
$ fn routes headers set otherapp hello header-name value fn routes update --memory 64 --type sync --image iron/hello
otherapp /hello headers updated header-name with value
$ fn routes headers view otherapp hello
otherapp /hello headers:
header-name: [value]
$ fn routes headers unset otherapp hello header-name
otherapp /hello removed header header-name
``` ```
To know exactly what configurations you can update just use the command
```
fn routes update --help
```
To understand how each configuration affect your function checkout the [Definitions](/docs/definitions.md#Routes) document.
## Changing target host ## Changing target host
`fn` is configured by default to talk http://localhost:8080. `fn` is configured by default to talk http://localhost:8080.

6
fn/glide.lock generated
View File

@@ -1,5 +1,5 @@
hash: f3c0e4634313b824f30782a3431b6fb8ad2feaf382c765e6f6930bfd50f53750 hash: 9356255bb45ddb833b045e985b2dbf0721791b431849a4f36a50b0897e888c3c
updated: 2016-12-01T21:51:57.931016569+01:00 updated: 2016-12-29T19:40:42.322381915-02:00
imports: imports:
- name: github.com/aws/aws-sdk-go - name: github.com/aws/aws-sdk-go
version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6 version: 90dec2183a5f5458ee79cbaf4b8e9ab910bc81a6
@@ -90,6 +90,8 @@ imports:
- lambda - lambda
- name: github.com/jmespath/go-jmespath - name: github.com/jmespath/go-jmespath
version: 3433f3ea46d9f8019119e7dd41274e112a2359a9 version: 3433f3ea46d9f8019119e7dd41274e112a2359a9
- name: github.com/jmoiron/jsonq
version: e874b168d07ecc7808bc950a17998a8aa3141d82
- name: github.com/juju/errgo - name: github.com/juju/errgo
version: 08cceb5d0b5331634b9826762a8fd53b29b86ad8 version: 08cceb5d0b5331634b9826762a8fd53b29b86ad8
subpackages: subpackages:

View File

@@ -14,9 +14,10 @@ import:
- bump - bump
- storage - storage
- package: github.com/iron-io/functions_go - package: github.com/iron-io/functions_go
version: 429df8920abd7c47dfcd6777dba278d6122ab93d version: f38f2174656467ec3ed404b7294e9ee172573e43
- package: github.com/iron-io/lambda - package: github.com/iron-io/lambda
subpackages: subpackages:
- lambda - lambda
- package: github.com/urfave/cli - package: github.com/urfave/cli
- package: gopkg.in/yaml.v2 - package: gopkg.in/yaml.v2
- package: github.com/jmoiron/jsonq

View File

@@ -14,6 +14,7 @@ import (
"time" "time"
functions "github.com/iron-io/functions_go" functions "github.com/iron-io/functions_go"
"github.com/jmoiron/jsonq"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@@ -81,6 +82,52 @@ func routes() cli.Command {
}, },
}, },
}, },
{
Name: "update",
Aliases: []string{"u"},
Usage: "update a route in an `app`",
ArgsUsage: "`app` /path",
Action: r.update,
Flags: []cli.Flag{
cli.StringFlag{
Name: "image,i",
Usage: "image name",
},
cli.Int64Flag{
Name: "memory,m",
Usage: "memory in MiB",
Value: 128,
},
cli.StringFlag{
Name: "type,t",
Usage: "route type - sync or async",
Value: "sync",
},
cli.StringSliceFlag{
Name: "config,c",
Usage: "route configuration",
},
cli.StringSliceFlag{
Name: "headers",
Usage: "route response headers",
},
cli.StringFlag{
Name: "format,f",
Usage: "hot container IO format - json or http",
Value: "",
},
cli.IntFlag{
Name: "max-concurrency",
Usage: "maximum concurrency for hot container",
Value: 1,
},
cli.DurationFlag{
Name: "timeout",
Usage: "route timeout",
Value: 30 * time.Second,
},
},
},
{ {
Name: "delete", Name: "delete",
Aliases: []string{"d"}, Aliases: []string{"d"},
@@ -89,69 +136,11 @@ func routes() cli.Command {
Action: r.delete, Action: r.delete,
}, },
{ {
Name: "config", Name: "inspect",
Usage: "operate a route configuration set", Aliases: []string{"i"},
Subcommands: []cli.Command{ Usage: "retrieve one or all routes properties",
{ ArgsUsage: "`app` /path [property.[key]]",
Name: "view", Action: r.inspect,
Aliases: []string{"v"},
Usage: "view all configuration keys for this route",
ArgsUsage: "`app` /path",
Action: r.configList,
Flags: []cli.Flag{
cli.BoolFlag{
Name: "shell,s",
Usage: "output in shell format",
},
cli.BoolFlag{
Name: "json,j",
Usage: "output in JSON format",
},
},
},
{
Name: "set",
Aliases: []string{"s"},
Usage: "store a configuration key for this route",
ArgsUsage: "`app` /path <key> <value>",
Action: r.configSet,
},
{
Name: "unset",
Aliases: []string{"u"},
Usage: "remove a configuration key for this route",
ArgsUsage: "`app` /path <key>",
Action: r.configUnset,
},
},
},
{
Name: "headers",
Usage: "operate a route's header configuration",
Subcommands: []cli.Command{
{
Name: "view",
Aliases: []string{"v"},
Usage: "view all route's headers",
ArgsUsage: "`app` /path",
Action: r.headersList,
},
{
Name: "set",
Aliases: []string{"s"},
Usage: "add header to a router",
ArgsUsage: "`app` /path <key> <value>",
Action: r.headersSet,
},
{
Name: "unset",
Aliases: []string{"u"},
Usage: "remove a configuration key for this route",
ArgsUsage: "`app` /path <key>",
Action: r.headersUnset,
},
},
}, },
}, },
} }
@@ -376,6 +365,49 @@ func (a *routesCmd) delete(c *cli.Context) error {
return nil return nil
} }
func (a *routesCmd) update(c *cli.Context) error {
if c.Args().Get(0) == "" {
return errors.New("error: routes creation takes at least one argument: an app name")
}
if err := resetBasePath(a.Configuration); err != nil {
return fmt.Errorf("error setting endpoint: %v", err)
}
appName := c.Args().Get(0)
route := c.Args().Get(1)
if route == "" {
return errors.New("error: route path is missing")
}
headers := map[string][]string{}
for _, header := range c.StringSlice("headers") {
parts := strings.Split(header, "=")
headers[parts[0]] = strings.Split(parts[1], ";")
}
patchedRoute := &functions.Route{
Path: route,
Image: c.String("image"),
Memory: c.Int64("memory"),
Type_: c.String("type"),
Config: extractEnvConfig(c.StringSlice("config")),
Headers: headers,
Format: c.String("format"),
MaxConcurrency: int32(c.Int64("max-concurrency")),
Timeout: int32(c.Int64("timeout")),
}
err := a.patchRoute(appName, route, patchedRoute)
if err != nil {
return err
}
fmt.Println(appName, route, "updated")
return nil
}
func (a *routesCmd) configList(c *cli.Context) error { func (a *routesCmd) configList(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: route configuration description takes two arguments: an app name and a route") return errors.New("error: route configuration description takes two arguments: an app name and a route")
@@ -421,21 +453,8 @@ func (a *routesCmd) configList(c *cli.Context) error {
return nil return nil
} }
func (a *routesCmd) configSet(c *cli.Context) error { func (a *routesCmd) patchRoute(appName, routePath string, r *functions.Route) error {
if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" { wrapper, _, err := a.AppsAppRoutesRouteGet(appName, routePath)
return errors.New("error: route configuration setting takes four arguments: an app name, a route, a key and a value")
}
if err := resetBasePath(a.Configuration); err != nil {
return fmt.Errorf("error setting endpoint: %v", err)
}
appName := c.Args().Get(0)
route := c.Args().Get(1)
key := c.Args().Get(2)
value := c.Args().Get(3)
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, route)
if err != nil { if err != nil {
return fmt.Errorf("error loading route: %v", err) return fmt.Errorf("error loading route: %v", err)
} }
@@ -444,69 +463,57 @@ func (a *routesCmd) configSet(c *cli.Context) error {
return errors.New(msg) return errors.New(msg)
} }
config := wrapper.Route.Config wrapper.Route.Path = ""
if r != nil {
if config == nil { if r.Config != nil {
config = make(map[string]string) for k, v := range r.Config {
if v == "" {
delete(r.Config, k)
continue
}
wrapper.Route.Config[k] = v
}
}
if r.Headers != nil {
for k, v := range r.Headers {
if v[0] == "" {
delete(r.Headers, k)
continue
}
wrapper.Route.Headers[k] = v
}
}
if r.Image != "" {
wrapper.Route.Image = r.Image
}
if r.Format != "" {
wrapper.Route.Format = r.Format
}
if r.MaxConcurrency > 0 {
wrapper.Route.MaxConcurrency = r.MaxConcurrency
}
if r.Memory > 0 {
wrapper.Route.Memory = r.Memory
}
if r.Timeout > 0 {
wrapper.Route.Timeout = r.Timeout
}
} }
config[key] = value if wrapper, _, err = a.AppsAppRoutesRoutePatch(appName, routePath, *wrapper); err != nil {
wrapper.Route.Config = config return fmt.Errorf("error updating route: %v", err)
if _, _, err := a.AppsAppRoutesRoutePatch(appName, route, *wrapper); err != nil {
return fmt.Errorf("error updating route configuration: %v", err)
}
fmt.Println(appName, wrapper.Route.Path, "updated", key, "with", value)
return nil
}
func (a *routesCmd) configUnset(c *cli.Context) error {
if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" {
return errors.New("error: route configuration setting takes four arguments: an app name, a route and a key")
}
if err := resetBasePath(a.Configuration); err != nil {
return fmt.Errorf("error setting endpoint: %v", err)
}
appName := c.Args().Get(0)
route := c.Args().Get(1)
key := c.Args().Get(2)
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, route)
if err != nil {
return fmt.Errorf("error loading app: %v", err)
} }
if msg := wrapper.Error_.Message; msg != "" { if msg := wrapper.Error_.Message; msg != "" {
return errors.New(msg) return errors.New(msg)
} }
config := wrapper.Route.Config
if config == nil {
config = make(map[string]string)
}
if _, ok := config[key]; !ok {
return fmt.Errorf("configuration key %s not found", key)
}
delete(config, key)
wrapper.Route.Config = config
if _, _, err := a.AppsAppRoutesRoutePatch(appName, route, *wrapper); err != nil {
return fmt.Errorf("error updating route configuration: %v", err)
}
fmt.Println(appName, wrapper.Route.Path, "removed", key)
return nil return nil
} }
func (a *routesCmd) headersList(c *cli.Context) error { func (a *routesCmd) inspect(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: route configuration description takes two arguments: an app name and a route") return errors.New("error: routes listing takes three arguments: an app name and a path")
} }
if err := resetBasePath(a.Configuration); err != nil { if err := resetBasePath(a.Configuration); err != nil {
@@ -515,108 +522,37 @@ func (a *routesCmd) headersList(c *cli.Context) error {
appName := c.Args().Get(0) appName := c.Args().Get(0)
route := c.Args().Get(1) route := c.Args().Get(1)
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, route) prop := c.Args().Get(2)
wrapper, resp, err := a.AppsAppRoutesRouteGet(appName, route)
if err != nil { if err != nil {
return fmt.Errorf("error loading route information: %v", err) return fmt.Errorf("error retrieving route: %v", err)
} }
if msg := wrapper.Error_.Message; msg != "" { if msg := wrapper.Error_.Message; msg != "" {
return errors.New(msg) return errors.New(msg)
} }
headers := wrapper.Route.Headers enc := json.NewEncoder(os.Stdout)
if len(headers) == 0 { enc.SetIndent("", "\t")
return errors.New("this route has no headers")
if prop == "" {
enc.Encode(wrapper.Route)
return nil
} }
fmt.Println(appName, wrapper.Route.Path, "headers:") var inspect struct{ Route map[string]interface{} }
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0) err = json.Unmarshal(resp.Payload, &inspect)
for k, v := range headers {
fmt.Fprint(w, k, ":\t", v, "\n")
}
w.Flush()
return nil
}
func (a *routesCmd) headersSet(c *cli.Context) error {
if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" {
return errors.New("error: route configuration setting takes four arguments: an app name, a route, a key and a value")
}
if err := resetBasePath(a.Configuration); err != nil {
return fmt.Errorf("error setting endpoint: %v", err)
}
appName := c.Args().Get(0)
route := c.Args().Get(1)
key := c.Args().Get(2)
value := c.Args().Get(3)
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, route)
if err != nil { if err != nil {
return fmt.Errorf("error loading route: %v", err) return fmt.Errorf("error inspect route: %v", err)
} }
if msg := wrapper.Error_.Message; msg != "" { jq := jsonq.NewQuery(inspect.Route)
return errors.New(msg) field, err := jq.Interface(strings.Split(prop, ".")...)
}
headers := wrapper.Route.Headers
if headers == nil {
headers = make(map[string][]string)
}
headers[key] = append(headers[key], value)
wrapper.Route.Headers = headers
if _, _, err := a.AppsAppRoutesRoutePatch(appName, route, *wrapper); err != nil {
return fmt.Errorf("error updating route configuration: %v", err)
}
fmt.Println(appName, wrapper.Route.Path, "headers updated", key, "with", value)
return nil
}
func (a *routesCmd) headersUnset(c *cli.Context) error {
if c.Args().Get(0) == "" || c.Args().Get(1) == "" || c.Args().Get(2) == "" {
return errors.New("error: route configuration setting takes four arguments: an app name, a route and a key")
}
if err := resetBasePath(a.Configuration); err != nil {
return fmt.Errorf("error setting endpoint: %v", err)
}
appName := c.Args().Get(0)
route := c.Args().Get(1)
key := c.Args().Get(2)
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, route)
if err != nil { if err != nil {
return fmt.Errorf("error loading app: %v", err) return errors.New("failed to inspect the property")
} }
enc.Encode(field)
if msg := wrapper.Error_.Message; msg != "" {
return errors.New(msg)
}
headers := wrapper.Route.Headers
if headers == nil {
headers = make(map[string][]string)
}
if _, ok := headers[key]; !ok {
return fmt.Errorf("configuration key %s not found", key)
}
delete(headers, key)
wrapper.Route.Headers = headers
if _, _, err := a.AppsAppRoutesRoutePatch(appName, route, *wrapper); err != nil {
return fmt.Errorf("error updating route configuration: %v", err)
}
fmt.Println(appName, wrapper.Route.Path, "removed header", key)
return nil return nil
} }