diff --git a/sample/.bundle/config b/sample/.bundle/config new file mode 100644 index 00000000..0fe0ab90 --- /dev/null +++ b/sample/.bundle/config @@ -0,0 +1,3 @@ +--- +BUNDLE_PATH: gems +BUNDLE_DISABLE_SHARED_GEMS: '1' diff --git a/sample/Gemfile b/sample/Gemfile new file mode 100644 index 00000000..3b174dea --- /dev/null +++ b/sample/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'moonshot', git: 'git@github.com:acquia/moonshot', ref: 'master' diff --git a/sample/Gemfile.lock b/sample/Gemfile.lock new file mode 100644 index 00000000..955de8e7 --- /dev/null +++ b/sample/Gemfile.lock @@ -0,0 +1,65 @@ +GIT + remote: git@github.com:acquia/moonshot + revision: 2413a55a250ee63514c92bf107679f4e38bcc8d4 + ref: master + specs: + moonshot (0.6.0) + aws-sdk (~> 2.2.0) + colorize + highline (~> 1.7.2) + interactive-logger (~> 0.1.1) + rotp (~> 2.1.1) + ruby-duration + semantic + thor (~> 0.19.1) + vandamme + +GEM + remote: https://rubygems.org/ + specs: + activesupport (4.2.6) + i18n (~> 0.7) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.3, >= 0.3.4) + tzinfo (~> 1.1) + aws-sdk (2.2.25) + aws-sdk-resources (= 2.2.25) + aws-sdk-core (2.2.25) + jmespath (~> 1.0) + aws-sdk-resources (2.2.25) + aws-sdk-core (= 2.2.25) + colorize (0.7.7) + github-markup (1.4.0) + highline (1.7.8) + i18n (0.7.0) + interactive-logger (0.1.1) + colorize (~> 0.7.7) + ruby-duration (~> 3.2, >= 3.2.1) + iso8601 (0.9.0) + jmespath (1.1.3) + json (1.8.3) + minitest (5.8.4) + redcarpet (3.3.4) + rotp (2.1.1) + ruby-duration (3.2.3) + activesupport (>= 3.0.0) + i18n + iso8601 + semantic (1.4.1) + thor (0.19.1) + thread_safe (0.3.5) + tzinfo (1.2.2) + thread_safe (~> 0.1) + vandamme (0.0.11) + github-markup (~> 1.3) + redcarpet (~> 3.3.2) + +PLATFORMS + ruby + +DEPENDENCIES + moonshot! + +BUNDLED WITH + 1.11.2 diff --git a/sample/README.md b/sample/README.md new file mode 100644 index 00000000..6da53eb2 --- /dev/null +++ b/sample/README.md @@ -0,0 +1,78 @@ +# Moonshot Sample Application + +A small PHP application that is used to demo the +[Moonshot](https://github.com/acquia/moonshot) deployment tool. + +## So, what's in it for me? + +After setup, you will be able to repeatedly deploy a PHP app to one or more +isolated environments on AWS by running the following command: + +``` +bundle exec bin/environment deploy-code +``` + +You will also be able to update the OS and supporting software by pulling the +latest changes in this repository and updating the stack with following command: + +``` +bundle exec bin/environment update +``` + +Lastly, you get a disposable, light-weight application that can be used to learn +and hack on Moonshot without having to muck with applications that actually +matter. + +## Setup + +1. Create a service role for Code Deploy in your AWS account. + + Run the commands in the [Configuring an AWS account](https://github.com/acquia/moonshot#codedeploy-role) + section of Moonshot's README. If you wish to do this manually, follow the + [Create a Service Role for AWS CodeDeploy](http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-create-service-role.html) + documentation, and make sure to name the role `CodeDeployRole` as that is + what Moonshot expects. + +2. Install Moonshot and it's dependencies. + + This step assumes that you have [Bundler](http://bundler.io/) and a modern + version of Ruby installed on your system. + + ```shell + bundle install + ``` + +3. Run the setup script to create a provisioning script, a parameters file for + your default environment, and a default `index.php` file. Change + `[S3_BUCKET]` to an S3 bucket you can write to, e.g. `my-s3-bucket`. + + ```shell + ./bin/setup.sh [S3-BUCKET] + ``` + +4. [OPTIONAL] Modify `./docroot/index.php` accordingly. + + By default, a simple phpinfo() page is displayed. + +## Usage + +Run the following commands to create your environment and deploy code to it. +Note that you will likely have to set the `AWS_REGION` environment variable +prior to running these commands. + +``` +bundle exec bin/environment create +bundle exec bin/environment deploy-code +``` + +Visit the AWS console, view your Cloud Formation stack, and click on the link in +the "URL" key's value in the "Outputs" tab to view your app. + +Tear down your stack by running the following command: + +``` +bundle exec bin/environment delete +``` + +See https://github.com/acquia/moonshot#basic-usage for more advanced +usage of Moonshot. diff --git a/sample/appspec.yml b/sample/appspec.yml new file mode 100644 index 00000000..02d0f3cc --- /dev/null +++ b/sample/appspec.yml @@ -0,0 +1,19 @@ +version: 0.0 +os: linux +files: + - source: docroot + destination: /usr/share/nginx/docroot +permissions: + - object: /usr/share/nginx + pattern: "**" + owner: root + group: root +hooks: + ApplicationStop: + - location: bin/aws-codedeploy-samples/load-balancing/elb/deregister_from_elb.sh + - location: bin/stop.sh + BeforeInstall: + - location: bin/clean.sh + ApplicationStart: + - location: bin/start.sh + - location: bin/aws-codedeploy-samples/load-balancing/elb/register_with_elb.sh diff --git a/sample/bin/aws-codedeploy-samples/LICENSE b/sample/bin/aws-codedeploy-samples/LICENSE new file mode 100644 index 00000000..f433b1a5 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/LICENSE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/sample/bin/aws-codedeploy-samples/README.md b/sample/bin/aws-codedeploy-samples/README.md new file mode 100644 index 00000000..f1c7ef3f --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/README.md @@ -0,0 +1,16 @@ +AWS CodeDeploy Samples +====================== + +The samples in the repository each demonstrate one of a few different scenarios. +These fall into one of a few different categories, including: + +- Sample Applications +- Integrations and templates for configuration management systems +- Integrations with load-balancers like Elastic Load Balancing +- Sample hooks for version control systems like Git. + +License +======= + +These samples and templates are all licensed under Apache 2.0. + diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/LICENSE.txt b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/LICENSE.txt new file mode 100755 index 00000000..1eac78a6 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/appspec.yml b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/appspec.yml new file mode 100755 index 00000000..7f0cdf94 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/appspec.yml @@ -0,0 +1,18 @@ +version: 0.0 +os: linux +files: + - source: /index.html + destination: /var/www/html/ +hooks: + BeforeInstall: + - location: scripts/install_dependencies + timeout: 300 + runas: root + - location: scripts/start_server + timeout: 300 + runas: root + ApplicationStop: + - location: scripts/stop_server + timeout: 300 + runas: root + diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/index.html b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/index.html new file mode 100755 index 00000000..34b7341d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/index.html @@ -0,0 +1,35 @@ + + + + + Sample Deployment + + + +
+

Congratulations

+

This application was deployed using AWS CodeDeploy.

+

For next steps, read the AWS CodeDeploy Documentation.

+
+ + + diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/install_dependencies b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/install_dependencies new file mode 100755 index 00000000..3d372993 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/install_dependencies @@ -0,0 +1,3 @@ +#!/bin/bash +yum install -y httpd + diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/start_server b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/start_server new file mode 100755 index 00000000..203194f2 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/start_server @@ -0,0 +1,3 @@ +#!/bin/bash +service httpd start + diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/stop_server b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/stop_server new file mode 100755 index 00000000..aef258ba --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Linux/scripts/stop_server @@ -0,0 +1,6 @@ +#!/bin/bash +isExistApp=`pgrep httpd` +if [[ -n $isExistApp ]]; then + service httpd stop +fi + diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/LICENSE.txt b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/LICENSE.txt new file mode 100755 index 00000000..7b080346 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2012-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/appspec.yml b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/appspec.yml new file mode 100755 index 00000000..9df98df6 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/appspec.yml @@ -0,0 +1,9 @@ +version: 0.0 +os: windows +files: + - source: \index.html + destination: C:\inetpub\wwwroot +hooks: + BeforeInstall: + - location: \before-install.bat + timeout: 900 diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/before-install.bat b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/before-install.bat new file mode 100755 index 00000000..fb2600e6 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/before-install.bat @@ -0,0 +1,3 @@ +REM Install Internet Information Server (IIS). +c:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -Command Import-Module -Name ServerManager +c:\Windows\Sysnative\WindowsPowerShell\v1.0\powershell.exe -Command Install-WindowsFeature Web-Server \ No newline at end of file diff --git a/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/index.html b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/index.html new file mode 100755 index 00000000..ca5a6e90 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/applications/SampleApp_Windows/index.html @@ -0,0 +1,37 @@ + + + + + Sample Deployment + + + +
+

Congratulations

+

This application was deployed using AWS CodeDeploy.

+

For next steps, read the AWS CodeDeploy Documentation.

