fn: add header configuration to route calls (#371)

This commit is contained in:
C Cirello
2016-12-01 17:51:26 +01:00
committed by GitHub
parent 81f06c78c3
commit e63cc04e68
5 changed files with 195 additions and 21 deletions

View File

@@ -6,7 +6,7 @@ swagger: '2.0'
info:
title: IronFunctions
description: The open source serverless platform.
version: "0.1.22"
version: "0.1.23"
# the domain of the service
host: "127.0.0.1:8080"
# array of all schemes that your API supports
@@ -326,8 +326,12 @@ definitions:
description: Name of Docker image to use in this route. You should include the image tag, which should be a version number, to be more accurate. Can be overridden on a per route basis with route.image.
type: string
headers:
type: string
type: object
description: Map of http headers that will be sent with the response
additionalProperties:
type: array
items:
type: string
memory:
type: integer
format: int64

View File

@@ -80,6 +80,9 @@ $ fn routes create otherapp /hello iron/hello # create route
$ fn routes delete otherapp hello # delete route
/hello deleted
$ fn routes headers set otherapp hello header-name value # add HTTP header to response
otherapp /hello headers updated header-name with value
$ fn version # shows version both of client and server
Client version: 0.1.0
Server version: 0.1.21
@@ -126,6 +129,23 @@ 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
```
## Changing target host
`fn` is configured by default to talk http://localhost:8080.

View File

@@ -35,6 +35,7 @@ type funcfile struct {
Format *string `yaml:"format,omitempty",json:"format,omitempty"`
Timeout *time.Duration `yaml:"timeout,omitempty",json:"timeout,omitempty"`
MaxConcurrency *int `yaml:"int,omitempty",json:"int,omitempty"`
Headers map[string][]string `yaml:"headers,omitempty",json:"headers,omitempty"`
Config map[string]string `yaml:"config,omitempty",json:"config,omitempty"`
Build []string `yaml:"build,omitempty",json:"build,omitempty"`
}

View File

@@ -114,9 +114,13 @@ func (p *publishcmd) route(path string, ff *funcfile) error {
Route: functions.Route{
Path: *ff.Route,
Image: ff.FullName(),
AppName: *ff.App,
Memory: *ff.Memory,
Type_: *ff.Type,
Config: expandEnvConfig(ff.Config),
Headers: ff.Headers,
Timeout: int32(ff.Timeout.Seconds()),
MaxConcurrency: int32(*ff.MaxConcurrency),
},
}

View File

@@ -71,7 +71,7 @@ func routes() cli.Command {
Value: "",
},
cli.IntFlag{
Name: "max-concurrency,m",
Name: "max-concurrency",
Usage: "maximum concurrency for hot container",
Value: 1,
},
@@ -126,6 +126,34 @@ func routes() cli.Command {
},
},
},
{
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,
},
},
},
},
}
}
@@ -458,3 +486,120 @@ func (a *routesCmd) configUnset(c *cli.Context) error {
fmt.Println(appName, wrapper.Route.Path, "removed", key)
return nil
}
func (a *routesCmd) headersList(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")
}
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)
wrapper, _, err := a.AppsAppRoutesRouteGet(appName, route)
if err != nil {
return fmt.Errorf("error loading route information: %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")
}
fmt.Println(wrapper.Route.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)
if err != nil {
return fmt.Errorf("error loading 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.AppsAppRoutesRoutePut(appName, route, *wrapper); err != nil {
return fmt.Errorf("error updating route configuration: %v", err)
}
fmt.Println(wrapper.Route.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 {
return fmt.Errorf("error loading app: %v", err)
}
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.AppsAppRoutesRoutePut(appName, route, *wrapper); err != nil {
return fmt.Errorf("error updating route configuration: %v", err)
}
fmt.Println(wrapper.Route.AppName, wrapper.Route.Path, "removed header", key)
return nil
}