{ "info": { "author": "Ryan Walker", "author_email": "ryan@wlkr.io", "bugtrack_url": null, "classifiers": [], "description": "Galley\n======\n\nEnd-to-end test orchestration with Docker.\n\nGetting Started Guide\n=====================\n\nRequirements:\n\n- Python 2.7.5\n- Docker >= 0.10.0\n\nInstallation\n------------\n\n1. Install Galley:\n\n ::\n\n pip install -r requirements\n python setup.py install\n\n2. Run Galley:\n\n ::\n\n $ galley -h\n usage: galley [-h] [--no-destroy] [config] [pattern]\n\n End-to-end testing orchestration with Docker.\n\n positional arguments:\n config Path to galley YAML file that defines Docker resources.\n pattern Test file pattern.\n\n optional arguments:\n -h, --help show this help message and exit\n --no-destroy Do not destroy images and containers.\n\n.galley.yml\n-----------\n\nYou define the environment you want Galley to create in the\n``.galley.yml`` file. Place this file in your application's root\ndirectory. ``.galley.yml`` consists of three sections: ``images``,\n``resources``, and ``testparams``.\n\nimages\n~~~~~~\n\n::\n\n images:\n 0:\n name: \"redis\"\n source: \"dockerfile/redis\"\n tag: \"latest\"\n action: \"pull\"\n persist: True\n 1:\n name: \"mongodb\"\n source: \"dockerfile/mongodb\"\n tag: \"latest\"\n action: \"pull\"\n persist: True\n 2:\n name: \"baconpancakes\"\n source: \"/path/to/baconpancakes\"\n action: \"build\"\n persist: False\n\nIn the ``images`` section, you describe what images you want Galley to\ncreate and how you would like to create them.\n\n- ``name``: A name for the image in Galley. This can be used by\n ``resources`` to describe what image to use when creating them. For\n ``build`` actions, this is also the name tagged to the image built.\n- ``source``: For images with an ``pull`` action, this is the\n ``repo/image`` of the image to be pulled. For ``build`` actions, this\n is the path to the directory containing the ``Dockerfile`` to build.\n If ``.galley.yml`` is in the same directory as your ``Dockerfile``,\n this can be ``\".\"``.\n- ``tag``: The image tag to pull.\n- ``action``: Available actions are ``pull`` and ``build``:\n\n - ``pull`` uses the ``docker pull`` command to pull the image\n described in ``source`` from the Docker Index.\n - ``build`` uses the ``docker build`` command to build an image from\n the ``Dockerfile`` located in the directory described in\n ``source``.\n\n- ``persist`` (optional): When Galley finishes testing, it destroys all\n images it created. To keep images from one Galley run to the next,\n set ``persist`` to ``True``. This is helpful if you have upstream\n images you will use every time since they will not have to be\n downloaded on each run.\n\ntestparams\n~~~~~~~~~~\n\n::\n\n testparams:\n pancakes: \"{{environ['GALLEY_PANCAKES']}}\"\n bacon: \"{{environ['GALLEY_BACON']}}\"\n\n``testparams`` are key-value attributes that can be referenced by your\ntests.\n\nresources\n~~~~~~~~~\n\n::\n\n resources:\n 0:\n name: \"redis\"\n image: \"{{redis}}\"\n host_port: \"{{random_port}}\"\n cont_port: 6379\n 1:\n name: \"mongodb\"\n image: \"{{mongodb}}\"\n host_port: \"{{random_port}}\"\n cont_port: 27017\n 2:\n name: \"baconpancakes\"\n image: \"{{baconpancakes}}\"\n host_port: \"{{random_port}}\"\n cont_port: 8080\n command: \"--role api\"\n host_volume: \"./docker/config\"\n cont_volume: \"config\"\n environment:\n BACONPANCAKES_ADDRESS: \"0.0.0.0:8080\"\n BACONPANCAKES_USERNAME: \"{{environ['GALLEY_BACONPANCAKES_USERNAME']}}\"\n BACONPANCAKES_PASSWORD: \"{{environ['GALLEY_BACONPANCAKES_PASSWORD']}}\"\n BACONPANCAKES_BROKER_URL: \"redis://{{host['ip']}}:{{resources[0]['host_port']}}\"\n BACONPANCAKES_CELERY_BACKEND: \"redis://{{host['ip']}}:{{resources[0]['host_port']}}\"\n BACONPANCAKES_CONNECTION_STRING: \"mongodb://{{host['ip']}}:{{resources[1]['host_port']}}\"\n\nIn the ``resources`` section, you describe what containers you would\nlike Galley to create and run from the images in the ``images`` section.\n\n- ``name``: The name of the resource. Currently this is not used by\n anything, but in the furture should be used as the name of the\n container and as a key when referenced by other resources.\n- ``image``: The ``id`` of the image to use to create the container. By\n using ``{{image_name}}``, Galley will replace this with the actual\n image ``id`` when the image is created.\n- ``host_port``: The port on the host to map to ``cont_port``. By using\n ``{{random_port}}`` a random available ephemeral port on the host\n will be selected.\n- ``cont_port``: The port in the container to map to ``host_port``.\n- ``command``: The command to use when running the container.\n- ``host_volume``: A directory on the host to mount into your container\n as ``cont_volume``. Note: volumes are currently not supported by any\n of the Docker options in OS X. This option only works in Linux.\n- ``cont_volume``: The directory in the container where ``host_volume``\n will be mounted.\n- ``environment``: Environment variables to inject into container when\n run.\n\n``.galley.yml`` Templating:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n- ``{{resources[..]}}``:\n\n - In the resources section, you can build a relationship from one\n resource to another by referencing another resources data. For\n example, since we are telling Galley to choose a\n ``{{random_port}}`` for our MongoDB and Redis instances, our\n ``baconpancakes`` app won't know how to talk to them. So, in the\n environment section we tell ``baconpancakes`` to find the Celery\n backend with the ``host_port`` from ``resource 0`` by using\n ``{{resources[0]['host_port']}}`` in the connection string. This\n tells Galley to go find the value of ``host_port`` for\n ``resource 0`` and fill it in.\n - Currently, Galley is not smart enough to resolve dependencies on\n its own; therefore, a resource can only reference values from\n resources that appear before it in the ``.galley.yml`` file. In\n the future, this will likely be resolved by explicitly describing\n dependencies.\n - Only available in the ``resources`` section.\n\n- ``{{host[..]}}``:\n\n - Host-level information can be referenced through the ``host``\n dictionary. The main usage of this is to provide the host's IP\n address in order to allow separate resources to communicate with\n each other.\n - Currently, the only host-level attribute available in ``host`` is\n ``ip``.\n - Only available in the ``resources`` section.\n\n- ``{{environ[..]}}``:\n\n - Replaced with referenced host environment variable.\n\nSample ``.galley.yml`` file:\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n::\n\n images:\n 0:\n name: \"redis\"\n source: \"dockerfile/redis\"\n tag: \"latest\"\n action: \"pull\"\n persist: True\n 1:\n name: \"mongodb\"\n source: \"dockerfile/mongodb\"\n tag: \"latest\"\n action: \"pull\"\n persist: True\n 2:\n name: \"baconpancakes\"\n source: \"/path/to/baconpancakes\"\n action: \"build\"\n persist: False\n resources:\n 0:\n name: \"redis\"\n image: \"{{redis}}\"\n host_port: \"{{random_port}}\"\n cont_port: 6379\n 1:\n name: \"mongodb\"\n image: \"{{mongodb}}\"\n host_port: \"{{random_port}}\"\n cont_port: 27017\n 2:\n name: \"baconpancakes\"\n image: \"{{baconpancakes}}\"\n host_port: \"{{random_port}}\"\n cont_port: 8080\n command: \"--role api\"\n host_volume: \"./docker/config\"\n cont_volume: \"config\"\n environment:\n BACONPANCAKES_ADDRESS: \"0.0.0.0:8080\"\n BACONPANCAKES_USERNAME: \"{{environ['GALLEY_BACONPANCAKES_USERNAME']}}\"\n BACONPANCAKES_PASSWORD: \"{{environ['GALLEY_BACONPANCAKES_PASSWORD']}}\"\n BACONPANCAKES_BROKER_URL: \"redis://{{host['ip']}}:{{resources[0]['host_port']}}\"\n BACONPANCAKES_CELERY_BACKEND: \"redis://{{host['ip']}}:{{resources[0]['host_port']}}\"\n BACONPANCAKES_CONNECTION_STRING: \"mongodb://{{host['ip']}}:{{resources[1]['host_port']}}\"\n testparams:\n pancakes: \"{{environ['GALLEY_PANCAKES']}}\"\n bacon: \"{{environ['GALLEY_BACON']}}\"\n\nGalleytests\n-----------\n\nAfter Galley completes creating your environment, it looks recursively\nfor any ``galleytest_*.py`` files in your current directory.\n\nWriting tests for Galley to use is easy! Galley uses `Python\nunittests `__ to test\nyour environment. Therefore, writing a test for Galley is just as easy\nand allows you to use any of ``unittest``'s `assert\nmethods `__.\nAll you need to do is make sure to import ``GalleyTestCase`` from\n``galley.test`` and pass ``GalleyTestCase`` into your test class:\n\n::\n\n import requests\n\n from galley.test import GalleyTestCase\n\n\n class TestWebGetRequest(GalleyTestCase):\n\n def test_web_status(self):\n env = self.environment\n web_ip = env['host']['ip']\n web_port = env['resources'][2]['host_port']\n url = \"http://%s:%d\" % (web_ip, web_port)\n response = requests.get(url)\n\n self.assertEqual(200, response.status_code)\n self.assertIn('MakinBaconPancakes', response.text)\n\nIn this test we want to check to make sure our web application started\nproperly and that the some expected content was found on the page. Since\nwe imported ``GalleyTestCase`` and passed it into our test class, we can\nalso reference our entire environment in our test by calling\n``self.envionment``. Here, we used this to find the IP address of the\nDocker host and the port our web application was mapped to. As you can\nsee, the ``self.assertEqual()`` and ``self.assertIn()`` functions come\nstraight from ``unittests``.\n\nGalley tests can be more complicated as well:\n\n::\n\n import requests\n import time\n\n from galley.test import GalleyTestCase\n\n\n class TestPancakes(GalleyTestCase):\n\n def test_pancakes(self):\n env = self.environment\n api_ip = env['host']['ip']\n api_port = env['resources'][2]['host_port']\n pancakes = env['testparams']['pancakes']\n bacon = env['testparams']['bacon']\n url = \"http://%s:%d/api/%s/%s\" % (api_ip, api_port, pancakes, bacon)\n response = requests.post(url)\n baconpancakes = response.json()\n status = response.status_code\n pancake_id = baconpancakes['id']\n\n self.assertEqual(201, status)\n self.assertEqual('REQUESTED', baconpancakes['status'])\n\n url = url + '/' + pancake_id\n for attempt in range(20):\n r = requests.get(url)\n pancake = r.json()\n try:\n self.assertEqual('MADE', baconpancakes['status'])\n except Exception:\n time.sleep(5)\n\n self.assertEqual('MADE', baconpancakes['status'])\n self.assertIn('bacon', baconpancakes.keys())\n\nTEST!\n-----\n\n::\n\n $ galley\n Pulling dockerfile/redis:latest from registry.\n Checking if image dockerfile/redis:latest exists.\n Found image dockerfile/redis:latest.\n Successfully pulled dockerfile/redis:latest.\n Pulling dockerfile/mongodb:latest from registry.\n Checking if image dockerfile/mongodb:latest exists.\n Found image dockerfile/mongodb:latest.\n Successfully pulled dockerfile/mongodb:latest.\n Building image . with tag baconpancakes.\n Checking if image c023ce32fc62 exists.\n Found image c023ce32fc62.\n Successfully built image c023ce32fc62 from ..\n Creating dockerfile/redis container.\n Successfully created dockerfile/redis container: 380c81fe0775\n Creating dockerfile/mongodb container.\n Successfully created dockerfile/mongodb container: 706e35d5e28f\n Creating c023ce32fc62 container.\n Successfully created c023ce32fc62 container: e0cc0d1cebc2\n Creating c023ce32fc62 container.\n Successfully created c023ce32fc62 container: 1f4d584d8dc0\n Starting container 380c81fe0775.\n Successfully started container 380c81fe0775.\n Starting container 706e35d5e28f.\n Successfully started container 706e35d5e28f.\n Starting container e0cc0d1cebc2.\n Successfully started container e0cc0d1cebc2.\n Starting container 1f4d584d8dc0.\n Successfully started container 1f4d584d8dc0.\n Waiting for containers to start...\n ...\n ----------------------------------------------------------------------\n Ran 3 tests in 30.617s\n\n OK\n\n Total Elapsed Time: 362.87 seconds.\n\nSpecial Install Instructions for OS X:\n--------------------------------------\n\nRequirements:\n\n- Python 2.7.5\n- `Vagrant `__\n- `VirtualBox `__\n- `docker-osx `__\n\n1. Setup VirtualBox and Vagrant.\n\n2. Install docker-osx:\n\n ::\n\n curl https://raw.github.com/noplay/docker-osx/0.8.0/docker-osx > /usr/local/bin/docker-osx\n chmod +x /usr/local/bin/docker-osx\n\n3. Start docker-osx:\n\n ::\n\n docker-osx start\n\n4. Once the script is done, you should see a line like this:\n\n ::\n\n To use docker:\n export DOCKER_HOST=tcp://172.16.42.43:4243\n and then use the docker command from os-x directly.\n\n Copy and paste the ``export DOCKER_HOST=tcp://172.16.42.43:4243``\n line and run this to set the ``DOCKER_HOST`` environment variable.\n Galley will need this to communicate with Docker.\n\n5. Verify Docker is working:\n\n ::\n\n docker version\n\n6. Proceed to the regular installation instructions.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://github.com/rackerlabs/galley", "keywords": null, "license": "UNKNOWN", "maintainer": null, "maintainer_email": null, "name": "galley", "package_url": "https://pypi.org/project/galley/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/galley/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://github.com/rackerlabs/galley" }, "release_url": "https://pypi.org/project/galley/0.2.3/", "requires_dist": null, "requires_python": null, "summary": "End-to-end test orchestration with Docker", "version": "0.2.3" }, "last_serial": 1082980, "releases": { "0.2.1": [ { "comment_text": "", "digests": { "md5": "4346ed3fbc9443d8d57ddea508c4d3ed", "sha256": "4848a14ddd834d3eaef44d889734bd46c5f0bed73b07af8a20f107aded368353" }, "downloads": -1, "filename": "galley-0.2.1.tar.gz", "has_sig": false, "md5_digest": "4346ed3fbc9443d8d57ddea508c4d3ed", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21124, "upload_time": "2014-05-02T22:04:18", "url": "https://files.pythonhosted.org/packages/0c/1f/d621c5664b52bf8d5e1d27662333015f825ef8f4d57e19a60bc19ba55994/galley-0.2.1.tar.gz" } ], "0.2.2": [ { "comment_text": "", "digests": { "md5": "e4818adbfab51a1bb900633fa9431510", "sha256": "8b62118c9347baf40117ab6720d1ecc53563f754c8a3c1dab87698ba26eb6626" }, "downloads": -1, "filename": "galley-0.2.2.tar.gz", "has_sig": false, "md5_digest": "e4818adbfab51a1bb900633fa9431510", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21140, "upload_time": "2014-05-05T15:17:21", "url": "https://files.pythonhosted.org/packages/b7/d7/6e6af7e5d54f8c55184171b34202124e60a8975e0927e99e39e49923079a/galley-0.2.2.tar.gz" } ], "0.2.3": [ { "comment_text": "", "digests": { "md5": "727e9bf7c0fc985c64d3ecba9e58d2b6", "sha256": "a7066042e7bb75780720a1571beefe775d1ebda63d7f2b86cf029bb46e26b29e" }, "downloads": -1, "filename": "galley-0.2.3.tar.gz", "has_sig": false, "md5_digest": "727e9bf7c0fc985c64d3ecba9e58d2b6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21209, "upload_time": "2014-05-06T17:52:36", "url": "https://files.pythonhosted.org/packages/89/77/2f6e9b3080954ee7550213fe46eff98c779cfa912d24296545e03bbb43c4/galley-0.2.3.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "727e9bf7c0fc985c64d3ecba9e58d2b6", "sha256": "a7066042e7bb75780720a1571beefe775d1ebda63d7f2b86cf029bb46e26b29e" }, "downloads": -1, "filename": "galley-0.2.3.tar.gz", "has_sig": false, "md5_digest": "727e9bf7c0fc985c64d3ecba9e58d2b6", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21209, "upload_time": "2014-05-06T17:52:36", "url": "https://files.pythonhosted.org/packages/89/77/2f6e9b3080954ee7550213fe46eff98c779cfa912d24296545e03bbb43c4/galley-0.2.3.tar.gz" } ] }