+
+ + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/README.md new file mode 100644 index 00000000..bc83520f --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/README.md @@ -0,0 +1,12 @@ +Ansible and AWS CodeDeploy +-------------------------- + +[Ansible](http://www.ansible.com) is a radically simple IT automation platform that makes your +applications and systems easier to deploy. If you already have a set of Ansible playbooks, but just +need somewhere to run them, our first template for Ansible and AWS CodeDeploy will demonstrate how a +couple of simple deployment hooks will ensure Ansible is available on the local deployment instance +and run the given playbooks. Alternatively, if you already have a process for building and +maintaining your inventory, we've also built an Ansible module that you can use to install and run +the AWS CodeDeploy host agent. + +[Learn more >](https://github.com/awslabs/aws-codedeploy-samples) diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/application_start.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/application_start.sh new file mode 100755 index 00000000..c4bcd0d5 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/application_start.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Deploy hooks are run via absolute path, so taking dirname of this script will give us the path to +# our deploy_hooks directory. +. $(dirname $0)/common_variables.sh + +ansible-playbook $DESTINATION_PATH/lamp_simple/site.yml -i $DESTINATION_PATH/lamp_simple/hosts --connection=local diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/appspec.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/appspec.yml new file mode 100644 index 00000000..fdf28303 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/appspec.yml @@ -0,0 +1,18 @@ +version: 0.0 +os: linux +files: + - source: / + destination: /etc/ansible/codedeploy +hooks: + BeforeInstall: + - location: before_install.sh + timeout: 300 + runas: root + ApplicationStart: + - location: application_start.sh + timeout: 300 + runas: root + ValidateService: + - location: verify_service.sh + timeout: 300 + runas: root diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/before_install.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/before_install.sh new file mode 100755 index 00000000..9bafb3bc --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/before_install.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Check for ansible tools, and attempt to install if not + +yum list installed python-pip &> /dev/null +if [ $? != 0 ]; then + yum install -y python-pip +fi + +pip list | grep -q ansible +if [ $? != 0 ]; then + pip install ansible +fi diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/common_variables.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/common_variables.sh new file mode 100644 index 00000000..ea921f5f --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/common_variables.sh @@ -0,0 +1,15 @@ +#!/bin/true +# +# This is not an executable script, just a set of names and variable declarations. +# +# Use it with: +# source common_variables.sh +# Or: +# . common_variables.sh + +APPLICATION_NAME="AnsibleApp" +DEPLOY_GROUP="ansible" +BUCKET="" +BUNDLE_PATH="ansible-playbooks.zip" + +DESTINATION_PATH="/etc/ansible/codedeploy" diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/LICENSE.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/LICENSE.md new file mode 100644 index 00000000..2b437ec8 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/LICENSE.md @@ -0,0 +1,4 @@ +Copyright (C) 2013 AnsibleWorks, Inc. + +This work is licensed under the Creative Commons Attribution 3.0 Unported License. +To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/deed.en_US. diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/README.md new file mode 100644 index 00000000..d800ff8d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/README.md @@ -0,0 +1,27 @@ +Building a simple LAMP stack and deploying Application using Ansible Playbooks. +------------------------------------------- + +These playbooks require Ansible 1.2. + +These playbooks are meant to be a reference and starter's guide to building +Ansible Playbooks. These playbooks were tested on CentOS 6.x so we recommend +that you use CentOS or RHEL to test these modules. + +This LAMP stack can be on a single node or multiple nodes. The inventory file +'hosts' defines the nodes in which the stacks should be configured. + + [webservers] + localhost + + [dbservers] + bensible + +Here the webserver would be configured on the local host and the dbserver on a +server called "bensible". The stack can be deployed using the following +command: + + ansible-playbook -i hosts site.yml + +Once done, you can check the results by browsing to http://localhost/index.php. +You should see a simple test page and a list of databases retrieved from the +database server. diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/group_vars/all b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/group_vars/all new file mode 100644 index 00000000..698c2e43 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/group_vars/all @@ -0,0 +1,6 @@ +--- +# Variables listed here are applicable to all host groups + +httpd_port: 80 +ntpserver: localhost +repository: https://github.com/bennojoy/mywebapp.git diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/group_vars/dbservers b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/group_vars/dbservers new file mode 100644 index 00000000..bb60d44b --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/group_vars/dbservers @@ -0,0 +1,9 @@ +--- +# The variables file used by the playbooks in the dbservers group. +# These don't have to be explicitly imported by vars_files: they are autopopulated. + +mysqlservice: mysqld +mysql_port: 3306 +dbuser: root +dbname: foodb +upassword: abc diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/hosts b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/hosts new file mode 100644 index 00000000..bd573cc0 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/hosts @@ -0,0 +1,5 @@ +[webservers] +localhost + +[dbservers] +localhost diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/handlers/main.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/handlers/main.yml new file mode 100644 index 00000000..2235e75d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/handlers/main.yml @@ -0,0 +1,9 @@ +--- +# Handler to handle common notifications. Handlers are called by other plays. +# See http://ansible.cc/docs/playbooks.html for more information about handlers. + +- name: restart ntp + service: name=ntpd state=restarted + +- name: restart iptables + service: name=iptables state=restarted diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/tasks/main.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/tasks/main.yml new file mode 100644 index 00000000..317d954c --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/tasks/main.yml @@ -0,0 +1,24 @@ +--- +# This playbook contains common plays that will be run on all nodes. + +- name: Install ntp + yum: name=ntp state=present + tags: ntp + +- name: Configure ntp file + template: src=ntp.conf.j2 dest=/etc/ntp.conf + tags: ntp + notify: restart ntp + +- name: Start the ntp service + service: name=ntpd state=started enabled=true + tags: ntp + +- name: Check if /etc/sysconfig/iptables exists + stat: path=/etc/sysconfig/iptables + register: ipt + +- name: Place basic iptables rules + template: src=iptables.j2 dest=/etc/sysconfig/iptables + notify: restart iptables + when: ipt is defined and ipt.stat.exists == false diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/templates/iptables.j2 b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/templates/iptables.j2 new file mode 100644 index 00000000..074a9ba7 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/templates/iptables.j2 @@ -0,0 +1,36 @@ +# {{ ansible_managed }} +*filter +:INPUT ACCEPT [0:0] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] + +#Syn-flood protection +-A INPUT -p tcp ! --syn -m state --state NEW -j DROP + +#Force SYN packets check +-A INPUT -f -j DROP + +#Force Fragments packets check +-A INPUT -p tcp --tcp-flags ALL ALL -j DROP + +#XMAS packets +-A INPUT -p tcp --tcp-flags ALL NONE -j DROP + +# Accept packets belonging to established and related connections +-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT + +# Allow pings +-A INPUT -p icmp --icmp-type any -j ACCEPT + +# Set access for localhost +-A INPUT -i lo -j ACCEPT + +# Allow SSH connections on tcp port 22 +-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT + +# Set default policies for INPUT, FORWARD and OUTPUT chains +-P INPUT DROP +-P FORWARD DROP +-P OUTPUT ACCEPT + +COMMIT diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/templates/ntp.conf.j2 b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/templates/ntp.conf.j2 new file mode 100644 index 00000000..6336c2ea --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/common/templates/ntp.conf.j2 @@ -0,0 +1,12 @@ + +driftfile /var/lib/ntp/drift + +restrict 127.0.0.1 +restrict -6 ::1 + +server {{ ntpserver }} + +includefile /etc/ntp/crypto/pw + +keys /etc/ntp/keys + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/handlers/main.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/handlers/main.yml new file mode 100644 index 00000000..fffc0d57 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/handlers/main.yml @@ -0,0 +1,8 @@ +--- +# Handler to handle DB tier notifications + +- name: restart mysql + service: name=mysqld state=restarted + +- name: restart iptables + service: name=iptables state=restarted diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/tasks/main.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/tasks/main.yml new file mode 100644 index 00000000..4e09e55d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/tasks/main.yml @@ -0,0 +1,27 @@ +--- +# This playbook will install mysql and create db user and give permissions. + +- name: Install Mysql package + yum: name={{ item }} state=installed + with_items: + - mysql-server + - MySQL-python + +- name: Create Mysql configuration file + template: src=my.cnf.j2 dest=/etc/my.cnf + notify: + - restart mysql + +- name: Start Mysql Service + service: name=mysqld state=started enabled=true + +- name: insert iptables rule + lineinfile: dest=/etc/sysconfig/iptables state=present regexp="{{ mysql_port }}" + insertafter="^:OUTPUT " line="-A INPUT -p tcp --dport {{ mysql_port }} -j ACCEPT" + notify: restart iptables + +- name: Create Application Database + mysql_db: name={{ dbname }} state=present + +- name: Create Application DB User + mysql_user: name={{ dbuser }} password={{ upassword }} priv=*.*:ALL host='%' state=present diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/templates/my.cnf.j2 b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/templates/my.cnf.j2 new file mode 100644 index 00000000..3944d063 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/db/templates/my.cnf.j2 @@ -0,0 +1,11 @@ +[mysqld] +datadir=/var/lib/mysql +socket=/var/lib/mysql/mysql.sock +user=mysql +# Disabling symbolic-links is recommended to prevent assorted security risks +symbolic-links=0 +port={{ mysql_port }} + +[mysqld_safe] +log-error=/var/log/mysqld.log +pid-file=/var/run/mysqld/mysqld.pid diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/handlers/main.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/handlers/main.yml new file mode 100644 index 00000000..79f13f91 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/handlers/main.yml @@ -0,0 +1,6 @@ +--- +# Handler for the webtier: handlers are called by other plays. +# See http://ansible.cc/docs/playbooks.html for more information about handlers. + +- name: restart iptables + service: name=iptables state=restarted diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/copy_code.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/copy_code.yml new file mode 100644 index 00000000..d1834e8d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/copy_code.yml @@ -0,0 +1,9 @@ +--- +# These tasks are responsible for copying the latest dev/production code from +# the version control system. + +- name: Copy the code from repository + git: repo={{ repository }} dest=/var/www/html/ansible + +- name: Creates the index.php file + template: src=index.php.j2 dest=/var/www/html/ansible/index.php diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/install_httpd.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/install_httpd.yml new file mode 100644 index 00000000..a5d718e1 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/install_httpd.yml @@ -0,0 +1,18 @@ +--- +# These tasks install http and the php modules. + +- name: Install http and php etc + yum: name={{ item }} state=present + with_items: + - httpd + - php + - php-mysql + - git + +- name: insert iptables rule for httpd + lineinfile: dest=/etc/sysconfig/iptables create=yes state=present regexp="{{ httpd_port }}" insertafter="^:OUTPUT " + line="-A INPUT -p tcp --dport {{ httpd_port }} -j ACCEPT" + notify: restart iptables + +- name: http service state + service: name=httpd state=started enabled=yes diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/main.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/main.yml new file mode 100644 index 00000000..796842ed --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/tasks/main.yml @@ -0,0 +1,3 @@ +--- +- include: install_httpd.yml +- include: copy_code.yml diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/templates/index.php.j2 b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/templates/index.php.j2 new file mode 100644 index 00000000..61fdb5d3 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/roles/web/templates/index.php.j2 @@ -0,0 +1,24 @@ + + + Ansible Application + + +
+ Homepage +
+"; + echo "List of Databases:
"; + {% for host in groups['dbservers'] %} + $link = mysql_connect('{{ hostvars[host].ansible_eth0.ipv4.address }}', '{{ hostvars[host].dbuser }}', '{{ hostvars[host].upassword }}') or die(mysql_error()); + {% endfor %} + $res = mysql_query("SHOW DATABASES"); + while ($row = mysql_fetch_assoc($res)) { + echo $row['Database'] . "\n"; + } +?> + + + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/site.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/site.yml new file mode 100644 index 00000000..f395725b --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/lamp_simple/site.yml @@ -0,0 +1,23 @@ +--- +# This playbook deploys the whole application stack in this site. + +- name: apply common configuration to all nodes + hosts: all + remote_user: root + + roles: + - common + +- name: configure and deploy the webservers and application code + hosts: webservers + remote_user: root + + roles: + - web + +- name: deploy MySQL and configure the databases + hosts: dbservers + remote_user: root + + roles: + - db diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/verify_service.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/verify_service.sh new file mode 100755 index 00000000..081b55b4 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/local-only/verify_service.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Deploy hooks are run via absolute path, so taking dirname of this script will give us the path to +# our deploy_hooks directory. +. $(dirname $0)/common_variables.sh + +curl -s http://localhost:80/ansible/index.php diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/module/library/aws_codedeploy b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/module/library/aws_codedeploy new file mode 100755 index 00000000..edbff32b --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/module/library/aws_codedeploy @@ -0,0 +1,129 @@ +#!/usr/bin/python + +import sys +import os +import urlparse +import shutil +from subprocess import Popen +from subprocess import PIPE + +DOCUMENTATION = ''' +--- +module: aws_codedeploy +short_description: Installs and starts the AWS CodeDeploy Agent +description: + - Installs and starts the AWS CodeDeploy Agent +version_added: 0.0 +author: Andrew Fitz Gibbon +options: + enabled: + description: + - Enable the agent + choices: [ "yes", "no" ] + required: true +''' + +try: + import boto + from boto.s3.connection import Location + from boto.utils import get_instance_metadata +except ImportError: + print "failed=True msg='boto required for this module'" + sys.exit(1) + +# Determine if we're running on an EC2 instance +def on_ec2(): + try: + instance_metadata = boto.utils.get_instance_metadata(timeout=2, num_retries=2) + if 'instance-id' in instance_metadata.keys() and len(instance_metadata['instance-id']) > 0: + return True + else: + return False + except: + return False + +# Determine what kind of instance +def os_type(): + os = 'na' + release = '' + + try: + release = str(Popen(['lsb_release', '-a'], stdout=PIPE).communicate()) + except: + release = str(Popen(['cat', '/etc/system-release'], stdout=PIPE).communicate()) + + if 'AmazonAMI' in release or 'Amazon Linux AMI' in release: + return 'amzn' + elif 'Debian' in release or 'Ubuntu' in release: + return 'deb' + else: + return os + +def get_package(module, pkg_type): + dest = '/tmp/codedeploy_package.' + pkg_type + + pkg_url = 'https://s3.amazonaws.com/aws-codedeploy-us-east-1/latest/codedeploy-agent' + if pkg_type == 'deb': + pkg_url += '_all.deb' + elif pkg_type == 'rpm': + pkg_url += '.noarch.rpm' + else: + raise ValueError('unknown package type') + + body, info = fetch_url(module, pkg_url) + + if info['status'] != 200: + module.fail_json(msg="Failed to download agent", + status_code=info['status'], + respose=info['msg'], + url=pkg_url) + + try: + f = open(dest, 'wb') + shutil.copyfileobj(body, f) + except Exception, e: + module.fail_json(msg="failed to write agent package to disk: %s" % str(e)) + + return dest + +def install_pacakge(source, pkg_type): + cmd = '' + if pkg_type == 'deb': + cmd = 'dpkg -i ' + source + '; apt-get -q -y -f install' + elif pkg_type == 'rpm': + cmd = 'yum install -q -y ' + source + else: + raise ValueError('unknown package type') + + os.system(cmd) # TODO catch return + +def main(): + module = AnsibleModule( + argument_spec = dict( + enabled = dict(required=True, choices=BOOLEANS) # TODO actually use this + ) + ) + + if not on_ec2(): + module.fail_json(msg="must install the AWS CodeDeploy on an EC2 instance") + + try: + os = os_type() + pkg_type = '' + if os == 'deb': + pkg_type = 'deb' + elif os == 'amzn': + pkg_type = 'rpm' + else: + module.fail_json(msg="must install the AWS CodeDeploy on a supported OS type") + + pkg = get_package(module, pkg_type) + install_pacakge(pkg, pkg_type) + except Exception, e: + module.fail_json(msg=str(e)) + + module.exit_json(installed=True) + +from ansible.module_utils.basic import * +from ansible.module_utils.urls import * +main() diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/module/site.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/module/site.yml new file mode 100644 index 00000000..9d6115d6 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/ansible/module/site.yml @@ -0,0 +1,6 @@ +--- +- hosts: all + tasks: + - name: install codedeploy agent + sudo: yes + action: aws_codedeploy enabled=true diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/README.md new file mode 100644 index 00000000..c9987dc8 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/README.md @@ -0,0 +1,12 @@ +Chef and AWS CodeDeploy +----------------------- + +[Chef](https://www.getchef.com) is a configuration management tool that enables users to write +recipes that describe how the instances in their fleet are configured. AWS provides two template +samples for integrating Chef and AWS CodeDeploy. The first is a Chef cookbook that will install and +start the AWS CodeDeploy host agent, giving you the ability to continue managing your host +infrastructure via Chef while also being able to take advantage of the power of AWS CodeDeploy. The +second sample template demonstrates how to use CodeDeploy to orchestrate running cookbooks and +recipes via chef-solo on each node. + +[Learn more >](https://github.com/awslabs/aws-codedeploy-samples) diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/README.md new file mode 100644 index 00000000..66702891 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/README.md @@ -0,0 +1,31 @@ +Installing the AWS CodeDeploy Agent with Chef +============================================= + +In the previous post, we learned how to use the power of AWS CodeDeploy to orchestrate chef-solo. It took +the perspective of having half of your dependencies already installed – namely, the CodeDeploy agent. For +this post, we'll look at it from a different angle: the CodeDeploy host agent isn't installed yet, but you +have a pre-existing Chef environment running on Amazon EC2 instances. + +Setup and Preconditions +----------------------- + +The post below makes a few assumptions about your environment that may or may not be true. First and +foremost is that you have a working Chef environment. We'll assume that you've worked through your +own workflow for managing that environment and your chef-repo. If you are still new to Chef, their +documentation has a lot of very helpful information: [http://docs.getchef.com](http://docs.getchef.com) + +AWS CodeDeploy Host Agent Cookbook +---------------------------------- + +We've built a custom Chef cookbook to help ease the process of installing the CodeDeploy agent. You +can download that cookbook +[here](https://github.com/awslabs/aws-codedeploy-samples/tree/master/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/recipes). +To install the CodeDeploy agent, simply download the linked archive, copy the codedeploy-agent +directory into your chef-repo, and add `recipe[codedeploy-agent]` to your run list. If you just want +to test this out with a chef-solo instance, we've included a sample configuration for you. + +The cookbook has three simple steps: + +1. Download the package for the CodeDeploy host agent. +1. Install the agent. +1. Start the agent. diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/metadata.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/metadata.rb new file mode 100644 index 00000000..0132dcae --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/metadata.rb @@ -0,0 +1,2 @@ +name 'codedeploy-agent' +recipe 'codedeploy-agent::default', 'Fetches, installs, and starts the AWS CodeDeploy host agent' diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/recipes/default.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/recipes/default.rb new file mode 100644 index 00000000..ba4604ff --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/cookbooks/codedeploy-agent/recipes/default.rb @@ -0,0 +1,14 @@ +remote_file "#{Chef::Config[:file_cache_path]}/codedeploy-agent-install" do + source "https://s3.amazonaws.com/aws-codedeploy-us-east-1/latest/install" + mode 0755 +end + +bash "install-codedeploy-agent" do + code <<-EOH + #{Chef::Config[:file_cache_path]}/codedeploy-agent-install auto + EOH +end + +service "codedeploy-agent" do + action [:enable, :start] +end diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/node.json b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/node.json new file mode 100644 index 00000000..6cfdecfb --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/node.json @@ -0,0 +1,3 @@ +{ + "run_list": [ "recipe[codedeploy-agent]" ] +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/solo.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/solo.rb new file mode 100644 index 00000000..2669c908 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/aws-codedeploy-agent/solo.rb @@ -0,0 +1,4 @@ +# These are here just for testing only. +file_cache_path "/etc/chef" +cookbook_path "/etc/chef/cookbooks" +json_attribs "/etc/chef/node.json" diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/README.md new file mode 100644 index 00000000..87ff5738 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/README.md @@ -0,0 +1,218 @@ +Using AWS CodeDeploy to Orchestrate chef-solo +============================================= + +[Chef](http://www.getchef.com/chef/) is a great tool for automating infrastructure management, but +sometimes running and maintaining a central Chef server – and ensuring that it's highly available – +can be cost-prohibitive. So you turn to chef-solo, where you are fully responsible for orchestrating +the distribution of cookbooks and running chef-solo. Up until now, you've used a set of custom +scripts (perhaps built on top of tools like Capistrano) to do that orchestration. Here, we'll show +you how AWS CodeDeploy can do all of the heavy lifting for you with almost no custom scripting. + +For this post, we'll start with an instance that already has the CodeDeploy agent installed. If you +haven't already – or cleaned up afterwards – please complete *Step 1: Set Up a New Amazon EC2 Instance* +in the [AWS CodeDeploy Getting Started Guide](http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-set-up-new-instance.html). + +*Note: The CloudFormation example is a lot easier to use: http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-use-cloud-formation-template.html* + +Prepare the bundle +------------------ + +Next, we prepare the bundle, or source content, that will contain our Chef cookbooks and +configuration. Here, we use a simple "hello world" cookbook, but you're free to substitute your own. +The full source for this example bundle is also available +[here](https://github.com/awslabs/aws-codedeploy-samples/tree/master/conf-mgmt/chef/solo). + +Note: You will have to use the Apache Maven command, "mvn package" to create the target/hello.war file. + +First, create directories for our application and deploy hooks. The base of these will be the root +of our CodeDeploy revision: + +```bash +mkdir -p chef-solo-example/deploy_hooks +cd chef-solo-example +``` + +Then create a simple AppSpec as `./appspec.yml`: + +```yml +version: 0.0 +os: linux +files: + - source: chef/ + destination: /etc/chef/codedeploy + - source: target/hello.war + destination: /var/lib/tomcat6/webapps +hooks: + BeforeInstall: + - location: deploy_hooks/install-chef.sh + timeout: 1800 + runas: root + ApplicationStart: + - location: deploy_hooks/chef-solo.sh + runas: root + ValidateService: + - location: deploy_hooks/verify_service.sh + runas: root +``` + +This AppSpec tells AWS CodeDeploy that we want all of our chef configurations to be installed into +`/etc/chef/codedeploy`, the war file for our app should be installed into the default tomcat6 +webapps directory, and that it should run the scripts in `deploy_hooks/` on the appropriate +deployment events. Specifically: one to ensure that Chef is properly installed and one to initiate +the chef-solo run. + +Before we run anything, our `BeforeInstall` checks that Chef and RubyGems are installed and attempts +to install them. It also runs `knife install` to fetch the tomcat cookbook (in a normal application, +it's more likely that you'd already have done this; we're doing it as part of the deployment to keep +the sample bundle small): + +```bash +#!/bin/bash + +yum list installed rubygems &> /dev/null +if [ $? != 0 ]; then + yum -y install gcc-c++ ruby-devel make autoconf automake rubygems +fi + +gem list | grep -q chef +if [ $? != 0 ]; then + gem install chef ohai +fi + +# Install the tomcat cookbook +yum list installed git &> /dev/null +if [ $? != 0 ]; then + yum install -y git +fi + +cd /etc/chef/codedeploy/ +if ! test -r .git; then + git init .; git add -A .; git commit -m "Init commit" +fi +if ! test -r ./cookbooks/tomcat; then + /usr/local/bin/knife cookbook site install tomcat -o ./cookbooks +fi +``` + +Then, once our files are installed into the correct locations, our `ApplicationStart` lifecycle hook +actually initiates the chef-solo run:: + +```bash +#!/bin/bash +/usr/local/bin/chef-solo -c /etc/chef/codedeploy/chef/solo.rb +``` + +Finally, the `ValidateService` hook checks to see whether or not our app is responding as expected: + +```bash +#!/bin/bash + +result=$(curl -s http://localhost/hello/) + +if [[ "$result" =~ "Hello World" ]]; then + exit 0 +else + exit 1 +fi +``` + +Our chef configuration in this case is simply to set a couple of default tomcat options: + +```ruby +node.default["tomcat"]["user"] = "root" +node.default["tomcat"]["port"] = 80 +``` + +And the node.json and solo.rb configurations are similarly straightforward, just running the tomcat +default recipe and our own configuration (which we've titled `homesite`): + +node.json: + +```javascript +{ + "run_list": [ "recipe[homesite]", "recipe[tomcat]" ] +} +``` + +solo.rb: + +```ruby +file_cache_path "/etc/chef/codedeploy/" +cookbook_path "/etc/chef/codedeploy/cookbooks" +json_attribs "/etc/chef/codedeploy/node.json" +``` + +The java app does nothing more than respond with 'Hello World' at the root of the app. You can take +a closer look by downloading the source at the link above. + +Now that we've set up our bundle, we're ready to get things set up in AWS CodeDeploy. + +Set Up the AWS CodeDeploy Application +------------------------------ + +Even though we might have an application and deployment group set up already set up on this +instance, it's a good practice to create new ones. First, we create the new application: + +```sh +aws deploy create-application --application-name chef-solo-example +``` + +Then, using the ***CodeDeployTrustRoleArn*** that was assigned to our AWS CloudFormation stack, we create a +new deployment group for the chef-solo-example application: + +```sh +aws deploy create-deployment-group \ + --application-name chef-solo-example \ + --deployment-group-name ChefSolo_DeploymentGroup \ + --deployment-config-name CodeDeployDefault.AllAtOnce \ + --ec2-tag-filters Key=Name,Value=CodeDeployDeployment,Type=KEY_AND_VALUE \ + --service-role-arn CodeDeployTrustRoleArn +``` + +In this deployment group, we've set the default deployment configuration to +`CodeDeployDefault.AllAtOnce`. This will deploy to all of our instances at the same time. In a real +production app, you'd probably want to set it to something more conservative like +`CodeDeployDefault.OneAtATime` or a custom configuration. + +Push and Deploy the Application +------------------------------- + +At this point, we have a running Amazon EC2 instance that has the AWS CodeDeploy agent installed, an +application bundle containing our Chef cookbooks, and an AWS CodeDeploy application ready to accept +deployments. + +We next need to upload our bundle and register it as a new revision in AWS CodeDeploy. The `aws deploy push` command in +the AWS CLI will take care of that for us (make sure you replace ***bucket-name*** with the name of +an Amazon S3 bucket you have set up for AWS CodeDeploy): + +```sh +aws deploy push \ + --application-name chef-solo-example \ + --s3-location s3://bucket-name/chef-solo.zip \ + --ignore-hidden-files +``` + +And now we're ready for a deployment: + +```sh +aws deploy create-deployment \ + --application-name chef-solo-example \ + --deployment-config-name CodeDeployDefault.AllAtOnce \ + --deployment-group-name ChefSolo_DeploymentGroup \ + --s3-location bucket=bucket-name,key=chef-solo.zip,bundleType=zip +``` + +Note here that we specify the deployment configuration again. This is so that we can override any +default that we might have set on the deployment group. + +Once the deployment finishes (which you can check with either the AWS CodeDeploy console, or the `aws deploy +get-deployment` CLI command), you should be able to log into the instance and verify that your +cookbooks were applied. + +Wrapping up +----------- + +Now you're ready to use the power of AWS CodeDeploy to orchestrate your fleet of chef-solo nodes. In our +next post, we'll demonstrate how you can use a Chef recipe to install the AWS CodeDeploy agent, thus +allowing your infrastructure to continue to be managed by Chef while your application deployments +are managed via AWS CodeDeploy. diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/appspec.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/appspec.yml new file mode 100644 index 00000000..8a2f97ce --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/appspec.yml @@ -0,0 +1,18 @@ +version: 0.0 +os: linux +files: + - source: chef/ + destination: /etc/chef/codedeploy + - source: target/hello.war + destination: /var/lib/tomcat6/webapps +hooks: + BeforeInstall: + - location: deploy_hooks/install-chef.sh + timeout: 1800 + runas: root + ApplicationStart: + - location: deploy_hooks/chef-solo.sh + runas: root + ValidateService: + - location: deploy_hooks/verify_service.sh + runas: root diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/Cheffile b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/Cheffile new file mode 100644 index 00000000..cb87cd2b --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/Cheffile @@ -0,0 +1,3 @@ +site "https://supermarket.getchef.com/api/v1" + +cookbook "tomcat" diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/node.json b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/node.json new file mode 100644 index 00000000..aa686923 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/node.json @@ -0,0 +1,3 @@ +{ + "run_list": [ "recipe[homesite]", "recipe[tomcat]" ] +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/site-cookbooks/homesite/recipes/default.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/site-cookbooks/homesite/recipes/default.rb new file mode 100644 index 00000000..f3fa2ff6 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/site-cookbooks/homesite/recipes/default.rb @@ -0,0 +1,2 @@ +node.default["tomcat"]["user"] = "root" +node.default["tomcat"]["port"] = 8888 diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/solo.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/solo.rb new file mode 100644 index 00000000..6ad67bc4 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/chef/solo.rb @@ -0,0 +1,6 @@ +file_cache_path "/etc/chef/codedeploy/" +cookbook_path [ + "/etc/chef/codedeploy/cookbooks", + "/etc/chef/codedeploy/site-cookbooks" + ] +json_attribs "/etc/chef/codedeploy/node.json" diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/chef-solo.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/chef-solo.sh new file mode 100755 index 00000000..c34ada90 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/chef-solo.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# First, make sure the tomcat cookbook is installed +cd /etc/chef/codedeploy/ +/usr/local/bin/librarian-chef install + +/usr/local/bin/chef-solo -c /etc/chef/codedeploy/solo.rb diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/install-chef.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/install-chef.sh new file mode 100755 index 00000000..ac4e1f5a --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/install-chef.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +if [ ! -z "`which yum`" ]; then + + # On rpm-based system, use yum to install rubygems and related packages for building native + # extensions + + for pkg in rubygems20 gcc-c++ ruby20-devel make autoconf automake; do + yum list installed $pkg &> /dev/null + if [ $? != 0 ]; then + yum -y install $pkg + fi + done + +elif [ ! -z "`which apt-get`" ]; then + # On debian-like system, use apt-get to install + + for pkg in ruby2.0-dev ruby2.0 make autoconf g++; do + aptitude show $pkg | grep -q 'State: installed' + if [ $? != 0 ]; then + apt-get -y install $pkg + fi + done +fi + +# Now, we can install the required gems +for gem in chef ohai librarian-chef io-console; do + gem2.0 list | grep -q $gem + if [ $? != 0 ]; then + gem2.0 install $gem + + if [ $? != 0 ]; then + echo "Failed to install required gems. Cannot continue with deployment" + exit 1 + fi + fi +done diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/verify_service.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/verify_service.sh new file mode 100755 index 00000000..c4100989 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/deploy_hooks/verify_service.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +result=$(curl -s http://localhost:8888/hello/) + +if [[ "$result" =~ "Hello World" ]]; then + exit 0 +else + exit 1 +fi diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/pom.xml b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/pom.xml new file mode 100644 index 00000000..e65844c2 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + com.amazonaws.sample + hello + war + 1.0-SNAPSHOT + hello Maven Webapp + http://maven.apache.org + + + + junit + junit + 3.8.1 + test + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + + hello + + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/src/main/java/com/amazonaws/sample/HelloServlet.java b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/src/main/java/com/amazonaws/sample/HelloServlet.java new file mode 100644 index 00000000..ba2a298a --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/src/main/java/com/amazonaws/sample/HelloServlet.java @@ -0,0 +1,20 @@ +package com.amazonaws.sample; + +import javax.servlet.http.HttpServlet; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.PrintWriter; + +public class HelloServlet extends HttpServlet { + + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException + { + PrintWriter writer = response.getWriter(); + writer.print("

Hello World

"); + } +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/src/main/webapp/WEB-INF/web.xml b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..d105e7aa --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/chef/solo/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,19 @@ + + + + Hello World Web Application + + + HelloServlet + com.amazonaws.sample.HelloServlet + + + + HelloServlet + / + + + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/README.md new file mode 100644 index 00000000..702d7a0f --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/README.md @@ -0,0 +1,12 @@ +Puppet and AWS CodeDeploy +----------------------- + +[Puppet](http://puppetlabs.com/) is a configuration management system that allows you to define the +state of your IT infrastructure, then automatically enforces the correct state. AWS provides a +couple of sample templates to get you working with Puppet and AWS CodeDeploy faster. The first is a +Puppet module that will install and start the AWS CodeDeploy host agent, giving you the ability to +continue managing your host infrastructure via Puppet while also being able to take advantage of the +power of AWS CodeDeploy. The second sample template demonstrates how to use CodeDeploy to +orchestrate running modules and manifests via masterless puppet on each node. + +[Learn more >](https://github.com/awslabs/aws-codedeploy-samples) diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.fixtures.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.fixtures.yml new file mode 100644 index 00000000..e7227119 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.fixtures.yml @@ -0,0 +1,5 @@ +fixtures: + repositories: + stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git" + symlinks: + codedeploy: "#{source_dir}" diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.gitignore b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.gitignore new file mode 100644 index 00000000..641e153a --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.gitignore @@ -0,0 +1,7 @@ +.*.sw? +pkg +spec/fixtures +.rspec_system +.vagrant +.bundle +vendor diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.rspec b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.rspec new file mode 100644 index 00000000..8c18f1ab --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/.rspec @@ -0,0 +1,2 @@ +--format documentation +--color diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Guardfile b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Guardfile new file mode 100644 index 00000000..fd50602a --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Guardfile @@ -0,0 +1,5 @@ +notification :off + +guard 'rake', :task => 'test' do + watch(%r{^manifests\/(.+)\.pp$}) +end diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/LICENSE b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/LICENSE new file mode 100644 index 00000000..9f710553 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Modulefile b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Modulefile new file mode 100644 index 00000000..47b968fc --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Modulefile @@ -0,0 +1,10 @@ +name 'aws-codedeploy' +version '0.1.0' +source '' +author 'Amazon Web Services' +license 'Apache 2.0' +summary '' +description '' +project_page '' + +dependency 'puppetlabs/stdlib' diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/README.md new file mode 100644 index 00000000..1fa108cd --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/README.md @@ -0,0 +1,50 @@ +AWS CodeDeploy Puppet Module +============================ + +#### Table of Contents + +1. [Overview](#overview) +2. [Module Description - What the module does and why it is useful](#module-description) +3. [Usage - Configuration options and additional functionality](#usage) +4. [Reference - An under-the-hood peek at what the module is doing and how](#reference) +5. [Limitations - OS compatibility, etc.](#limitations) +6. [Development - Guide for contributing to the module](#development) + +## Overview + +This module installs the AWS CodeDeploy Agent. + +## Module Description + +The AWS CodeDeploy agent will copy packages to an EC2 instance from a configured S3 bucket. This +module should be used on an EC2 instance that already has Puppet installed. At this time, the only +supported operating systems are RedHat variants and Amazon Linux, through the installation of an RPM +package. + +## Usage + +Put the classes, types, and resources for customizing, configuring, and doing the fancy stuff with +your module here. + + +```puppet + codedeploy {} +``` + +##Reference + +###Class: codedeploy + +####Parameters + +* **package_source** - The url to install the CodeDeploy package from. Defaults to +`https://s3.amazonaws.com/aws-codedeploy-us-east-1/latest/codedeploy-agent.noarch.rpm` + +##Limitations + + * This module requires a RedHat or Amazon Linux based operating system that is capable of + installing RPM packages. + +##Development + +Please see our [GitHub repositories](https://github.com/awslabs) diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Rakefile b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Rakefile new file mode 100644 index 00000000..d913e842 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/Rakefile @@ -0,0 +1,36 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' +require 'puppet-syntax/tasks/puppet-syntax' + +# These gems aren't always present, for instance +# if run with --without development +begin + require 'puppet_blacksmith/rake_tasks' +rescue LoadError +end + +PuppetLint.configuration.relative = true +PuppetLint.configuration.send("disable_80chars") +PuppetLint.configuration.log_format = "%{path}:%{linenumber}:%{check}:%{KIND}:%{message}" +PuppetLint.configuration.fail_on_warnings = true + +# Forsake support for Puppet 2.6.2 for the benefit of cleaner code. +# http://puppet-lint.com/checks/class_parameter_defaults/ +PuppetLint.configuration.send('disable_class_parameter_defaults') +# http://puppet-lint.com/checks/class_inherits_from_params_class/ +PuppetLint.configuration.send('disable_class_inherits_from_params_class') + +exclude_paths = [ + "pkg/**/*", + "vendor/**/*", + "spec/**/*", +] +PuppetLint.configuration.ignore_paths = exclude_paths +PuppetSyntax.exclude_paths = exclude_paths + +desc "Run syntax, lint, and spec tests." +task :test => [ + :syntax, + :lint, + :spec, +] diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/init.pp b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/init.pp new file mode 100644 index 00000000..274c6439 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/init.pp @@ -0,0 +1,19 @@ +# == Class: codedeploy +# +# Install and manage the AWS CodeDeploy agent +# +# === Parameters +# +# [*package_source*] +# URL or filepath passed to package provider to install agent +# +class codedeploy ( + $package_source = $codedeploy::params::package_source, +) inherits codedeploy::params { + + validate_string($package_source) + + class { 'codedeploy::install': } ~> + class { 'codedeploy::service': } -> + Class['codedeploy'] +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/install.pp b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/install.pp new file mode 100644 index 00000000..6c87c571 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/install.pp @@ -0,0 +1,10 @@ +# == Class codedeploy::install +# +class codedeploy::install { + + package { 'codedeploy-agent': + ensure => present, + source => $codedeploy::package_source, + provider => rpm, + } +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/params.pp b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/params.pp new file mode 100644 index 00000000..e805bb17 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/params.pp @@ -0,0 +1,15 @@ +# == Class codedeploy::params +# +# This class is meant to be called from codedeploy +# It sets variables according to platform +# +class codedeploy::params { + case $::osfamily { + 'RedHat', 'Linux': { + $package_source = 'https://s3.amazonaws.com/aws-codedeploy-us-east-1/latest/codedeploy-agent.noarch.rpm' + } + default: { + fail("${::operatingsystem} not supported") + } + } +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/service.pp b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/service.pp new file mode 100644 index 00000000..db9fb755 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/manifests/service.pp @@ -0,0 +1,12 @@ +# == Class codedeploy::service +# +# This class is meant to be called from codedeploy +# It ensure the service is running +# +class codedeploy::service { + + service { 'codedeploy-agent': + ensure => running, + enable => true, + } +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/metadata.json b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/metadata.json new file mode 100644 index 00000000..d11c250c --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/metadata.json @@ -0,0 +1,16 @@ +{ + "name": "aws-codedeploy", + "version": "0.1.0", + "author": "Amazon Web Services", + "summary": "", + "license": "Apache 2.0", + "source": "", + "project_page": "", + "issues_url": "", + "dependencies": [ + { + "name": "puppetlabs-stdlib", + "version_requirement": ">= 1.0.0" + } + ] +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/classes/coverage_spec.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/classes/coverage_spec.rb new file mode 100644 index 00000000..12513b83 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/classes/coverage_spec.rb @@ -0,0 +1 @@ +at_exit { RSpec::Puppet::Coverage.report! } diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/classes/example_spec.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/classes/example_spec.rb new file mode 100644 index 00000000..c707ba98 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/classes/example_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'codedeploy' do + let(:facts) {{ + :osfamily => 'RedHat', + }} + describe "codedeploy class without any parameters" do + + it { should compile.with_all_deps } + + it { should contain_class('codedeploy::params') } + it { should contain_class('codedeploy::install') } + it { should contain_class('codedeploy::service').that_subscribes_to('codedeploy::install') } + + it { should contain_service('codedeploy-agent') } + it { should contain_package('codedeploy-agent') + .with_ensure('present') + .with_source('https://s3.amazonaws.com/aws-codedeploy-us-east-1/latest/codedeploy-agent.noarch.rpm') + } + end + + describe "codedeploy class with custom package source" do + let(:params) {{ :package_source => 'https://example.com/package.rpm' }} + it { should contain_package('codedeploy-agent').with_source('https://example.com/package.rpm') } + end + + context 'unsupported operating system' do + describe 'codedeploy class without any parameters on Solaris/Nexenta' do + let(:facts) {{ + :osfamily => 'Solaris', + :operatingsystem => 'Nexenta', + }} + + it { expect { should contain_package('codedeploy') }.to raise_error(Puppet::Error, /Nexenta not supported/) } + end + end +end diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/spec_helper.rb b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/spec_helper.rb new file mode 100644 index 00000000..2c6f5664 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/tests/init.pp b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/tests/init.pp new file mode 100644 index 00000000..bdcaade2 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/aws-codedeploy/tests/init.pp @@ -0,0 +1,12 @@ +# The baseline for module testing used by Puppet Labs is that each manifest +# should have a corresponding test manifest that declares that class or defined +# type. +# +# Tests are then run by using puppet apply --noop (to check for compilation +# errors and view a log of events) or by fully applying the test in a virtual +# environment (to compare the resulting system state to the desired state). +# +# Learn more about module testing here: +# http://docs.puppetlabs.com/guides/tests_smoke.html +# +include codedeploy diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/README.md new file mode 100644 index 00000000..b5d3b36d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/README.md @@ -0,0 +1,202 @@ +Using AWS CodeDeploy to Orchestrate Masterless Puppet +===================================================== + +[Puppet](http://puppetlabs.com/) is a great tool for automating infrastructure management. You may +be familiar with using custom scripts (perhaps built on top of tools like Capistrano) to orchestrate +application deployments. Here, we'll show you how AWS CodeDeploy can do all of the heavy lifting for +you with almost no custom scripting. + +For this post, we'll start with an instance that already has the CodeDeploy agent installed. If you +haven't already – or cleaned up afterwards – please complete *Step 1: Set Up an Amazon EC2 Instance* +in the [AWS CodeDeploy Getting Started Guide](http://docs.aws.amazon.com/codedeploy/latest/userguide/how-to-set-up-new-instance.html). + +Prepare the bundle +------------------ + +To start, we prepare the bundle, or source content, that will contain our Puppet modules and +configuration. The sample app we use here is a simple Java web app, but you're free to substitute +your own. The full source for this example bundle is also available [here](https://github.com/awslabs/aws-codedeploy-samples/tree/master/conf-mgmt/puppet/masterless). + +First, create directories for our application and deploy hooks. The base of these will be the root +of our CodeDeploy revision: + +```bash +mkdir -p puppet-example/deploy_hooks +cd puppet-example +``` + +Then create a simple AppSpec as `./appspec.yml`: + +```yml +version: 0.0 +os: linux +files: + - source: puppet/ + destination: /etc/puppet/codedeploy + - source: target/hello.war + destination: /var/lib/tomcat6/webapps +hooks: + BeforeInstall: + - location: deploy_hooks/install-puppet.sh + timeout: 1800 + runas: root + ApplicationStart: + - location: deploy_hooks/puppet-apply.sh + runas: root + ValidateService: + - location: deploy_hooks/verify_service.sh + runas: root +``` + +This AppSpec tells AWS CodeDeploy that we want all of our Puppet manifests to be installed into +`/etc/puppet/codedeploy`, the war file for our app should be installed into the default tomcat6 +webapps directory, and that it should run the scripts in `deploy_hooks/` on the appropriate +deployment events. Specifically: one to ensure that Puppet is properly installed and one to run +`puppet apply`. + +Before we run anything though, our `BeforeInstall` checks that Puppet is installed and attempts to +install it. It also runs `puppet module install` for the tomcat module and its dependencies. + +```bash +#!/bin/bash + +# Check to see that Puppet itself is installed +yum list installed puppet &> /dev/null +if [ $? != 0 ]; then + yum -y install puppet +fi + +# Create the base directory for the system-wide Puppet modules +mkdir -p /etc/puppet/modules + +puppet="/usr/bin/puppet" + +# Check for each of the modules we need. If they're not installed, install them. +for module in puppetlabs/stdlib puppetlabs/java puppetlabs/tomcat stahnma/epel; do + $puppet module list | grep -q $(basename $module) + if [ $? != 0 ]; then + $puppet module install $module + fi +done + +exit 0 +``` + +Then, once our files are installed into the correct locations, our `ApplicationStart` lifecycle hook +actually runs `puppet apply`: + +```bash +#!/bin/bash + +BASE_DIR="/etc/puppet/" + +/usr/bin/puppet apply --modulepath=${BASE_DIR}/modules ${BASE_DIR}/codedeploy/manifests/hello_world.pp +``` + +Finally, the `ValidateService` hook checks to see whether or not our app is responding as expected: + +```bash +#!/bin/bash + +result=$(curl -s http://localhost:8080/hello/) + +if [[ "$result" =~ "Hello World" ]]; then + exit 0 +else + exit 1 +fi +``` + +Our Puppet manifest in this case is simply to set a couple of default tomcat options and start a +tomcat instance: + +``` +class { 'tomcat': } + +class { 'epel': }-> +tomcat::instance{ 'default': + install_from_source => false, + package_name => 'tomcat6', + package_ensure => 'present', +}-> +tomcat::service { 'default': + use_jsvc => false, + use_init => true, + service_name => 'tomcat6', +} +``` + +The java app does nothing more than respond with 'Hello World' at the root of the app. You can take +a closer look by downloading the source at the link above. + +Now that we've set up our bundle, we're ready to get things set up in AWS CodeDeploy. + +Set Up the AWS CodeDeploy Application +------------------------------------- + +Even though we might have an application and deployment group already set up on this +instance, it's a good practice to create new ones. First, we create the new application: + +```sh +aws deploy create-application --application-name puppet-example +``` + +Then, using the ***CodeDeployTrustRoleArn*** that was assigned to our AWS CloudFormation stack, we create a +new deployment group for the puppet-example application: + +```sh +aws deploy create-deployment-group \ + --application-name puppet-example \ + --deployment-group-name puppet_DeploymentGroup \ + --deployment-config-name CodeDeployDefault.AllAtOnce \ + --ec2-tag-filters Key=Name,Value=CodeDeployDeployment,Type=KEY_AND_VALUE \ + --service-role-arn CodeDeployTrustRoleArn +``` + +In this deployment group, we've set the default deployment configuration to +`CodeDeployDefault.AllAtOnce`. This will deploy to all of our instances at the same time. In a real +production app, you'd probably want to set it to something more conservative like +`CodeDeployDefault.OneAtATime` or a custom configuration. + +Push and Deploy the Application +------------------------------- + +At this point, we have a running Amazon EC2 instance that has the AWS CodeDeploy agent installed, an +application bundle containing our Puppet manifests, and an AWS CodeDeploy application ready to accept +deployments. + +We next need to upload our bundle and register it as a new revision in AWS CodeDeploy. The `aws deploy push` command in +the AWS CLI will take care of that for us (make sure you replace ***bucket-name*** with the name of +an Amazon S3 bucket you have set up for AWS CodeDeploy): + +```sh +aws deploy push \ + --application-name puppet-example \ + --s3-location s3://bucket-name/puppet-example.zip \ + --ignore-hidden-files true +``` + +And now we're ready for a deployment: + +```sh +aws deploy create-deployment \ + --application-name puppet-example \ + --deployment-config-name CodeDeployDefault.AllAtOnce \ + --deployment-group-name puppet_DeploymentGroup \ + --revision bucket=bucket-name,key=puppet-example.zip,bundleType=zip +``` + +Note here that we specify the deployment configuration again. This is so that we can override any +default that we might have set on the deployment group. + +Once the deployment finishes (which you can check with either the AWS CodeDeploy console, or the `aws deploy +get-deployment` CLI command), you should be able to log into the instance and verify that the app is +up and running. + +Wrapping up +----------- + +Now you're ready to use the power of AWS CodeDeploy to orchestrate your fleet of Puppet nodes. In our +next post, we'll demonstrate how you can use a Puppet module to install the AWS CodeDeploy agent, thus +allowing your infrastructure to continue to be managed by Puppet while your application deployments +are managed via AWS CodeDeploy. diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/application_vars.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/application_vars.sh new file mode 100644 index 00000000..ba84062a --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/application_vars.sh @@ -0,0 +1,6 @@ +APPLICATION_NAME="PuppetMasterless" +DEPLOY_GROUP="PuppetMasterless" +BUCKET="" +BUNDLE_PATH="puppet-masterless.tar" +EC2_TAG_KEY='Puppet' +EC2_TAG_VALUE='Masterless' diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/appspec.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/appspec.yml new file mode 100644 index 00000000..7d25ba01 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/appspec.yml @@ -0,0 +1,17 @@ +version: 0.0 +os: linux +files: + - source: puppet/ + destination: /etc/puppet/codedeploy + - source: target/hello.war + destination: /var/lib/tomcat6/webapps +hooks: + BeforeInstall: + - location: deploy_hooks/install-puppet.sh + runas: root + ApplicationStart: + - location: deploy_hooks/puppet-apply.sh + runas: root + ValidateService: + - location: deploy_hooks/verify_service.sh + runas: root diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/install-puppet.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/install-puppet.sh new file mode 100755 index 00000000..5b5132ff --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/install-puppet.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Check to see that puppet itself is installed +yum list installed puppet &> /dev/null +if [ $? != 0 ]; then + yum -y install puppet +fi + +# Create the base directory for the system-wide puppet modules +mkdir -p /etc/puppet/modules + +puppet="/usr/bin/puppet" + +# Check for each of the modules we need. If they're not installed, install them. +for module in puppetlabs/stdlib puppetlabs/java puppetlabs/tomcat stahnma/epel; do + $puppet module list | grep -q $(basename $module) + if [ $? != 0 ]; then + $puppet module install $module + fi +done + +exit 0 diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/puppet-apply.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/puppet-apply.sh new file mode 100755 index 00000000..fb2115b1 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/puppet-apply.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +BASE_DIR="/etc/puppet/" + +/usr/bin/puppet apply --modulepath=${BASE_DIR}/modules ${BASE_DIR}/codedeploy/manifests/hello_world.pp diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/verify_service.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/verify_service.sh new file mode 100755 index 00000000..138138cf --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/deploy_hooks/verify_service.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +result=$(curl -s http://localhost:8080/hello/) + +if [[ "$result" =~ "Hello World" ]]; then + exit 0 +else + exit 1 +fi diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/pom.xml b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/pom.xml new file mode 100644 index 00000000..e65844c2 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + com.amazonaws.sample + hello + war + 1.0-SNAPSHOT + hello Maven Webapp + http://maven.apache.org + + + + junit + junit + 3.8.1 + test + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + + hello + + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/puppet/manifests/hello_world.pp b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/puppet/manifests/hello_world.pp new file mode 100644 index 00000000..b8770e45 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/puppet/manifests/hello_world.pp @@ -0,0 +1,13 @@ +class { 'tomcat': } + +class { 'epel': }-> +tomcat::instance{ 'default': + install_from_source => false, + package_name => 'tomcat6', + package_ensure => 'present', +}-> +tomcat::service { 'default': + use_jsvc => false, + use_init => true, + service_name => 'tomcat6', +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/src/main/java/com/amazonaws/sample/HelloServlet.java b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/src/main/java/com/amazonaws/sample/HelloServlet.java new file mode 100644 index 00000000..ba2a298a --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/src/main/java/com/amazonaws/sample/HelloServlet.java @@ -0,0 +1,20 @@ +package com.amazonaws.sample; + +import javax.servlet.http.HttpServlet; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.io.PrintWriter; + +public class HelloServlet extends HttpServlet { + + protected void doGet(HttpServletRequest request, + HttpServletResponse response) + throws ServletException, IOException + { + PrintWriter writer = response.getWriter(); + writer.print("

Hello World

"); + } +} diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/src/main/webapp/WEB-INF/web.xml b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..d105e7aa --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/puppet/masterless/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,19 @@ + + + + Hello World Web Application + + + HelloServlet + com.amazonaws.sample.HelloServlet + + + + HelloServlet + / + + + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/README.md b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/README.md new file mode 100644 index 00000000..676cc8cd --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/README.md @@ -0,0 +1,12 @@ +SaltStack and AWS CodeDeploy +---------------------------- + +[Salt](http://www.saltstack.com) is a fast, scalable and flexible systems management software for +data center automation, cloud orchestration, server provisioning, configuration management and more. +The two sample templates AWS provides offer examples for how you can integrate existing Salt +infrastructure with AWS CodeDeploy. On the one hand, you can use the AWS CodeDeploy module to +install and run the CodeDeploy host agent on your minions. On the other hand, you can use AWS +CodeDeploy via a couple of simple deployment hooks to orchestrate running your Salt States. Either +way, you get to take advantage of the power of both Salt and AWS CodeDeploy. + +[Learn more >](https://github.com/awslabs/aws-codedeploy-samples) diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/TODO.txt b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/TODO.txt new file mode 100644 index 00000000..2f141c18 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/TODO.txt @@ -0,0 +1 @@ +* write scripts for running salt modules in masterless mode via CodeDeploy diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/application_vars.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/application_vars.sh new file mode 100644 index 00000000..07354dad --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/application_vars.sh @@ -0,0 +1,8 @@ +APPLICATION_NAME="SaltMasterless" +DEPLOY_GROUP="Salt" +BUCKET="" +BUNDLE_PATH="salt-masterless.tar" +EC2_TAG_KEY='Salt' +EC2_TAG_VALUE='Masterless' + +DESTINATION_PATH="/etc/salt/codedeploy" diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/appspec.yml b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/appspec.yml new file mode 100644 index 00000000..7dc99d68 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/appspec.yml @@ -0,0 +1,17 @@ +version: 0.0 +os: linux +files: + - source: salt/ + destination: /etc/salt/codedeploy/ + - source: index.html + destination: /var/www/html/salt/ +hooks: + BeforeInstall: + - location: deploy_hooks/before_install.sh + runas: root + ApplicationStart: + - location: deploy_hooks/application_start.sh + runas: root + ValidateService: + - location: deploy_hooks/verify_service.sh + runas: root diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/application_start.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/application_start.sh new file mode 100755 index 00000000..17394ce5 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/application_start.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Deploy hooks are run via absolute path, so taking dirname of this script will give us the path to +# our deploy_hooks directory. +. $(dirname $0)/../application_vars.sh + +salt-call --local --file-root=$DESTINATION_PATH state.highstate diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/before_install.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/before_install.sh new file mode 100755 index 00000000..e1c11f76 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/before_install.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Check for salt, and attempt to install if not + +yum list installed | grep salt-minion &> /dev/null +if [ $? != 0 ]; then + yum install -y --enablerepo=epel salt-minion +fi diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/verify_service.sh b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/verify_service.sh new file mode 100755 index 00000000..8282c010 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/deploy_hooks/verify_service.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Deploy hooks are run via absolute path, so taking dirname of this script will give us the path to +# our deploy_hooks directory. +. $(dirname $0)/../application_vars.sh + +result=$(curl -s http://localhost/salt/index.html) + +if [[ "$result" =~ "Hello World" ]]; then + exit 0 +else + exit 1 +fi diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/index.html b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/index.html new file mode 100644 index 00000000..04a5e97f --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/index.html @@ -0,0 +1,9 @@ + + + AWS CodeDeploy with Salt Masterless + + +

Hello World

+ + + diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/salt/top.sls b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/salt/top.sls new file mode 100644 index 00000000..53f2952d --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/salt/top.sls @@ -0,0 +1,3 @@ +base: + '*': + - webserver diff --git a/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/salt/webserver.sls b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/salt/webserver.sls new file mode 100644 index 00000000..3585a425 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/conf-mgmt/salt/local-only/salt/webserver.sls @@ -0,0 +1,3 @@ +httpd: + pkg: + - installed diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/README.md b/sample/bin/aws-codedeploy-samples/load-balancing/elb/README.md new file mode 100644 index 00000000..69938c4e --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/README.md @@ -0,0 +1,52 @@ +# ELB and ASG lifecycle event scripts + +Often when running a web service, you'll have your instances behind a load balancer. But when +deploying new code to these instances, you don't want the load balancer to continue sending customer +traffic to an instance while the deployment is in progress. Lifecycle event scripts give you the +ability to integrate your AWS CodeDeploy deployments with instances that are behind an Elastic Load +Balancer or in an Auto Scaling group. Simply set the name (or names) of the Elastic Load Balancer +your instances are a part of, set the scripts in the appropriate lifecycle events, and the scripts +will take care of deregistering the instance, waiting for connection draining, and re-registering +after the deployment finishes. + +## Requirements + +The register and deregister scripts have a couple of dependencies in order to properly interact with +Elastic Load Balancing and AutoScaling: + +1. The [AWS CLI](http://aws.amazon.com/cli/). In order to take advantage of +AutoScaling's Standby feature, the CLI must be at least version 1.3.25. If you +have Python and PIP already installed, the CLI can simply be installed with `pip +install awscli`. Otherwise, follow the [installation instructions](http://docs.aws.amazon.com/cli/latest/userguide/installing.html) +in the CLI's user guide. +1. An instance profile with a policy that allows, at minimum, the following actions: + +``` + elasticloadbalancing:Describe* + elasticloadbalancing:DeregisterInstancesFromLoadBalancer + elasticloadbalancing:RegisterInstancesWithLoadBalancer + autoscaling:Describe* + autoscaling:EnterStandby + autoscaling:ExitStandby + autoscaling:UpdateAutoScalingGroup +``` + +Note: the AWS CodeDeploy Agent requires that an instance profile be attached to all instances that +are to participate in AWS CodeDeploy deployments. For more information on creating an instance +profile for AWS CodeDeploy, see the [Create an IAM Instance Profile for Your Amazon EC2 Instances]() +topic in the documentation. +1. All instances are assumed to already have the AWS CodeDeploy Agent installed. + +## Installing the Scripts + +To use these scripts in your own application: + +1. Install the AWS CLI on all your instances. +1. Update the policies on the EC2 instance profile to allow the above actions. +1. Copy the `.sh` files in this directory into your application source. +1. Edit your application's `appspec.yml` to run `deregister_from_elb.sh` on the ApplicationStop event, +and `register_with_elb.sh` on the ApplicationStart event. +1. Edit `common_functions.sh` to set `ELB_LIST` to contain the name(s) of the Elastic Load +Balancer(s) your deployment group is a part of. Make sure the entries in ELB_LIST are separated by space. +1. Deploy! + diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/appspec.yml b/sample/bin/aws-codedeploy-samples/load-balancing/elb/appspec.yml new file mode 100644 index 00000000..6d6c58aa --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/appspec.yml @@ -0,0 +1,12 @@ +version: 0.0 +os: linux +files: + - source: / + destination: /tmp/elb-test +hooks: + ApplicationStop: + - location: deregister_from_elb.sh + - location: stop_httpd.sh + ApplicationStart: + - location: start_httpd.sh + - location: register_with_elb.sh diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/common_functions.sh b/sample/bin/aws-codedeploy-samples/load-balancing/elb/common_functions.sh new file mode 100644 index 00000000..6442d8a9 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/common_functions.sh @@ -0,0 +1,499 @@ +#!/bin/bash +# +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +# ELB_LIST defines which Elastic Load Balancers this instance should be part of. +# The elements in ELB_LIST should be seperated by space. +ELB_LIST="" + +# Under normal circumstances, you shouldn't need to change anything below this line. +# ----------------------------------------------------------------------------- + +export PATH="$PATH:/usr/bin:/usr/local/bin" + +# If true, all messages will be printed. If false, only fatal errors are printed. +DEBUG=true + +# Number of times to check for a resouce to be in the desired state. +WAITER_ATTEMPTS=60 + +# Number of seconds to wait between attempts for resource to be in a state. +WAITER_INTERVAL=1 + +# AutoScaling Standby features at minimum require this version to work. +MIN_CLI_VERSION='1.3.25' + +# Usage: get_instance_region +# +# Writes to STDOUT the AWS region as known by the local instance. +get_instance_region() { + if [ -z "$AWS_REGION" ]; then + AWS_REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document \ + | grep -i region \ + | awk -F\" '{print $4}') + fi + + echo $AWS_REGION +} + +AWS_CLI="aws --region $(get_instance_region)" + +# Usage: autoscaling_group_name +# +# Prints to STDOUT the name of the AutoScaling group this instance is a part of and returns 0. If +# it is not part of any groups, then it prints nothing. On error calling autoscaling, returns +# non-zero. +autoscaling_group_name() { + local instance_id=$1 + + # This operates under the assumption that instances are only ever part of a single ASG. + local autoscaling_name=$($AWS_CLI autoscaling describe-auto-scaling-instances \ + --instance-ids $instance_id \ + --output text \ + --query AutoScalingInstances[0].AutoScalingGroupName) + + if [ $? != 0 ]; then + return 1 + elif [ "$autoscaling_name" == "None" ]; then + echo "" + else + echo $autoscaling_name + fi + + return 0 +} + +# Usage: autoscaling_enter_standby +# +# Move into the Standby state in AutoScaling group . Doing so will +# pull it out of any Elastic Load Balancer that might be in front of the group. +# +# Returns 0 if the instance was successfully moved to standby. Non-zero otherwise. +autoscaling_enter_standby() { + local instance_id=$1 + local asg_name=$2 + + msg "Checking if this instance has already been moved in the Standby state" + local instance_state=$(get_instance_state_asg $instance_id) + if [ $? != 0 ]; then + msg "Unable to get this instance's lifecycle state." + return 1 + fi + + if [ "$instance_state" == "Standby" ]; then + msg "Instance is already in Standby; nothing to do." + return 0 + fi + + if [ "$instance_state" == "Pending:Wait" ]; then + msg "Instance is Pending:Wait; nothing to do." + return 0 + fi + + msg "Checking to see if ASG $asg_name will let us decrease desired capacity" + local min_desired=$($AWS_CLI autoscaling describe-auto-scaling-groups \ + --auto-scaling-group-name $asg_name \ + --query 'AutoScalingGroups[0].[MinSize, DesiredCapacity]' \ + --output text) + + local min_cap=$(echo $min_desired | awk '{print $1}') + local desired_cap=$(echo $min_desired | awk '{print $2}') + + if [ -z "$min_cap" -o -z "$desired_cap" ]; then + msg "Unable to determine minimum and desired capacity for ASG $asg_name." + msg "Attempting to put this instance into standby regardless." + elif [ $min_cap == $desired_cap -a $min_cap -gt 0 ]; then + local new_min=$(($min_cap - 1)) + msg "Decrementing ASG $asg_name's minimum size to $new_min" + msg $($AWS_CLI autoscaling update-auto-scaling-group \ + --auto-scaling-group-name $asg_name \ + --min-size $new_min) + if [ $? != 0 ]; then + msg "Failed to reduce ASG $asg_name's minimum size to $new_min. Cannot put this instance into Standby." + return 1 + else + msg "ASG $asg_name's minimum size has been decremented, creating flag file /tmp/asgmindecremented" + # Create a "flag" file to denote that the ASG min has been decremented + touch /tmp/asgmindecremented + fi + fi + + msg "Putting instance $instance_id into Standby" + $AWS_CLI autoscaling enter-standby \ + --instance-ids $instance_id \ + --auto-scaling-group-name $asg_name \ + --should-decrement-desired-capacity + if [ $? != 0 ]; then + msg "Failed to put instance $instance_id into Standby for ASG $asg_name." + return 1 + fi + + msg "Waiting for move to Standby to finish" + wait_for_state "autoscaling" $instance_id "Standby" + if [ $? != 0 ]; then + local wait_timeout=$(($WAITER_INTERVAL * $WAITER_ATTEMPTS)) + msg "Instance $instance_id did not make it to standby after $wait_timeout seconds" + return 1 + fi + + return 0 +} + +# Usage: autoscaling_exit_standby +# +# Attempts to move instance out of Standby and into InService. Returns 0 if +# successful. +autoscaling_exit_standby() { + local instance_id=$1 + local asg_name=$2 + + msg "Checking if this instance has already been moved out of Standby state" + local instance_state=$(get_instance_state_asg $instance_id) + if [ $? != 0 ]; then + msg "Unable to get this instance's lifecycle state." + return 1 + fi + + if [ "$instance_state" == "InService" ]; then + msg "Instance is already InService; nothing to do." + return 0 + fi + + if [ "$instance_state" == "Pending:Wait" ]; then + msg "Instance is Pending:Wait; nothing to do." + return 0 + fi + + msg "Moving instance $instance_id out of Standby" + $AWS_CLI autoscaling exit-standby \ + --instance-ids $instance_id \ + --auto-scaling-group-name $asg_name + if [ $? != 0 ]; then + msg "Failed to put instance $instance_id back into InService for ASG $asg_name." + return 1 + fi + + msg "Waiting for exit-standby to finish" + wait_for_state "autoscaling" $instance_id "InService" + if [ $? != 0 ]; then + local wait_timeout=$(($WAITER_INTERVAL * $WAITER_ATTEMPTS)) + msg "Instance $instance_id did not make it to InService after $wait_timeout seconds" + return 1 + fi + + if [ -a /tmp/asgmindecremented ]; then + local min_desired=$($AWS_CLI autoscaling describe-auto-scaling-groups \ + --auto-scaling-group-name $asg_name \ + --query 'AutoScalingGroups[0].[MinSize, DesiredCapacity]' \ + --output text) + + local min_cap=$(echo $min_desired | awk '{print $1}') + + local new_min=$(($min_cap + 1)) + msg "Incrementing ASG $asg_name's minimum size to $new_min" + msg $($AWS_CLI autoscaling update-auto-scaling-group \ + --auto-scaling-group-name $asg_name \ + --min-size $new_min) + if [ $? != 0 ]; then + msg "Failed to increase ASG $asg_name's minimum size to $new_min." + return 1 + else + msg "Successfully incremented ASG $asg_name's minimum size" + msg "Removing /tmp/asgmindecremented flag file" + rm -f /tmp/asgmindecremented + fi + else + msg "Auto scaling group was not decremented previously, not incrementing min value" + fi + + return 0 +} + +# Usage: get_instance_state_asg +# +# Gets the state of the given as known by the AutoScaling group it's a part of. +# Health is printed to STDOUT and the function returns 0. Otherwise, no output and return is +# non-zero. +get_instance_state_asg() { + local instance_id=$1 + + local state=$($AWS_CLI autoscaling describe-auto-scaling-instances \ + --instance-ids $instance_id \ + --query "AutoScalingInstances[?InstanceId == \`$instance_id\`].LifecycleState | [0]" \ + --output text) + if [ $? != 0 ]; then + return 1 + else + echo $state + return 0 + fi +} + +reset_waiter_timeout() { + local elb=$1 + + local health_check_values=$($AWS_CLI elb describe-load-balancers \ + --load-balancer-name $elb \ + --query 'LoadBalancerDescriptions[0].HealthCheck.[HealthyThreshold, Interval]' \ + --output text) + + WAITER_ATTEMPTS=$(echo $health_check_values | awk '{print $1}') + WAITER_INTERVAL=$(echo $health_check_values | awk '{print $2}') +} + +# Usage: wait_for_state [ELB name] +# +# Waits for the state of to be in as seen by . Returns 0 if +# it successfully made it to that state; non-zero if not. By default, checks $WAITER_ATTEMPTS +# times, every $WAITER_INTERVAL seconds. If giving an [ELB name] to check under, these are reset +# to that ELB's HealthThreshold and Interval values. +wait_for_state() { + local service=$1 + local instance_id=$2 + local state_name=$3 + local elb=$4 + + local instance_state_cmd + if [ "$service" == "elb" ]; then + instance_state_cmd="get_instance_health_elb $instance_id $elb" + reset_waiter_timeout $elb + elif [ "$service" == "autoscaling" ]; then + instance_state_cmd="get_instance_state_asg $instance_id" + else + msg "Cannot wait for instance state; unknown service type, '$service'" + return 1 + fi + + msg "Checking $WAITER_ATTEMPTS times, every $WAITER_INTERVAL seconds, for instance $instance_id to be in state $state_name" + + local instance_state=$($instance_state_cmd) + local count=1 + + msg "Instance is currently in state: $instance_state" + while [ "$instance_state" != "$state_name" ]; do + if [ $count -ge $WAITER_ATTEMPTS ]; then + local timeout=$(($WAITER_ATTEMPTS * $WAITER_INTERVAL)) + msg "Instance failed to reach state, $state_name within $timeout seconds" + return 1 + fi + + sleep $WAITER_INTERVAL + + instance_state=$($instance_state_cmd) + count=$(($count + 1)) + msg "Instance is currently in state: $instance_state" + done + + return 0 +} + +# Usage: get_instance_health_elb +# +# Gets the health of the given as known by . If it's a valid health +# status (one of InService|OutOfService|Unknown), then the health is printed to STDOUT and the +# function returns 0. Otherwise, no output and return is non-zero. +get_instance_health_elb() { + local instance_id=$1 + local elb_name=$2 + + msg "Checking status of instance '$instance_id' in load balancer '$elb_name'" + + # If describe-instance-health for this instance returns an error, then it's not part of + # this ELB. But, if the call was successful let's still double check that the status is + # valid. + local instance_status=$($AWS_CLI elb describe-instance-health \ + --load-balancer-name $elb_name \ + --instances $instance_id \ + --query 'InstanceStates[].State' \ + --output text 2>/dev/null) + + if [ $? == 0 ]; then + case "$instance_status" in + InService|OutOfService|Unknown) + echo -n $instance_status + return 0 + ;; + *) + msg "Instance '$instance_id' not part of ELB '$elb_name'" + return 1 + esac + fi +} + +# Usage: validate_elb +# +# Validates that the Elastic Load Balancer with name exists, is describable, and +# contains as one of its instances. +# +# If any of these checks are false, the function returns non-zero. +validate_elb() { + local instance_id=$1 + local elb_name=$2 + + # Get the list of active instances for this LB. + local elb_instances=$($AWS_CLI elb describe-load-balancers \ + --load-balancer-name $elb_name \ + --query 'LoadBalancerDescriptions[*].Instances[*].InstanceId' \ + --output text) + if [ $? != 0 ]; then + msg "Couldn't describe ELB instance named '$elb_name'" + return 1 + fi + + msg "Checking health of '$instance_id' as known by ELB '$elb_name'" + local instance_health=$(get_instance_health_elb $instance_id $elb_name) + if [ $? != 0 ]; then + return 1 + fi + + return 0 +} + +# Usage: get_elb_list +# +# Finds all the ELBs that this instance is registered to. After execution, the variable +# "ELB_LIST" will contain the list of load balancers for the given instance. +# +# If the given instance ID isn't found registered to any ELBs, the function returns non-zero +get_elb_list() { + local instance_id=$1 + + local asg_name=$($AWS_CLI autoscaling describe-auto-scaling-instances \ + --instance-ids $instance_id \ + --query AutoScalingInstances[*].AutoScalingGroupName \ + --output text | sed -e $'s/\t/ /g') + local elb_list="" + + if [ -z "$asg_name" ]; then + msg "Instance is not part of an ASG. Looking up from ELB." + local all_balancers=$($AWS_CLI elb describe-load-balancers \ + --query LoadBalancerDescriptions[*].LoadBalancerName \ + --output text | sed -e $'s/\t/ /g') + for elb in $all_balancers; do + local instance_health + instance_health=$(get_instance_health_elb $instance_id $elb) + if [ $? == 0 ]; then + elb_list="$elb_list $elb" + fi + done + else + elb_list=$($AWS_CLI autoscaling describe-auto-scaling-groups \ + --auto-scaling-group-names $asg_name \ + --query AutoScalingGroups[*].LoadBalancerNames \ + --output text | sed -e $'s/\t/ /g') + fi + + if [ -z "$elb_list" ]; then + return 1 + else + msg "Got load balancer list of: $elb_list" + ELB_LIST=$elb_list + return 0 + fi +} + +# Usage: deregister_instance +# +# Deregisters from . +deregister_instance() { + local instance_id=$1 + local elb_name=$2 + + $AWS_CLI elb deregister-instances-from-load-balancer \ + --load-balancer-name $elb_name \ + --instances $instance_id 1> /dev/null + + return $? +} + +# Usage: register_instance +# +# Registers to . +register_instance() { + local instance_id=$1 + local elb_name=$2 + + $AWS_CLI elb register-instances-with-load-balancer \ + --load-balancer-name $elb_name \ + --instances $instance_id 1> /dev/null + + return $? +} + +# Usage: check_cli_version [version-to-check] [desired version] +# +# Without any arguments, checks that the installed version of the AWS CLI is at least at version +# $MIN_CLI_VERSION. Returns non-zero if the version is not high enough. +check_cli_version() { + if [ -z $1 ]; then + version=$($AWS_CLI --version 2>&1 | cut -f1 -d' ' | cut -f2 -d/) + else + version=$1 + fi + + if [ -z "$2" ]; then + min_version=$MIN_CLI_VERSION + else + min_version=$2 + fi + + x=$(echo $version | cut -f1 -d.) + y=$(echo $version | cut -f2 -d.) + z=$(echo $version | cut -f3 -d.) + + min_x=$(echo $min_version | cut -f1 -d.) + min_y=$(echo $min_version | cut -f2 -d.) + min_z=$(echo $min_version | cut -f3 -d.) + + msg "Checking minimum required CLI version (${min_version}) against installed version ($version)" + + if [ $x -lt $min_x ]; then + return 1 + elif [ $y -lt $min_y ]; then + return 1 + elif [ $y -gt $min_y ]; then + return 0 + elif [ $z -ge $min_z ]; then + return 0 + else + return 1 + fi +} + +# Usage: msg +# +# Writes to STDERR only if $DEBUG is true, otherwise has no effect. +msg() { + local message=$1 + $DEBUG && echo $message 1>&2 +} + +# Usage: error_exit +# +# Writes to STDERR as a "fatal" and immediately exits the currently running script. +error_exit() { + local message=$1 + + echo "[FATAL] $message" 1>&2 + exit 1 +} + +# Usage: get_instance_id +# +# Writes to STDOUT the EC2 instance ID for the local instance. Returns non-zero if the local +# instance metadata URL is inaccessible. +get_instance_id() { + curl -s http://169.254.169.254/latest/meta-data/instance-id + return $? +} diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/deregister_from_elb.sh b/sample/bin/aws-codedeploy-samples/load-balancing/elb/deregister_from_elb.sh new file mode 100755 index 00000000..54063156 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/deregister_from_elb.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +. $(dirname $0)/common_functions.sh + +msg "Running AWS CLI with region: $(get_instance_region)" + +# get this instance's ID +INSTANCE_ID=$(get_instance_id) +if [ $? != 0 -o -z "$INSTANCE_ID" ]; then + error_exit "Unable to get this instance's ID; cannot continue." +fi + +# Get current time +msg "Started $(basename $0) at $(/bin/date "+%F %T")" +start_sec=$(/bin/date +%s.%N) + +msg "Checking if instance $INSTANCE_ID is part of an AutoScaling group" +asg=$(autoscaling_group_name $INSTANCE_ID) +if [ $? == 0 -a -n "$asg" ]; then + msg "Found AutoScaling group for instance $INSTANCE_ID: $asg" + + msg "Checking that installed CLI version is at least at version required for AutoScaling Standby" + check_cli_version + if [ $? != 0 ]; then + error_exit "CLI must be at least version ${MIN_CLI_X}.${MIN_CLI_Y}.${MIN_CLI_Z} to work with AutoScaling Standby" + fi + + msg "Attempting to put instance into Standby" + autoscaling_enter_standby $INSTANCE_ID $asg + if [ $? != 0 ]; then + error_exit "Failed to move instance into standby" + else + msg "Instance is in standby" + exit 0 + fi +fi + +msg "Instance is not part of an ASG, continuing..." + +msg "Checking that user set at least one load balancer" +if test -z "$ELB_LIST"; then + error_exit "Must have at least one load balancer to deregister from" +fi + +# Loop through all LBs the user set, and attempt to deregister this instance from them. +for elb in $ELB_LIST; do + msg "Checking validity of load balancer named '$elb'" + validate_elb $INSTANCE_ID $elb + if [ $? != 0 ]; then + msg "Error validating $elb; cannot continue with this LB" + continue + fi + + msg "Deregistering $INSTANCE_ID from $elb" + deregister_instance $INSTANCE_ID $elb + + if [ $? != 0 ]; then + error_exit "Failed to deregister instance $INSTANCE_ID from ELB $elb" + fi +done + +# Wait for all Deregistrations to finish +msg "Waiting for instance to de-register from its load balancers" +for elb in $ELB_LIST; do + wait_for_state "elb" $INSTANCE_ID "OutOfService" $elb + if [ $? != 0 ]; then + error_exit "Failed waiting for $INSTANCE_ID to leave $elb" + fi +done + +msg "Finished $(basename $0) at $(/bin/date "+%F %T")" + +end_sec=$(/bin/date +%s.%N) +elapsed_seconds=$(echo "$end_sec - $start_sec" | /usr/bin/bc) + +msg "Elapsed time: $elapsed_seconds" diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/register_with_elb.sh b/sample/bin/aws-codedeploy-samples/load-balancing/elb/register_with_elb.sh new file mode 100755 index 00000000..cfca1556 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/register_with_elb.sh @@ -0,0 +1,89 @@ +#!/bin/bash +# +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +. $(dirname $0)/common_functions.sh + +msg "Running AWS CLI with region: $(get_instance_region)" + +# get this instance's ID +INSTANCE_ID=$(get_instance_id) +if [ $? != 0 -o -z "$INSTANCE_ID" ]; then + error_exit "Unable to get this instance's ID; cannot continue." +fi + +# Get current time +msg "Started $(basename $0) at $(/bin/date "+%F %T")" +start_sec=$(/bin/date +%s.%N) + +msg "Checking if instance $INSTANCE_ID is part of an AutoScaling group" +asg=$(autoscaling_group_name $INSTANCE_ID) +if [ $? == 0 -a -n "$asg" ]; then + msg "Found AutoScaling group for instance $INSTANCE_ID: $asg" + + msg "Checking that installed CLI version is at least at version required for AutoScaling Standby" + check_cli_version + if [ $? != 0 ]; then + error_exit "CLI must be at least version ${MIN_CLI_X}.${MIN_CLI_Y}.${MIN_CLI_Z} to work with AutoScaling Standby" + fi + + msg "Attempting to move instance out of Standby" + autoscaling_exit_standby $INSTANCE_ID $asg + if [ $? != 0 ]; then + error_exit "Failed to move instance out of standby" + else + msg "Instance is no longer in Standby" + exit 0 + fi +fi + +msg "Instance is not part of an ASG, continuing..." + +msg "Checking that user set at least one load balancer" +if test -z "$ELB_LIST"; then + error_exit "Must have at least one load balancer to register to" +fi + +# Loop through all LBs the user set, and attempt to register this instance to them. +for elb in $ELB_LIST; do + msg "Checking validity of load balancer named '$elb'" + validate_elb $INSTANCE_ID $elb + if [ $? != 0 ]; then + msg "Error validating $elb; cannot continue with this LB" + continue + fi + + msg "Registering $INSTANCE_ID to $elb" + register_instance $INSTANCE_ID $elb + + if [ $? != 0 ]; then + error_exit "Failed to register instance $INSTANCE_ID from ELB $elb" + fi +done + +# Wait for all Registrations to finish +msg "Waiting for instance to register to its load balancers" +for elb in $ELB_LIST; do + wait_for_state "elb" $INSTANCE_ID "InService" $elb + if [ $? != 0 ]; then + error_exit "Failed waiting for $INSTANCE_ID to return to $elb" + fi +done + +msg "Finished $(basename $0) at $(/bin/date "+%F %T")" + +end_sec=$(/bin/date +%s.%N) +elapsed_seconds=$(echo "$end_sec - $start_sec" | /usr/bin/bc) + +msg "Elapsed time: $elapsed_seconds" diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/start_httpd.sh b/sample/bin/aws-codedeploy-samples/load-balancing/elb/start_httpd.sh new file mode 100755 index 00000000..20177145 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/start_httpd.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Here is where you'd want to start your http daemon. For example: +#service httpd start +#exit $? + +# In this case, since it's just a placeholder, we don't need to do anything. +exit 0 diff --git a/sample/bin/aws-codedeploy-samples/load-balancing/elb/stop_httpd.sh b/sample/bin/aws-codedeploy-samples/load-balancing/elb/stop_httpd.sh new file mode 100755 index 00000000..814d8fea --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/load-balancing/elb/stop_httpd.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Here is where you'd want to stop your http daemon. For example: +#service httpd stop +#exit $? + +# In this case, since it's just a placeholder, we don't need to do anything. +exit 0 diff --git a/sample/bin/aws-codedeploy-samples/version-control/git/README.md b/sample/bin/aws-codedeploy-samples/version-control/git/README.md new file mode 100755 index 00000000..cdbcf659 --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/version-control/git/README.md @@ -0,0 +1,25 @@ +Git Hooks +========= + +The scripts here provide basic functionality to hook AWS CodeDeploy into git +events. The primary script is provided as a `pre-push` hook, which executes the +`aws deploy push` command for the local repository before git finishes pushing +to the remote. It then starts a new deployment of the revision. Both the AWS +CodeDeploy Application and Deployment Group must already exist. + + !!! CAUTION !!! Because this does an S3 upload on every push, you may incur S3 transfer charges. + + --Note, you need to make this script executable ( chmod +x pre-push ) after installing it in ./.git/hooks/pre-push + +No changes to the script itself should be required. Instead, it pulls the +necessary information from git config. The required keys are +`aws-codedeploy.application-name`, `aws-codedeploy.s3bucket`, and +`aws-codedeploy.deployment-group`. They can be set with the following commands +(replace values with your own): + + git config aws-codedeploy.application-name MyApplication + git config aws-codedeploy.s3bucket MyS3Bucket + git config aws-codedeploy.deployment-group MyDeploymentGroup + +The deployment created with this script will use the default deployment +configuration for the configured deployment group. diff --git a/sample/bin/aws-codedeploy-samples/version-control/git/pre-push b/sample/bin/aws-codedeploy-samples/version-control/git/pre-push new file mode 100755 index 00000000..93342c1b --- /dev/null +++ b/sample/bin/aws-codedeploy-samples/version-control/git/pre-push @@ -0,0 +1,45 @@ +#!/bin/bash + +# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +AWS="/usr/local/bin/aws" + +APPLICATION_NAME=$(git config --get aws-codedeploy.application-name) +DEPLOYMENT_GROUP=$(git config --get aws-codedeploy.deployment-group) +BUCKET_NAME=$(git config --get aws-codedeploy.s3bucket) +BUNDLE_NAME=$(echo $(basename `pwd`).zip) + +# `aws deploy push` will overwrite the bundle in S3. If you'd rather ensure that you have a unique +# object for each push, you could, for example, append the commit hash: +#BUNDLE_NAME=$(echo $(basename `pwd`)-$(git log -n 1 --format=%H).zip) + +# Call `aws deploy push` to create a new revision of the current repo +echo "Pushing $BUNDLE_NAME to s3://${BUCKET_NAME} and registering with application '${APPLICATION_NAME}'" 1>&2 +$AWS deploy push \ + --application-name ${APPLICATION_NAME} \ + --s3-location s3://${BUCKET_NAME}/${BUNDLE_NAME} \ + --ignore-hidden-files \ + --source . + +revision_json="{\"revisionType\":\"S3\",\"s3Location\":{\"bucket\":\"${BUCKET_NAME}\",\"bundleType\":\"zip\",\"key\":\"${BUNDLE_NAME}\"}}" + +if [ $? != 0 ]; then + echo "Push to codedeploy failed; skipping create-deployment" 1>&2 +else + echo "Deploying s3://${BUCKET_NAME}/${BUNDLE_NAME} to application ${APPLICATION_NAME} and deployment group ${DEPLOYMENT_GROUP}" 1>&2 + $AWS deploy create-deployment \ + --application-name ${APPLICATION_NAME} \ + --deployment-group-name ${DEPLOYMENT_GROUP} \ + --revision $revision_json +fi diff --git a/sample/bin/build.sh b/sample/bin/build.sh new file mode 100755 index 00000000..88a8f7f3 --- /dev/null +++ b/sample/bin/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +CWD=$(pwd) +PROJECT_DIR=$(dirname $0)/.. +BUILD_DIR=$PROJECT_DIR/build/$(date +"%s") + +mkdir $BUILD_DIR +cp $PROJECT_DIR/appspec.yml $BUILD_DIR +cp -r $PROJECT_DIR/bin $BUILD_DIR +cp -r $PROJECT_DIR/docroot $BUILD_DIR +rm -f $BUILD_DIR/docroot/index.php.dist + +cd $BUILD_DIR +tar -zcvf output.tar.gz ./ +cd $CWD + +mv $BUILD_DIR/output.tar.gz $PROJECT_DIR +rm -rf $BUILD_DIR diff --git a/sample/bin/clean.sh b/sample/bin/clean.sh new file mode 100755 index 00000000..78f526d3 --- /dev/null +++ b/sample/bin/clean.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rm -rf /usr/share/nginx/docroot diff --git a/sample/bin/environment.dist b/sample/bin/environment.dist new file mode 100755 index 00000000..e5c34f17 --- /dev/null +++ b/sample/bin/environment.dist @@ -0,0 +1,19 @@ +#!/usr/bin/env ruby + +require 'moonshot' + +# Set up Moonshot tooling for our environment. +class MoonshotSampleApp < Moonshot::CLI + self.application_name = 'moonshot-sample-app' + self.artifact_repository = S3Bucket.new('{{bucket}}') + self.build_mechanism = Script.new('bin/build.sh') + self.deployment_mechanism = CodeDeploy.new(asg: 'AutoScalingGroup') +end + +begin + MoonshotSampleApp.start +rescue => e + warn "Uncaught exception: #{e.class}: #{e.message}" + warn "at: #{e.backtrace.first}" + exit(1) +end diff --git a/sample/bin/setup.sh b/sample/bin/setup.sh new file mode 100755 index 00000000..d9444967 --- /dev/null +++ b/sample/bin/setup.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash -e + +if [ -z "$1" ]; then + >&2 echo "Usage: $0 [S3-BUCKET]" + exit 1 +fi + +# The project's root directory. +PROJECT_DIR=$(dirname $0)/.. + +# Name of the user's default environment. +STACK_NAME=moonshot-sample-app-dev-$(echo $USER | sed 's/[^a-zA-Z0-9_]*//g') + +# Creates the provisioning script and parameters file. +cp $PROJECT_DIR/bin/environment.dist $PROJECT_DIR/bin/environment +cp $PROJECT_DIR/cloud_formation/parameters/moonshot-sample-app.yml.dist $PROJECT_DIR/cloud_formation/parameters/$STACK_NAME.yml + +# Changes the S3 bucket in the provisioning script and parameters file. +sed -i '' "s#{{bucket}}#$1#" "$PROJECT_DIR/bin/environment" +sed -i '' "s#{{bucket}}#$1#" "$PROJECT_DIR/cloud_formation/parameters/$STACK_NAME.yml" + +# Create an index.php file, which is the "application". +cp $PROJECT_DIR/docroot/index.php.dist $PROJECT_DIR/docroot/index.php diff --git a/sample/bin/start.sh b/sample/bin/start.sh new file mode 100755 index 00000000..4f07f4fd --- /dev/null +++ b/sample/bin/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +service nginx start diff --git a/sample/bin/stop.sh b/sample/bin/stop.sh new file mode 100755 index 00000000..eac2e66c --- /dev/null +++ b/sample/bin/stop.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +service nginx stop diff --git a/sample/build/.placeholder b/sample/build/.placeholder new file mode 100644 index 00000000..e69de29b diff --git a/sample/cloud_formation/moonshot-sample-app.json b/sample/cloud_formation/moonshot-sample-app.json new file mode 100644 index 00000000..c06d9fdd --- /dev/null +++ b/sample/cloud_formation/moonshot-sample-app.json @@ -0,0 +1,287 @@ +{ + "AWSTemplateFormatVersion" : "2010-09-09", + "Parameters" : { + "ArtifactBucket" : { + "Type" : "String", + "Description" : "The S3 bucket that contains the build artifacts that CodeDeploy will deploy." + }, + "AvailabilityZone1" : { + "Type" : "AWS::EC2::AvailabilityZone::Name" + }, + "AvailabilityZone2" : { + "Type" : "AWS::EC2::AvailabilityZone::Name" + }, + "DesiredCapacity" : { + "Type" : "Number", + "Default" : "2", + "Description" : "The desired number of EC2 instances used for the application." + } + }, + "Outputs" : { + "URL" : { + "Description" : "The application's URL", + "Value" : { "Fn::Join" : [ "", [ "http://", { "Fn::GetAtt" : [ "LoadBalancer", "DNSName" ] } ] ] } + } + }, + "Mappings" : { + "RegionMap" : { + "ap-northeast-1" : { "AMI" : "" }, + "ap-southeast-1" : { "AMI" : "" }, + "ap-southeast-2" : { "AMI" : "" }, + "eu-central-1" : { "AMI" : "" }, + "eu-west-1" : { "AMI" : "" }, + "us-east-1" : { "AMI" : "" }, + "us-west-2" : { "AMI" : "" } + } + }, + "Resources" : { + + "VPC" : { + "Type" : "AWS::EC2::VPC", + "Properties" : { + "CidrBlock" : "10.176.0.0/16", + "Tags" : [ + { "Key" : "Name", "Value" : { "Ref" : "AWS::StackName" } } + ] + } + }, + + "InternetGateway" : { + "Type" : "AWS::EC2::InternetGateway" + }, + "VPCGatewayAttachment" : { + "Type" : "AWS::EC2::VPCGatewayAttachment", + "Properties" : { + "InternetGatewayId" : { "Ref" : "InternetGateway" }, + "VpcId" : { "Ref" : "VPC" } + } + }, + + "SubnetZone1" : { + "Type" : "AWS::EC2::Subnet", + "Properties" : { + "AvailabilityZone" : { "Ref" : "AvailabilityZone1" }, + "CidrBlock" : "10.176.10.0/26", + "VpcId" : { "Ref" : "VPC" } + } + }, + "SubnetZone2" : { + "Type" : "AWS::EC2::Subnet", + "Properties" : { + "AvailabilityZone" : { "Ref" : "AvailabilityZone2" }, + "CidrBlock" : "10.176.10.64/26", + "VpcId" : { "Ref" : "VPC" } + } + }, + "RouteTable" : { + "Type" : "AWS::EC2::RouteTable", + "Properties" : { + "VpcId" : { "Ref" : "VPC" } + } + }, + "Route" : { + "Type" : "AWS::EC2::Route", + "Properties" : { + "RouteTableId" : { "Ref" : "RouteTable" }, + "DestinationCidrBlock" : "0.0.0.0/0", + "GatewayId" : { "Ref" : "InternetGateway" } + } + }, + "SubnetRouteTableAssociationZone1" : { + "Type" : "AWS::EC2::SubnetRouteTableAssociation", + "Properties" : { + "RouteTableId" : { "Ref" : "RouteTable" }, + "SubnetId" : { "Ref" : "SubnetZone1" } + } + }, + "SubnetRouteTableAssociationZone2" : { + "Type" : "AWS::EC2::SubnetRouteTableAssociation", + "Properties" : { + "RouteTableId" : { "Ref" : "RouteTable" }, + "SubnetId" : { "Ref" : "SubnetZone2" } + } + }, + + "SecurityGroupElb" : { + "Type" : "AWS::EC2::SecurityGroup", + "Properties" : { + "VpcId" : { "Ref" : "VPC" }, + "GroupDescription" : "SecurityGroupElb", + "SecurityGroupIngress" : [ + { + "IpProtocol" : "tcp", + "FromPort" : "80", + "ToPort" : "80", + "CidrIp" : "0.0.0.0/0" + } + ] + } + }, + "LoadBalancer" : { + "Type" : "AWS::ElasticLoadBalancing::LoadBalancer", + "Properties" : { + "Listeners" : [ + { + "InstancePort" : "80", + "LoadBalancerPort" : "80", + "Protocol" : "HTTP" + } + ], + "CrossZone" : true, + "ConnectionDrainingPolicy" : { + "Enabled" : true, + "Timeout" : 15 + }, + "Scheme" : "internet-facing", + "SecurityGroups" : [ + { "Ref" : "SecurityGroupElb" } + ], + "HealthCheck" : { + "HealthyThreshold" : "3", + "Interval" : "15", + "Target" : "HTTP:80/index.php", + "Timeout" : "5", + "UnhealthyThreshold" : "3" + }, + "Subnets" : [ + { "Ref" : "SubnetZone1" }, + { "Ref" : "SubnetZone2" } + ] + } + }, + + "Role" : { + "Type" : "AWS::IAM::Role", + "Properties" : { + "Path" : "/", + "AssumeRolePolicyDocument" : { + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Principal" : { + "Service" : [ "ec2.amazonaws.com" ] + }, + "Action" : [ "sts:AssumeRole" ] + } + ] + }, + "Policies" : [ + { + "PolicyName" : "ArtifactAccess", + "PolicyDocument" : { + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "s3:GetObject" + ], + "Resource" : { "Fn::Join" : [ "", [ "arn:aws:s3:::", { "Ref" : "ArtifactBucket" }, "/*" ] ] } + } + ] + } + }, + { + "PolicyName" : "ElbAutoDrainAccess", + "PolicyDocument" : { + "Version" : "2012-10-17", + "Statement" : [ + { + "Effect" : "Allow", + "Action" : [ + "elasticloadbalancing:Describe*", + "elasticloadbalancing:DeregisterInstancesFromLoadBalancer", + "elasticloadbalancing:RegisterInstancesWithLoadBalancer", + "autoscaling:Describe*", + "autoscaling:EnterStandby", + "autoscaling:ExitStandby", + "autoscaling:UpdateAutoScalingGroup" + ], + "Resource" : "*" + } + ] + } + } + ] + } + }, + "InstanceProfile" : { + "Type" : "AWS::IAM::InstanceProfile", + "Properties" : { + "Path" : "/", + "Roles" : [ + { "Ref" : "Role" } + ] + } + }, + "SecurityGroup" : { + "Type" : "AWS::EC2::SecurityGroup", + "Properties" : { + "VpcId" : { "Ref" : "VPC" }, + "GroupDescription" : "SecurityGroup", + "SecurityGroupIngress" : [ + { + "IpProtocol" : "tcp", + "FromPort" : "80", + "ToPort" : "80", + "SourceSecurityGroupId" : { "Ref" : "SecurityGroupElb" } + } + ], + "SecurityGroupEgress" : [ + { + "IpProtocol" : "-1", + "FromPort" : "-1", + "ToPort" : "-1", + "CidrIp" : "0.0.0.0/0" + } + ] + } + }, + + "LaunchConfiguration" : { + "Type" : "AWS::AutoScaling::LaunchConfiguration", + "Properties" : { + "AssociatePublicIpAddress" : true, + "ImageId" : { "Fn::FindInMap" : [ "RegionMap", { "Ref" : "AWS::Region" }, "AMI" ] }, + "IamInstanceProfile" : { "Ref" : "InstanceProfile" }, + "InstanceType" : "t2.micro", + "SecurityGroups" : [ { "Ref" : "SecurityGroup" } ], + "UserData" : { + "Fn::Base64" : { "Fn::Join" : [ "", [ + "#!/bin/bash -v", + "\necho '<:3)~~ eek! a mouse!'" + ] ] } + } + } + }, + "AutoScalingGroup" : { + "Type" : "AWS::AutoScaling::AutoScalingGroup", + "DependsOn" : [ "VPCGatewayAttachment" ], + "Properties" : { + "AvailabilityZones" : [ + { "Ref" : "AvailabilityZone1" }, + { "Ref" : "AvailabilityZone2" } + ], + "DesiredCapacity" : { "Ref" : "DesiredCapacity" }, + "HealthCheckGracePeriod" : "600", + "HealthCheckType" : "ELB", + "LaunchConfigurationName" : { "Ref" : "LaunchConfiguration" }, + "LoadBalancerNames" : [ { "Ref" : "LoadBalancer" } ], + "MaxSize" : 5, + "MinSize" : { "Ref" : "DesiredCapacity" }, + "TerminationPolicies" : [ "OldestLaunchConfiguration" ], + "VPCZoneIdentifier" : [ + { "Ref" : "SubnetZone1" }, + { "Ref" : "SubnetZone2" } + ] + }, + "UpdatePolicy" : { + "AutoScalingRollingUpdate" : { + "MaxBatchSize" : "1", + "MinInstancesInService" : { "Ref" : "DesiredCapacity" } + } + } + } + } +} diff --git a/sample/cloud_formation/parameters/moonshot-sample-app.yml.dist b/sample/cloud_formation/parameters/moonshot-sample-app.yml.dist new file mode 100644 index 00000000..4febbf1d --- /dev/null +++ b/sample/cloud_formation/parameters/moonshot-sample-app.yml.dist @@ -0,0 +1,5 @@ +--- +ArtifactBucket: "{{bucket}}" +AvailabilityZone1: "us-east-1a" +AvailabilityZone2: "us-east-1b" +DesiredCapacity: "2" diff --git a/sample/docroot/index.php.dist b/sample/docroot/index.php.dist new file mode 100644 index 00000000..83f15493 --- /dev/null +++ b/sample/docroot/index.php.dist @@ -0,0 +1,3 @@ +