Package: galley
GalleyEnd-to-end test orchestration with Docker.Getting Started GuideRequirements:Python 2.7.5Docker >= 0.10.0InstallationInstall Galley:pip install -r requirements
python setup.py installRun Galley:$ galley -h
usage: galley [-h] [--no-destroy] [config] [pattern]

End-to-end testing orchestration with Docker.

positional arguments:
  config        Path to galley YAML file that defines Docker resources.
  pattern       Test file pattern.

optional arguments:
  -h, --help    show this help message and exit
  --no-destroy  Do not destroy images and containers..galley.ymlYou define the environment you want Galley to create in the.galley.ymlfile. Place this file in your application’s root
directory..galley.ymlconsists of three sections:images,resources, andtestparams.imagesimages:
  0:
    name: "redis"
    source: "dockerfile/redis"
    tag: "latest"
    action: "pull"
    persist: True
  1:
    name: "mongodb"
    source: "dockerfile/mongodb"
    tag: "latest"
    action: "pull"
    persist: True
  2:
    name: "baconpancakes"
    source: "/path/to/baconpancakes"
    action: "build"
    persist: FalseIn theimagessection, you describe what images you want Galley to
create and how you would like to create them.name: A name for the image in Galley. This can be used byresourcesto describe what image to use when creating them. Forbuildactions, this is also the name tagged to the image built.source: For images with anpullaction, this is therepo/imageof the image to be pulled. Forbuildactions, this
is the path to the directory containing theDockerfileto build.
If.galley.ymlis in the same directory as yourDockerfile,
this can be".".tag: The image tag to pull.action: Available actions arepullandbuild:pulluses thedocker pullcommand to pull the image
described insourcefrom the Docker Index.builduses thedocker buildcommand to build an image from
theDockerfilelocated in the directory described insource.persist(optional): When Galley finishes testing, it destroys all
images it created. To keep images from one Galley run to the next,
setpersisttoTrue. This is helpful if you have upstream
images you will use every time since they will not have to be
downloaded on each run.testparamstestparams:
  pancakes: "{{environ['GALLEY_PANCAKES']}}"
  bacon: "{{environ['GALLEY_BACON']}}"testparamsare key-value attributes that can be referenced by your
tests.resourcesresources:
  0:
    name: "redis"
    image: "{{redis}}"
    host_port: "{{random_port}}"
    cont_port: 6379
  1:
    name: "mongodb"
    image: "{{mongodb}}"
    host_port: "{{random_port}}"
    cont_port: 27017
  2:
    name: "baconpancakes"
    image: "{{baconpancakes}}"
    host_port: "{{random_port}}"
    cont_port: 8080
    command: "--role api"
    host_volume: "./docker/config"
    cont_volume: "config"
    environment:
      BACONPANCAKES_ADDRESS: "0.0.0.0:8080"
      BACONPANCAKES_USERNAME: "{{environ['GALLEY_BACONPANCAKES_USERNAME']}}"
      BACONPANCAKES_PASSWORD: "{{environ['GALLEY_BACONPANCAKES_PASSWORD']}}"
      BACONPANCAKES_BROKER_URL: "redis://{{host['ip']}}:{{resources[0]['host_port']}}"
      BACONPANCAKES_CELERY_BACKEND: "redis://{{host['ip']}}:{{resources[0]['host_port']}}"
      BACONPANCAKES_CONNECTION_STRING: "mongodb://{{host['ip']}}:{{resources[1]['host_port']}}"In theresourcessection, you describe what containers you would
