quick-start with docker-compose  (#1)

* initial docker-compose setup
* add initial travis build
* configure the apigateway to deploy bigger functions
* added a Redis server for API GW cache
* enabled the local dev workflow, from a local OW git clone
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..400c127
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+openwhisk-master
+openwhisk-master*
+
+*.iml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..8fcada2
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,16 @@
+sudo: required
+
+env:
+  global:
+    - DOCKER_COMPOSE_VERSION: 1.8.1
+  matrix:
+    - TOOL: docker-compose
+
+services:
+  - docker
+
+before_install:
+  - ${TOOL}/.travis/setup.sh
+
+script:
+  - ${TOOL}/.travis/build.sh
\ No newline at end of file
diff --git a/README.md b/README.md
index 1bcaaa3..1534a75 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,30 @@
 Developer tools for OpenWhisk
 =============================
 
-### Docker-compose
-Deploy OpenWhisk using docker-compose.
 
+### Installing OpenWhisk
+
+* Using Docker-Compose. See the [README](docker-compose/README.md) for more details. 
+ 
+  [![Build Status](https://travis-ci.org/openwhisk/openwhisk-devtools.svg?branch=docker-compose)](https://travis-ci.org/openwhisk/openwhisk-devtools)
+  
+  ```bash
+  cd docker-compose
+  make quick-start
+  ```
+  
+  This is useful for creating local development environments. 
+  The build downloads by default the latest code from the master branch, but it also allows developers to work with their local clones by providing the local path to the OpenWhisk repo:
+      
+  ```bash
+  PROJECT_HOME=/path/to/openwhisk make quick-start
+  ```    
+
+### Travis builds
+
+Each tool in this repository has to provide travis build scripts inside a `.travis` folder. 
+The folder should define 2 scripts:
+* `setup.sh` - invoked during `before_install` phase 
+* `build.sh` - invokes during `script` phase 
+
+For an example check out [docker-compose/.travis](docker-compose/.travis) folder.
diff --git a/docker-compose/.travis/build.sh b/docker-compose/.travis/build.sh
new file mode 100755
index 0000000..6a61d15
--- /dev/null
+++ b/docker-compose/.travis/build.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+SCRIPTDIR=$(cd $(dirname "$0") && pwd)
+ROOTDIR="$SCRIPTDIR/../"
+
+cd $ROOTDIR
+PATH=$PATH:/usr/local/bin/ make quick-start stop
diff --git a/docker-compose/.travis/setup.sh b/docker-compose/.travis/setup.sh
new file mode 100755
index 0000000..12746a8
--- /dev/null
+++ b/docker-compose/.travis/setup.sh
@@ -0,0 +1,14 @@
+# This script assumes Docker is already installed
+# see tools/travis/setup.sh
+#!/bin/bash
+
+version_exists=`(docker-compose --version | grep ${DOCKER_COMPOSE_VERSION}) || echo "false"`
+if [ "${version_exists}" == "false" ]
+then
+    echo "Installing Docker Compose ${DOCKER_COMPOSE_VERSION}"
+    sudo rm /usr/local/bin/docker-compose
+    curl -L https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
+    chmod +x docker-compose
+    sudo mv docker-compose /usr/local/bin
+fi
+echo "Docker Compose Version:" `docker-compose --version`
diff --git a/docker-compose/Makefile b/docker-compose/Makefile
new file mode 100644
index 0000000..2199ffc
--- /dev/null
+++ b/docker-compose/Makefile
@@ -0,0 +1,171 @@
+DOCKER_HOST_IP ?= $(shell echo ${DOCKER_HOST} | grep -o "[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}" || echo localhost)
+PROJECT_HOME ?= openwhisk-master
+WSK_CLI ?= $(PROJECT_HOME)/bin/wsk
+
+OPEN_WHISK_DB_PROTOCOL ?= http
+OPEN_WHISK_DB_HOST ?= $(DOCKER_HOST_IP)
+OPEN_WHISK_DB_PORT ?= 5984
+OPEN_WHISK_DB_PROVIDER ?= CouchDB
+OPEN_WHISK_DB_USERNAME ?= whisk_admin
+OPEN_WHISK_DB_PASSWORD ?= some_passw0rd
+DB_SUBJECTS_DBS ?= subjects
+OPEN_WHISK_DB_ACTIONS ?= whisk_actions
+OPEN_WHISK_DB_GW ?= whisk_gateway
+
+ifndef VERBOSE
+.SILENT:
+endif
+
+# Quick-Start is a simple way to get started with OpenWhisk locally
+#   1. at start it builds the project and the docker containers
+#   2. then it starts all components using docker-compose
+#   3. it runs a sample hello-world function
+#   To stop and cleanup the environment use: make destroy
+quick-start: download docker run quick-start-pause hello-world quick-start-info
+
+.PHONY: download
+download:
+	rm -rf ./openwhisk-master*
+	if [ "$(PROJECT_HOME)" = "openwhisk-master" ]; then \
+	    curl -O ./openwhisk-master.tar.gz -L https://api.github.com/repos/openwhisk/openwhisk/tarball/master > ./openwhisk-master.tar.gz; \
+	    mkdir openwhisk-master; \
+	    tar -xvf ./openwhisk-master.tar.gz --strip 1 -C openwhisk-master; \
+	else \
+	     echo "Skipping downloading the code from git as PROJECT_HOME is not default:" $(PROJECT_HOME); \
+	fi
+
+.PHONY: quick-start-pause
+quick-start-pause:
+	echo "waiting for the Whisk invoker to come up ... "
+	until $$(curl --output /dev/null --silent --head --fail http://$(DOCKER_HOST_IP):8085/ping); do printf '.'; sleep 5; done
+	sleep 30
+
+.PHONY: quick-start-info
+quick-start-info:
+	echo "$$(tput setaf 2)To invoke the function again use: $$(tput setaf 4)make hello-world$$(tput sgr0)"
+	echo "$$(tput setaf 2)To stop openwhisk use: $$(tput setaf 4)make destroy$$(tput sgr0)"
+
+docker:
+	echo "building the docker images ... "
+	cd $(PROJECT_HOME) && \
+	    ./gradlew distdocker -x :core:swift3Action:distDocker -x :core:swiftAction:distDocker
+
+.PHONY: run
+run: check-required-ports setup start-docker-compose init-couchdb init-couchdb-actions init-whisk-cli
+
+.PHONY: check-required-ports
+check-required-ports:
+	echo "checking required ports ... "
+	for port in 80 443 2888 5984 8085 8888 9092 2888 8400 8500 8600 8302; do \
+		pid=`lsof -Pi :$$port -sTCP:LISTEN -t` ; \
+		if [ ! -z "$$pid" ];  then echo "$$(tput setaf 1)Port $$port is taken by PID:$$pid.$$(tput sgr0)"; exit 1; fi; \
+	done
+	echo " ... OK"
+
+.PHONY: setup
+setup:
+	mkdir -p ~/tmp/openwhisk/apigateway/ssl
+	cd $(PROJECT_HOME)/ansible/roles/nginx/files/ && ./genssl.sh $(DOCKER_HOST_IP)
+	cp $(PROJECT_HOME)/ansible/roles/nginx/files/*.pem ~/tmp/openwhisk/apigateway/ssl
+	cp -r ./apigateway/* ~/tmp/openwhisk/apigateway/
+
+.PHONY: start-docker-compose
+start-docker-compose:
+	DOCKER_COMPOSE_HOST=$(DOCKER_HOST_IP) docker-compose --project-name openwhisk up 2>&1 > ~/tmp/openwhisk/docker-compose.log &
+
+.PHONY: init-couchdb
+init-couchdb:
+	echo "waiting for the database to come up ... "
+	until $$(curl --output /dev/null --silent --head --fail http://$(DOCKER_HOST_IP):5984/_all_dbs); do printf '.'; sleep 5; done
+
+	echo "initializing the database ... "
+	# the folder config/keys is referenced from createSubjects.sh
+	mkdir -p db/config/keys
+	cp $(PROJECT_HOME)/ansible/files/auth.* db/config/keys
+
+	OPEN_WHISK_DB_PROVIDER=$(OPEN_WHISK_DB_PROVIDER) \
+	    OPEN_WHISK_DB_PROTOCOL=$(OPEN_WHISK_DB_PROTOCOL) \
+	    OPEN_WHISK_DB_HOST=$(OPEN_WHISK_DB_HOST) OPEN_WHISK_DB_PORT=$(OPEN_WHISK_DB_PORT) \
+	    OPEN_WHISK_DB_USERNAME=$(OPEN_WHISK_DB_USERNAME) OPEN_WHISK_DB_PASSWORD=$(OPEN_WHISK_DB_PASSWORD) \
+	    DB_SUBJECTS_DBS=$(DB_SUBJECTS_DBS) DB_WHISK_AUTHS=$(DB_SUBJECTS_DBS) \
+	    PROJECT_HOME=$(PROJECT_HOME) \
+	    db/createSubjects.sh
+
+	# cleanup the files referenced by createSubjects.sh
+	rm -rf db/config/keys
+
+.PHONY: init-couchdb-actions
+init-couchdb-actions:
+	echo "initializing CouchDB Views ... "
+	echo "" > $(PROJECT_HOME)/whisk.properties
+	echo db.provider=$(OPEN_WHISK_DB_PROVIDER) >> $(PROJECT_HOME)/whisk.properties
+	echo db.protocol=$(OPEN_WHISK_DB_PROTOCOL) >> $(PROJECT_HOME)/whisk.properties
+	echo db.host=$(OPEN_WHISK_DB_HOST) >> $(PROJECT_HOME)/whisk.properties
+	echo db.port=$(OPEN_WHISK_DB_PORT) >> $(PROJECT_HOME)/whisk.properties
+	echo db.username=$(OPEN_WHISK_DB_USERNAME) >> $(PROJECT_HOME)/whisk.properties
+	echo db.password=$(OPEN_WHISK_DB_PASSWORD) >> $(PROJECT_HOME)/whisk.properties
+	echo db.whisk.actions=$(OPEN_WHISK_DB_ACTIONS) >> $(PROJECT_HOME)/whisk.properties
+	echo db.whisk.apigw=$(OPEN_WHISK_DB_GW) >> $(PROJECT_HOME)/whisk.properties
+	cd $(PROJECT_HOME)/ && tools/db/wipeTransientDBs.sh
+	rm $(PROJECT_HOME)/whisk.properties
+
+.PHONY: init-whisk-cli
+init-whisk-cli:
+	echo "waiting for the Whisk controller to come up ... "
+	until $$(curl --output /dev/null --silent --head --fail http://$(DOCKER_HOST_IP):8888/ping); do printf '.'; sleep 5; done
+	echo "initializing CLI ... "
+	$(WSK_CLI) -v property set --namespace guest --auth `cat $(PROJECT_HOME)/ansible/files/auth.guest` --apihost $(DOCKER_HOST_IP):443 -i
+
+.PHONY: stop
+stop:
+	DOCKER_COMPOSE_HOST=$(DOCKER_HOST_IP) docker-compose --project-name openwhisk stop
+
+.PHONY: destroy
+destroy: stop
+	DOCKER_COMPOSE_HOST=$(DOCKER_HOST_IP) docker-compose --project-name openwhisk rm
+	echo "cleaning other openwhisk containers started by the invoker ... "
+	docker ps | grep whisk | awk '{print $$1}' | xargs docker stop | xargs docker rm
+	echo "cleaning dangling docker volumes ... "
+	docker volume ls -qf dangling=true | xargs docker volume rm
+	rm -rf ~/tmp/openwhisk
+	rm -rf ./openwhisk-master*
+
+# This task runs a hello-world function
+#   1. It creates the function
+#   2. It executes it
+#   3. At the end it deletes it
+.PHONY: hello-world
+hello-world: create-hello-world-function
+	echo "invoking the hello-world function ... "
+
+	echo "$$(tput setaf 4)adding the function to whisk ...$$(tput sgr0)"
+	$(WSK_CLI) -i action create hello hello.js
+
+	echo "$$(tput setaf 4)invoking the function ...$$(tput sgr0)"
+	res=`$(WSK_CLI) -i action invoke hello --blocking --result` \
+	    && echo "invokation result:" $$res \
+	    && (echo $$res | grep "Hello, World") || ($(WSK_CLI) -i action delete hello && exit 1)
+
+	echo "$$(tput setaf 1)deleting the function ...$$(tput sgr0)"
+	$(WSK_CLI) -i action delete hello
+	rm hello.js
+
+.PHONY: create-hello-world-function
+create-hello-world-function:
+	echo "$$(tput setaf 2)creating the hello.js function ...$$(tput sgr0)"
+	echo 'function main(params) {var name = params.name || "World"; return { payload:  "Hello, " + name + "!" }; }' > hello.js
+
+# Using the hello-world function this task executes a performance test using Apache Benchmark
+.PHONY: hello-world-perf-test
+hello-world-perf-test: create-hello-world-function
+	$(WSK_CLI) -i action create hello-perf hello.js
+
+	docker run \
+	    --net openwhisk_default \
+	    --link controller jordi/ab ab -k -n 2000 -c 20 \
+	    -m POST -H "Authorization:Basic MjNiYzQ2YjEtNzFmNi00ZWQ1LThjNTQtODE2YWE0ZjhjNTAyOjEyM3pPM3haQ0xyTU42djJCS0sxZFhZRnBYbFBrY2NPRnFtMTJDZEFzTWdSVTRWck5aOWx5R1ZDR3VNREdJd1A=" \
+	            -H "Content-Type:application/json" \
+	            http://controller:8888/api/v1/namespaces/guest/actions/hello-perf?blocking=true
+
+	$(WSK_CLI) -i action delete hello-perf
+	rm hello.js
diff --git a/docker-compose/README.md b/docker-compose/README.md
new file mode 100644
index 0000000..3d9c01c
--- /dev/null
+++ b/docker-compose/README.md
@@ -0,0 +1,98 @@
+# How to setup OpenWhisk with Docker Compose
+
+[![Build Status](https://travis-ci.org/openwhisk/openwhisk-devtools.svg?branch=docker-compose)](https://travis-ci.org/openwhisk/openwhisk-devtools)
+
+An easy way to try OpenWhisk locally is to use Docker Compose.
+
+#### Prerequisites
+
+The following are required to build and deploy OpenWhisk with Docker Compose:
+
+- [Docker 1.12+](https://www.docker.com/products/docker) 
+- [Docker Compose 1.6+](https://docs.docker.com/compose/install/)
+- [Java 8](http://www.oracle.com/technetwork/java/javase/downloads/index.html)
+
+Available Ports:
+
+- `5984` for CouchDB
+- `2181` for Zookeeper
+- `9092` for Kafka
+- `8400`, `8500`, `8600`, `8302` for Consul
+- `8888` for OpenWhisk's Controller
+- `8085` for OpenWhisk's Invoker
+- `80` and `443` for the API Gateway
+
+# Quick Start
+
+```bash
+make quick-start
+```
+
+This command downloads the `master` branch from the [OpenWhisk](https://github.com/openwhisk/openwhisk) repo, it  builds OpenWhisk, the docker containers, it starts the system and it executes a simple `hello-world` function.
+At the end of the execution it prints the output of the function:
+```javascript
+{
+    "payload": "Hello, World!"
+}
+```
+
+# Build
+
+```bash
+make docker
+```
+
+This command builds the docker containers for local testing and development.
+
+> NOTE: The build may skip some components such as Swift actions in order to finish the build faster.
+
+# Start
+
+```bash
+make run
+```
+
+This command starts OpenWhisk by calling `docker-compose up`, it initializes the database and the CLI.
+
+# Stop
+
+The following command stops the `docker-compose`:
+
+```bash
+make stop
+```
+
+To remove the stopped containers, clean the database files and the temporary files use:
+ 
+ ```bash
+ make destroy
+ ```
+
+# Running a hello-world function
+
+Once OpenWhisk is up and running you can execute a `hello-world` function:
+
+```bash
+make hello-world
+```
+
+This command creates a new JS action, it invokes it, and then it deletes it. 
+  The javascript action is:
+```javascript
+function main(params) {
+    var name = params.name || "World";
+    return {payload: "Hello, " + name + "!"};
+}
+```  
+The result of the invokation should be printed on the terminal:
+```
+{
+    "payload": "Hello, World!"
+} 
+```
+
+## Logs
+
+- OpenWhisk Controller - `~/tmp/openwhisk/controller/logs/`
+- OpenWhisk Invoker - `~/tmp/openwhisk/invoker/logs/`
+- `docker-compose` logs - `~/tmp/openwhisk/docker-compose.log`
diff --git a/docker-compose/apigateway/conf/whisk-docker-compose.conf b/docker-compose/apigateway/conf/whisk-docker-compose.conf
new file mode 100644
index 0000000..945ab96
--- /dev/null
+++ b/docker-compose/apigateway/conf/whisk-docker-compose.conf
@@ -0,0 +1,42 @@
+# virtual host loaded by the API Gateway for local development with docker-compose
+
+# NOTE: This upstream should come from a service discovery component
+#        All the upstreams could be generated sepparately and only included in the API Gateway
+upstream whisk_controller {
+  server whisk.controller:8888;
+  keepalive 16;
+}
+
+server {
+  listen 443 default ssl;
+
+  ssl_session_cache    shared:SSL:1m;
+  ssl_session_timeout  10m;
+  ssl_certificate      /etc/ssl/openwhisk-cert.pem;
+  ssl_certificate_key  /etc/ssl/openwhisk-key.pem;
+  ssl_verify_client off;
+  ssl_protocols        TLSv1 TLSv1.1 TLSv1.2;
+  ssl_ciphers RC4:HIGH:!aNULL:!MD5;
+  ssl_prefer_server_ciphers on;
+  proxy_ssl_session_reuse off;
+
+  proxy_http_version 1.1;
+  proxy_set_header Connection "";
+
+  client_max_body_size 3m;     # allow bigger functions to be deployed
+
+  location /docs {
+    proxy_pass http://whisk_controller;
+  }
+
+  location /api-docs {
+    proxy_pass http://whisk_controller;
+  }
+
+  location /api/v1 {
+    proxy_pass http://whisk_controller;
+    proxy_read_timeout 70s; # 60+10 additional seconds to allow controller to terminate request
+  }
+
+}
+
diff --git a/docker-compose/apigateway/env.conf/api-gateway-env-vars.server.conf b/docker-compose/apigateway/env.conf/api-gateway-env-vars.server.conf
new file mode 100644
index 0000000..8ea5af0
--- /dev/null
+++ b/docker-compose/apigateway/env.conf/api-gateway-env-vars.server.conf
@@ -0,0 +1,4 @@
+# TO BE INCLUDED FOR EACH VHOST
+# Use this file to overwrite any nginx variables based on the environment
+
+# set $my_var my_value;
\ No newline at end of file
diff --git a/docker-compose/apigateway/env.conf/api-gateway-env.http.conf b/docker-compose/apigateway/env.conf/api-gateway-env.http.conf
new file mode 100644
index 0000000..08a1c1f
--- /dev/null
+++ b/docker-compose/apigateway/env.conf/api-gateway-env.http.conf
@@ -0,0 +1,2 @@
+# INCLUDED IN HTTP BLOCK
+more_set_headers 'Server: openwhisk/api-gateway-1.9.3.1';
\ No newline at end of file
diff --git a/docker-compose/apigateway/env.conf/api-gateway-upstreams.http.conf b/docker-compose/apigateway/env.conf/api-gateway-upstreams.http.conf
new file mode 100644
index 0000000..4b5591a
--- /dev/null
+++ b/docker-compose/apigateway/env.conf/api-gateway-upstreams.http.conf
@@ -0,0 +1,9 @@
+upstream api-gateway-redis {
+    # redis.docker is defined by docker-compose
+    server redis.docker:6379;
+}
+
+upstream api-gateway-redis-replica {
+    # redis.docker is defined by docker-compose
+    server redis.docker:6379;
+}
\ No newline at end of file
diff --git a/docker-compose/db/createSubjects.sh b/docker-compose/db/createSubjects.sh
new file mode 100755
index 0000000..1c07473
--- /dev/null
+++ b/docker-compose/db/createSubjects.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+
+#
+# create the subjects databases (in Cloudant or CouchDB)
+#
+# Usage: createSubjects.sh
+
+SCRIPTDIR="$(cd $(dirname "$0")/ && pwd)"
+
+URL_BASE="$OPEN_WHISK_DB_PROTOCOL://$OPEN_WHISK_DB_HOST:$OPEN_WHISK_DB_PORT"
+
+if [ "$OPEN_WHISK_DB_PROVIDER" == "CouchDB" ]; then
+    CURL_ADMIN="curl -s -k --user $OPEN_WHISK_DB_USERNAME:$OPEN_WHISK_DB_PASSWORD"
+
+    # First part of confirmation prompt.
+    echo "About to drop and recreate database '$DB_SUBJECTS_DBS' on:"
+    echo "  $URL_BASE"
+
+else
+    echo "Unrecognized OPEN_WHISK_DB_PROVIDER: '$OPEN_WHISK_DB_PROVIDER'"
+    exit 1
+fi
+
+GUEST_KEY=`cat "$SCRIPTDIR/config/keys/auth.guest"`
+WHISK_SYSTEM_KEY=`cat "$SCRIPTDIR/config/keys/auth.whisk.system"`
+
+# array of keys that need to be recreated in the auth table if it is ever dropped or in case of a fresh deployment
+SUBJECTS_KEYS=("guest:$GUEST_KEY" "whisk.system:$WHISK_SYSTEM_KEY")
+
+for db in $DB_SUBJECTS_DBS
+do
+    echo $db
+
+    # drop the database
+    CMD="$CURL_ADMIN -X DELETE $URL_BASE/$db"
+    echo $CMD
+    $CMD
+
+    # create the database
+    CMD="$CURL_ADMIN -X PUT $URL_BASE/$db"
+    echo $CMD
+    $CMD
+done
+
+# recreate the "full" index on the "auth" database
+$CURL_ADMIN -X POST -H 'Content-Type: application/json' \
+        -d @$PROJECT_HOME/ansible/files/auth_index.json \
+    $URL_BASE/$DB_WHISK_AUTHS;
+
+# recreate necessary "auth" keys
+for key in "${SUBJECTS_KEYS[@]}" ; do
+    SUBJECT="${key%%:*}"
+    UUID="${key%:*}"
+    UUID="${UUID##*:}"
+    KEY="${key##*:}"
+    echo Create key for $SUBJECT
+    $CURL_ADMIN -X POST -H 'Content-Type: application/json' \
+        -d "{
+            \"_id\": \"$SUBJECT\",
+            \"subject\": \"$SUBJECT\",
+            \"uuid\": \"$UUID\",
+            \"key\": \"$KEY\"
+         }" \
+    $URL_BASE/$DB_WHISK_AUTHS;
+done
diff --git a/docker-compose/docker-compose.yml b/docker-compose/docker-compose.yml
new file mode 100644
index 0000000..0b59fb8
--- /dev/null
+++ b/docker-compose/docker-compose.yml
@@ -0,0 +1,153 @@
+version: '2'
+services:
+  db:
+    image: couchdb:1.6
+    ports:
+      - "5984:5984"
+    environment:
+      COUCHDB_USER: whisk_admin
+      COUCHDB_PASSWORD: some_passw0rd
+    volumes:
+      - ~/tmp/openwhisk/couchdb:/usr/local/var/lib/couchdb:rw
+
+  # KAFKA SERVICES
+  zookeeper:
+    image: wurstmeister/zookeeper
+    ports:
+      - "2181:2181"
+  kafka:
+    image: wurstmeister/kafka
+    links:
+      - zookeeper
+    depends_on:
+      - zookeeper
+    ports:
+      - "9092:9092"
+    environment:
+      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
+    volumes:
+      - /var/run/docker.sock:/var/run/docker.sock
+
+  # CONSUL & REGISTRATOR SERVICES
+  consul:
+    image: consul:v0.7.0
+    ports:
+      - "8400:8400" # CLI
+      - "8500:8500" # HTTP
+      - "8600:8600" # DNS
+      - "8302:8302" # Serf LAN
+    environment:
+      CONSUL_LOCAL_CONFIG: '{"log_level":"warn"}'
+  registrator:
+    image: gliderlabs/registrator
+    command: [-resync, "5", "consul://consul.docker:8500"]
+    volumes:
+      - /var/run/docker.sock:/tmp/docker.sock
+    links:
+      - consul:consul.docker
+    depends_on:
+      - consul
+
+  # WHISK CONTROLLER
+  controller:
+    image: whisk/controller:latest
+    links:
+      - consul:consul.docker
+      - db:db.docker
+      - kafka:kafka.docker
+    depends_on:
+      - consul
+      - db
+      - kafka
+    env_file:
+      - ./docker-whisk-controller.env
+    environment:
+      COMPONENT_NAME: controller
+      PORT: 8888
+
+      CONSULSERVER_HOST: consul.docker
+      CONSUL_HOST_PORT4: 8500
+
+      KAFKA_HOST: kafka.docker
+      KAFKA_HOST_PORT: 9092
+
+      DB_PROVIDER: CouchDB
+      DB_PROTOCOL: http
+      DB_PORT: 5984
+      DB_HOST: db.docker
+      DB_USERNAME: whisk_admin
+      DB_PASSWORD: some_passw0rd
+
+      LOADBALANCER_HOST: ${DOCKER_COMPOSE_HOST}
+      LOADBALANCER_HOST_PORT: 443
+    volumes:
+      - ~/tmp/openwhisk/controller/logs:/logs
+    ports:
+      - "8888:8888"
+
+  # WHISK INVOKER AGENT
+  invoker:
+    image: whisk/invoker:latest
+    command: /bin/sh -c "/invoker/bin/invoker 0 >> /logs/invoker-local_logs.log 2>&1"
+    links:
+      - consul:consul.docker
+      - db:db.docker
+      - kafka:kafka.docker
+    depends_on:
+      - consul
+      - db
+      - kafka
+    env_file:
+      - ./docker-whisk-controller.env # env vars shared
+    environment:
+      COMPONENT_NAME: invoker
+      SERVICE_NAME: invoker0
+      PORT: 8085
+
+      CONSULSERVER_HOST: consul.docker
+      CONSUL_HOST_PORT4: 8500
+
+      KAFKA_HOST: kafka.docker
+      KAFKA_HOST_PORT: 9092
+
+      DB_PROVIDER: CouchDB
+      DB_PROTOCOL: http
+      DB_PORT: 5984
+      DB_HOST: db.docker
+      DB_USERNAME: whisk_admin
+      DB_PASSWORD: some_passw0rd
+
+      EDGE_HOST: ${DOCKER_COMPOSE_HOST}
+      EDGE_HOST_APIPORT: 443
+
+      DOCKER_REGISTRY: ""
+      DOCKER_IMAGE_PREFIX: whisk
+      INVOKER_CONTAINER_NETWORK: openwhisk_default
+    volumes:
+      - ~/tmp/openwhisk/invoker/logs:/logs
+      - /var/run/docker.sock:/var/run/docker.sock
+      - /var/lib/docker/containers:/containers
+    ports:
+      - "8085:8085"
+
+  # The API Gateway is currently used to expose the Controller API
+  #   see apigateway/conf/whisk-docker-compose.conf
+  redis:
+    image: redis:2.8
+    expose:
+      - "6379"
+  apigateway:
+    image: adobeapiplatform/apigateway:1.1.0
+    links:
+      - controller:whisk.controller
+      - redis:redis.docker
+    depends_on:
+      - controller
+      - redis
+    volumes:
+      - ~/tmp/openwhisk/apigateway/ssl:/etc/ssl:ro
+      - ~/tmp/openwhisk/apigateway/conf:/etc/api-gateway/generated-conf.d:ro
+      - ~/tmp/openwhisk/apigateway/env.conf:/etc/api-gateway/environment.conf.d:ro
+    ports:
+      - "80:80"
+      - "443:443"
diff --git a/docker-compose/docker-whisk-controller.env b/docker-compose/docker-whisk-controller.env
new file mode 100644
index 0000000..df34852
--- /dev/null
+++ b/docker-compose/docker-whisk-controller.env
@@ -0,0 +1,30 @@
+# this file contains generic properties  for local development
+# which are not specific to the docker-compose environment ( i.e. hostnames, ports, etc )
+WHISK_VERSION_NAME="local"
+WHISK_VERSION_DATE="09/01/2016"
+WHISK_VERSION_BUILDNO="latest"
+WHISK_LOGS_DIR="/logs"
+
+SERVICE_CHECK_HTTP=/ping
+SERVICE_CHECK_TIMEOUT=2s
+SERVICE_CHECK_INTERVAL=15s
+
+DB_PREFIX=local
+DB_WHISK_ACTIONS=whisk_actions
+DB_WHISK_AUTHS=subjects
+
+KAFKA_NUMPARTITIONS=1
+
+DEFAULTLIMITS_ACTIONS_INVOKES_PERMINUTE=60000
+DEFAULTLIMITS_ACTIONS_INVOKES_CONCURRENT=5000
+DEFAULTLIMITS_TRIGGERS_FIRES_PERMINUTE=60000
+DEFAULTLIMITS_ACTIONS_INVOKES_CONCURRENTINSYSTEM=5000
+DEFAULTLIMITS_ACTIONS_SEQUENCE_MAXLENGTH=20
+LIMITS_ACTIONS_INVOKES_PERHOUR=3600000
+LIMITS_ACTIONS_INVOKES_PERMINUTE=60000
+LIMITS_ACTIONS_INVOKES_CONCURRENT=5000
+LIMITS_TRIGGERS_FIRES_PERMINUTE=60000
+LIMITS_ACTIONS_INVOKES_CONCURRENTINSYSTEM=5000
+
+WHISK_API_HOST=https://${DOCKER_COMPOSE_HOST}
+