diff --git a/.travis.yml b/.travis.yml
index 267cc9bf97ae883cb8b32972e647d4b718e177ea..843cb4bffb8f3f1c9f743b029d74f3c062bed56a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,14 +7,14 @@ jdk:
 install: true
 
 env:
-  - GROUP=weaveworksdemos COMMIT=$TRAVIS_COMMIT TAG=$TRAVIS_TAG;
+  - GROUP=weaveworksdemos COMMIT=$TRAVIS_COMMIT TAG=$TRAVIS_TAG REPO=cart;
 
 script:
   - set -e
-  - ./scripts/build.sh;
+  - travis_wait ./scripts/build.sh;
   - ./test/test.sh unit.py
   - ./test/test.sh component.py
-#  - ./test/test.sh container.py --tag $TAG
+  - ./test/test.sh container.py --tag $TAG
 
 after_success:
   - set -e;
@@ -25,3 +25,20 @@ after_success:
     fi;
   - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS;
   - ./scripts/push.sh
+
+before_install:
+- openssl aes-256-cbc -K $encrypted_71d9c8a3a58a_key -iv $encrypted_71d9c8a3a58a_iv
+  -in cart_deploy_rsa.enc -out cart_deploy_rsa -d
+before_deploy:
+  - eval "$(ssh-agent -s)"
+  - chmod 600 $TRAVIS_BUILD_DIR/${REPO}_deploy_rsa
+  - ssh-add $TRAVIS_BUILD_DIR/${REPO}_deploy_rsa
+addons:
+  ssh_known_hosts: $BASTION
+deploy:
+  provider: script
+  skip_cleanup: true
+  # The deploy.sh file actually points to the deploy file on the bastion. Not one in the repo.
+  script: ssh -o StrictHostKeyChecking=no $BASTION_USER@$BASTION ./deploy.sh ${REPO} $COMMIT
+  on:
+    branch: master
diff --git a/README.md b/README.md
index dd11a28949af1ea5e39b1232a702416440ab42e7..77f40e4ae299390f9e9e3edefabef6d773b868c8 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
 [![Build Status](https://travis-ci.org/microservices-demo/carts.svg?branch=master)](https://travis-ci.org/microservices-demo/carts) [![Coverage Status](https://coveralls.io/repos/github/microservices-demo/carts/badge.svg?branch=master)](https://coveralls.io/github/microservices-demo/carts?branch=master)
+[![](https://images.microbadger.com/badges/image/weaveworksdemos/cart.svg)](http://microbadger.com/images/weaveworksdemos/cart "Get your own image badge on microbadger.com")
 # cart
 A microservices-demo service that provides shopping carts for users.
 
diff --git a/api-spec/cart.json b/api-spec/cart.json
new file mode 100644
index 0000000000000000000000000000000000000000..856eb3248c0079fa8d08b7e7b4e8e4d36ccfcbdd
--- /dev/null
+++ b/api-spec/cart.json
@@ -0,0 +1,206 @@
+{
+    "swagger": "2.0",
+    "info": {
+        "version": "",
+        "title": "Carts and items",
+        "description": "Carts and items resources",
+        "license": {
+	    "name": "MIT",
+	    "url": "http://github.com/gruntjs/grunt/blob/master/LICENSE-MIT"
+	}
+    },
+    "host": "carts",
+    "basePath": "/",
+    "securityDefinitions": {},
+    "schemes": [
+        "http"
+    ],
+    "consumes": [
+        "application/json;charset=UTF-8",
+	"text/plain"
+    ],
+    "produces": [
+        "application/json;charset=UTF-8",
+	"text/plain"
+
+    ],
+    "paths": {
+        "/carts/{customerId}": {
+	    "get": {
+	        "description": "",
+	        "operationId": "Get cart",
+	        "produces": [
+		    "application/json;charset=UTF-8"
+		],
+	        "parameters": [
+		    {
+		        "name": "customerId",
+		        "in": "path",
+		        "required": true,
+		        "type": "string",
+		        "x-example": "1"
+		    }
+		],
+	        "responses": {
+		    "200": {
+		        "description": "Returns cart",
+		        "schema": {
+			    "$ref": "#/definitions/Getcartresponse"
+			}
+		    }
+		}
+	    },
+	    "delete": {
+	        "description": "",
+	        "operationId": "Delete cart",
+	        "produces": [
+		    "application/json;charset=UTF-8"
+		],
+	        "parameters": [
+		    {
+		        "name": "customerId",
+		        "in": "path",
+		        "required": true,
+		        "type": "string",
+			"x-example": "1"
+		    }
+		],
+	        "responses": {
+		    "202": {
+		        "description": ""
+		    }
+		}
+	    }
+	},
+        "/carts/{customerId}/items": {
+	    "post": {
+	        "description": "",
+	        "operationId": "Add an item to the cart",
+	        "produces": [
+		    "application/json;charset=UTF-8"
+		],
+	        "parameters": [
+		    {
+			"name": "customerId",
+			"in": "path",
+			"required": true,
+			"type": "string",
+			"x-example": "579f21ae98684924944651bf"
+		    },
+		    {
+			"name": "body",
+			"in": "body",
+			"required": true,
+			"schema": {
+			    "$ref": "#/definitions/CartItem",
+			    "example": {
+				"itemId":"819e1fbf-8b7e-4f6d-811f-693534916a8b",
+				"quantity": 20,
+				"unitPrice" : 99.0
+			    }
+			}
+		    }
+		],
+	        "responses": {
+		    "201": {
+		        "description": "",
+			"schema": {
+			    "$ref": "#/definitions/CartItem"
+			}
+		    }
+		}
+	    },
+	    "patch": {
+	        "description": "Update an item",
+	        "operationId": "Update item",
+	        "produces": [
+		    "application/json;charset=UTF-8"
+		],
+	        "parameters": [
+		    {
+			"name": "customerId",
+		        "in": "path",
+		        "required": true,
+		        "type": "string",
+			"x-example": "579f21ae98684924944651bf"
+		    },
+		    {
+		        "name": "body",
+		        "in": "body",
+		        "required": true,
+		        "schema": {
+			    "type": "object"
+			}
+		    }
+		],
+	        "responses": {
+		    "200": {
+		        "description": ""
+		    }
+		}
+	    }
+	},
+        "/carts/{customerId}/items/{itemId}": {
+	    "delete": {
+	        "description": "Delete cart item",
+	        "operationId": "delete",
+
+	        "parameters": [
+		    {
+		        "name": "itemId",
+		        "in": "path",
+		        "required": true,
+		        "type": "string",
+			"x-example": "819e1fbf-8b7e-4f6d-811f-693534916a8b"
+		    },
+		    {
+		        "name": "customerId",
+		        "in": "path",
+		        "required": true,
+		        "type": "string",
+			"x-example": "579f21ae98684924944651bf"
+		    }
+		],
+	        "responses": {
+		    "202": {
+		        "description": "Delete response"
+		    }
+		}
+	    }
+	}
+    },
+    "definitions": {
+        "Getcartresponse": {
+	    "title": "Get cart response",
+	    "type": "object",
+	    "properties": {
+	        "customerId": {
+		    "type": "string"
+		}
+	    },
+	    "required": [
+	        "customerId"
+	    ]
+	},
+        "CartItem": {
+	    "title": "Cart item",
+	    "type": "object",
+	    "properties": {
+	        "itemId": {
+		    "type": "string"
+		},
+	        "quantity": {
+		    "type": "integer"
+		},
+	        "unitPrice": {
+		    "type": "number"
+		}
+	    },
+	    "required": [
+	        "itemId",
+	        "quantity",
+	        "unitPrice"
+	    ]
+	}
+    }
+}
diff --git a/api-spec/hooks.js b/api-spec/hooks.js
new file mode 100644
index 0000000000000000000000000000000000000000..879c9d1ed37f54bf955de834a6785fe50674bb1b
--- /dev/null
+++ b/api-spec/hooks.js
@@ -0,0 +1,93 @@
+const hooks = require('hooks');
+const {MongoClient} = require('mongodb');
+const ObjectID = require('mongodb').ObjectID;
+
+let db;
+
+const address = [
+    {"_id":ObjectID("579f21ae98684924944651bd"),"_class":"works.weave.socks.accounts.entities.Address","number":"69","street":"Wilson Street","city":"Hartlepool","postcode":"TS26 8JU","country":"United Kingdom"},
+    {"_id":ObjectID("579f21ae98684924944651c0"),"_class":"works.weave.socks.accounts.entities.Address","number":"122","street":"Radstone WayNet","city":"Northampton","postcode":"NN2 8NT","country":"United Kingdom"},
+    {"_id":ObjectID("579f21ae98684924944651c3"),"_class":"works.weave.socks.accounts.entities.Address","number":"3","street":"Radstone Way","city":"Northampton","postcode":"NN2 8NT","country":"United Kingdom"}
+];
+
+
+const card = [
+    {"_id":ObjectID("579f21ae98684924944651be"),"_class":"works.weave.socks.accounts.entities.Card","longNum":"8575776807334952","expires":"08/19","ccv":"014"},
+    {"_id":ObjectID("579f21ae98684924944651c1"),"_class":"works.weave.socks.accounts.entities.Card","longNum":"8918468841895184","expires":"08/19","ccv":"597"},
+    {"_id":ObjectID("579f21ae98684924944651c4"),"_class":"works.weave.socks.accounts.entities.Card","longNum":"6426429851404909","expires":"08/19","ccv":"381"}
+];
+
+const cart = [
+    {"_id":ObjectID("579f21de98689ebf2bf1cd2f"),"_class":"works.weave.socks.cart.entities.Cart","customerId":"579f21ae98684924944651bf","items":[{"$ref":"item","$id":ObjectID("579f227698689ebf2bf1cd31")},{"$ref":"item","$id":ObjectID("579f22ac98689ebf2bf1cd32")}]},
+    {"_id":ObjectID("579f21e298689ebf2bf1cd30"),"_class":"works.weave.socks.cart.entities.Cart","customerId":"579f21ae98684924944651bfaa","items":[]}
+];
+
+
+const item = [
+    {"_id":ObjectID("579f227698689ebf2bf1cd31"),"_class":"works.weave.socks.cart.entities.Item","itemId":"819e1fbf-8b7e-4f6d-811f-693534916a8b","quantity":20,"unitPrice":99.0}
+];
+
+
+const customer = [
+    {"_id":"579f21ae98684924944651bf","_class":"works.weave.socks.accounts.entities.Customer","firstName":"Eve","lastName":"Berger","username":"Eve_Berger","addresses":[{"$ref":"address","$id":ObjectID("579f21ae98684924944651bd")}],"cards":[{"$ref":"card","$id":ObjectID("579f21ae98684924944651be")}]
+    },
+    {"_id":"579f21ae98684924944651c2","_class":"works.weave.socks.accounts.entities.Customer","firstName":"User","lastName":"Name","username":"user","addresses":[{"$ref":"address","$id":ObjectID("579f21ae98684924944651c0")}],"cards":[{"$ref":"card","$id":ObjectID("579f21ae98684924944651c1")}]},
+    {"_id":"579f21ae98684924944651c5","_class":"works.weave.socks.accounts.entities.Customer","firstName":"User1","lastName":"Name1","username":"user1","addresses":[{"$ref":"address","$id":ObjectID("579f21ae98684924944651c3")}],"cards":[{"$ref":"card","$id":ObjectID("579f21ae98684924944651c4")}]}
+];
+
+
+// Setup database connection before Dredd starts testing
+hooks.beforeAll((transactions, done) => {
+    var MongoEndpoint = process.env.MONGO_ENDPOINT ||  'mongodb://localhost:32769/data';
+    MongoClient.connect(MongoEndpoint, function(err, conn) {
+	if (err) {
+	    console.error(err);
+	}
+	db = conn;
+	done(err);
+    });
+});
+
+hooks.beforeEach((transaction, done) => {
+    db.dropDatabase(function (err, res) {
+	var promisesToKeep = [
+	    db.collection('customer').insertMany(customer),
+	    db.collection('card').insertMany(card),
+	    db.collection('cart').insertMany(cart),
+	    db.collection('address').insertMany(address),
+	    db.collection('item').insertMany(item)
+	];
+	Promise.all(promisesToKeep).then(function(vls) {
+	    done();
+	}, function(vls) {
+	    done();
+	});
+    })
+    
+});
+
+
+hooks.before("/carts/{customerId}/items > POST", function(transaction, done) {
+    transaction.request.headers['Content-Type'] = 'application/json';
+    transaction.request.body = JSON.stringify(
+	{
+	    "itemId":"819e1fbf-8b7e-4f6d-811f-693534916a8b",
+	    "quantity": 20,
+	    "unitPrice" : 99.0
+	}
+    );
+
+    done();
+});
+
+// TODO: Can't make POST and PUT work, skipping for now 
+
+// hooks.before("/carts/{customerId}/items > POST", function(transaction, done) {
+//     transaction.skip = true;
+//     done();
+// });
+
+hooks.before("/carts/{customerId}/items > PATCH", function(transaction, done) {
+    transaction.skip = true;
+    done();
+});
diff --git a/cart_deploy_rsa.enc b/cart_deploy_rsa.enc
new file mode 100644
index 0000000000000000000000000000000000000000..bef5caa09c2722048e643d526e70862c2306be6d
Binary files /dev/null and b/cart_deploy_rsa.enc differ
diff --git a/scripts/build.sh b/scripts/build.sh
index 1edf51470767ece80dd02560b7ac43892806d0d1..6e7cdb1f4cb78f828a7fccf8d45bbf9c202bb8f0 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -24,7 +24,7 @@ else
 fi
 CODE_DIR=$(cd $SCRIPT_DIR/..; pwd)
 echo $CODE_DIR
-$DOCKER_CMD run --rm -v $HOME/.m2:/root/.m2 -v $CODE_DIR:/usr/src/mymaven -w /usr/src/mymaven maven:3.2-jdk-8 mvn -DskipTests package
+$DOCKER_CMD run --rm -v $HOME/.m2:/root/.m2 -v $CODE_DIR:/usr/src/mymaven -w /usr/src/mymaven maven:3.2-jdk-8 mvn -q -DskipTests package
 
 cp $CODE_DIR/target/*.jar $CODE_DIR/docker/cart
 
diff --git a/test/container.py b/test/container.py
index 426fb3f58cb2488766c8ab6a947097d82bdc7a90..dd8638b8b984ceac36c3a6b1306a2c8654232a18 100644
--- a/test/container.py
+++ b/test/container.py
@@ -1,22 +1,22 @@
 import argparse
 import sys
 import unittest
+import os
+from util.Api import Api
 from time import sleep
 
-from util.Api import Api
 from util.Docker import Docker
 from util.Dredd import Dredd
 
-
 class CartContainerTest(unittest.TestCase):
     TAG = "latest"
+    COMMIT = ""
     container_name = Docker().random_container_name('cart')
     mongo_container_name = Docker().random_container_name('cart-db')
-
     def __init__(self, methodName='runTest'):
         super(CartContainerTest, self).__init__(methodName)
         self.ip = ""
-
+        
     def setUp(self):
         Docker().start_container(container_name=self.mongo_container_name, image="mongo", host="cart-db")
         command = ['docker', 'run',
@@ -25,7 +25,7 @@ class CartContainerTest(unittest.TestCase):
                    '-h', 'cart',
                    '--link',
                    CartContainerTest.mongo_container_name,
-                   'weaveworksdemos/cart:' + self.TAG]
+                   'weaveworksdemos/cart:' + self.COMMIT]
         Docker().execute(command)
         self.ip = Docker().get_container_ip(CartContainerTest.container_name)
 
@@ -34,26 +34,34 @@ class CartContainerTest(unittest.TestCase):
         Docker().kill_and_remove(CartContainerTest.mongo_container_name)
 
     def test_api_validated(self):
-        limit = 60
-        while Api().noResponse('http://' + self.ip + ':80/carts/579f21ae98684924944651bf'):
+        limit = 30
+        while Api().noResponse('http://' + self.ip + ':80/carts/'):
             if limit == 0:
                 self.fail("Couldn't get the API running")
             limit = limit - 1
             sleep(1)
-
-        out = Dredd().test_against_endpoint("carts/carts.json", CartContainerTest.container_name, "http://cart/",
-                                            "mongodb://cart-db:27017/data", self.mongo_container_name)
+        
+        out = Dredd().test_against_endpoint(
+            "cart", "http://cart/",
+            links=[self.mongo_container_name, self.container_name],
+            env=[("MONGO_ENDPOINT", "mongodb://cart-db:27017/data")],
+            dump_streams=True)
         self.assertGreater(out.find("0 failing"), -1)
         self.assertGreater(out.find("0 errors"), -1)
         print(out)
 
-
 if __name__ == '__main__':
     parser = argparse.ArgumentParser()
-    parser.add_argument('--tag', default="latest", help='The tag of the image to use. (default: latest)')
+    default_tag = "latest"
+    parser.add_argument('--tag', default=default_tag, help='The tag of the image to use. (default: latest)')
     parser.add_argument('unittest_args', nargs='*')
     args = parser.parse_args()
     CartContainerTest.TAG = args.tag
+
+    if CartContainerTest.TAG == "":
+        CartContainerTest.TAG = default_tag
+
+    CartContainerTest.COMMIT = os.environ["COMMIT"]   
     # Now set the sys.argv to the unittest_args (leaving sys.argv[0] alone)
     sys.argv[1:] = args.unittest_args
     unittest.main()
diff --git a/test/test.sh b/test/test.sh
index f84b64db1f9aa5c0f69022cf5a03c110657eba85..13adbeec19ae60dc4ba63cce5964362b2fa6d474 100755
--- a/test/test.sh
+++ b/test/test.sh
@@ -21,14 +21,16 @@ echo "Testing $1"
 CODE_DIR=$(cd $SCRIPT_DIR/..; pwd)
 echo "$@"
 $DOCKER_CMD run \
-    --rm \
-    --name test \
-    -v /var/run/docker.sock:/var/run/docker.sock \
-    -v $CODE_DIR:$CODE_DIR -w $CODE_DIR \
-    -e COVERALLS_TOKEN=$COVERALLS_TOKEN \
-    -e TRAVIS_JOB_ID=$TRAVIS_JOB_ID \
-    -e TRAVIS_BRANCH=$TRAVIS_BRANCH \
-    -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST \
-    -e TRAVIS=$TRAVIS \
-    test-container \
-    sh -c export PYTHONPATH=\$PYTHONPATH:\$PWD/test ; python test/"$@"
+	    --rm \
+	    --name test \
+	    -v /var/run/docker.sock:/var/run/docker.sock \
+	    -v $CODE_DIR:$CODE_DIR -w $CODE_DIR \
+	    -e COVERALLS_TOKEN=$COVERALLS_TOKEN \
+	    -e TRAVIS_JOB_ID=$TRAVIS_JOB_ID \
+	    -e TRAVIS_BRANCH=$TRAVIS_BRANCH \
+	    -e TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST \
+	    -e TRAVIS=$TRAVIS \
+	    -e TAG=$TAG \
+	    -e COMMIT=$COMMIT \
+	    test-container \
+	    sh -c "export PYTHONPATH=\$PYTHONPATH:\$PWD/test ; python test/$@"
diff --git a/test/unit.py b/test/unit.py
index 0b8acc59711c178b41e6e09706e01119e994c28a..4c9aeac01cf549ae61f2f2246171c80cc45d222b 100644
--- a/test/unit.py
+++ b/test/unit.py
@@ -11,7 +11,7 @@ class JavaServices(unittest.TestCase):
         code_dir = script_dir + "/.."
         home = expanduser("~")
         command = ['docker', 'run', '--rm', '-v', home + '/.m2:/root/.m2', '-v', code_dir + ':/usr/src/mymaven', '-w',
-                   '/usr/src/mymaven', 'maven:3.2-jdk-8', 'mvn', 'test']
+                   '/usr/src/mymaven', 'maven:3.2-jdk-8', 'mvn', '-q', 'test']
         print(Docker().execute(command))
 
 
diff --git a/test/util/Api.py b/test/util/Api.py
index 34953e00bea565ea97ca0a9b6b5300010bfc4ef2..64468d83dd636157eb91af0653fd4eb20180bb31 100644
--- a/test/util/Api.py
+++ b/test/util/Api.py
@@ -1,10 +1,9 @@
 import requests
 
-
 class Api:
     def noResponse(self, url):
         try:
             r = requests.get(url, timeout=5)
         except requests.exceptions.ConnectionError:
             return True
-        return r.status_code > 299
+        return False
diff --git a/test/util/Docker.py b/test/util/Docker.py
index c739f03d7110823bb66fd5b70fa57eeaa7a925f2..7711ba0f7922d1bb6fce35cb03755bc28e62c946 100644
--- a/test/util/Docker.py
+++ b/test/util/Docker.py
@@ -1,18 +1,22 @@
 import re
-from random import random
 from subprocess import Popen, PIPE
-
+from random import random
 
 # From http://blog.bordage.pro/avoid-docker-py/
 class Docker:
     def kill_and_remove(self, ctr_name):
         command = ['docker', 'rm', '-f', ctr_name]
-        self.execute(command)
+        try:
+            self.execute(command)
+            return True
+        except RuntimeError as e:
+            print(e)
+            return False
 
     def random_container_name(self, prefix):
         retstr = prefix + '-'
         for i in range(5):
-            retstr += chr(int(round(random() * (122 - 97) + 97)))
+            retstr += chr(int(round(random() * (122-97) + 97)))
         return retstr
 
     def get_container_ip(self, ctr_name):
@@ -21,17 +25,13 @@ class Docker:
                    ctr_name]
         return re.sub(r'[^0-9.]*', '', self.execute(command))
 
-    def execute(self, command):
+    def execute(self, command, dump_streams=False):
         print("Running: " + ' '.join(command))
         p = Popen(command, stdout=PIPE, stderr=PIPE)
-        out = p.stdout.read()
-        stderr = p.stderr.read()
-        if p.wait() != 0:
-            p.stdout.close()
-            p.stderr.close()
-            raise RuntimeError(str(stderr.decode('utf-8')))
-        p.stdout.close()
-        p.stderr.close()
+        out, err = p.communicate()
+        if dump_streams == True:
+            print(out.decode('utf-8'))
+            print(err.decode('utf-8'))
         return str(out.decode('utf-8'))
 
     def start_container(self, container_name="", image="", cmd="", host=""):
diff --git a/test/util/Dredd.py b/test/util/Dredd.py
index 03b0f899a4758e1e33d9fedb833aaf407963c716..fae932e692cb7943ec4b0807bba2e3d24f0528a9 100644
--- a/test/util/Dredd.py
+++ b/test/util/Dredd.py
@@ -1,25 +1,30 @@
 from util.Docker import Docker
-
+from util.Api import Api
+import os
+import unittest
 
 class Dredd:
-    image = 'weaveworksdemos/openapi'
+    image = 'weaveworksdemos/openapi:snapshot'
     container_name = ''
-
-    def test_against_endpoint(self, json_spec, endpoint_container_name, api_endpoint, mongo_endpoint_url,
-                              mongo_container_name):
+    def test_against_endpoint(self, service, api_endpoint, links=[], env=[], dump_streams=False):
         self.container_name = Docker().random_container_name('openapi')
         command = ['docker', 'run',
                    '-h', 'openapi',
                    '--name', self.container_name,
-                   '--link', mongo_container_name,
-                   '--link', endpoint_container_name,
-                   '--env', "MONGO_ENDPOINT={0}".format(mongo_endpoint_url),
-                   Dredd.image,
-                   "/usr/src/app/{0}".format(json_spec),
-                   api_endpoint,
-                   "-f",
-                   "/usr/src/app/hooks.js"
-                   ]
-        out = Docker().execute(command)
+                   '-v', "{0}:{1}".format(os.getcwd() + "/api-spec/", "/tmp/specs/")]
+        
+        if links != []:
+            [command.extend(["--link", x]) for x in links]
+            
+        if env != []:
+            [command.extend(["--env", "{}={}".format(x[0], x[1])]) for x in env]
+            
+        command.extend([Dredd.image,
+                        "/tmp/specs/{0}.json".format(service),
+                        api_endpoint,
+                        "-f",
+                        "/tmp/specs/hooks.js".format(service)])
+        out = Docker().execute(command, dump_streams=dump_streams)
+ 
         Docker().kill_and_remove(self.container_name)
         return out