ami_spec 1.4.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 87eebf7f488963ef90dec3e34678f6c75bd36eaf896467edaeb1651271ad8d8a
4
- data.tar.gz: 6425586e1732b2e78cb4099047f1ba6e5e99d08deb0dbd5aa4031daa24beb597
3
+ metadata.gz: a2a7a5034e02deb64f10fae91426c5771688bbae9c8e56764c6af2faa13ebe7c
4
+ data.tar.gz: b006c1eeeae2d04e63d077717fabf1aa0ebe29a8361f88c6958d92401866ff7f
5
5
  SHA512:
6
- metadata.gz: 93dd91143f960c39da8cfdf24522a9daf271d4aaf3a69db345d4621e40c9a10ca43158abe19a0e2268cca9bbc699267fe9c54b954275d5e7bccb404eb2e487f7
7
- data.tar.gz: 7e1a7762ed90bfd5266ab9a869fe1a78af16e065fe8d5c8ac0663376195e9029d7b23c8d8d7ab818cba7fdf5d2ee821eab723b8b176459880685851e4a055ebe
6
+ metadata.gz: c9e66fea702fe7f9edf9aa8679ec2c386e1c46079c9371b5cbae2964b25b9d84eb60ed65a54b34e54d123ab27e7bc2756dd4c9ee2fefffdd751187802d74976a
7
+ data.tar.gz: 1aaf85899a5269204bc817680c83c386e1b8132727712c49145cfa52627b72ea7301c8be6d08b28ae143daab2aa67aa0a61ec07854fb6b4a896c230b5843ccde
data/README.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # AmiSpec
2
2
 
