{"id":18510770,"url":"https://github.com/envato/ami-spec","last_synced_at":"2025-08-22T01:20:42.274Z","repository":{"id":45951003,"uuid":"45950742","full_name":"envato/ami-spec","owner":"envato","description":"Acceptance testing your AMIs","archived":false,"fork":false,"pushed_at":"2024-12-28T03:14:03.000Z","size":148,"stargazers_count":49,"open_issues_count":0,"forks_count":14,"subscribers_count":82,"default_branch":"master","last_synced_at":"2025-03-28T12:08:10.195Z","etag":null,"topics":["ami","aws-ec2","infrastructure-as-code","infrastructure-testing","serverspec"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/envato.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-11-11T00:55:25.000Z","updated_at":"2024-12-28T03:14:08.000Z","dependencies_parsed_at":"2024-02-17T01:27:57.039Z","dependency_job_id":"748fc6dd-2432-425b-898e-969d314f8227","html_url":"https://github.com/envato/ami-spec","commit_stats":{"total_commits":158,"total_committers":13,"mean_commits":"12.153846153846153","dds":0.4113924050632911,"last_synced_commit":"d25cab21cf73083c28668fa3be8fa9e5a5f9e181"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envato%2Fami-spec","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envato%2Fami-spec/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envato%2Fami-spec/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/envato%2Fami-spec/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/envato","download_url":"https://codeload.github.com/envato/ami-spec/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247182367,"owners_count":20897380,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ami","aws-ec2","infrastructure-as-code","infrastructure-testing","serverspec"],"created_at":"2024-11-06T15:24:59.584Z","updated_at":"2025-04-04T13:11:52.967Z","avatar_url":"https://github.com/envato.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AmiSpec\n\n[![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/ami-spec/blob/master/LICENSE.txt)\n[![Gem Version](https://badge.fury.io/rb/ami_spec.svg)](https://badge.fury.io/rb/ami_spec)\n[![Build Status](https://github.com/envato/ami-spec/workflows/tests/badge.svg?branch=master)](https://github.com/envato/ami-spec/actions?query=branch%3Amaster+workflow%3Atests)\n\nAcceptance testing your AMIs.\n\nAmiSpec is a RubyGem used to launch an Amazon Machine Image (AMI) and run ServerSpecs against it. It wraps around the AWS API and ServerSpec to spin up, test and tear down instances.\n\n## Project Goals\n\n1. To decouple the building of AMIs from testing them. Other approaches to this problem involve copying ServerSpec tests to an EC2 instance before it's converted to an AMI and running the tests there.\nThe problem with this approach is:\n\n- It does not test the instance in the state it will be in when it's actually in production.\n- It does makes it harder to replace the AMI builder software (i.e. [Packer](https://github.com/mitchellh/packer)).\n- The software required to test the AMI must exist in the AMI.\n\n2. To run tests as fast as possible; this approach is slightly slower than the alternative listed above (about 1-2 minutes), but should not be onerous.\n\n## Installation\n\nSystem-wide: gem install ami\\_spec\n\nWith bundler:\n\nAdd `gem 'ami_spec'` to your Gemfile.\nRun `bundle install`\n\n## CLI Usage\n\n```cli\n$ bundle exec ami_spec --help\nOptions:\n  -r, --role=\u003cs\u003e                            The role to test, this should map to a directory in the spec\n                                            folder\n  -a, --ami=\u003cs\u003e                             The ami ID to run tests against\n  -o, --role-ami-file=\u003cs\u003e                   A file containing comma separated roles and amis. i.e.\n                                            web_server,ami-id.\n  -s, --specs=\u003cs\u003e                           The directory to find ServerSpecs\n  -u, --subnet-id=\u003cs\u003e                       The subnet to start the instance in. If not provided a subnet\n                                            will be chosen from the default VPC\n  -k, --key-name=\u003cs\u003e                        The SSH key name to assign to instances. If not provided a\n                                            temporary key pair will be generated in AWS\n  -e, --key-file=\u003cs\u003e                        The SSH private key file associated to the key_name\n  -h, --ssh-user=\u003cs\u003e                        The user to ssh to the instance as\n  -w, --aws-region=\u003cs\u003e                      The AWS region, defaults to AWS_DEFAULT_REGION environment\n                                            variable\n  -i, --aws-instance-type=\u003cs\u003e               The ec2 instance type, defaults to t2.micro (default:\n                                            t2.micro)\n  -c, --aws-security-groups=\u003cs\u003e             Security groups IDs to associate to the launched instances. May be\n                                            specified multiple times. If not provided a temporary security\n                                            group will be generated in AWS\n  -n, --allow-any-temporary-security-group  The temporary security group will allow SSH connections \n                                            from any IP address (0.0.0.0/0), otherwise allow the subnet's block\n  -p, --aws-public-ip                       Launch instances with a public IP and use that IP for SSH\n  -q, --associate-public-ip                 Launch instances with a public IP and use the Private IP for SSH\n  -t, --ssh-retries=\u003ci\u003e                     The number of times we should try sshing to the ec2 instance\n                                            before giving up. Defaults to 30 (default: 30)\n  -g, --tags=\u003cs\u003e                            Additional tags to add to launched instances in the form of\n                                            comma separated key=value pairs. i.e. Name=AmiSpec (default: )\n  -d, --debug                               Don't terminate instances on exit\n  -b, --buildkite                           Output section separators for buildkite\n  -f, --wait-for-rc                         Wait for oldschool SystemV scripts to run before conducting\n                                            tests. Currently only supports Ubuntu with upstart\n  -l, --user-data-file=\u003cs\u003e                  File path for aws ec2 user data\n  -m, --iam-instance-profile-arn=\u003cs\u003e        IAM instance profile to use\n  --help                                    Show this message\n\n```\n\nAmiSpec will launch an EC2 instance from the given AMI (`--ami`), in a subnet (`--subnet-id`) with a key-pair (`--key-name`)\nand try to SSH to it (`--ssh-user` and `--key-file`).\nWhen the instances becomes reachable it will run all Specs inside the role spec directory (`--role` i.e. `my_project/spec/web_server`).\n\nAlternative to the `--ami` and `--role` variables, a file of comma separated roles and AMIs (`ROLE,AMI\\n`) can be supplied to `--role-ami-file`.\n\n## ServerSpec test layout\n\nAmiSpec expects the usual ServerSpec configuration layout as generated by \"serverspec-init\":\n\n    spec/\n    ├── webserver\n    │   └── webserver_spec.rb\n    └── spec_helper.rb\n\nThe \\*\\_spec.rb files under the role (e.g. webserver) contain the ServerSpec\ntests that you want to run. The spec_helper.rb file can be very simple:\n\n    require 'serverspec'\n\n    set :backend, :ssh\n\nNote that the backend *needs* to be :ssh or ami_spec might run the tests on\nyour local machine, not in EC2.\n\n## Example usage\n\nTo test a custom AMI using a pre-created security group that allows SSH from anywhere:\n\n```cli\nami_spec --role webserver\\\n --specs spec\\\n --aws-region us-east-1\\\n --ami ami-0123456789abcdef0\\\n --key-name default\\\n --key-file ~/.ssh/default.pem\\\n --ssh-user ubuntu\\\n --aws-public-ip\\\n --aws-security-groups sg-0123456789abcdef0\n```\n\n## Known caveats\n\n### RSpec conditions in examples\n\n[ServerSpecs advanced tips](http://serverspec.org/advanced_tips.html) provides a mechanism to conditionally apply tests based on server information.\n\n```ruby\ndescribe file('/usr/lib64'), :if =\u003e os[:arch] == 'x86_64' do\n  it { should be_directory }\nend\n```\n\nIf these are used in shared examples, say loaded via a rspec helper, this doesn't work with AmiSpec, because the evaluation of `os[:arch] == 'x86_64'` is done when the spec is loaded not at run time.\n\nWorking around this is tricky. We need to move the evaluation of `os[:arch]` to runtime not load time. Since RSpec example metadata can only be a bool, string or symbol we set a metadata key of `:os_arch` to the value we expect:\n\n```ruby\ndescribe file('/usr/lib64'), :os_arch =\u003e 'x86_64' do\n  it { should be_directory }\nend\n```\n\nWe then have to set an RSpec exclusion of examples where the architecture does not match the host under test's architecture. This can be done in the `spec_helper` with a lambda function that tests this:\n\n```ruby\nRSpec.configure do |c|\n  c.filter_run_excluding :os_arch =\u003e lambda { |arch| arch if os[:arch] != arch }\nend\n```\n\nWe are exluding any example with the metadata key :os_arch where the value does not match our architecture. Similar examples can be included for os family etc.\n\n## Development Status\n\nActive and ready for Production\n\n## Contributing\n\nFor bug fixes, documentation changes, and small features:\n1. Fork it ( https://github.com/envato/ami-spec/fork )\n2. Create your feature branch (git checkout -b my-new-feature)\n3. Commit your changes (git commit -am 'Add some feature')\n4. Push to the branch (git push origin my-new-feature)\n5. Create a new Pull Request\n\n## Running tests\n\nUse the following command to run non-integration tests:\n```\nbundle exec rake spec\n```\n\nIf you're working on the `WaitForRC` feature you can run it's integration tests by first bringing up the containers, then executing the integration tests:\n```\ndocker-compose -f spec/containers/docker-compose.yml up -d\nbundle exec rspec . --tag integration\ndocker-compose -f spec/containers/docker-compose.yml down\n```\n\n## Maintainers\n\nPatrick Robinson (@patrobinson)\n\n## License\n\nAmiSpec uses the MIT license. See [LICENSE.txt](./LICENSE.txt)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenvato%2Fami-spec","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fenvato%2Fami-spec","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fenvato%2Fami-spec/lists"}