like Galley to create and run from the images in theimagessection.name: The name of the resource. Currently this is not used by
anything, but in the furture should be used as the name of the
container and as a key when referenced by other resources.image: Theidof the image to use to create the container. By
using{{image_name}}, Galley will replace this with the actual
imageidwhen the image is created.host_port: The port on the host to map tocont_port. By using{{random_port}}a random available ephemeral port on the host
will be selected.cont_port: The port in the container to map tohost_port.command: The command to use when running the container.host_volume: A directory on the host to mount into your container
ascont_volume. Note: volumes are currently not supported by any
of the Docker options in OS X. This option only works in Linux.cont_volume: The directory in the container wherehost_volumewill be mounted.environment: Environment variables to inject into container when
run..galley.ymlTemplating:{{resources[..]}}:In the resources section, you can build a relationship from one
resource to another by referencing another resources data. For
example, since we are telling Galley to choose a{{random_port}}for our MongoDB and Redis instances, ourbaconpancakesapp won’t know how to talk to them. So, in the
environment section we tellbaconpancakesto find the Celery
backend with thehost_portfromresource 0by using{{resources[0]['host_port']}}in the connection string. This
tells Galley to go find the value ofhost_portforresource 0and fill it in.Currently, Galley is not smart enough to resolve dependencies on
its own; therefore, a resource can only reference values from
resources that appear before it in the.galley.ymlfile. In
the future, this will likely be resolved by explicitly describing
dependencies.Only available in theresourcessection.{{host[..]}}:Host-level information can be referenced through thehostdictionary. The main usage of this is to provide the host’s IP
address in order to allow separate resources to communicate with
each other.Currently, the only host-level attribute available inhostisip.Only available in theresourcessection.{{environ[..]}}:Replaced with referenced host environment variable.Sample.galley.ymlfile:images:
  0:
    name: "redis"
    source: "dockerfile/redis"
    tag: "latest"
    action: "pull"
    persist: True
  1:
    name: "mongodb"
    source: "dockerfile/mongodb"
    tag: "latest"
    action: "pull"
    persist: True
  2:
    name: "baconpancakes"
    source: "/path/to/baconpancakes"
    action: "build"
    persist: False
resources:
  0:
    name: "redis"
    image: "{{redis}}"
    host_port: "{{random_port}}"
    cont_port: 6379
  1:
    name: "mongodb"
    image: "{{mongodb}}"
    host_port: "{{random_port}}"
    cont_port: 27017
  2:
    name: "baconpancakes"
    image: "{{baconpancakes}}"
    host_port: "{{random_port}}"
    cont_port: 8080
    command: "--role api"
    host_volume: "./docker/config"
    cont_volume: "config"
    environment:
      BACONPANCAKES_ADDRESS: "0.0.0.0:8080"
      BACONPANCAKES_USERNAME: "{{environ['GALLEY_BACONPANCAKES_USERNAME']}}"
      BACONPANCAKES_PASSWORD: "{{environ['GALLEY_BACONPANCAKES_PASSWORD']}}"
      BACONPANCAKES_BROKER_URL: "redis://{{host['ip']}}:{{resources[0]['host_port']}}"
      BACONPANCAKES_CELERY_BACKEND: "redis://{{host['ip']}}:{{resources[0]['host_port']}}"
      BACONPANCAKES_CONNECTION_STRING: "mongodb://{{host['ip']}}:{{resources[1]['host_port']}}"
testparams:
  pancakes: "{{environ['GALLEY_PANCAKES']}}"
  bacon: "{{environ['GALLEY_BACON']}}"GalleytestsAfter Galley completes creating your environment, it looks recursively
for anygalleytest_*.pyfiles in your current directory.Writing tests for Galley to use is easy! Galley usesPython
unitteststo test
your environment. Therefore, writing a test for Galley is just as easy
and allows you to use any ofunittest’sassert
methods.
All you need to do is make sure to importGalleyTestCasefromgalley.testand passGalleyTestCaseinto your test class:import requests

from galley.test import GalleyTestCase