3
+ [![License MIT](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://github.com/envato/ami-spec/blob/master/LICENSE.txt)
4
+ [![Gem Version](https://badge.fury.io/rb/ami_spec.svg)](https://badge.fury.io/rb/ami_spec)
5
+ [![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)
6
+
3
7
  Acceptance testing your AMIs.
4
8
 
5
9
  AmiSpec 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.
@@ -29,36 +33,40 @@ Run `bundle install`
29
33
  ```cli
30
34
  $ bundle exec ami_spec --help
31
35
  Options:
32
- -r, --role=<s> The role to test, this should map to a directory in the spec
33
- folder
34
- -a, --ami=<s> The ami ID to run tests against
35
- -o, --role-ami-file=<s> A file containing comma separated roles and amis. i.e.
36
- web_server,ami-id.
37
- -s, --specs=<s> The directory to find ServerSpecs
38
- -u, --subnet-id=<s> The subnet to start the instance in
39
- -k, --key-name=<s> The SSH key name to assign to instances. If not provided a
40
- temporary key pair will be generated in AWS
41
- -e, --key-file=<s> The SSH private key file associated to the key_name
42
- -h, --ssh-user=<s> The user to ssh to the instance as
43
- -w, --aws-region=<s> The AWS region, defaults to AWS_DEFAULT_REGION environment
44
- variable
45
- -i, --aws-instance-type=<s> The ec2 instance type, defaults to t2.micro (default:
46
- t2.micro)
47
- -c, --aws-security-groups=<s> Security groups to associate to the launched instances. May be
48
- specified multiple times. If not provided a temporary security
49
- group will be generated in AWS
50
- -p, --aws-public-ip Launch instances with a public IP
51
- -t, --ssh-retries=<i> The number of times we should try sshing to the ec2 instance
52
- before giving up. Defaults to 30 (default: 30)
53
- -g, --tags=<s> Additional tags to add to launched instances in the form of
54
- comma separated key=value pairs. i.e. Name=AmiSpec (default: )
55
- -d, --debug Don't terminate instances on exit
56
- -b, --buildkite Output section separators for buildkite
57
- -f, --wait-for-rc Wait for oldschool SystemV scripts to run before conducting
58
- tests. Currently only supports Ubuntu with upstart
59
- -l, --user-data-file=<s> File path for aws ec2 user data
60
- -m, --iam-instance-profile-arn=<s> IAM instance profile to use
61
- --help Show this message
36
+ -r, --role=<s> The role to test, this should map to a directory in the spec
37
+ folder
38
+ -a, --ami=<s> The ami ID to run tests against
39
+ -o, --role-ami-file=<s> A file containing comma separated roles and amis. i.e.
40
+ web_server,ami-id.
41
+ -s, --specs=<s> The directory to find ServerSpecs
42
+ -u, --subnet-id=<s> The subnet to start the instance in. If not provided a subnet
43
+ will be chosen from the default VPC
44
+ -k, --key-name=<s> The SSH key name to assign to instances. If not provided a
45
+ temporary key pair will be generated in AWS
46
+ -e, --key-file=<s> The SSH private key file associated to the key_name
47
+ -h, --ssh-user=<s> The user to ssh to the instance as
48
+ -w, --aws-region=<s> The AWS region, defaults to AWS_DEFAULT_REGION environment
49
+ variable
50
+ -i, --aws-instance-type=<s> The ec2 instance type, defaults to t2.micro (default:
51
+ t2.micro)
52
+ -c, --aws-security-groups=<s> Security groups IDs to associate to the launched instances. May be
53
+ specified multiple times. If not provided a temporary security
54
+ group will be generated in AWS
55
+ -n, --allow-any-temporary-security-group The temporary security group will allow SSH connections
56
+ from any IP address (0.0.0.0/0), otherwise allow the subnet's block
57
+ -p, --aws-public-ip Launch instances with a public IP and use that IP for SSH
58
+ -q, --associate-public-ip Launch instances with a public IP and use the Private IP for SSH
59
+ -t, --ssh-retries=<i> The number of times we should try sshing to the ec2 instance
60
+ before giving up. Defaults to 30 (default: 30)
61
+ -g, --tags=<s> Additional tags to add to launched instances in the form of
62
+ comma separated key=value pairs. i.e. Name=AmiSpec (default: )
63
+ -d, --debug Don't terminate instances on exit
64
+ -b, --buildkite Output section separators for buildkite
65
+ -f, --wait-for-rc Wait for oldschool SystemV scripts to run before conducting
66
+ tests. Currently only supports Ubuntu with upstart
67
+ -l, --user-data-file=<s> File path for aws ec2 user data
68
+ -m, --iam-instance-profile-arn=<s> IAM instance profile to use
69
+ --help Show this message
62
70
 
63
71
  ```
64
72
 
@@ -68,6 +76,41 @@ When the instances becomes reachable it will run all Specs inside the role spec
68
76
 
69
77
  Alternative to the `--ami` and `--role` variables, a file of comma separated roles and AMIs (`ROLE,AMI\n`) can be supplied to `--role-ami-file`.
70
78
 
79
+ ## ServerSpec test layout
80
+
81
+ AmiSpec expects the usual ServerSpec configuration layout as generated by "serverspec-init":
82
+
83
+ spec/
84
+ ├── webserver
85
+ │   └── webserver_spec.rb
86
+ └── spec_helper.rb
87
+
88
+ The \*\_spec.rb files under the role (e.g. webserver) contain the ServerSpec
89
+ tests that you want to run. The spec_helper.rb file can be very simple:
90
+
91
+ require 'serverspec'
92
+
93
+ set :backend, :ssh
94
+
95
+ Note that the backend *needs* to be :ssh or ami_spec might run the tests on
96
+ your local machine, not in EC2.
97
+
98
+ ## Example usage
99
+
100
+ To test a custom AMI using a pre-created security group that allows SSH from anywhere:
101
+
102
+ ```cli
103
+ ami_spec --role webserver\
104
+ --specs spec\
105
+ --aws-region us-east-1\
106
+ --ami ami-0123456789abcdef0\
107
+ --key-name default\
108
+ --key-file ~/.ssh/default.pem\
109
+ --ssh-user ubuntu\
110
+ --aws-public-ip\
111
+ --aws-security-groups sg-0123456789abcdef0
112
+ ```
113
+
71
114
  ## Known caveats
72
115
 
73
116
  ### RSpec conditions in examples
@@ -0,0 +1,10 @@
1
+ require 'aws-sdk-ec2'
2
+
3
+ module AmiSpec
4
+ class AwsDefaultVpc
5
+ def self.find_subnet(ec2: Aws::EC2::Resource.new)
6
+ default_vpc = ec2.vpcs(filters: [{name: 'isDefault', values: ['true']}]).first
7
+ default_vpc && default_vpc.subnets.first
8
+ end
9
+ end
10
+ end
@@ -19,6 +19,7 @@ module AmiSpec
19
19
  @key_name = options.fetch(:key_name)
20
20
  @instance_type = options.fetch(:aws_instance_type)
21
21
  @public_ip = options.fetch(:aws_public_ip)
22
+ @associate_public_ip = options.fetch(:associate_public_ip)
22
23
  @region = options.fetch(:aws_region)
23
24
  @security_group_ids = options.fetch(:aws_security_groups)
24
25
  @tags = ec2ify_tags(options.fetch(:tags))
@@ -61,7 +62,7 @@ module AmiSpec
61
62
  key_name: @key_name,
62
63
  network_interfaces: [{
63
64
  device_index: 0,
64
- associate_public_ip_address: @public_ip,
65
+ associate_public_ip_address: @public_ip || @associate_public_ip,
65
66
  subnet_id: @subnet_id,
66
67
  }]
67
68
  }
@@ -12,6 +12,7 @@ module AmiSpec
12
12
  property :aws_public_ip
13
13
  property :aws_region
14
14
  property :aws_security_groups
15
+ property :associate_public_ip
15
16
  property :tags
16
17
  property :user_data_file
17
18
  property :iam_instance_profile_arn
@@ -1,3 +1,3 @@
1
1
  module AmiSpec
2
- VERSION = '1.4.0'
2
+ VERSION = '1.8.0'
3
3
  end
@@ -0,0 +1,11 @@
1
+ require 'net/ssh'
2
+
3
+ module AmiSpec
4
+ class WaitForCloudInit
5
+ def self.wait(ip_address, user, key, port=22)
6
+ Net::SSH.start(ip_address, user, keys: [key], :verify_host_key => :never, port: port) do |ssh|
7
+ ssh.exec! '/usr/bin/cloud-init status --wait'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -9,7 +9,7 @@ module AmiSpec
9
9
  while retries < max_retries
10
10
  begin
11
11
  Net::SSH.start(ip_address, user, keys: [key], :verify_host_key => :never) { |ssh| ssh.exec 'echo boo!' }
12
- rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Timeout::Error => error
12
+ rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Timeout::Error, Net::SSH::Exception => error
13
13
  last_error = error
14
14
  sleep 1
15
15
  else
data/lib/ami_spec.rb CHANGED
@@ -1,11 +1,13 @@
1
1
  require 'ami_spec/aws_instance'
2
2
  require 'ami_spec/aws_instance_options'
3
+ require 'ami_spec/aws_default_vpc'
3
4
  require 'ami_spec/aws_key_pair'
4
5
  require 'ami_spec/aws_security_group'
5
6
  require 'ami_spec/server_spec'
6
7
  require 'ami_spec/server_spec_options'
7
8
  require 'ami_spec/wait_for_ssh'
8
9
  require 'ami_spec/wait_for_rc'
10
+ require 'ami_spec/wait_for_cloud_init'
9
11
  require 'optimist'
10
12
  require 'logger'
11
13
 
@@ -36,7 +38,9 @@ module AmiSpec
36
38
  # aws_instance_type::
37
39
  # AWS ec2 instance type
38
40
  # aws_public_ip::
39
- # Should the instances get a public IP address
41
+ # Should the instances get a public IP address and use that IP for SSH
42
+ # associate_public_ip::
43
+ # Launch instances with a public IP but don't use that IP for SSH
40
44
  # ssh_user::
41
45
  # The username to SSH to the AMI with.
42
46
  # ssh_retries::
@@ -54,6 +58,13 @@ module AmiSpec
54
58
 
55
59
  ec2 = Aws::EC2::Resource.new(options[:aws_region] ? {region: options[:aws_region]} : {})
56
60
 
61
+ if options[:subnet_id].nil?
62
+ default_vpc_subnet = AwsDefaultVpc.find_subnet(ec2: ec2)
63
+ raise 'No default VPC subnet found. Please specify a subnet id.' if default_vpc_subnet.nil?
64
+ options[:subnet_id] = default_vpc_subnet.id
65
+ logger.info("Using subnet #{options[:subnet_id]} from the default VPC")
66
+ end
67
+
57
68
  unless options[:key_name]
58
69
  key_pair = AwsKeyPair.create(ec2: ec2, logger: logger)
59
70
  options[:key_name] = key_pair.key_name
@@ -81,6 +92,7 @@ module AmiSpec
81
92
  ip_address = options[:aws_public_ip] ? instance.public_ip_address : instance.private_ip_address
82
93
  WaitForSSH.wait(ip_address, options[:ssh_user], options[:key_file], options[:ssh_retries])
83
94
  WaitForRC.wait(ip_address, options[:ssh_user], options[:key_file]) if options[:wait_for_rc]
95
+ WaitForCloudInit.wait(ip_address, options[:ssh_user], options[:key_file]) if options[:wait_for_cloud_init]
84
96
 
85
97
  server_spec_options = ServerSpecOptions.new(options.merge(instance: instance))
86
98
  results << ServerSpec.new(server_spec_options).run
@@ -94,7 +106,7 @@ module AmiSpec
94
106
  end
95
107
 
96
108
  def self.stop_instances(instances, debug)
97
- instances.each do |instance|
109
+ instances && instances.each do |instance|
98
110
  begin
99
111
  if debug
100
112
  puts "EC2 instance ##{instance.instance_id} has not been stopped due to debug mode."
@@ -111,30 +123,45 @@ module AmiSpec
111
123
 
112
124
  def self.invoke
113
125
  options = Optimist::options do
114
- opt :role, "The role to test, this should map to a directory in the spec folder", type: :string
115
- opt :ami, "The ami ID to run tests against", type: :string
116
- opt :role_ami_file, "A file containing comma separated roles and amis. i.e.\nweb_server,ami-id.",
117
- type: :string
118
- opt :specs, "The directory to find ServerSpecs", type: :string, required: true
119
- opt :subnet_id, "The subnet to start the instance in", type: :string, required: true
120
- opt :key_name, "The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS",
121
- type: :string
122
- opt :key_file, "The SSH private key file associated to the key_name", type: :string
123
- opt :ssh_user, "The user to ssh to the instance as", type: :string, required: true
124
- opt :aws_region, "The AWS region, defaults to AWS_DEFAULT_REGION environment variable", type: :string
125
- opt :aws_instance_type, "The ec2 instance type, defaults to t2.micro", type: :string, default: 't2.micro'
126
- opt :aws_security_groups, "Security groups to associate to the launched instances. May be specified multiple times. If not provided a temporary security group will be generated in AWS",
127
- type: :string, default: nil, multi: true
128
- opt :allow_any_temporary_security_group, "The temporary security group will allow SSH connections from any IP address (0.0.0.0/0)"
129
- opt :aws_public_ip, "Launch instances with a public IP"
130
- opt :ssh_retries, "The number of times we should try sshing to the ec2 instance before giving up. Defaults to 30",
131
- type: :int, default: 30
132
- opt :tags, "Additional tags to add to launched instances in the form of comma separated key=value pairs. i.e. Name=AmiSpec", type: :string, default: ""
133
- opt :debug, "Don't terminate instances on exit"
134
- opt :buildkite, "Output section separators for buildkite"
135
- opt :wait_for_rc, "Wait for oldschool SystemV scripts to run before conducting tests. Currently only supports Ubuntu with upstart"
136
- opt :user_data_file, "File path for aws ec2 user data", type: :string, default: nil
137
- opt :iam_instance_profile_arn, "IAM instance profile to use", type: :string
126
+ opt :role,
127
+ 'The role to test, this should map to a directory in the spec folder',
128
+ type: :string, short: :r
129
+ opt :ami, 'The ami ID to run tests against', type: :string, short: :a
130
+ opt :role_ami_file,
131
+ 'A file containing comma separated roles and amis. i.e.
132
+ web_server,ami-id.',
133
+ type: :string, short: :o
134
+ opt :specs, 'The directory to find ServerSpecs',
135
+ type: :string, required: true, short: :s
136
+ opt :subnet_id,
137
+ 'The subnet to start the instance in. If not provided a subnet will be chosen from the default VPC',
138
+ type: :string, short: :u
139
+ opt :key_name, 'The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS',
140
+ type: :string, short: :k
141
+ opt :key_file, 'The SSH private key file associated to the key_name', type: :string, short: :e
142
+ opt :ssh_user, 'The user to ssh to the instance as', type: :string, required: true, short: :h
143
+ opt :aws_region, 'The AWS region, defaults to AWS_DEFAULT_REGION environment variable',
144
+ type: :string, short: :w
145
+ opt :aws_instance_type, 'The ec2 instance type, defaults to t2.micro',
146
+ type: :string, default: 't2.micro', short: :i
147
+ opt :aws_security_groups,
148
+ 'Security groups to associate to the launched instances. May be specified multiple times. If not provided a temporary security group will be generated in AWS',
149
+ type: :string, default: nil, multi: true, short: :c
150
+ opt :allow_any_temporary_security_group, 'The temporary security group will allow SSH connections from any IP address (0.0.0.0/0)',
151
+ short: :n
152
+ opt :aws_public_ip, 'Launch instances with a public IP and use that IP for SSH', short: :p
153
+ opt :associate_public_ip, "Launch instances with a public IP but don't use that IP for SSH", short: :q
154
+ opt :ssh_retries, 'The number of times we should try sshing to the ec2 instance before giving up. Defaults to 30',
155
+ type: :int, default: 30, short: :t
156
+ opt :tags, 'Additional tags to add to launched instances in the form of comma separated key=value pairs. i.e. Name=AmiSpec',
157
+ type: :string, default: '', short: :g
158
+ opt :debug, "Don't terminate instances on exit", short: :d
159
+ opt :buildkite, 'Output section separators for buildkite', short: :b
160
+ opt :wait_for_rc, 'Wait for oldschool SystemV scripts to run before conducting tests. Currently only supports Ubuntu with upstart',
161
+ short: :f
162
+ opt :wait_for_cloud_init, 'Wait for Cloud Init to complete before running tests'
163
+ opt :user_data_file, 'File path for aws ec2 user data', type: :string, default: nil, short: :l
164
+ opt :iam_instance_profile_arn, 'IAM instance profile to use', type: :string, short: :m
138
165
  end
139
166
 
140
167
  if options[:role] && options[:ami]
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ami_spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Robinson
8
8
  - Martin Jagusch
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-07-29 00:00:00.000000000 Z
12
+ date: 2021-11-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk-ec2
@@ -120,6 +120,7 @@ files:
120
120
  - README.md
121
121
  - bin/ami_spec
122
122
  - lib/ami_spec.rb
123
+ - lib/ami_spec/aws_default_vpc.rb
123
124
  - lib/ami_spec/aws_instance.rb
124
125
  - lib/ami_spec/aws_instance_options.rb
125
126
  - lib/ami_spec/aws_key_pair.rb
@@ -127,12 +128,13 @@ files:
127
128
  - lib/ami_spec/server_spec.rb
128
129
  - lib/ami_spec/server_spec_options.rb
129
130
  - lib/ami_spec/version.rb
131
+ - lib/ami_spec/wait_for_cloud_init.rb
130
132
  - lib/ami_spec/wait_for_rc.rb
131
133
  - lib/ami_spec/wait_for_ssh.rb
132
134
  homepage: https://github.com/envato/ami-spec
133
135
  licenses: []
134
136
  metadata: {}
135
- post_install_message:
137
+ post_install_message:
136
138
  rdoc_options: []
137
139
  require_paths:
138
140
  - lib
@@ -148,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
150
  version: '0'
149
151
  requirements: []
150
152
  rubygems_version: 3.0.4
151
- signing_key:
153
+ signing_key:
152
154
  specification_version: 4
153
155
  summary: Acceptance testing your AMIs
154
156
  test_files: []