diff --git a/sample-functions/AlpineFunction/Dockerfile b/sample-functions/AlpineFunction/Dockerfile index 8ef0a298..623139e4 100644 --- a/sample-functions/AlpineFunction/Dockerfile +++ b/sample-functions/AlpineFunction/Dockerfile @@ -1,8 +1,11 @@ FROM alpine:latest -ADD https://github.com/alexellis/faas/releases/download/v0.5-alpha/fwatchdog /usr/bin +ADD https://github.com/alexellis/faas/releases/download/0.5.1-alpha/fwatchdog /usr/bin +# COPY ./fwatchdog /usr/bin/ RUN chmod +x /usr/bin/fwatchdog # Populate example here # ENV fprocess="wc -l" + +HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 CMD ["fwatchdog"] diff --git a/sample-functions/AlpineFunction/README.md b/sample-functions/AlpineFunction/README.md new file mode 100644 index 00000000..6d72b970 --- /dev/null +++ b/sample-functions/AlpineFunction/README.md @@ -0,0 +1,5 @@ +## AlpineFunction + +This is a base image for Alpine Linux which already has the watchdog added and configured with a healthcheck. + +This image is published on the Docker hub as `functions/alpine`. diff --git a/sample-functions/ApiKeyProtected/Dockerfile b/sample-functions/ApiKeyProtected/Dockerfile index 2dde4e45..95e6dc06 100644 --- a/sample-functions/ApiKeyProtected/Dockerfile +++ b/sample-functions/ApiKeyProtected/Dockerfile @@ -6,8 +6,8 @@ EXPOSE 8080 ENV http_proxy "" ENV https_proxy "" -# ADD https://github.com/alexellis/faas/releases/download/v0.5-alpha/fwatchdog /usr/bin -COPY fwatchdog /usr/bin/ +ADD https://github.com/alexellis/faas/releases/download/0.5.1-alpha/fwatchdog /usr/bin +# COPY fwatchdog /usr/bin/ RUN chmod +x /usr/bin/fwatchdog COPY app . diff --git a/sample-functions/WebhookStash/Dockerfile b/sample-functions/WebhookStash/Dockerfile index 5d2f7504..b34e38c4 100644 --- a/sample-functions/WebhookStash/Dockerfile +++ b/sample-functions/WebhookStash/Dockerfile @@ -6,7 +6,8 @@ RUN go get -d -v RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . -ADD https://github.com/alexellis/faas/releases/download/v0.5-alpha/fwatchdog /usr/bin +ADD https://github.com/alexellis/faas/releases/download/0.5.1-alpha/fwatchdog /usr/bin + RUN chmod +x /usr/bin/fwatchdog # COPY fwatchdog /usr/bin/ diff --git a/watchdog/README.md b/watchdog/README.md index 25e2b765..70d6b3b3 100644 --- a/watchdog/README.md +++ b/watchdog/README.md @@ -25,13 +25,35 @@ ENV fprocess="/bin/cat" CMD ["fwatchdog"] ``` +**Implementing the a healthcheck** + +Docker swarm will keep your function out of the DNS-RR / IPVS pool if the task (container) is not healthy. + +Here is an example of the `echo` function implementing a healthcheck with a 5-second checking interval. + +``` +FROM functions/alpine + +ENV fprocess="cat /etc/hostname" + +HEALTHCHECK --interval=5s CMD [ -e /tmp/.lock ] || exit 1 +``` + +The watchdog process creates a .lock file in `/tmp/` on starting its internal Golang HTTP server. `[ -e file_name ]` is shell to check if a file exists. + +Swarm tutorial on Healthchecks: + + * [Test-drive Docker Healthcheck in 10 minutes](http://blog.alexellis.io/test-drive-healthcheck/) + + **Environmental overrides:** A number of environmental overrides can be added for additional flexibility and options: | Option | Usage | |------------------------|--------------| -| `fprocess` | The process to invoke for each function call. This must be a UNIX binary and accept input via STDIN and output via STDOUT. | +| `fprocess` | The process to invoke for each function call. This must be a UNIX binary and accept input via STDIN and output via STDOUT. | | `marshal_requests` | Instead of re-directing the raw HTTP body into your fprocess, it will first be marshalled into JSON. Use this if you need to work with HTTP headers | -| `write_timeout` | HTTP timeout for writing a response body from your function | -| `read_timeout` | HTTP timeout for reading the payload from the client caller | +| `write_timeout` | HTTP timeout for writing a response body from your function | +| `read_timeout` | HTTP timeout for reading the payload from the client caller | +| `suppress_lock` | The watchdog will attempt to write a lockfile to /tmp/ for swarm healthchecks - set this to true to disable behaviour. | diff --git a/watchdog/build.sh b/watchdog/build.sh index 3cc5d5ee..01413a27 100755 --- a/watchdog/build.sh +++ b/watchdog/build.sh @@ -3,7 +3,7 @@ # Below makes use of "builder pattern" so that binary is extracted separate # from the golang runtime/SDK -docker build --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \ +docker build --no-cache --build-arg https_proxy=$https_proxy --build-arg http_proxy=$http_proxy \ -t functions/watchdog:build . docker create --name buildoutput functions/watchdog:build echo docker cp buildoutput:/go/src/github.com/alexellis/faas/watchdog/watchdog ./fwatchdog diff --git a/watchdog/config_test.go b/watchdog/config_test.go index 7291a9ac..71a9e02c 100644 --- a/watchdog/config_test.go +++ b/watchdog/config_test.go @@ -29,19 +29,46 @@ func TestRead_WriteDebug_DefaultIsTrueConfig(t *testing.T) { config := readConfig.Read(defaults) if config.writeDebug != true { - t.Logf("writeDebug should have been true") + t.Logf("writeDebug should have been true (unspecified)") t.Fail() } } -func TestRead_WriteDebug_FalseConfig(t *testing.T) { + +func TestRead_WriteDebug_FalseOverrideConfig(t *testing.T) { defaults := NewEnvBucket() readConfig := ReadConfig{} - defaults.Setenv("writeDebug", "true") + defaults.Setenv("write_debug", "false") + + config := readConfig.Read(defaults) + + if config.writeDebug != false { + t.Logf("writeDebug should have been false (specified)") + t.Fail() + } +} + +func TestRead_WriteDebug_TrueConfig(t *testing.T) { + defaults := NewEnvBucket() + readConfig := ReadConfig{} + defaults.Setenv("write_debug", "true") config := readConfig.Read(defaults) if config.writeDebug != true { - t.Logf("writeDebug should have been true") + t.Logf("writeDebug should have been true (specified)") + t.Fail() + } +} + +func TestRead_SuppressLockConfig(t *testing.T) { + defaults := NewEnvBucket() + readConfig := ReadConfig{} + defaults.Setenv("suppress_lock", "true") + + config := readConfig.Read(defaults) + + if config.suppressLock != true { + t.Logf("suppress_lock envVariable incorrect, got: %s.\n", config.faasProcess) t.Fail() } } diff --git a/watchdog/main.go b/watchdog/main.go index 417e054b..076d61f3 100644 --- a/watchdog/main.go +++ b/watchdog/main.go @@ -136,9 +136,11 @@ func main() { http.HandleFunc("/", makeRequestHandler(&config)) if config.suppressLock == false { - writeErr := ioutil.WriteFile("/tmp/.lock", []byte{}, 0660) + path := "/tmp/.lock" + log.Printf("Writing lock-file to: %s\n", path) + writeErr := ioutil.WriteFile(path, []byte{}, 0660) if writeErr != nil { - log.Panicf("Cannot write /tmp/.lock for healthcheck: %s \n", writeErr.Error()) + log.Panicf("Cannot write %s. Error: %s\n", path, writeErr.Error()) } } diff --git a/watchdog/readconfig.go b/watchdog/readconfig.go index 53215ce4..de47485a 100644 --- a/watchdog/readconfig.go +++ b/watchdog/readconfig.go @@ -18,7 +18,7 @@ func parseBoolValue(val string) bool { if val == "true" { return true } - return true + return false } func parseIntValue(val string) int { @@ -55,11 +55,15 @@ func (ReadConfig) Read(hasEnv HasEnv) WatchdogConfig { cfg.readTimeout = time.Duration(readTimeout) * time.Second cfg.writeTimeout = time.Duration(writeTimeout) * time.Second - cfg.writeDebug = parseBoolValue(hasEnv.Getenv("write_debug")) + if len(hasEnv.Getenv("write_debug")) > 0 { + cfg.writeDebug = parseBoolValue(hasEnv.Getenv("write_debug")) + } cfg.marshalRequest = parseBoolValue(hasEnv.Getenv("marshal_request")) cfg.debugHeaders = parseBoolValue(hasEnv.Getenv("debug_headers")) + cfg.suppressLock = parseBoolValue(hasEnv.Getenv("suppress_lock")) + return cfg }