class TestWebGetRequest(GalleyTestCase):

    def test_web_status(self):
        env = self.environment
        web_ip = env['host']['ip']
        web_port = env['resources'][2]['host_port']
        url = "http://%s:%d" % (web_ip, web_port)
        response = requests.get(url)

        self.assertEqual(200, response.status_code)
        self.assertIn('<title>MakinBaconPancakes</title>', response.text)In this test we want to check to make sure our web application started
properly and that the some expected content was found on the page. Since
we importedGalleyTestCaseand passed it into our test class, we can
also reference our entire environment in our test by callingself.envionment. Here, we used this to find the IP address of the
Docker host and the port our web application was mapped to. As you can
see, theself.assertEqual()andself.assertIn()functions come
straight fromunittests.Galley tests can be more complicated as well:import requests
import time

from galley.test import GalleyTestCase


class TestPancakes(GalleyTestCase):

    def test_pancakes(self):
        env = self.environment
        api_ip = env['host']['ip']
        api_port = env['resources'][2]['host_port']
        pancakes = env['testparams']['pancakes']
        bacon = env['testparams']['bacon']
        url = "http://%s:%d/api/%s/%s" % (api_ip, api_port, pancakes, bacon)
        response = requests.post(url)
        baconpancakes = response.json()
        status = response.status_code
        pancake_id = baconpancakes['id']

        self.assertEqual(201, status)
        self.assertEqual('REQUESTED', baconpancakes['status'])

        url = url + '/' + pancake_id
        for attempt in range(20):
            r = requests.get(url)
            pancake = r.json()
            try:
                self.assertEqual('MADE', baconpancakes['status'])
            except Exception:
                time.sleep(5)

        self.assertEqual('MADE', baconpancakes['status'])
        self.assertIn('bacon', baconpancakes.keys())TEST!$ galley
Pulling dockerfile/redis:latest from registry.
Checking if image dockerfile/redis:latest exists.
Found image dockerfile/redis:latest.
Successfully pulled dockerfile/redis:latest.
Pulling dockerfile/mongodb:latest from registry.
Checking if image dockerfile/mongodb:latest exists.
Found image dockerfile/mongodb:latest.
Successfully pulled dockerfile/mongodb:latest.
Building image . with tag baconpancakes.
Checking if image c023ce32fc62 exists.
Found image c023ce32fc62.
Successfully built image c023ce32fc62 from ..
Creating dockerfile/redis container.
Successfully created dockerfile/redis container: 380c81fe0775
Creating dockerfile/mongodb container.
Successfully created dockerfile/mongodb container: 706e35d5e28f
Creating c023ce32fc62 container.
Successfully created c023ce32fc62 container: e0cc0d1cebc2
Creating c023ce32fc62 container.
Successfully created c023ce32fc62 container: 1f4d584d8dc0
Starting container 380c81fe0775.
Successfully started container 380c81fe0775.
Starting container 706e35d5e28f.
Successfully started container 706e35d5e28f.
Starting container e0cc0d1cebc2.
Successfully started container e0cc0d1cebc2.
Starting container 1f4d584d8dc0.
Successfully started container 1f4d584d8dc0.
Waiting for containers to start...
...
----------------------------------------------------------------------
Ran 3 tests in 30.617s

OK

Total Elapsed Time: 362.87 seconds.Special Install Instructions for OS X:Requirements:Python 2.7.5VagrantVirtualBoxdocker-osxSetup VirtualBox and Vagrant.Install docker-osx:curl https://raw.github.com/noplay/docker-osx/0.8.0/docker-osx > /usr/local/bin/docker-osx
chmod +x /usr/local/bin/docker-osxStart docker-osx:docker-osx startOnce the script is done, you should see a line like this:To use docker:
export DOCKER_HOST=tcp://172.16.42.43:4243
and then use the docker command from os-x directly.Copy and paste theexportDOCKER_HOST=tcp://172.16.42.43:4243line and run this to set theDOCKER_HOSTenvironment variable.
Galley will need this to communicate with Docker.Verify Docker is working:docker versionProceed to the regular installation instructions.