mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Merge pull request #299 from fnproject/fn-the-things
FN_ prefix env vars
This commit is contained in:
@@ -30,9 +30,7 @@ import (
|
||||
// TODO handle timeouts / no response in sync & async (sync is json+503 atm, not 504, async is empty log+status)
|
||||
// see also: server/runner.go wrapping the response writer there, but need to handle async too (push down?)
|
||||
// TODO herd launch prevention part deux
|
||||
// TODO plumb FXLB-WAIT back - can we use headers now? maybe let's use api
|
||||
// TODO none of the Datastore methods actually use the ctx for timeouts :(
|
||||
// TODO not adding padding if call times out to store appropriately (ctx timed out, happenstance it works now cuz of ^)
|
||||
// TODO storing logs / call can push call over the timeout
|
||||
// TODO all Datastore methods need to take unit of tenancy (app or route) at least (e.g. not just call id)
|
||||
// TODO limit the request body length when making calls
|
||||
// TODO discuss concrete policy for hot launch or timeout / timeout vs time left
|
||||
@@ -50,7 +48,6 @@ import (
|
||||
// dies). need coordination w/ db.
|
||||
// TODO if a cold call times out but container is created but hasn't replied, could
|
||||
// end up that the client doesn't get a reply until long after the timeout (b/c of container removal, async it?)
|
||||
// TODO we should prob not be logging all async output to the logs by default...
|
||||
// TODO the call api should fill in all the fields
|
||||
// TODO the log api should be plaintext (or at least offer it)
|
||||
// TODO func logger needs to be hanged, dragged and quartered. in reverse order.
|
||||
|
||||
275
api/agent/agent_test.go
Normal file
275
api/agent/agent_test.go
Normal file
@@ -0,0 +1,275 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fnproject/fn/api/datastore"
|
||||
"github.com/fnproject/fn/api/models"
|
||||
"github.com/fnproject/fn/api/mqs"
|
||||
)
|
||||
|
||||
func TestCallConfigurationRequest(t *testing.T) {
|
||||
appName := "myapp"
|
||||
path := "/sleeper"
|
||||
image := "fnproject/sleeper"
|
||||
const timeout = 1
|
||||
const idleTimeout = 20
|
||||
const memory = 256
|
||||
typ := "sync"
|
||||
format := "default"
|
||||
|
||||
cfg := models.Config{"APP_VAR": "FOO"}
|
||||
rCfg := models.Config{"ROUTE_VAR": "BAR"}
|
||||
|
||||
ds := datastore.NewMockInit(
|
||||
[]*models.App{
|
||||
{Name: appName, Config: cfg},
|
||||
},
|
||||
[]*models.Route{
|
||||
{
|
||||
Config: rCfg,
|
||||
Path: path,
|
||||
AppName: appName,
|
||||
Image: image,
|
||||
Type: typ,
|
||||
Format: format,
|
||||
Timeout: timeout,
|
||||
IdleTimeout: idleTimeout,
|
||||
Memory: memory,
|
||||
},
|
||||
}, nil,
|
||||
)
|
||||
|
||||
a := New(ds, new(mqs.Mock))
|
||||
defer a.Close()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
method := "GET"
|
||||
url := "http://127.0.0.1:8080/r/" + appName + path
|
||||
payload := "payload"
|
||||
contentLength := strconv.Itoa(len(payload))
|
||||
req, err := http.NewRequest(method, url, strings.NewReader(payload))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req.Header.Add("MYREALHEADER", "FOOLORD")
|
||||
req.Header.Add("MYREALHEADER", "FOOPEASANT")
|
||||
req.Header.Add("Content-Length", contentLength)
|
||||
req.Header.Add("FN_ROUTE", "thewrongroute") // ensures that this doesn't leak out, should be overwritten
|
||||
|
||||
call, err := a.GetCall(
|
||||
WithWriter(w), // XXX (reed): order matters [for now]
|
||||
FromRequest(appName, path, req),
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
model := call.Model()
|
||||
|
||||
// make sure the values are all set correctly
|
||||
if model.ID == "" {
|
||||
t.Fatal("model does not have id, GetCall should assign id")
|
||||
}
|
||||
if model.AppName != appName {
|
||||
t.Fatal("app name mismatch", model.AppName, appName)
|
||||
}
|
||||
if model.Path != path {
|
||||
t.Fatal("path mismatch", model.Path, path)
|
||||
}
|
||||
if model.Image != image {
|
||||
t.Fatal("image mismatch", model.Image, image)
|
||||
}
|
||||
if model.Type != "sync" {
|
||||
t.Fatal("route type mismatch", model.Type)
|
||||
}
|
||||
if model.Priority == nil {
|
||||
t.Fatal("GetCall should make priority non-nil so that async works because for whatever reason some clowns plumbed it all over the mqs even though the user can't specify it gg")
|
||||
}
|
||||
if model.Timeout != timeout {
|
||||
t.Fatal("timeout mismatch", model.Timeout, timeout)
|
||||
}
|
||||
if model.IdleTimeout != idleTimeout {
|
||||
t.Fatal("idle timeout mismatch", model.IdleTimeout, idleTimeout)
|
||||
}
|
||||
if time.Time(model.CreatedAt).IsZero() {
|
||||
t.Fatal("GetCall should stamp CreatedAt, got nil timestamp")
|
||||
}
|
||||
if model.URL != url {
|
||||
t.Fatal("url mismatch", model.URL, url)
|
||||
}
|
||||
if model.Method != method {
|
||||
t.Fatal("method mismatch", model.Method, method)
|
||||
}
|
||||
if model.Payload != "" { // NOTE: this is expected atm
|
||||
t.Fatal("GetCall FromRequest should not fill payload, got non-empty payload", model.Payload)
|
||||
}
|
||||
|
||||
expectedBase := map[string]string{
|
||||
"FN_FORMAT": format,
|
||||
"FN_APP_NAME": appName,
|
||||
"FN_ROUTE": path,
|
||||
"FN_MEMORY": strconv.Itoa(memory),
|
||||
"FN_TYPE": typ,
|
||||
"APP_VAR": "FOO",
|
||||
"ROUTE_VAR": "BAR",
|
||||
}
|
||||
|
||||
expectedEnv := make(map[string]string)
|
||||
for k, v := range expectedBase {
|
||||
expectedEnv[k] = v
|
||||
}
|
||||
|
||||
for k, v := range expectedBase {
|
||||
if v2 := model.BaseEnv[k]; v2 != v {
|
||||
t.Fatal("base var mismatch", k, v, v2, model.BaseEnv)
|
||||
}
|
||||
delete(expectedBase, k)
|
||||
}
|
||||
|
||||
if len(expectedBase) > 0 {
|
||||
t.Fatal("got extra vars in base env set, add me to tests ;)", expectedBase)
|
||||
}
|
||||
|
||||
expectedEnv["FN_CALL_ID"] = model.ID
|
||||
expectedEnv["FN_METHOD"] = method
|
||||
expectedEnv["FN_REQUEST_URL"] = url
|
||||
|
||||
// do this before the "real" headers get sucked in cuz they are formatted differently
|
||||
expectedHeaders := make(http.Header)
|
||||
for k, v := range expectedEnv {
|
||||
expectedHeaders.Add(k, v)
|
||||
}
|
||||
|
||||
// from the request headers (look different in env than in req.Header, idk, up to user anger)
|
||||
// req headers down cases things
|
||||
expectedEnv["FN_HEADER_Myrealheader"] = "FOOLORD, FOOPEASANT"
|
||||
expectedEnv["FN_HEADER_Content_Length"] = contentLength
|
||||
|
||||
for k, v := range expectedEnv {
|
||||
if v2 := model.EnvVars[k]; v2 != v {
|
||||
t.Fatal("env var mismatch", k, v, v2, model.EnvVars)
|
||||
}
|
||||
delete(expectedEnv, k)
|
||||
}
|
||||
|
||||
if len(expectedEnv) > 0 {
|
||||
t.Fatal("got extra vars in base env set, add me to tests ;)", expectedBase)
|
||||
}
|
||||
|
||||
expectedHeaders.Add("MYREALHEADER", "FOOLORD")
|
||||
expectedHeaders.Add("MYREALHEADER", "FOOPEASANT")
|
||||
expectedHeaders.Add("Content-Length", contentLength)
|
||||
|
||||
for k, vs := range req.Header {
|
||||
for i, v := range expectedHeaders[k] {
|
||||
if i >= len(vs) || vs[i] != v {
|
||||
t.Fatal("header mismatch", k, vs)
|
||||
}
|
||||
}
|
||||
delete(expectedHeaders, k)
|
||||
}
|
||||
|
||||
if len(expectedHeaders) > 0 {
|
||||
t.Fatal("got extra headers, bad")
|
||||
}
|
||||
|
||||
if w.Header()["Fn_call_id"][0] != model.ID {
|
||||
t.Fatal("response writer should have the call id, or else")
|
||||
}
|
||||
|
||||
// TODO check response writer for route headers
|
||||
|
||||
// TODO idk what param even is or how to get them, but need to test those
|
||||
// TODO we should double check the things we're rewriting defaults of, like type, format, timeout, idle_timeout
|
||||
}
|
||||
|
||||
func TestCallConfigurationModel(t *testing.T) {
|
||||
appName := "myapp"
|
||||
path := "/sleeper"
|
||||
image := "fnproject/sleeper"
|
||||
const timeout = 1
|
||||
const idleTimeout = 20
|
||||
const memory = 256
|
||||
method := "GET"
|
||||
url := "http://127.0.0.1:8080/r/" + appName + path
|
||||
payload := "payload"
|
||||
typ := "sync"
|
||||
format := "default"
|
||||
env := map[string]string{
|
||||
"FN_FORMAT": format,
|
||||
"FN_APP_NAME": appName,
|
||||
"FN_ROUTE": path,
|
||||
"FN_MEMORY": strconv.Itoa(memory),
|
||||
"FN_TYPE": typ,
|
||||
"APP_VAR": "FOO",
|
||||
"ROUTE_VAR": "BAR",
|
||||
"DOUBLE_VAR": "BIZ, BAZ",
|
||||
}
|
||||
|
||||
cm := &models.Call{
|
||||
BaseEnv: env,
|
||||
EnvVars: env,
|
||||
AppName: appName,
|
||||
Path: path,
|
||||
Image: image,
|
||||
Type: typ,
|
||||
Format: format,
|
||||
Timeout: timeout,
|
||||
IdleTimeout: idleTimeout,
|
||||
Memory: memory,
|
||||
Payload: payload,
|
||||
URL: url,
|
||||
Method: method,
|
||||
}
|
||||
|
||||
// FromModel doesn't need a datastore, for now...
|
||||
ds := datastore.NewMockInit(nil, nil, nil)
|
||||
|
||||
a := New(ds, new(mqs.Mock))
|
||||
defer a.Close()
|
||||
|
||||
callI, err := a.GetCall(FromModel(cm))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// make sure headers seem reasonable
|
||||
req := callI.(*call).req
|
||||
|
||||
// NOTE these are added as is atm, and if the env vars were comma joined
|
||||
// they are not again here comma separated.
|
||||
expectedHeaders := make(http.Header)
|
||||
for k, v := range env {
|
||||
expectedHeaders.Add(k, v)
|
||||
}
|
||||
|
||||
for k, vs := range req.Header {
|
||||
for i, v := range expectedHeaders[k] {
|
||||
if i >= len(vs) || vs[i] != v {
|
||||
t.Fatal("header mismatch", k, vs)
|
||||
}
|
||||
}
|
||||
delete(expectedHeaders, k)
|
||||
}
|
||||
|
||||
if len(expectedHeaders) > 0 {
|
||||
t.Fatal("got extra headers, bad")
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
io.Copy(&b, req.Body)
|
||||
|
||||
if b.String() != payload {
|
||||
t.Fatal("expected payload to match, but it was a lie")
|
||||
}
|
||||
}
|
||||
@@ -65,12 +65,8 @@ func FromRequest(appName, path string, req *http.Request) CallOpt {
|
||||
|
||||
// baseVars are the vars on the route & app, not on this specific request [for hot functions]
|
||||
baseVars := make(map[string]string, len(app.Config)+len(route.Config)+3)
|
||||
baseVars["FN_FORMAT"] = route.Format
|
||||
baseVars["APP_NAME"] = appName
|
||||
baseVars["ROUTE"] = route.Path
|
||||
baseVars["MEMORY_MB"] = fmt.Sprintf("%d", route.Memory)
|
||||
|
||||
// app config
|
||||
// add app & route config before our standard additions
|
||||
for k, v := range app.Config {
|
||||
k = toEnvName("", k)
|
||||
baseVars[k] = v
|
||||
@@ -80,6 +76,12 @@ func FromRequest(appName, path string, req *http.Request) CallOpt {
|
||||
baseVars[k] = v
|
||||
}
|
||||
|
||||
baseVars["FN_FORMAT"] = route.Format
|
||||
baseVars["FN_APP_NAME"] = appName
|
||||
baseVars["FN_ROUTE"] = route.Path
|
||||
baseVars["FN_MEMORY"] = fmt.Sprintf("%d", route.Memory)
|
||||
baseVars["FN_TYPE"] = route.Type
|
||||
|
||||
// envVars contains the full set of env vars, per request + base
|
||||
envVars := make(map[string]string, len(baseVars)+len(params)+len(req.Header)+3)
|
||||
|
||||
@@ -87,37 +89,51 @@ func FromRequest(appName, path string, req *http.Request) CallOpt {
|
||||
envVars[k] = v
|
||||
}
|
||||
|
||||
envVars["CALL_ID"] = id
|
||||
envVars["METHOD"] = req.Method
|
||||
envVars["REQUEST_URL"] = fmt.Sprintf("%v://%v%v", func() string {
|
||||
envVars["FN_CALL_ID"] = id
|
||||
envVars["FN_METHOD"] = req.Method
|
||||
envVars["FN_REQUEST_URL"] = func() string {
|
||||
if req.URL.Scheme == "" {
|
||||
if req.TLS == nil {
|
||||
return "http"
|
||||
req.URL.Scheme = "http"
|
||||
} else {
|
||||
req.URL.Scheme = "https"
|
||||
}
|
||||
return "https"
|
||||
}(), req.Host, req.URL.String())
|
||||
}
|
||||
if req.URL.Host == "" {
|
||||
req.URL.Host = req.Host
|
||||
}
|
||||
return req.URL.String()
|
||||
}()
|
||||
|
||||
// params
|
||||
for _, param := range params {
|
||||
envVars[toEnvName("PARAM", param.Key)] = param.Value
|
||||
envVars[toEnvName("FN_PARAM", param.Key)] = param.Value
|
||||
}
|
||||
|
||||
headerVars := make(map[string]string, len(req.Header))
|
||||
|
||||
for k, v := range req.Header {
|
||||
headerVars[toEnvName("HEADER", k)] = strings.Join(v, ", ")
|
||||
if !noOverrideVars(k) { // NOTE if we don't do this, they'll leak in (don't want people relying on this behavior)
|
||||
headerVars[toEnvName("FN_HEADER", k)] = strings.Join(v, ", ")
|
||||
}
|
||||
}
|
||||
|
||||
// add all the env vars we build to the request headers
|
||||
// TODO should we save req.Headers and copy OVER app.Config / route.Config ?
|
||||
for k, v := range envVars {
|
||||
if noOverrideVars(k) {
|
||||
// overwrite the passed in request headers explicitly with the generated ones
|
||||
req.Header.Set(k, v)
|
||||
} else {
|
||||
req.Header.Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range headerVars {
|
||||
envVars[k] = v
|
||||
}
|
||||
|
||||
// TODO this relies on ordering of opts, but tests make sure it works, probably re-plumb/destroy headers
|
||||
// TODO async should probably supply an http.ResponseWriter that records the logs, to attach response headers to
|
||||
if rw, ok := c.w.(http.ResponseWriter); ok {
|
||||
rw.Header().Add("FN_CALL_ID", id)
|
||||
for k, vs := range route.Headers {
|
||||
@@ -161,6 +177,27 @@ func FromRequest(appName, path string, req *http.Request) CallOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func noOverrideVars(key string) bool {
|
||||
// descrepency in casing b/w req headers and env vars, force matches
|
||||
return overrideVars[strings.ToUpper(key)]
|
||||
}
|
||||
|
||||
// overrideVars means that the app config, route config or header vars
|
||||
// must not overwrite the generated values in call construction.
|
||||
var overrideVars = map[string]bool{
|
||||
"FN_FORMAT": true,
|
||||
"FN_APP_NAME": true,
|
||||
"FN_ROUTE": true,
|
||||
"FN_MEMORY": true,
|
||||
"FN_TYPE": true,
|
||||
"FN_CALL_ID": true,
|
||||
"FN_METHOD": true,
|
||||
"FN_REQUEST_URL": true,
|
||||
}
|
||||
|
||||
// TODO this currently relies on FromRequest having happened before to create the model
|
||||
// here, to be a fully qualified model. We probably should double check but having a way
|
||||
// to bypass will likely be what's used anyway unless forced.
|
||||
func FromModel(mCall *models.Call) CallOpt {
|
||||
return func(a *agent, c *call) error {
|
||||
c.Call = mCall
|
||||
@@ -246,6 +283,10 @@ func (c *call) Start(ctx context.Context) error {
|
||||
c.StartedAt = strfmt.DateTime(time.Now())
|
||||
c.Status = "running"
|
||||
|
||||
if rw, ok := c.w.(http.ResponseWriter); ok { // TODO need to figure out better way to wire response headers in
|
||||
rw.Header().Set("XXX-FXLB-WAIT", time.Time(c.StartedAt).Sub(time.Time(c.CreatedAt)).String())
|
||||
}
|
||||
|
||||
if c.Type == models.TypeAsync {
|
||||
// XXX (reed): make sure MQ reservation is lengthy. to skirt MQ semantics,
|
||||
// we could add a new message to MQ w/ delay of call.Timeout and delete the
|
||||
|
||||
@@ -41,8 +41,10 @@ func (h *HTTPProtocol) Dispatch(w io.Writer, req *http.Request) error {
|
||||
return err
|
||||
}
|
||||
|
||||
for k, v := range res.Header {
|
||||
rw.Header()[k] = v
|
||||
for k, vs := range res.Header {
|
||||
for _, v := range vs {
|
||||
rw.Header().Add(k, v) // on top of any specified on the route
|
||||
}
|
||||
}
|
||||
rw.WriteHeader(res.StatusCode)
|
||||
// TODO should we TCP_CORK ?
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/http"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fnproject/fn/api"
|
||||
"github.com/fnproject/fn/api/agent"
|
||||
@@ -58,8 +59,9 @@ func (s *Server) serve(c *gin.Context, appName, path string) {
|
||||
}
|
||||
|
||||
// TODO we could add FireBeforeDispatch right here with Call in hand
|
||||
model := call.Model()
|
||||
|
||||
if model := call.Model(); model.Type == "async" {
|
||||
if model.Type == "async" {
|
||||
// TODO we should push this into GetCall somehow (CallOpt maybe) or maybe agent.Queue(Call) ?
|
||||
buf := bytes.NewBuffer(make([]byte, 0, c.Request.ContentLength)) // TODO sync.Pool me
|
||||
_, err := buf.ReadFrom(c.Request.Body)
|
||||
@@ -86,6 +88,10 @@ func (s *Server) serve(c *gin.Context, appName, path string) {
|
||||
// we could filter that error out here too as right now it yells a little
|
||||
|
||||
if err == context.DeadlineExceeded {
|
||||
// TODO maneuver
|
||||
// add this, since it means that start may not have been called [and it's relevant]
|
||||
c.Writer.Header().Add("XXX-FXLB-WAIT", time.Now().Sub(time.Time(model.CreatedAt)).String())
|
||||
|
||||
err = models.ErrCallTimeout // 504 w/ friendly note
|
||||
}
|
||||
// NOTE: if the task wrote the headers already then this will fail to write
|
||||
|
||||
@@ -31,15 +31,18 @@ To read in the function body, just read from STDIN.
|
||||
|
||||
You will also have access to a set of environment variables.
|
||||
|
||||
* REQUEST_URL - the full URL for the request ([parsing example](https://github.com/fnproject/fn/tree/master/examples/tutorial/params))
|
||||
* APP_NAME - the name of the application that matched this route, eg: `myapp`
|
||||
* ROUTE - the matched route, eg: `/hello`
|
||||
* METHOD - the HTTP method for the request, eg: `GET` or `POST`
|
||||
* CALL_ID - a unique ID for each function execution.
|
||||
* FORMAT - a string representing one of the [function formats](function-format.md), currently either `default` or `http`. Default is `default`.
|
||||
* HEADER_X - the HTTP headers that were set for this request. Replace X with the upper cased name of the header and replace dashes in the header with underscores.
|
||||
* X - any [configuration values](https://gitlab.oracledx.com/odx/functions/blob/master/fn/README.md#application-level-configuration) you've set
|
||||
* `FN_REQUEST_URL` - the full URL for the request ([parsing example](https://github.com/fnproject/fn/tree/master/examples/tutorial/params))
|
||||
* `FN_APP_NAME` - the name of the application that matched this route, eg: `myapp`
|
||||
* `FN_ROUTE` - the matched route, eg: `/hello`
|
||||
* `FN_METHOD` - the HTTP method for the request, eg: `GET` or `POST`
|
||||
* `FN_CALL_ID` - a unique ID for each function execution.
|
||||
* `FN_FORMAT` - a string representing one of the [function formats](function-format.md), currently either `default` or `http`. Default is `default`.
|
||||
* `FN_MEMORY` - a number representing the amount of memory available to the call, in MB
|
||||
* `FN_TYPE` - the type for this call, currently 'sync' or 'async'
|
||||
* `FN_HEADER_$X` - the HTTP headers that were set for this request. Replace $X with the upper cased name of the header and replace dashes in the header with underscores.
|
||||
* `$X` - any [configuration values](https://gitlab.oracledx.com/odx/functions/blob/master/fn/README.md#application-level-configuration) you've set
|
||||
for the Application or the Route. Replace X with the upper cased name of the config variable you set. Ex: `minio_secret=secret` will be exposed via MINIO_SECRET env var.
|
||||
* `FN_PARAM_$Y` - any variables found from parsing the URL. Replace $Y with any `:var` from the url.
|
||||
|
||||
Warning: these may change before release.
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
var noAuth = map[string]interface{}{}
|
||||
|
||||
func main() {
|
||||
request := fmt.Sprintf("%s %s", os.Getenv("METHOD"), os.Getenv("ROUTE"))
|
||||
request := fmt.Sprintf("%s %s", os.Getenv("FN_METHOD"), os.Getenv("FN_ROUTE"))
|
||||
|
||||
dbURI := os.Getenv("DB")
|
||||
if dbURI == "" {
|
||||
@@ -36,7 +36,7 @@ func main() {
|
||||
}
|
||||
|
||||
// GETTING TOKEN
|
||||
if os.Getenv("ROUTE") == "/token" {
|
||||
if os.Getenv("FN_ROUTE") == "/token" {
|
||||
route.HandleToken(db)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func HandlePostRead(db *database.Database, auth map[string]interface{}) {
|
||||
id := os.Getenv("PARAM_ID")
|
||||
id := os.Getenv("FN_PARAM_ID")
|
||||
|
||||
if id == "" {
|
||||
SendError("Missing post ID")
|
||||
|
||||
@@ -63,7 +63,7 @@ func HandleToken(db *database.Database) {
|
||||
}
|
||||
|
||||
func Authentication() (map[string]interface{}, bool) {
|
||||
authorization := os.Getenv("HEADER_AUTHORIZATION")
|
||||
authorization := os.Getenv("FN_HEADER_AUTHORIZATION")
|
||||
|
||||
p := strings.Split(authorization, " ")
|
||||
if len(p) <= 1 {
|
||||
|
||||
@@ -9,8 +9,8 @@ docker rm test-mongo-func
|
||||
|
||||
docker run -p 27017:27017 --name test-mongo-func -d mongo
|
||||
|
||||
echo '{ "title": "My New Post", "body": "Hello world!", "user": "test" }' | docker run --rm -i -e METHOD=POST -e ROUTE=/posts -e DB=mongo:27017 --link test-mongo-func:mongo -e TEST=1 username/func-blog
|
||||
docker run --rm -i -e METHOD=GET -e ROUTE=/posts -e DB=mongo:27017 --link test-mongo-func:mongo -e TEST=1 username/func-blog
|
||||
echo '{ "title": "My New Post", "body": "Hello world!", "user": "test" }' | docker run --rm -i -e FN_METHOD=POST -e FN_ROUTE=/posts -e DB=mongo:27017 --link test-mongo-func:mongo -e TEST=1 username/func-blog
|
||||
docker run --rm -i -e FN_METHOD=GET -e FN_ROUTE=/posts -e DB=mongo:27017 --link test-mongo-func:mongo -e TEST=1 username/func-blog
|
||||
|
||||
docker stop test-mongo-func
|
||||
docker rm test-mongo-func
|
||||
@@ -21,21 +21,21 @@ if payload != ""
|
||||
end
|
||||
|
||||
# Also check for expected env vars: https://gitlab.oracledx.com/odx/functions/blob/master/docs/writing.md#inputs
|
||||
e = ENV["REQUEST_URL"]
|
||||
e = ENV["FN_REQUEST_URL"]
|
||||
puts e
|
||||
uri = URI.parse(e)
|
||||
if !uri.scheme.start_with?('http')
|
||||
raise "invalid REQUEST_URL, does not start with http"
|
||||
end
|
||||
e = ENV["METHOD"]
|
||||
e = ENV["FN_METHOD"]
|
||||
if !(e == "GET" || e == "POST" || e == "DELETE" || e == "PATCH" || e == "PUT")
|
||||
raise "Invalid METHOD: #{e}"
|
||||
end
|
||||
e = ENV["APP_NAME"]
|
||||
e = ENV["FN_APP_NAME"]
|
||||
if e == nil || e == ''
|
||||
raise "No APP_NAME found"
|
||||
end
|
||||
e = ENV["ROUTE"]
|
||||
e = ENV["FN_ROUTE"]
|
||||
if e == nil || e == ''
|
||||
raise "No ROUTE found"
|
||||
end
|
||||
|
||||
@@ -16,11 +16,11 @@ import (
|
||||
|
||||
var (
|
||||
// command to execute, 'SELECT' or 'INSERT'
|
||||
command = os.Getenv("HEADER_COMMAND")
|
||||
command = os.Getenv("FN_HEADER_COMMAND")
|
||||
// postgres host:port, e.g. 'postgres:5432'
|
||||
server = os.Getenv("HEADER_SERVER")
|
||||
server = os.Getenv("FN_HEADER_SERVER")
|
||||
// postgres table name
|
||||
table = os.Getenv("HEADER_TABLE")
|
||||
table = os.Getenv("FN_HEADER_TABLE")
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
envvar := os.Getenv("HEADER_ENVVAR")
|
||||
envvar := os.Getenv("FN_HEADER_ENVVAR")
|
||||
if envvar != "" {
|
||||
fmt.Println("HEADER_ENVVAR:", envvar)
|
||||
fmt.Println("FN_HEADER_ENVVAR:", envvar)
|
||||
}
|
||||
fmt.Println("hw")
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ tests:
|
||||
hw
|
||||
- name: envvar
|
||||
out: |
|
||||
HEADER_ENVVAR: trololo
|
||||
FN_HEADER_ENVVAR: trololo
|
||||
hw
|
||||
env:
|
||||
envvar: trololo
|
||||
|
||||
@@ -9,7 +9,7 @@ tests:
|
||||
hw
|
||||
- name: envvar
|
||||
out: |
|
||||
HEADER_ENVVAR: trololo
|
||||
FN_HEADER_ENVVAR: trololo
|
||||
hw
|
||||
env:
|
||||
envvar: trololo
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := os.Getenv("REQUEST_URL")
|
||||
s := os.Getenv("FN_REQUEST_URL")
|
||||
|
||||
fmt.Printf("REQUEST_URL --> %v\n\n", s)
|
||||
fmt.Printf("FN_REQUEST_URL --> %v\n\n", s)
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
|
||||
@@ -30,7 +30,7 @@ func main() {
|
||||
numLoops := 1
|
||||
|
||||
// Parse the query string
|
||||
s := strings.Split(os.Getenv("REQUEST_URL"), "?")
|
||||
s := strings.Split(os.Getenv("FN_REQUEST_URL"), "?")
|
||||
if len(s) > 1 {
|
||||
for _, pair := range strings.Split(s[1], "&") {
|
||||
kv := strings.Split(pair, "=")
|
||||
|
||||
Reference in New Issue
Block a user