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: info:
title: IronFunctions title: IronFunctions
description: The open source serverless platform. description: The open source serverless platform.
version: "0.1.22" version: "0.1.23"
# the domain of the service # the domain of the service
host: "127.0.0.1:8080" host: "127.0.0.1:8080"
# array of all schemes that your API supports # 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. 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 type: string
headers: headers:
type: string type: object
description: Map of http headers that will be sent with the response description: Map of http headers that will be sent with the response
additionalProperties:
type: array
items:
type: string
memory: memory:
type: integer type: integer
format: int64 format: int64

View File

@@ -80,6 +80,9 @@ $ fn routes create otherapp /hello iron/hello # create route
$ fn routes delete otherapp hello # delete route $ fn routes delete otherapp hello # delete route
/hello deleted /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 $ fn version # shows version both of client and server
Client version: 0.1.0 Client version: 0.1.0
Server version: 0.1.21 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 route, thus you will be able to change any of these attributes later in time
if necessary. 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 ## Changing target host
`fn` is configured by default to talk http://localhost:8080. `fn` is configured by default to talk http://localhost:8080.

View File

@@ -24,19 +24,20 @@ var (
) )
type funcfile struct { type funcfile struct {
App *string `yaml:"app,omitempty",json:"app,omitempty"` App *string `yaml:"app,omitempty",json:"app,omitempty"`
Name string `yaml:"name,omitempty",json:"name,omitempty"` Name string `yaml:"name,omitempty",json:"name,omitempty"`
Version string `yaml:"version,omitempty",json:"version,omitempty"` Version string `yaml:"version,omitempty",json:"version,omitempty"`
Runtime *string `yaml:"runtime,omitempty",json:"runtime,omitempty"` Runtime *string `yaml:"runtime,omitempty",json:"runtime,omitempty"`
Entrypoint *string `yaml:"entrypoint,omitempty",json:"entrypoint,omitempty"` Entrypoint *string `yaml:"entrypoint,omitempty",json:"entrypoint,omitempty"`
Route *string `yaml:"route,omitempty",json:"route,omitempty"` Route *string `yaml:"route,omitempty",json:"route,omitempty"`
Type *string `yaml:"type,omitempty",json:"type,omitempty"` Type *string `yaml:"type,omitempty",json:"type,omitempty"`
Memory *int64 `yaml:"memory,omitempty",json:"memory,omitempty"` Memory *int64 `yaml:"memory,omitempty",json:"memory,omitempty"`
Format *string `yaml:"format,omitempty",json:"format,omitempty"` Format *string `yaml:"format,omitempty",json:"format,omitempty"`
Timeout *time.Duration `yaml:"timeout,omitempty",json:"timeout,omitempty"` Timeout *time.Duration `yaml:"timeout,omitempty",json:"timeout,omitempty"`
MaxConcurrency *int `yaml:"int,omitempty",json:"int,omitempty"` MaxConcurrency *int `yaml:"int,omitempty",json:"int,omitempty"`
Config map[string]string `yaml:"config,omitempty",json:"config,omitempty"` Headers map[string][]string `yaml:"headers,omitempty",json:"headers,omitempty"`
Build []string `yaml:"build,omitempty",json:"build,omitempty"` Config map[string]string `yaml:"config,omitempty",json:"config,omitempty"`
Build []string `yaml:"build,omitempty",json:"build,omitempty"`
} }
func (ff *funcfile) FullName() string { func (ff *funcfile) FullName() string {

View File

@@ -112,11 +112,15 @@ func (p *publishcmd) route(path string, ff *funcfile) error {
body := functions.RouteWrapper{ body := functions.RouteWrapper{
Route: functions.Route{ Route: functions.Route{
Path: *ff.Route, Path: *ff.Route,
Image: ff.FullName(), Image: ff.FullName(),
Memory: *ff.Memory, AppName: *ff.App,
Type_: *ff.Type, Memory: *ff.Memory,
Config: expandEnvConfig(ff.Config), 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: "", Value: "",
}, },
cli.IntFlag{ cli.IntFlag{
Name: "max-concurrency,m", Name: "max-concurrency",
Usage: "maximum concurrency for hot container", Usage: "maximum concurrency for hot container",
Value: 1, 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) fmt.Println(appName, wrapper.Route.Path, "removed", key)
return nil 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
}