├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── democontainer ├── Dockerfile └── demo │ ├── demo.py │ ├── requirements.txt │ └── tests │ └── test_demo.py └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .ropeproject 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | 118 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test 2 | 3 | test: 4 | docker exec -ti `docker ps -q -f 'name=_demo_'` py.test 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-compose-fake-aws 2 | 3 | Demonstration of using Docker Compose with fake (mock) AWS services for development 4 | 5 | Once you have docker and docker-compose installed, 6 | 7 | ``` 8 | $ docker-compose up -d # start the container running 9 | $ docker-compose logs # make sure things are up and running 10 | $ make test # run simple tests 11 | ``` 12 | 13 | This example has been written up in more detail on my blog: [Using docker-compose for local development with AWS services](http://serialized.net/2015/05/using-docker-compose-for-local-development-with-aws-services/) 14 | -------------------------------------------------------------------------------- /democontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu 2 | RUN apt-get update && \ 3 | apt-get install python \ 4 | python-dev python-pip -y 5 | 6 | # Separate this from the normal /demo install so code changes 7 | # don't dirty the cache for this layer 8 | ADD /demo/requirements.txt /tmp/ 9 | RUN pip install -r /tmp/requirements.txt 10 | 11 | ADD /demo /demo 12 | 13 | WORKDIR /demo 14 | 15 | CMD ["./demo.py"] 16 | -------------------------------------------------------------------------------- /democontainer/demo/demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import boto 4 | from boto.sqs.message import Message 5 | import time 6 | import signal 7 | import sys 8 | import os 9 | import logging 10 | 11 | """ Simple 'hello world' to connect to AWS services. 12 | - Connect to SQS 13 | - create a queue if it doesn't exist 14 | - send a message 15 | - receive a message 16 | - store the value in S3 17 | """ 18 | 19 | if "DEBUG_LOGGING" in os.environ: 20 | logging.basicConfig(stream=sys.stdout, level=logging.DEBUG) 21 | else: 22 | logging.basicConfig(stream=sys.stdout, level=logging.INFO) 23 | 24 | logger = logging.getLogger('aws_demo') 25 | 26 | 27 | def get_sqs_queue(): 28 | """ Return an SQS Queue object, creating the queue if needed """ 29 | args = {} 30 | 31 | if "SQS_SECURE" in os.environ: 32 | args['is_secure'] = False if \ 33 | os.environ["SQS_SECURE"] == "False" else True 34 | 35 | if "SQS_PORT" in os.environ: 36 | args['port'] = os.environ["SQS_PORT"] 37 | 38 | region = os.environ.get('SQS_REGION', 'us-west-2') 39 | queue = os.environ.get('SQS_QUEUE_NAME') 40 | conn = boto.sqs.connect_to_region(region, **args) 41 | 42 | if queue not in [str(q.name) for q in conn.get_all_queues()]: 43 | conn.create_queue(queue) 44 | 45 | return conn.lookup(queue) 46 | 47 | 48 | def get_s3_key(path="hello.txt"): 49 | """ Return a key handle at the appropriate S3 bucket and path """ 50 | 51 | bucket = os.environ['S3_BUCKET_NAME'] 52 | 53 | c = boto.connect_s3() 54 | try: 55 | b = c.get_bucket(bucket) 56 | except boto.exception.S3ResponseError: 57 | c.create_bucket(bucket) 58 | b = c.get_bucket(bucket) 59 | 60 | k = boto.s3.key.Key(b) 61 | k.key = path 62 | return k 63 | 64 | 65 | def hello_aws_world(queue, key): 66 | 67 | logger.info("Sending message") 68 | 69 | m = Message() 70 | m.set_body("Hello World!") 71 | queue.write(m) 72 | 73 | logger.info("Receive the message") 74 | 75 | res = queue.get_messages(1) 76 | rec_m = res.pop() 77 | rec_body = rec_m.get_body() 78 | queue.delete_message(rec_m) 79 | 80 | logger.info("Got message <{}>".format(rec_body)) 81 | 82 | key.set_contents_from_string(rec_body) 83 | 84 | logger.info("Storing contents into s3 at {}".format(key.key)) 85 | 86 | 87 | def main(): 88 | signal.signal(signal.SIGTERM, sigterm_handler) 89 | queue = get_sqs_queue() 90 | key = get_s3_key() 91 | 92 | while True: 93 | hello_aws_world(queue, key) 94 | time.sleep(2) 95 | 96 | 97 | def sigterm_handler(signal, frame): 98 | sys.exit(0) 99 | 100 | 101 | if __name__ == '__main__': 102 | main() 103 | -------------------------------------------------------------------------------- /democontainer/demo/requirements.txt: -------------------------------------------------------------------------------- 1 | pytest 2 | boto 3 | awscli 4 | -------------------------------------------------------------------------------- /democontainer/demo/tests/test_demo.py: -------------------------------------------------------------------------------- 1 | import boto 2 | import os 3 | 4 | 5 | def test_key_contents(): 6 | """ Ensure that the test process is putting the right 7 | values in 'hello.txt' """ 8 | 9 | bucket = os.environ['S3_BUCKET_NAME'] 10 | c = boto.connect_s3() 11 | b = c.get_bucket(bucket) 12 | 13 | keys = [k.name for k in b.list()] 14 | assert 'hello.txt' in keys 15 | 16 | key = b.get_key('hello.txt') 17 | contents = key.get_contents_as_string() 18 | 19 | assert contents == 'Hello World!' 20 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | --- 2 | demo: 3 | build: democontainer 4 | environment: 5 | - S3_BUCKET_NAME=testbucket 6 | - AWS_SECRET_ACCESS_KEY=fake 7 | - AWS_ACCESS_KEY_ID=alsofake 8 | - SQS_QUEUE_NAME=testqueue 9 | - SQS_REGION=us-west-2 10 | - SQS_SECURE=False 11 | - SQS_PORT=9324 12 | links: 13 | - fakesqs:us-west-2.queue.amazonaws.com 14 | - fakes3ssl:testbucket.s3.amazonaws.com 15 | fakesqs: 16 | image: pakohan/elasticmq 17 | command: ["java", "-jar", "elasticmq-server-0.8.5.jar"] 18 | ports: 19 | - "9324:9324" 20 | fakes3: 21 | image: lphoward/fake-s3 22 | ports: 23 | - "4569:4569" 24 | fakes3ssl: 25 | image: cbachich/ssl-proxy 26 | ports: 27 | - "443:443" 28 | links: 29 | - fakes3:proxyapp 30 | environment: 31 | - PORT=443 32 | - TARGET_PORT=4569 33 | --------------------------------------------------------------------------------