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
```
`--memory` is number of usable MiB for this function. If during the execution it
exceeds this maximum threshold, it will halt and return an error in the logs.
You can also update existent routes configurations using the command `fn routes update`
`--type` is the type of the function. Either `sync`, in which the client waits
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.
For example:
`--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
$ fn routes headers set otherapp hello header-name value
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
fn routes update --memory 64 --type sync --image iron/hello
```
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
`fn` is configured by default to talk http://localhost:8080.

6
fn/glide.lock generated
View File

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

View File

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

View File

@@ -14,6 +14,7 @@ import (
"time"
functions "github.com/iron-io/functions_go"
"github.com/jmoiron/jsonq"
"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",
Aliases: []string{"d"},
@@ -89,69 +136,11 @@ func routes() cli.Command {
Action: r.delete,
},
{
Name: "config",
Usage: "operate a route configuration set",
Subcommands: []cli.Command{
{
Name: "view",
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,
},
},
Name: "inspect",
Aliases: []string{"i"},
Usage: "retrieve one or all routes properties",
ArgsUsage: "`app` /path [property.[key]]",
Action: r.inspect,
},
},
}
@@ -376,6 +365,49 @@ func (a *routesCmd) delete(c *cli.Context) error {
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 {
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")
@@ -421,21 +453,8 @@ func (a *routesCmd) configList(c *cli.Context) error {
return nil
}
func (a *routesCmd) configSet(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)
func (a *routesCmd) patchRoute(appName, routePath string, r *functions.Route) error {
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, routePath)
if err != nil {
return fmt.Errorf("error loading route: %v", err)
}
@@ -444,69 +463,57 @@ func (a *routesCmd) configSet(c *cli.Context) error {
return errors.New(msg)
}
config := wrapper.Route.Config
if config == nil {
config = make(map[string]string)
wrapper.Route.Path = ""
if r != nil {
if r.Config != nil {
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
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, "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 wrapper, _, err = a.AppsAppRoutesRoutePatch(appName, routePath, *wrapper); err != nil {
return fmt.Errorf("error updating route: %v", err)
}
if msg := wrapper.Error_.Message; 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
}
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) == "" {
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 {
@@ -515,108 +522,37 @@ func (a *routesCmd) headersList(c *cli.Context) error {
appName := c.Args().Get(0)
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 {
return fmt.Errorf("error loading route information: %v", err)
return fmt.Errorf("error retrieving route: %v", err)
}
if msg := wrapper.Error_.Message; msg != "" {
return errors.New(msg)
}
headers := wrapper.Route.Headers
if len(headers) == 0 {
return errors.New("this route has no headers")
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", "\t")
if prop == "" {
enc.Encode(wrapper.Route)
return nil
}
fmt.Println(appName, wrapper.Route.Path, "headers:")
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, ' ', 0)
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)
var inspect struct{ Route map[string]interface{} }
err = json.Unmarshal(resp.Payload, &inspect)
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 != "" {
return errors.New(msg)
}
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)
jq := jsonq.NewQuery(inspect.Route)
field, err := jq.Interface(strings.Split(prop, ".")...)
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
}