This is an automated email from the ASF dual-hosted git repository.
rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/openwhisk-runtime-go.git
The following commit(s) were added to refs/heads/master by this push:
new 23ab27f Enhance proxy for asynchornous handshake on action init (#121)
23ab27f is described below
commit 23ab27f7e607377eef68fcb39104807d43f2e657
Author: Michele Sciabarra <[email protected]>
AuthorDate: Tue Feb 18 17:04:35 2020 +0100
Enhance proxy for asynchornous handshake on action init (#121)
This commit add an explicit "ack" between the proxy and the action runner.
The new protocol requires explicit opt in via environment variables set
accordingly. This change allows the proxy to reliabliy detect functions that
fail at init time without using timed heuristics (that don't work for slow to
come up runtimes, leading to errors at the "run" step instead). The
enhancements also better separate logs from the action runtime.
---
CHANGES.md | 5 ++
README.md | 2 +-
docs/ACTION.md | 8 ++-
docs/ENVVARS.md | 50 ++++++++++++++
examples/golang-hello-vendor/src/hello/Gopkg.toml | 15 +++++
examples/golang-main-vendor/src/main/Gopkg.toml | 15 +++++
.../main/Gopkg.toml => openwhisk/_test/badack.sh | 38 +----------
.../main/Gopkg.toml => openwhisk/_test/badack2.sh | 38 +----------
.../Gopkg.toml => openwhisk/_test/badcompile.sh | 38 +----------
openwhisk/_test/build.sh | 3 +
.../Gopkg.toml => openwhisk/_test/helloack/exec | 43 +++---------
openwhisk/actionProxy.go | 30 ++++++++-
openwhisk/actionProxy_test.go | 46 +++++++++++++
openwhisk/compiler.go | 9 ++-
openwhisk/executor.go | 78 ++++++++++++++++++++--
openwhisk/executor_test.go | 65 ++++++++++++++++--
openwhisk/initHandler.go | 18 ++++-
openwhisk/initHandler_test.go | 6 +-
openwhisk/version.go | 2 +-
19 files changed, 344 insertions(+), 165 deletions(-)
diff --git a/CHANGES.md b/CHANGES.md
index 4af8485..9e0526a 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -16,6 +16,11 @@
# limitations under the License.
#
-->
+# 1.16.0 (next release)
+- added OW_WAIT_FOR_ACK such at if true, the proxy waits for an
acknowledgement from the action on startup
+- added OW_EXECUTION_ENV to validate the execution environment before starting
an action
+- write compilation logs to standard out
+
# 1.15.0
- added OW_ACTION_VERSION to action environment (PR#113)
- propagate API_HOST from parent to child process (PR#115)
diff --git a/README.md b/README.md
index 960d0a2..7009c42 100644
--- a/README.md
+++ b/README.md
@@ -38,6 +38,7 @@ This repository contains both the OpenWhisk runtime for
Golang Actions, as well
- Writing [Generic](docs/ACTION.md#generic) actions, in bash or as a generic
linux binary
- Deployment for [Generic](docs/DEPLOY.md#generic) actions
- The [ActionLoop](docs/ACTION.md#actionloop) protocol for generic actions
+- Environment [Variables](docs/ENVVARS.md) to configure the proxy
# Change Log
@@ -45,4 +46,3 @@ This repository contains both the OpenWhisk runtime for
Golang Actions, as well
# License
[Apache 2.0](LICENSE.txt)
-
diff --git a/docs/ACTION.md b/docs/ACTION.md
index 840feb2..8fc639f 100644
--- a/docs/ACTION.md
+++ b/docs/ACTION.md
@@ -75,7 +75,9 @@ The `actionloop` runtime can execute generic Linux
executable in an efficient w
### The Action Loop Protocol
-The protocol can be specified informally as follow.
+The protocol can be specified informally as follows.
+
+- Send an acknowledgement after initialization when required. If the
environment variable `__OW_WAIT_FOR_ACK` is not empty, write on file descriptor
3 the string `{ "ok": true }`.
- Read one line from standard input (file descriptor 0).
- Parse the line as a JSON object. Currently the object will be in currently
in the format:
@@ -108,6 +110,10 @@ In the current actionloop image there is `bash` and the
`jq` command, so you can
```bash
#!/bin/bash
+# send an ack if required
+if test -n "$__OW_WAIT_FOR_ACK"
+ then echo '{"ok":true}' >&3
+fi
# read input forever line by line
while read line
do
diff --git a/docs/ENVVARS.md b/docs/ENVVARS.md
new file mode 100644
index 0000000..22210b1
--- /dev/null
+++ b/docs/ENVVARS.md
@@ -0,0 +1,50 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+-->
+
+# Environment Variables
+
+## Enviroment variables that control behaviour of the proxy
+
+The following variables are usually set in the Dockerfile
+
+`OW_COMPILER` points to the compiler script to use to compile actions.
+
+`OW_SAVE_JAR` enables checking that an upload file is a jar (that is itself a
zip file) and it will not expand it if there is a subdirectory names "META-INF"
(so it is a jar file). Used to support uploading of Java jars.
+
+`OW_WAIT_FOR_ACK` enables waiting for an acknowledgement in the actionloop
protocol. It should be enabled in all the newer runtimes. Do not enable in
existing runtimes as it would break existing actions built for that runtime.
+
+`OW_EXECUTION_ENV` enables detection and verification of the compilation
environent. The compiler is expected to create a file named `exec.env` in the
same folder as the `exec` file to be run. If this variable is set, before
starting an action, the init will check that the content of the `exec.env`,
trimmed of spaces and new lines, is the same, to ensure an action is executed
in the right execution environment.
+
+`OW_LOG_INIT_ERROR` enables logging of compilation error; the default
behaviour is to return errors in the answer of the init.
+
+## Environment variables propagated to actions and to the compilation script
+
+The proxy itself sets the following environment variables:
+
+`__OW_EXECUTION_ENV` is the same value that the proxy receive as
`OW_EXECUTION_ENV`
+
+`__OW_WAIT_FOR_ACK` is set if the proxy has the variable `OW_WAIT_FOR_ACK` set.
+
+Any other environment variable set in the Dockerfile that starts with `__OW_`
are propagated to the proxy and can override also the values set by the proxy.
+
+Furthermore, actions receive their own environment variables and the values
set overrides the variables set from the proxy and in the environment.
+
+
+
+
diff --git a/examples/golang-hello-vendor/src/hello/Gopkg.toml
b/examples/golang-hello-vendor/src/hello/Gopkg.toml
index 3fc5f20..4dad4be 100644
--- a/examples/golang-hello-vendor/src/hello/Gopkg.toml
+++ b/examples/golang-hello-vendor/src/hello/Gopkg.toml
@@ -17,6 +17,21 @@
# Gopkg.toml example
#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
diff --git a/examples/golang-main-vendor/src/main/Gopkg.toml
b/examples/golang-main-vendor/src/main/Gopkg.toml
index 48e74a1..c3dfb0e 100644
--- a/examples/golang-main-vendor/src/main/Gopkg.toml
+++ b/examples/golang-main-vendor/src/main/Gopkg.toml
@@ -17,6 +17,21 @@
# Gopkg.toml example
#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
# for detailed Gopkg.toml documentation.
#
diff --git a/examples/golang-main-vendor/src/main/Gopkg.toml
b/openwhisk/_test/badack.sh
old mode 100644
new mode 100755
similarity index 51%
copy from examples/golang-main-vendor/src/main/Gopkg.toml
copy to openwhisk/_test/badack.sh
index 48e74a1..524122b
--- a/examples/golang-main-vendor/src/main/Gopkg.toml
+++ b/openwhisk/_test/badack.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -14,38 +15,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-# Gopkg.toml example
-#
-# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
-# for detailed Gopkg.toml documentation.
-#
-# required = ["github.com/user/thing/cmd/thing"]
-# ignored = ["github.com/user/project/pkgX",
"bitbucket.org/user/project/pkgA/pkgY"]
-#
-# [[constraint]]
-# name = "github.com/user/project"
-# version = "1.0.0"
-#
-# [[constraint]]
-# name = "github.com/user/project2"
-# branch = "dev"
-# source = "github.com/myfork/project2"
-#
-# [[override]]
-# name = "github.com/x/y"
-# version = "2.4.0"
-#
-# [prune]
-# non-go = false
-# go-tests = true
-# unused-packages = true
-
-
-[[constraint]]
- name = "github.com/rs/zerolog"
- version = "1.9.1"
-
-[prune]
- go-tests = true
- unused-packages = true
+echo "bad ack" >&3
+read
diff --git a/examples/golang-main-vendor/src/main/Gopkg.toml
b/openwhisk/_test/badack2.sh
old mode 100644
new mode 100755
similarity index 51%
copy from examples/golang-main-vendor/src/main/Gopkg.toml
copy to openwhisk/_test/badack2.sh
index 48e74a1..0412f00
--- a/examples/golang-main-vendor/src/main/Gopkg.toml
+++ b/openwhisk/_test/badack2.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -14,38 +15,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-# Gopkg.toml example
-#
-# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
-# for detailed Gopkg.toml documentation.
-#
-# required = ["github.com/user/thing/cmd/thing"]
-# ignored = ["github.com/user/project/pkgX",
"bitbucket.org/user/project/pkgA/pkgY"]
-#
-# [[constraint]]
-# name = "github.com/user/project"
-# version = "1.0.0"
-#
-# [[constraint]]
-# name = "github.com/user/project2"
-# branch = "dev"
-# source = "github.com/myfork/project2"
-#
-# [[override]]
-# name = "github.com/x/y"
-# version = "2.4.0"
-#
-# [prune]
-# non-go = false
-# go-tests = true
-# unused-packages = true
-
-
-[[constraint]]
- name = "github.com/rs/zerolog"
- version = "1.9.1"
-
-[prune]
- go-tests = true
- unused-packages = true
+echo "{}" >&3
+read
diff --git a/examples/golang-main-vendor/src/main/Gopkg.toml
b/openwhisk/_test/badcompile.sh
old mode 100644
new mode 100755
similarity index 51%
copy from examples/golang-main-vendor/src/main/Gopkg.toml
copy to openwhisk/_test/badcompile.sh
index 48e74a1..ef5d986
--- a/examples/golang-main-vendor/src/main/Gopkg.toml
+++ b/openwhisk/_test/badcompile.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -14,38 +15,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-# Gopkg.toml example
-#
-# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
-# for detailed Gopkg.toml documentation.
-#
-# required = ["github.com/user/thing/cmd/thing"]
-# ignored = ["github.com/user/project/pkgX",
"bitbucket.org/user/project/pkgA/pkgY"]
-#
-# [[constraint]]
-# name = "github.com/user/project"
-# version = "1.0.0"
-#
-# [[constraint]]
-# name = "github.com/user/project2"
-# branch = "dev"
-# source = "github.com/myfork/project2"
-#
-# [[override]]
-# name = "github.com/x/y"
-# version = "2.4.0"
-#
-# [prune]
-# non-go = false
-# go-tests = true
-# unused-packages = true
-
-
-[[constraint]]
- name = "github.com/rs/zerolog"
- version = "1.9.1"
-
-[prune]
- go-tests = true
- unused-packages = true
+echo "error in stdout"
+echo "error in stderr" >&2
diff --git a/openwhisk/_test/build.sh b/openwhisk/_test/build.sh
index 1922b73..fe17a65 100755
--- a/openwhisk/_test/build.sh
+++ b/openwhisk/_test/build.sh
@@ -57,4 +57,7 @@ cd jar ; zip -q -r ../sample.jar * ; cd ..
build exec
test -e exec.zip && rm exec.zip
zip -q -r exec.zip exec etc dir
+echo exec/env >helloack/exec.env
+zip -j helloack.zip helloack/*
+
diff --git a/examples/golang-main-vendor/src/main/Gopkg.toml
b/openwhisk/_test/helloack/exec
old mode 100644
new mode 100755
similarity index 51%
copy from examples/golang-main-vendor/src/main/Gopkg.toml
copy to openwhisk/_test/helloack/exec
index 48e74a1..7f14074
--- a/examples/golang-main-vendor/src/main/Gopkg.toml
+++ b/openwhisk/_test/helloack/exec
@@ -1,3 +1,4 @@
+#!/bin/bash
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
@@ -14,38 +15,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-# Gopkg.toml example
-#
-# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html
-# for detailed Gopkg.toml documentation.
-#
-# required = ["github.com/user/thing/cmd/thing"]
-# ignored = ["github.com/user/project/pkgX",
"bitbucket.org/user/project/pkgA/pkgY"]
-#
-# [[constraint]]
-# name = "github.com/user/project"
-# version = "1.0.0"
-#
-# [[constraint]]
-# name = "github.com/user/project2"
-# branch = "dev"
-# source = "github.com/myfork/project2"
-#
-# [[override]]
-# name = "github.com/x/y"
-# version = "2.4.0"
-#
-# [prune]
-# non-go = false
-# go-tests = true
-# unused-packages = true
-
-
-[[constraint]]
- name = "github.com/rs/zerolog"
- version = "1.9.1"
-
-[prune]
- go-tests = true
- unused-packages = true
+echo '{"ok":true}' >&3
+while read line
+do
+ name="$(echo $line | jq -r .value.name)"
+ echo msg="hello $name"
+ echo '{"hello": "'$name'"}' >&3
+done
diff --git a/openwhisk/actionProxy.go b/openwhisk/actionProxy.go
index 805207c..bceab1a 100644
--- a/openwhisk/actionProxy.go
+++ b/openwhisk/actionProxy.go
@@ -72,6 +72,18 @@ func NewActionProxy(baseDir string, compiler string, outFile
*os.File, errFile *
//SetEnv sets the environment
func (ap *ActionProxy) SetEnv(env map[string]interface{}) {
+ // Propagate proxy version
+ ap.env["__OW_PROXY_VERSION"] = Version
+ // propagate OW_EXECUTION_ENV as __OW_EXECUTION_ENV
+ ee := os.Getenv("OW_EXECUTION_ENV")
+ if ee != "" {
+ ap.env["__OW_EXECUTION_ENV"] = ee
+ }
+ // require an ack
+ wa := os.Getenv("OW_WAIT_FOR_ACK")
+ if wa != "" {
+ ap.env["__OW_WAIT_FOR_ACK"] = wa
+ }
// propagate all the variables starting with "__OW_"
for _, v := range os.Environ() {
if strings.HasPrefix(v, "__OW_") {
@@ -107,6 +119,20 @@ func (ap *ActionProxy) StartLatestAction() error {
return fmt.Errorf("no valid actions available")
}
+ // check version
+ execEnv := os.Getenv("OW_EXECUTION_ENV")
+ if execEnv != "" {
+ execEnvFile := fmt.Sprintf("%s/%d/bin/exec.env", ap.baseDir,
highestDir)
+ execEnvData, err := ioutil.ReadFile(execEnvFile)
+ if err != nil {
+ return err
+ }
+ if strings.TrimSpace(string(execEnvData)) != execEnv {
+ fmt.Printf("Expected exec.env should start with
%s\nActual value: %s", execEnv, execEnvData)
+ return fmt.Errorf("Execution environment version
mismatch. See logs for details.")
+ }
+ }
+
// save the current executor
curExecutor := ap.theExecutor
@@ -115,7 +141,9 @@ func (ap *ActionProxy) StartLatestAction() error {
os.Chmod(executable, 0755)
newExecutor := NewExecutor(ap.outFile, ap.errFile, executable, ap.env)
Debug("starting %s", executable)
- err := newExecutor.Start()
+
+ // start executor
+ err := newExecutor.Start(os.Getenv("OW_WAIT_FOR_ACK") != "")
if err == nil {
ap.theExecutor = newExecutor
if curExecutor != nil {
diff --git a/openwhisk/actionProxy_test.go b/openwhisk/actionProxy_test.go
index 2fdbd06..e28772c 100644
--- a/openwhisk/actionProxy_test.go
+++ b/openwhisk/actionProxy_test.go
@@ -126,6 +126,24 @@ func Example_compile_src() {
// ./action/c2/out/exec
}
+func Example_badcompile() {
+
+ os.Setenv("OW_LOG_INIT_ERROR", "1")
+ ts, cur, log := startTestServer("_test/badcompile.sh")
+ res, _, _ := doPost(ts.URL+"/init", initBytes([]byte("hello"), "main"))
+ fmt.Print(res)
+ stopTestServer(ts, cur, log)
+ os.Setenv("OW_LOG_INIT_ERROR", "")
+ // Unordered output:
+ // {"error":"The action failed to generate or locate a binary. See logs
for details."}
+ // error in stdout
+ // error in stderr
+ //
+ // XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+ // XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+
+}
+
func Example_SetEnv() {
ap := NewActionProxy("", "", nil, nil)
fmt.Println(ap.env)
@@ -144,3 +162,31 @@ func Example_SetEnv() {
// [1,2,3] {"a":1,"b":2} string 123
}
+
+func Example_executionEnv_nocheck() {
+ os.Setenv("OW_EXECUTION_ENV", "")
+ ts, cur, log := startTestServer("")
+ res, _, _ := doPost(ts.URL+"/init", initBinary("_test/helloack.zip",
"main"))
+ fmt.Print(res)
+ stopTestServer(ts, cur, log)
+ // Output:
+ // {"ok":true}
+}
+
+func Example_executionEnv_check() {
+ os.Setenv("OW_EXECUTION_ENV", "bad/env")
+ ts, cur, log := startTestServer("")
+ res, _, _ := doPost(ts.URL+"/init", initBinary("_test/helloack.zip",
"main"))
+ fmt.Print(res)
+ os.Setenv("OW_EXECUTION_ENV", "exec/env")
+ res, _, _ = doPost(ts.URL+"/init", initBinary("_test/helloack.zip",
"main"))
+ fmt.Print(res)
+ stopTestServer(ts, cur, log)
+ // reset value
+ os.Setenv("OW_EXECUTION_ENV", "")
+ // Output:
+ // Expected exec.env should start with bad/env
+ // Actual value: exec/env
+ // {"error":"cannot start action: Execution environment version
mismatch. See logs for details."}
+ // {"ok":true}
+}
diff --git a/openwhisk/compiler.go b/openwhisk/compiler.go
index 686c5e2..0236be4 100644
--- a/openwhisk/compiler.go
+++ b/openwhisk/compiler.go
@@ -54,15 +54,18 @@ func (ap *ActionProxy) CompileAction(main string, srcDir
string, binDir string)
var cmd *exec.Cmd
cmd = exec.Command(ap.compiler, main, srcDir, binDir)
cmd.Env = []string{"PATH=" + os.Getenv("PATH")}
+ for k, v := range ap.env {
+ cmd.Env = append(cmd.Env, k+"="+v)
+ }
// gather stdout and stderr
out, err := cmd.CombinedOutput()
Debug("compiler out: %s, %v", out, err)
- if err != nil {
- return err
- }
if len(out) > 0 {
return fmt.Errorf("%s", out)
}
+ if err != nil {
+ return err
+ }
return nil
}
diff --git a/openwhisk/executor.go b/openwhisk/executor.go
index 707018d..2744738 100644
--- a/openwhisk/executor.go
+++ b/openwhisk/executor.go
@@ -19,6 +19,8 @@ package openwhisk
import (
"bufio"
+ "encoding/json"
+ "errors"
"fmt"
"io"
"os"
@@ -79,7 +81,26 @@ func (proc *Executor) Interact(in []byte) ([]byte, error) {
// input to the subprocess
proc.input.Write(in)
proc.input.Write([]byte("\n"))
- out, err := proc.output.ReadBytes('\n')
+
+ chout := make(chan []byte)
+ go func() {
+ out, err := proc.output.ReadBytes('\n')
+ if err == nil {
+ chout <- out
+ } else {
+ chout <- []byte{}
+ }
+ }()
+ var err error
+ var out []byte
+ select {
+ case out = <-chout:
+ if len(out) == 0 {
+ err = errors.New("no answer from the action")
+ }
+ case <-proc.exited:
+ err = errors.New("command exited")
+ }
proc.cmd.Stdout.Write([]byte(OutputGuard))
proc.cmd.Stderr.Write([]byte(OutputGuard))
return out, err
@@ -95,10 +116,16 @@ func (proc *Executor) Exited() bool {
}
}
+// ActionAck is the expected data structure for the action acknowledgement
+type ActionAck struct {
+ Ok bool `json:"ok"`
+}
+
// Start execution of the command
-// wait a bit to check if the command exited
+// if the flag ack is true, wait forever for an acknowledgement
+// if the flag ack is false wait a bit to check if the command exited
// returns an error if the program fails
-func (proc *Executor) Start() error {
+func (proc *Executor) Start(waitForAck bool) error {
// start the underlying executable
Debug("Start:")
err := proc.cmd.Start()
@@ -108,15 +135,54 @@ func (proc *Executor) Start() error {
return fmt.Errorf("command exited")
}
Debug("pid: %d", proc.cmd.Process.Pid)
+
go func() {
proc.cmd.Wait()
proc.exited <- true
}()
+
+ // not waiting for an ack, so use a timeout
+ if !waitForAck {
+ select {
+ case <-proc.exited:
+ return fmt.Errorf("command exited")
+ case <-time.After(DefaultTimeoutStart):
+ return nil
+ }
+ }
+
+ // wait for acknowledgement
+ Debug("waiting for an ack")
+ ack := make(chan error)
+ go func() {
+ out, err := proc.output.ReadBytes('\n')
+ Debug("received ack %s", out)
+ if err != nil {
+ ack <- err
+ return
+ }
+ // parse ack
+ var ackData ActionAck
+ err = json.Unmarshal(out, &ackData)
+ if err != nil {
+ ack <- err
+ return
+ }
+ // check ack
+ if !ackData.Ok {
+ ack <- fmt.Errorf("The action did not initialize
properly.")
+ return
+ }
+ ack <- nil
+ }()
+ // wait for ack or unexpected termination
select {
+ // ack received
+ case err = <-ack:
+ return err
+ // process exited
case <-proc.exited:
- return fmt.Errorf("command exited")
- case <-time.After(DefaultTimeoutStart):
- return nil
+ return fmt.Errorf("command exited before ack")
}
}
diff --git a/openwhisk/executor_test.go b/openwhisk/executor_test.go
index 93702ad..bae8962 100644
--- a/openwhisk/executor_test.go
+++ b/openwhisk/executor_test.go
@@ -26,19 +26,19 @@ var m = map[string]string{}
func ExampleNewExecutor_failed() {
log, _ := ioutil.TempFile("", "log")
proc := NewExecutor(log, log, "true", m)
- err := proc.Start()
+ err := proc.Start(false)
fmt.Println(err)
proc.Stop()
proc = NewExecutor(log, log, "/bin/pwd", m)
- err = proc.Start()
+ err = proc.Start(false)
fmt.Println(err)
proc.Stop()
proc = NewExecutor(log, log, "donotexist", m)
- err = proc.Start()
+ err = proc.Start(false)
fmt.Println(err)
proc.Stop()
proc = NewExecutor(log, log, "/etc/passwd", m)
- err = proc.Start()
+ err = proc.Start(false)
fmt.Println(err)
proc.Stop()
// Output:
@@ -51,7 +51,7 @@ func ExampleNewExecutor_failed() {
func ExampleNewExecutor_bc() {
log, _ := ioutil.TempFile("", "log")
proc := NewExecutor(log, log, "_test/bc.sh", m)
- err := proc.Start()
+ err := proc.Start(false)
fmt.Println(err)
res, _ := proc.Interact([]byte("2+2"))
fmt.Printf("%s", res)
@@ -67,7 +67,7 @@ func ExampleNewExecutor_bc() {
func ExampleNewExecutor_hello() {
log, _ := ioutil.TempFile("", "log")
proc := NewExecutor(log, log, "_test/hello.sh", m)
- err := proc.Start()
+ err := proc.Start(false)
fmt.Println(err)
res, _ := proc.Interact([]byte(`{"value":{"name":"Mike"}}`))
fmt.Printf("%s", res)
@@ -84,7 +84,7 @@ func ExampleNewExecutor_hello() {
func ExampleNewExecutor_env() {
log, _ := ioutil.TempFile("", "log")
proc := NewExecutor(log, log, "_test/env.sh",
map[string]string{"TEST_HELLO": "WORLD", "TEST_HI": "ALL"})
- err := proc.Start()
+ err := proc.Start(false)
fmt.Println(err)
res, _ := proc.Interact([]byte(`{"value":{"name":"Mike"}}`))
fmt.Printf("%s", res)
@@ -96,3 +96,54 @@ func ExampleNewExecutor_env() {
// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
// XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
}
+
+func ExampleNewExecutor_ack() {
+ log, _ := ioutil.TempFile("", "log")
+ proc := NewExecutor(log, log, "_test/hi", m)
+ err := proc.Start(true)
+ fmt.Println(err)
+ proc.Stop()
+ dump(log)
+ // Output:
+ // command exited before ack
+ // hi
+}
+
+func ExampleNewExecutor_badack() {
+ log, _ := ioutil.TempFile("", "log")
+ proc := NewExecutor(log, log, "_test/badack.sh", m)
+ err := proc.Start(true)
+ fmt.Println(err)
+ proc.Stop()
+ dump(log)
+ // Output:
+ // invalid character 'b' looking for beginning of value
+}
+
+func ExampleNewExecutor_badack2() {
+ log, _ := ioutil.TempFile("", "log")
+ proc := NewExecutor(log, log, "_test/badack2.sh", m)
+ err := proc.Start(true)
+ fmt.Println(err)
+ proc.Stop()
+ dump(log)
+ // Output:
+ // The action did not initialize properly.
+}
+
+func ExampleNewExecutor_helloack() {
+ log, _ := ioutil.TempFile("", "log")
+ proc := NewExecutor(log, log, "_test/helloack/exec", m)
+ err := proc.Start(true)
+ fmt.Println(err)
+ res, _ := proc.Interact([]byte(`{"value":{"name":"Mike"}}`))
+ fmt.Printf("%s", res)
+ proc.Stop()
+ dump(log)
+ // Output:
+ // <nil>
+ // {"hello": "Mike"}
+ // msg=hello Mike
+ // XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+ // XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX
+}
diff --git a/openwhisk/initHandler.go b/openwhisk/initHandler.go
index 0664508..1391c63 100644
--- a/openwhisk/initHandler.go
+++ b/openwhisk/initHandler.go
@@ -117,14 +117,28 @@ func (ap *ActionProxy) initHandler(w http.ResponseWriter,
r *http.Request) {
// if a compiler is defined try to compile
_, err = ap.ExtractAndCompile(&buf, main)
if err != nil {
- sendError(w, http.StatusBadGateway, err.Error())
+ if os.Getenv("OW_LOG_INIT_ERROR") == "" {
+ sendError(w, http.StatusBadGateway, err.Error())
+ } else {
+ ap.errFile.Write([]byte(err.Error() + "\n"))
+ ap.outFile.Write([]byte(OutputGuard))
+ ap.errFile.Write([]byte(OutputGuard))
+ sendError(w, http.StatusBadGateway, "The action failed
to generate or locate a binary. See logs for details.")
+ }
return
}
// start an action
err = ap.StartLatestAction()
if err != nil {
- sendError(w, http.StatusBadRequest, "cannot start action:
"+err.Error())
+ if os.Getenv("OW_LOG_INIT_ERROR") == "" {
+ sendError(w, http.StatusBadGateway, "cannot start
action: "+err.Error())
+ } else {
+ ap.errFile.Write([]byte(err.Error() + "\n"))
+ ap.outFile.Write([]byte(OutputGuard))
+ ap.errFile.Write([]byte(OutputGuard))
+ sendError(w, http.StatusBadGateway, "Cannot start
action. Check logs for details.")
+ }
return
}
ap.initialized = true
diff --git a/openwhisk/initHandler_test.go b/openwhisk/initHandler_test.go
index e18edd7..89e9545 100644
--- a/openwhisk/initHandler_test.go
+++ b/openwhisk/initHandler_test.go
@@ -221,9 +221,9 @@ func Example_badinit_nocompiler() {
// Output:
// 500 {"error":"no action defined yet"}
// 403 {"error":"Missing main/no code to execute."}
- // 400 {"error":"cannot start action: command exited"}
- // 400 {"error":"cannot start action: command exited"}
- // 400 {"error":"cannot start action: command exited"}
+ // 502 {"error":"cannot start action: command exited"}
+ // 502 {"error":"cannot start action: command exited"}
+ // 502 {"error":"cannot start action: command exited"}
// 500 {"error":"no action defined yet"}
// hi
}
diff --git a/openwhisk/version.go b/openwhisk/version.go
index 2bc050c..fd310fe 100644
--- a/openwhisk/version.go
+++ b/openwhisk/version.go
@@ -17,4 +17,4 @@
package openwhisk
// Version number - internal
-var Version = "1.14.0"
+var Version = "1.16.0"