Sync the Gateway configuration with a remote location (#290)

* sync the Gateway configuration with a remote location
* updated health-check to include config status as well
diff --git a/Dockerfile b/Dockerfile
index 8592d61..148e79b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -15,7 +15,7 @@
 #
 # apigateway
 #
-# VERSION               1.9.7.3
+# VERSION               1.13.6.1
 #
 # From https://hub.docker.com/_/alpine/
 #
@@ -227,6 +227,10 @@
     && make && make install \
     && rm -rf /tmp/api-gateway
 
+ENV CONFIG_SUPERVISOR_VERSION 1.0.1-RC1
+COPY build_config_supervisor.sh /tmp/build_config_supervisor.sh 
+RUN sh +x /tmp/build_config_supervisor.sh 
+
 COPY init.sh /etc/init-container.sh
 # add the default configuration for the Gateway
 COPY . /etc/api-gateway
diff --git a/README.md b/README.md
index 02b11da..576db0a 100644
--- a/README.md
+++ b/README.md
@@ -53,6 +53,32 @@
 - [v2 Management Interface](https://github.com/openwhisk/openwhisk-apigateway/blob/master/doc/v2/management_interface_v2.md)
 - [v1 Management Interface](https://github.com/openwhisk/openwhisk-apigateway/blob/master/doc/v1/management_interface_v1.md)
 
+## Syncing configuration from a remote source
+
+The Gateway can sync its configuration with a remote folder in the cloud such as Amazon S3, Google Cloud Storage, IBM Cloud Object Storage, Dropbox, and [many others](https://rclone.org/). The configuration is monitored for changes, and when a file is changed, the Gateway is reloaded automatically. This is very useful to gracefully update the Gateway on the fly, without impacting the active traffic; if the new configuration is invalid, the Gateway doesn't break, running with the last known valid configuration.
+
+This feature is enabled by configuring a few environment variables:
+* `REMOTE_CONFIG` - the location where the configuration should be synced from. I.e. `s3://api-gateway-config` . The remote location is synced into is `/etc/api-gateway`.
+The default configuration is found in this project's root folder.
+* (optional) `REMOTE_CONFIG_SYNC_INTERVAL` - how often to check for changes in the remote location. The default value is `10s`
+* (optional) `REMOTE_CONFIG_RELOAD_CMD` - which command to execute in order to reload the Gateway. The default value is: `api-gateway -s reload`
+
+Syncing is done through [rclone sync](https://rclone.org/commands/rclone_sync/). `rclone` has a rich set of [options](https://rclone.org/commands/rclone_sync/) such as what to exclude when syncing, or what to include. An important configuration is `--config`, pointing to the config file in `/root/.config/rclone/rclone.conf`. The Gateway should be started with `/root/.config/rclone` folder mounted so that `rclone.conf` is present.  To generate a new `rclone` configuration simply execute:
+
+```
+docker run -ti --rm --entrypoint=rclone -v `pwd`/rclone/:/root/.config/rclone/ openwhisk/apigateway config
+```  
+
+This runs an interactive `rclone config` command and stores the resulted configuration in `./rclone/rclone.conf` file.  
+
+To test this locally, _simulate_ a remote folder using a local location, by mounting it in `/tmp` folder as follows:
+
+```bash
+docker run -ti --rm -p 80:80 \
+    -v `pwd`:/tmp/api-gateway-local -e REMOTE_CONFIG="/tmp/api-gateway-local" \
+    -e REDIS_HOST=redis_host -e REDIS_PORT=redis_port openwhisk/apigateway
+```
+Then make changes to any configuration file ( i.e. `api-gateway.conf` ), save it, then watch as the Gateway reloads the updated configuration automatically.
 
 ## Developer Guide
 
diff --git a/build_config_supervisor.sh b/build_config_supervisor.sh
new file mode 100644
index 0000000..eccb7bf
--- /dev/null
+++ b/build_config_supervisor.sh
@@ -0,0 +1,40 @@
+#ENV CONFIG_SUPERVISOR_VERSION 1.0.1-RC1
+export GOPATH=/usr/lib/go/bin
+export GOBIN=/usr/lib/go/bin
+export PATH=$PATH:/usr/lib/go/bin
+echo " ... installing api-gateway-config-supervisor  ... " \
+apk update 
+apk add gcc make git go 
+mkdir -p /tmp/api-gateway 
+curl -k -L https://github.com/adobe-apiplatform/api-gateway-config-supervisor/archive/${CONFIG_SUPERVISOR_VERSION}.tar.gz -o /tmp/api-gateway/api-gateway-config-supervisor-${CONFIG_SUPERVISOR_VERSION}.tar.gz 
+cd /tmp/api-gateway
+tar -xf /tmp/api-gateway/api-gateway-config-supervisor-${CONFIG_SUPERVISOR_VERSION}.tar.gz
+mkdir -p /tmp/go
+mv /tmp/api-gateway/api-gateway-config-supervisor-${CONFIG_SUPERVISOR_VERSION}/* /tmp/go
+cd /tmp/go
+make setup
+mkdir -p /tmp/go/Godeps/_workspace
+ln -s /tmp/go/vendor /tmp/go/Godeps/_workspace/src
+mkdir -p /tmp/go-src/src/github.com/adobe-apiplatform
+ln -s /tmp/go /tmp/go-src/src/github.com/adobe-apiplatform/api-gateway-config-supervisor
+GOPATH=/tmp/go/vendor:/tmp/go-src CGO_ENABLED=0 GOOS=linux /usr/lib/go/bin/godep  go build -ldflags "-s" -a -installsuffix cgo -o api-gateway-config-supervisor ./
+mv /tmp/go/api-gateway-config-supervisor /usr/local/sbin/
+
+echo "installing rclone sync ... " 
+go get github.com/ncw/rclone
+mv /usr/lib/go/bin/rclone /usr/local/sbin/ 
+mkdir -p /root/.config/rclone/
+cat <<EOF > /root/.config/rclone/rclone.conf
+[local] 
+type = local 
+nounc = true
+EOF
+
+echo " cleaning up ... " 
+rm -rf /usr/lib/go/bin/src
+rm -rf /tmp/go
+rm -rf /tmp/go-src
+rm -rf /usr/lib/go/bin/pkg/
+rm -rf /usr/lib/go/bin/godep
+apk del make git go gcc
+rm -rf /var/cache/apk/*
\ No newline at end of file
diff --git a/conf.d/includes/basic_endpoints.conf b/conf.d/includes/basic_endpoints.conf
index 9356865..e8ff50f 100644
--- a/conf.d/includes/basic_endpoints.conf
+++ b/conf.d/includes/basic_endpoints.conf
@@ -38,16 +38,30 @@
 
 location /health-check {
     access_log off;
-    content_by_lua '
-        ngx.say("API-Platform is running!")
+    default_type application/json;
+    content_by_lua_block {
+        local cjson = require "cjson"
+        local s = {}
+        s.status = "API-Gateway is running"        
         if jit then
-            ngx.say("LuaJIT-" .. jit.version);
+            s.lua_version = jit.version
         else
-            ngx.say("LuaJIT is not enabled");
+            s.lua_version = "LuaJIT is not enabled"
         end
-    ';
+
+        local config_status = ngx.location.capture("/config-status");
+        if (config_status) then
+            s.config = cjson.decode(config_status.body)
+        end
+
+        ngx.say(cjson.encode(s))
+    }
 }
 
+location /config-status {
+  access_log off;
+  proxy_pass http://127.0.0.1:8888/health-check;
+}
 
 
 location /RequestDenied {
diff --git a/init.sh b/init.sh
index 565d540..20d846a 100755
--- a/init.sh
+++ b/init.sh
@@ -26,6 +26,7 @@
 # i.e s3://api-gateway-config
 remote_config=${REMOTE_CONFIG}
 remote_config_sync_interval=${REMOTE_CONFIG_SYNC_INTERVAL:-10s}
+remote_config_reload_cmd=${REMOTE_CONFIG_RELOAD_CMD:-api-gateway -s reload}
 
 echo "Starting api-gateway ..."
 if [ "${debug_mode}" == "true" ]; then
@@ -37,6 +38,19 @@
 /usr/local/sbin/api-gateway -V
 echo "------"
 
+sync_cmd="echo ''" # when left empty, the config supervisor would only watch /etc/api-gateway for changes
+if [[ -n "${remote_config}" ]]; then
+    echo "   ... using configuration from: ${remote_config}"
+    sync_cmd="rclone sync ${remote_config} /etc/api-gateway/"
+fi
+
+api-gateway-config-supervisor \
+    --reload-cmd="${remote_config_reload_cmd}" \
+    --sync-folder=/etc/api-gateway \
+    --sync-interval=${remote_config_sync_interval} \
+    --sync-cmd="${sync_cmd}" \
+    --http-addr=127.0.0.1:8888 &
+
 echo resolver $(awk 'BEGIN{ORS=" "} /nameserver/{print $2}' /etc/resolv.conf | sed "s/ $/;/g") > /etc/api-gateway/conf.d/includes/resolvers.conf
 echo "   ...  with dns $(cat /etc/api-gateway/conf.d/includes/resolvers.conf)"
 
@@ -52,6 +66,4 @@
          -f /var/log/api-gateway/gateway_error.log -f /var/log/api-gateway/management.log
 else
     echo "REDIS_HOST and/or REDIS_PORT not defined"
-fi
-
-
+fi
\ No newline at end of file