ami_spec 1.1.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +72 -27
- data/bin/ami_spec +0 -1
- data/lib/ami_spec.rb +87 -30
- data/lib/ami_spec/aws_default_vpc.rb +10 -0
- data/lib/ami_spec/aws_instance.rb +7 -2
- data/lib/ami_spec/aws_key_pair.rb +36 -0
- data/lib/ami_spec/aws_security_group.rb +72 -0
- data/lib/ami_spec/server_spec.rb +6 -2
- data/lib/ami_spec/version.rb +1 -1
- data/lib/ami_spec/wait_for_cloud_init.rb +11 -0
- data/lib/ami_spec/wait_for_rc.rb +1 -1
- data/lib/ami_spec/wait_for_ssh.rb +2 -2
- metadata +29 -60
- data/.gitignore +0 -2
- data/.ruby-version +0 -1
- data/.travis.yml +0 -6
- data/Gemfile +0 -3
- data/Rakefile +0 -8
- data/ami_spec.gemspec +0 -27
- data/spec/ami_spec_spec.rb +0 -74
- data/spec/aws_instance_spec.rb +0 -142
- data/spec/containers/Dockerfile.amazon_linux +0 -9
- data/spec/containers/Dockerfile.trusty +0 -8
- data/spec/containers/Dockerfile.xenial +0 -22
- data/spec/containers/README.md +0 -5
- data/spec/containers/ami-spec +0 -27
- data/spec/containers/ami-spec.pub +0 -1
- data/spec/containers/docker-compose.yml +0 -28
- data/spec/containers/rc.conf +0 -17
- data/spec/containers/sshd_config +0 -17
- data/spec/spec_helper.rb +0 -2
- data/spec/wait_for_rc_spec.rb +0 -25
- data/spec/wait_for_ssh_spec.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 718f947b58e6bedd2093507f23b7bcc1c8deae4503dde3603c6768700729948a
|
4
|
+
data.tar.gz: b8c05a3702f5951932d372f6c3788a2ee71f9d0ead934a4bdfb52cae382bbe70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2ceb57cf5b2ea84f085a835e47e1533065338479c23bf3da88ff962d5c046c6109f2c0b93461ac4c1d5093a14bc7d522e4c154d1f34225ef3813ebd70f6ecda
|
7
|
+
data.tar.gz: ca6403909f96efb50c412ed722e6449b51ea147677f10d2e7729e31d02adc8ae82561eeaf2bbe054ab774bdf9086fe7b690254512f590c49d977920f99e2f371
|
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,33 +33,39 @@ Run `bundle install`
|
|
29
33
|
```cli
|
30
34
|
$ bundle exec ami_spec --help
|
31
35
|
Options:
|
32
|
-
-r, --role=<s>
|
33
|
-
|
34
|
-
-a, --ami=<s>
|
35
|
-
-o, --role-ami-file=<s>
|
36
|
-
|
37
|
-
-s, --specs=<s>
|
38
|
-
-u, --subnet-id=<s>
|
39
|
-
|
40
|
-
-
|
41
|
-
|
42
|
-
-
|
43
|
-
|
44
|
-
-
|
45
|
-
|
46
|
-
-
|
47
|
-
|
48
|
-
-
|
49
|
-
|
50
|
-
|
51
|
-
-
|
52
|
-
|
53
|
-
-
|
54
|
-
-
|
55
|
-
|
56
|
-
-
|
57
|
-
|
58
|
-
--
|
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
|
58
|
+
-t, --ssh-retries=<i> The number of times we should try sshing to the ec2 instance
|
59
|
+
before giving up. Defaults to 30 (default: 30)
|
60
|
+
-g, --tags=<s> Additional tags to add to launched instances in the form of
|
61
|
+
comma separated key=value pairs. i.e. Name=AmiSpec (default: )
|
62
|
+
-d, --debug Don't terminate instances on exit
|
63
|
+
-b, --buildkite Output section separators for buildkite
|
64
|
+
-f, --wait-for-rc Wait for oldschool SystemV scripts to run before conducting
|
65
|
+
tests. Currently only supports Ubuntu with upstart
|
66
|
+
-l, --user-data-file=<s> File path for aws ec2 user data
|
67
|
+
-m, --iam-instance-profile-arn=<s> IAM instance profile to use
|
68
|
+
--help Show this message
|
59
69
|
|
60
70
|
```
|
61
71
|
|
@@ -65,6 +75,41 @@ When the instances becomes reachable it will run all Specs inside the role spec
|
|
65
75
|
|
66
76
|
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`.
|
67
77
|
|
78
|
+
## ServerSpec test layout
|
79
|
+
|
80
|
+
AmiSpec expects the usual ServerSpec configuration layout as generated by "serverspec-init":
|
81
|
+
|
82
|
+
spec/
|
83
|
+
├── webserver
|
84
|
+
│ └── webserver_spec.rb
|
85
|
+
└── spec_helper.rb
|
86
|
+
|
87
|
+
The \*\_spec.rb files under the role (e.g. webserver) contain the ServerSpec
|
88
|
+
tests that you want to run. The spec_helper.rb file can be very simple:
|
89
|
+
|
90
|
+
require 'serverspec'
|
91
|
+
|
92
|
+
set :backend, :ssh
|
93
|
+
|
94
|
+
Note that the backend *needs* to be :ssh or ami_spec might run the tests on
|
95
|
+
your local machine, not in EC2.
|
96
|
+
|
97
|
+
## Example usage
|
98
|
+
|
99
|
+
To test a custom AMI using a pre-created security group that allows SSH from anywhere:
|
100
|
+
|
101
|
+
```cli
|
102
|
+
ami_spec --role webserver\
|
103
|
+
--specs spec\
|
104
|
+
--aws-region us-east-1\
|
105
|
+
--ami ami-0123456789abcdef0\
|
106
|
+
--key-name default\
|
107
|
+
--key-file ~/.ssh/default.pem\
|
108
|
+
--ssh-user ubuntu\
|
109
|
+
--aws-public-ip\
|
110
|
+
--aws-security-groups sg-0123456789abcdef0
|
111
|
+
```
|
112
|
+
|
68
113
|
## Known caveats
|
69
114
|
|
70
115
|
### RSpec conditions in examples
|
data/bin/ami_spec
CHANGED
data/lib/ami_spec.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
require 'ami_spec/aws_instance'
|
2
2
|
require 'ami_spec/aws_instance_options'
|
3
|
+
require 'ami_spec/aws_default_vpc'
|
4
|
+
require 'ami_spec/aws_key_pair'
|
5
|
+
require 'ami_spec/aws_security_group'
|
3
6
|
require 'ami_spec/server_spec'
|
4
7
|
require 'ami_spec/server_spec_options'
|
5
8
|
require 'ami_spec/wait_for_ssh'
|
6
9
|
require 'ami_spec/wait_for_rc'
|
7
|
-
require '
|
10
|
+
require 'ami_spec/wait_for_cloud_init'
|
11
|
+
require 'optimist'
|
12
|
+
require 'logger'
|
8
13
|
|
9
14
|
module AmiSpec
|
10
15
|
class InstanceConnectionTimeout < StandardError; end
|
@@ -19,7 +24,7 @@ module AmiSpec
|
|
19
24
|
# subnet_id::
|
20
25
|
# The subnet_id to start instances in.
|
21
26
|
# key_name::
|
22
|
-
# The SSH key name to assign to instances.
|
27
|
+
# The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS
|
23
28
|
# key_file::
|
24
29
|
# The SSH key file to use to connect to the host.
|
25
30
|
# aws_region::
|
@@ -27,7 +32,9 @@ module AmiSpec
|
|
27
32
|
# Defaults to AWS_DEFAULT_REGION
|
28
33
|
# aws_security_group_ids::
|
29
34
|
# AWS Security groups to assign to the instances
|
30
|
-
#
|
35
|
+
# If not provided a temporary security group will be generated in AWS
|
36
|
+
# allow_any_temporary_security_group::
|
37
|
+
# The temporary security group will allow SSH connections from any IP address (0.0.0.0/0)
|
31
38
|
# aws_instance_type::
|
32
39
|
# AWS ec2 instance type
|
33
40
|
# aws_public_ip::
|
@@ -45,6 +52,33 @@ module AmiSpec
|
|
45
52
|
# == Returns:
|
46
53
|
# Boolean - The result of all the server specs.
|
47
54
|
def self.run(options)
|
55
|
+
logger = Logger.new(STDOUT, formatter: proc { |_sev, _time, _name, message| "#{message}\n" })
|
56
|
+
|
57
|
+
ec2 = Aws::EC2::Resource.new(options[:aws_region] ? {region: options[:aws_region]} : {})
|
58
|
+
|
59
|
+
if options[:subnet_id].nil?
|
60
|
+
default_vpc_subnet = AwsDefaultVpc.find_subnet(ec2: ec2)
|
61
|
+
raise 'No default VPC subnet found. Please specify a subnet id.' if default_vpc_subnet.nil?
|
62
|
+
options[:subnet_id] = default_vpc_subnet.id
|
63
|
+
logger.info("Using subnet #{options[:subnet_id]} from the default VPC")
|
64
|
+
end
|
65
|
+
|
66
|
+
unless options[:key_name]
|
67
|
+
key_pair = AwsKeyPair.create(ec2: ec2, logger: logger)
|
68
|
+
options[:key_name] = key_pair.key_name
|
69
|
+
options[:key_file] = key_pair.key_file
|
70
|
+
end
|
71
|
+
|
72
|
+
if options[:aws_security_groups].nil? || options[:aws_security_groups].empty?
|
73
|
+
temporary_security_group = AwsSecurityGroup.create(
|
74
|
+
ec2: ec2,
|
75
|
+
subnet_id: options[:subnet_id],
|
76
|
+
allow_any_ip: options[:allow_any_temporary_security_group],
|
77
|
+
logger: logger
|
78
|
+
)
|
79
|
+
options[:aws_security_groups] = [temporary_security_group.group_id]
|
80
|
+
end
|
81
|
+
|
48
82
|
instances = []
|
49
83
|
options[:amis].each_pair do |role, ami|
|
50
84
|
aws_instance_options = AwsInstanceOptions.new(options.merge(role: role, ami: ami))
|
@@ -56,6 +90,7 @@ module AmiSpec
|
|
56
90
|
ip_address = options[:aws_public_ip] ? instance.public_ip_address : instance.private_ip_address
|
57
91
|
WaitForSSH.wait(ip_address, options[:ssh_user], options[:key_file], options[:ssh_retries])
|
58
92
|
WaitForRC.wait(ip_address, options[:ssh_user], options[:key_file]) if options[:wait_for_rc]
|
93
|
+
WaitForCloudInit.wait(ip_address, options[:ssh_user], options[:key_file]) if options[:wait_for_cloud_init]
|
59
94
|
|
60
95
|
server_spec_options = ServerSpecOptions.new(options.merge(instance: instance))
|
61
96
|
results << ServerSpec.new(server_spec_options).run
|
@@ -64,10 +99,12 @@ module AmiSpec
|
|
64
99
|
results.all?
|
65
100
|
ensure
|
66
101
|
stop_instances(instances, options[:debug])
|
102
|
+
key_pair.delete if key_pair
|
103
|
+
temporary_security_group.delete if temporary_security_group
|
67
104
|
end
|
68
105
|
|
69
106
|
def self.stop_instances(instances, debug)
|
70
|
-
instances.each do |instance|
|
107
|
+
instances && instances.each do |instance|
|
71
108
|
begin
|
72
109
|
if debug
|
73
110
|
puts "EC2 instance ##{instance.instance_id} has not been stopped due to debug mode."
|
@@ -83,29 +120,45 @@ module AmiSpec
|
|
83
120
|
private_class_method :stop_instances
|
84
121
|
|
85
122
|
def self.invoke
|
86
|
-
options =
|
87
|
-
opt :role,
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
opt :
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
opt :
|
96
|
-
|
97
|
-
opt :
|
98
|
-
|
99
|
-
type: :
|
100
|
-
opt :
|
101
|
-
|
102
|
-
|
103
|
-
opt :
|
104
|
-
opt :
|
105
|
-
|
106
|
-
opt :
|
107
|
-
|
108
|
-
opt :
|
123
|
+
options = Optimist::options do
|
124
|
+
opt :role,
|
125
|
+
'The role to test, this should map to a directory in the spec folder',
|
126
|
+
type: :string, short: :r
|
127
|
+
opt :ami, 'The ami ID to run tests against', type: :string, short: :a
|
128
|
+
opt :role_ami_file,
|
129
|
+
'A file containing comma separated roles and amis. i.e.
|
130
|
+
web_server,ami-id.',
|
131
|
+
type: :string, short: :o
|
132
|
+
opt :specs, 'The directory to find ServerSpecs',
|
133
|
+
type: :string, required: true, short: :s
|
134
|
+
opt :subnet_id,
|
135
|
+
'The subnet to start the instance in. If not provided a subnet will be chosen from the default VPC',
|
136
|
+
type: :string, short: :u
|
137
|
+
opt :key_name, 'The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS',
|
138
|
+
type: :string, short: :k
|
139
|
+
opt :key_file, 'The SSH private key file associated to the key_name', type: :string, short: :e
|
140
|
+
opt :ssh_user, 'The user to ssh to the instance as', type: :string, required: true, short: :h
|
141
|
+
opt :aws_region, 'The AWS region, defaults to AWS_DEFAULT_REGION environment variable',
|
142
|
+
type: :string, short: :w
|
143
|
+
opt :aws_instance_type, 'The ec2 instance type, defaults to t2.micro',
|
144
|
+
type: :string, default: 't2.micro', short: :i
|
145
|
+
opt :aws_security_groups,
|
146
|
+
'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',
|
147
|
+
type: :string, default: nil, multi: true, short: :c
|
148
|
+
opt :allow_any_temporary_security_group, 'The temporary security group will allow SSH connections from any IP address (0.0.0.0/0)',
|
149
|
+
short: :n
|
150
|
+
opt :aws_public_ip, 'Launch instances with a public IP', short: :p
|
151
|
+
opt :ssh_retries, 'The number of times we should try sshing to the ec2 instance before giving up. Defaults to 30',
|
152
|
+
type: :int, default: 30, short: :t
|
153
|
+
opt :tags, 'Additional tags to add to launched instances in the form of comma separated key=value pairs. i.e. Name=AmiSpec',
|
154
|
+
type: :string, default: '', short: :g
|
155
|
+
opt :debug, "Don't terminate instances on exit", short: :d
|
156
|
+
opt :buildkite, 'Output section separators for buildkite', short: :b
|
157
|
+
opt :wait_for_rc, 'Wait for oldschool SystemV scripts to run before conducting tests. Currently only supports Ubuntu with upstart',
|
158
|
+
short: :f
|
159
|
+
opt :wait_for_cloud_init, 'Wait for Cloud Init to complete before running tests'
|
160
|
+
opt :user_data_file, 'File path for aws ec2 user data', type: :string, default: nil, short: :l
|
161
|
+
opt :iam_instance_profile_arn, 'IAM instance profile to use', type: :string, short: :m
|
109
162
|
end
|
110
163
|
|
111
164
|
if options[:role] && options[:ami]
|
@@ -121,9 +174,7 @@ module AmiSpec
|
|
121
174
|
fail "You must specify either role and ami or role_ami_file"
|
122
175
|
end
|
123
176
|
|
124
|
-
|
125
|
-
fail "Key file #{options[:key_file]} not found"
|
126
|
-
end
|
177
|
+
fail "Key file #{options[:key_file]} not found" if options[:key_name] && !File.exist?(options.fetch(:key_file))
|
127
178
|
|
128
179
|
if options[:user_data_file] and !File.exist? options[:user_data_file]
|
129
180
|
fail "User Data file #{options[:user_data_file]} not found"
|
@@ -131,6 +182,12 @@ module AmiSpec
|
|
131
182
|
|
132
183
|
options[:tags] = parse_tags(options[:tags])
|
133
184
|
|
185
|
+
options[:amis].each_pair do |role, _|
|
186
|
+
unless Dir.exist?("#{options[:specs]}/#{role}")
|
187
|
+
fail "Role directory #{options[:specs]}/#{role} does not exist. If you'd like to skip the role '#{role}', create the directory but leave it empty (except for a .gitignore file)."
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
134
191
|
exit run(options)
|
135
192
|
end
|
136
193
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'aws-sdk'
|
1
|
+
require 'aws-sdk-ec2'
|
2
2
|
require 'forwardable'
|
3
3
|
require 'base64'
|
4
4
|
|
@@ -26,7 +26,7 @@ module AmiSpec
|
|
26
26
|
@iam_instance_profile_arn = options.fetch(:iam_instance_profile_arn, nil)
|
27
27
|
end
|
28
28
|
|
29
|
-
def_delegators :@instance, :instance_id, :tags, :
|
29
|
+
def_delegators :@instance, :instance_id, :tags, :private_ip_address, :public_ip_address
|
30
30
|
|
31
31
|
def start
|
32
32
|
client = Aws::EC2::Client.new(client_options)
|
@@ -37,6 +37,11 @@ module AmiSpec
|
|
37
37
|
tag_instance
|
38
38
|
end
|
39
39
|
|
40
|
+
def terminate
|
41
|
+
@instance.terminate
|
42
|
+
@instance.wait_until_terminated
|
43
|
+
end
|
44
|
+
|
40
45
|
private
|
41
46
|
|
42
47
|
def client_options
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'aws-sdk-ec2'
|
2
|
+
require 'logger'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'pathname'
|
6
|
+
|
7
|
+
module AmiSpec
|
8
|
+
class AwsKeyPair
|
9
|
+
|
10
|
+
def self.create(**args)
|
11
|
+
new(**args).tap(&:create)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(ec2: Aws::EC2::Resource.new, key_name_prefix: 'ami-spec-', logger: Logger.new(STDOUT))
|
15
|
+
@ec2 = ec2
|
16
|
+
@key_name = "#{key_name_prefix}#{SecureRandom.uuid}"
|
17
|
+
@logger = logger
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :key_name, :key_file
|
21
|
+
|
22
|
+
def create
|
23
|
+
@logger.info "Creating temporary AWS key pair: #{@key_name}"
|
24
|
+
@key_pair = @ec2.create_key_pair(key_name: @key_name)
|
25
|
+
@temp_file = Tempfile.new('key')
|
26
|
+
@temp_file.write(@key_pair.key_material)
|
27
|
+
@temp_file.close
|
28
|
+
@key_file = Pathname.new(@temp_file.path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete
|
32
|
+
@logger.info "Deleting temporary AWS key pair: #{@key_name}"
|
33
|
+
@key_pair.delete
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'aws-sdk-ec2'
|
2
|
+
require 'forwardable'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module AmiSpec
|
6
|
+
class AwsSecurityGroup
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def self.create(**args)
|
10
|
+
new(**args).tap(&:create)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(ec2: Aws::EC2::Resource.new,
|
14
|
+
group_name_prefix: "ami-spec-",
|
15
|
+
connection_port: 22,
|
16
|
+
subnet_id:,
|
17
|
+
allow_any_ip: false,
|
18
|
+
logger: Logger.new(STDOUT))
|
19
|
+
@ec2 = ec2
|
20
|
+
@group_name = "#{group_name_prefix}#{SecureRandom.uuid}"
|
21
|
+
@connection_port = connection_port
|
22
|
+
@subnet_id = subnet_id
|
23
|
+
@allow_any_ip = allow_any_ip
|
24
|
+
@logger = logger
|
25
|
+
end
|
26
|
+
|
27
|
+
def_delegators :@security_group, :group_id
|
28
|
+
attr_reader :group_name
|
29
|
+
|
30
|
+
def create
|
31
|
+
@logger.info "Creating temporary AWS security group: #{@group_name}"
|
32
|
+
create_security_group
|
33
|
+
allow_ingress_via_connection_port
|
34
|
+
end
|
35
|
+
|
36
|
+
def delete
|
37
|
+
@logger.info "Deleting temporary AWS security group: #{@group_name}"
|
38
|
+
@security_group.delete
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def create_security_group
|
44
|
+
@security_group = @ec2.create_security_group(
|
45
|
+
group_name: @group_name,
|
46
|
+
description: "A temporary security group for running AmiSpec",
|
47
|
+
vpc_id: subnet.vpc_id,
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
def allow_ingress_via_connection_port
|
52
|
+
@security_group.authorize_ingress(
|
53
|
+
ip_permissions: [
|
54
|
+
{
|
55
|
+
ip_protocol: "tcp",
|
56
|
+
from_port: @connection_port,
|
57
|
+
to_port: @connection_port,
|
58
|
+
ip_ranges: [{cidr_ip: cidr_block}],
|
59
|
+
},
|
60
|
+
],
|
61
|
+
)
|
62
|
+
end
|
63
|
+
|
64
|
+
def cidr_block
|
65
|
+
@allow_any_ip ? "0.0.0.0/0" : subnet.cidr_block
|
66
|
+
end
|
67
|
+
|
68
|
+
def subnet
|
69
|
+
@subnet ||= @ec2.subnet(@subnet_id)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/ami_spec/server_spec.rb
CHANGED
@@ -28,11 +28,15 @@ module AmiSpec
|
|
28
28
|
end
|
29
29
|
|
30
30
|
$LOAD_PATH.unshift(@spec) unless $LOAD_PATH.include?(@spec)
|
31
|
-
|
31
|
+
begin
|
32
|
+
require File.join(@spec, 'spec_helper')
|
33
|
+
rescue LoadError
|
34
|
+
puts 'Spec Helper does not exist. Skipping!'
|
35
|
+
end
|
32
36
|
|
33
37
|
set :backend, :ssh
|
34
38
|
set :host, @ip
|
35
|
-
set :ssh_options, :user => @user, :keys => [@key_file], :
|
39
|
+
set :ssh_options, :user => @user, :keys => [@key_file], :verify_host_key => :never
|
36
40
|
|
37
41
|
RSpec.configuration.fail_fast = true if @debug
|
38
42
|
|
data/lib/ami_spec/version.rb
CHANGED
@@ -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
|
data/lib/ami_spec/wait_for_rc.rb
CHANGED
@@ -3,7 +3,7 @@ require 'net/ssh'
|
|
3
3
|
module AmiSpec
|
4
4
|
class WaitForRC
|
5
5
|
def self.wait(ip_address, user, key, port=22)
|
6
|
-
Net::SSH.start(ip_address, user, keys: [key],
|
6
|
+
Net::SSH.start(ip_address, user, keys: [key], :verify_host_key => :never, port: port) do |ssh|
|
7
7
|
distrib_stdout = ""
|
8
8
|
# Determine the OS family
|
9
9
|
ssh.exec!("source /etc/*release && echo -n $DISTRIB_ID && echo -n $ID") do |channel, stream, data|
|
@@ -8,8 +8,8 @@ module AmiSpec
|
|
8
8
|
|
9
9
|
while retries < max_retries
|
10
10
|
begin
|
11
|
-
Net::SSH.start(ip_address, user, keys: [key],
|
12
|
-
rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Timeout::Error => error
|
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, Net::SSH::Exception => error
|
13
13
|
last_error = error
|
14
14
|
sleep 1
|
15
15
|
else
|
metadata
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ami_spec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.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:
|
12
|
+
date: 2021-07-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name: aws-sdk
|
15
|
+
name: aws-sdk-ec2
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '1'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '1'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -32,7 +32,7 @@ dependencies:
|
|
32
32
|
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
|
-
type: :
|
35
|
+
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
@@ -43,16 +43,16 @@ dependencies:
|
|
43
43
|
name: serverspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - "
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: '
|
48
|
+
version: '2'
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - "
|
53
|
+
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: '
|
55
|
+
version: '2'
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: specinfra
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,19 +68,19 @@ dependencies:
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '2.45'
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
|
-
name:
|
71
|
+
name: optimist
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
73
73
|
requirements:
|
74
|
-
- - "
|
74
|
+
- - "~>"
|
75
75
|
- !ruby/object:Gem::Version
|
76
|
-
version: '
|
76
|
+
version: '3'
|
77
77
|
type: :runtime
|
78
78
|
prerelease: false
|
79
79
|
version_requirements: !ruby/object:Gem::Requirement
|
80
80
|
requirements:
|
81
|
-
- - "
|
81
|
+
- - "~>"
|
82
82
|
- !ruby/object:Gem::Version
|
83
|
-
version: '
|
83
|
+
version: '3'
|
84
84
|
- !ruby/object:Gem::Dependency
|
85
85
|
name: hashie
|
86
86
|
requirement: !ruby/object:Gem::Requirement
|
@@ -99,16 +99,16 @@ dependencies:
|
|
99
99
|
name: net-ssh
|
100
100
|
requirement: !ruby/object:Gem::Requirement
|
101
101
|
requirements:
|
102
|
-
- - "
|
102
|
+
- - "~>"
|
103
103
|
- !ruby/object:Gem::Version
|
104
|
-
version: '
|
104
|
+
version: '5'
|
105
105
|
type: :runtime
|
106
106
|
prerelease: false
|
107
107
|
version_requirements: !ruby/object:Gem::Requirement
|
108
108
|
requirements:
|
109
|
-
- - "
|
109
|
+
- - "~>"
|
110
110
|
- !ruby/object:Gem::Version
|
111
|
-
version: '
|
111
|
+
version: '5'
|
112
112
|
description: Acceptance testing your AMIs
|
113
113
|
email: []
|
114
114
|
executables:
|
@@ -116,41 +116,25 @@ executables:
|
|
116
116
|
extensions: []
|
117
117
|
extra_rdoc_files: []
|
118
118
|
files:
|
119
|
-
- ".gitignore"
|
120
|
-
- ".ruby-version"
|
121
|
-
- ".travis.yml"
|
122
|
-
- Gemfile
|
123
119
|
- LICENSE.txt
|
124
120
|
- README.md
|
125
|
-
- Rakefile
|
126
|
-
- ami_spec.gemspec
|
127
121
|
- bin/ami_spec
|
128
122
|
- lib/ami_spec.rb
|
123
|
+
- lib/ami_spec/aws_default_vpc.rb
|
129
124
|
- lib/ami_spec/aws_instance.rb
|
130
125
|
- lib/ami_spec/aws_instance_options.rb
|
126
|
+
- lib/ami_spec/aws_key_pair.rb
|
127
|
+
- lib/ami_spec/aws_security_group.rb
|
131
128
|
- lib/ami_spec/server_spec.rb
|
132
129
|
- lib/ami_spec/server_spec_options.rb
|
133
130
|
- lib/ami_spec/version.rb
|
131
|
+
- lib/ami_spec/wait_for_cloud_init.rb
|
134
132
|
- lib/ami_spec/wait_for_rc.rb
|
135
133
|
- lib/ami_spec/wait_for_ssh.rb
|
136
|
-
- spec/ami_spec_spec.rb
|
137
|
-
- spec/aws_instance_spec.rb
|
138
|
-
- spec/containers/Dockerfile.amazon_linux
|
139
|
-
- spec/containers/Dockerfile.trusty
|
140
|
-
- spec/containers/Dockerfile.xenial
|
141
|
-
- spec/containers/README.md
|
142
|
-
- spec/containers/ami-spec
|
143
|
-
- spec/containers/ami-spec.pub
|
144
|
-
- spec/containers/docker-compose.yml
|
145
|
-
- spec/containers/rc.conf
|
146
|
-
- spec/containers/sshd_config
|
147
|
-
- spec/spec_helper.rb
|
148
|
-
- spec/wait_for_rc_spec.rb
|
149
|
-
- spec/wait_for_ssh_spec.rb
|
150
134
|
homepage: https://github.com/envato/ami-spec
|
151
135
|
licenses: []
|
152
136
|
metadata: {}
|
153
|
-
post_install_message:
|
137
|
+
post_install_message:
|
154
138
|
rdoc_options: []
|
155
139
|
require_paths:
|
156
140
|
- lib
|
@@ -158,30 +142,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
158
142
|
requirements:
|
159
143
|
- - ">="
|
160
144
|
- !ruby/object:Gem::Version
|
161
|
-
version:
|
145
|
+
version: 2.2.6
|
162
146
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
163
147
|
requirements:
|
164
148
|
- - ">="
|
165
149
|
- !ruby/object:Gem::Version
|
166
150
|
version: '0'
|
167
151
|
requirements: []
|
168
|
-
|
169
|
-
|
170
|
-
signing_key:
|
152
|
+
rubygems_version: 3.0.4
|
153
|
+
signing_key:
|
171
154
|
specification_version: 4
|
172
155
|
summary: Acceptance testing your AMIs
|
173
|
-
test_files:
|
174
|
-
- spec/ami_spec_spec.rb
|
175
|
-
- spec/aws_instance_spec.rb
|
176
|
-
- spec/containers/Dockerfile.amazon_linux
|
177
|
-
- spec/containers/Dockerfile.trusty
|
178
|
-
- spec/containers/Dockerfile.xenial
|
179
|
-
- spec/containers/README.md
|
180
|
-
- spec/containers/ami-spec
|
181
|
-
- spec/containers/ami-spec.pub
|
182
|
-
- spec/containers/docker-compose.yml
|
183
|
-
- spec/containers/rc.conf
|
184
|
-
- spec/containers/sshd_config
|
185
|
-
- spec/spec_helper.rb
|
186
|
-
- spec/wait_for_rc_spec.rb
|
187
|
-
- spec/wait_for_ssh_spec.rb
|
156
|
+
test_files: []
|
data/.gitignore
DELETED
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.9.3-p551
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
data/Rakefile
DELETED
data/ami_spec.gemspec
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
lib = File.expand_path('../lib', __FILE__)
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
|
4
|
-
require 'ami_spec/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |gem|
|
7
|
-
gem.name = 'ami_spec'
|
8
|
-
gem.version = AmiSpec::VERSION
|
9
|
-
gem.authors = ['Patrick Robinson', 'Martin Jagusch']
|
10
|
-
gem.email = []
|
11
|
-
gem.description = 'Acceptance testing your AMIs'
|
12
|
-
gem.summary = gem.description
|
13
|
-
gem.homepage = 'https://github.com/envato/ami-spec'
|
14
|
-
|
15
|
-
gem.files = `git ls-files`.split($/)
|
16
|
-
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
-
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
-
gem.require_paths = ['lib']
|
19
|
-
|
20
|
-
gem.add_dependency 'aws-sdk', '~> 2'
|
21
|
-
gem.add_dependency 'rake'
|
22
|
-
gem.add_dependency 'serverspec'
|
23
|
-
gem.add_dependency 'specinfra', '>= 2.45'
|
24
|
-
gem.add_dependency 'trollop'
|
25
|
-
gem.add_dependency 'hashie'
|
26
|
-
gem.add_dependency 'net-ssh', '< 3.0'
|
27
|
-
end
|
data/spec/ami_spec_spec.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe AmiSpec do
|
4
|
-
let(:amis) { {'web_server' => 'ami-1234abcd', 'db_server' => 'ami-1234abcd'} }
|
5
|
-
let(:ec2_double) { instance_double(AmiSpec::AwsInstance) }
|
6
|
-
let(:state) { double(name: 'running') }
|
7
|
-
let(:test_result) { true }
|
8
|
-
let(:server_spec_double) { double(run: test_result) }
|
9
|
-
subject do
|
10
|
-
described_class.run(
|
11
|
-
amis: amis,
|
12
|
-
specs: '/tmp/foobar',
|
13
|
-
subnet_id: 'subnet-1234abcd',
|
14
|
-
key_name: 'key',
|
15
|
-
key_file: 'key.pem',
|
16
|
-
aws_public_ip: false,
|
17
|
-
aws_instance_type: 't2.micro',
|
18
|
-
ssh_user: 'ubuntu',
|
19
|
-
debug: false,
|
20
|
-
ssh_retries: 30,
|
21
|
-
)
|
22
|
-
end
|
23
|
-
|
24
|
-
describe '#invoke' do
|
25
|
-
it 'raises a system exit with no arguments' do
|
26
|
-
expect{ described_class.invoke }.to raise_error(SystemExit)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe '#run' do
|
31
|
-
before do
|
32
|
-
allow(AmiSpec::WaitForSSH).to receive(:wait).and_return(true)
|
33
|
-
allow(AmiSpec::AwsInstance).to receive(:start).and_return(ec2_double)
|
34
|
-
allow(AmiSpec::ServerSpec).to receive(:new).and_return(server_spec_double)
|
35
|
-
allow(ec2_double).to receive(:terminate).and_return(true)
|
36
|
-
allow(ec2_double).to receive(:private_ip_address).and_return('127.0.0.1')
|
37
|
-
allow_any_instance_of(Object).to receive(:sleep)
|
38
|
-
end
|
39
|
-
|
40
|
-
context 'successful tests' do
|
41
|
-
it 'calls aws instance for each ami' do
|
42
|
-
expect(AmiSpec::AwsInstance).to receive(:start).with(hash_including(role: 'web_server'))
|
43
|
-
expect(AmiSpec::AwsInstance).to receive(:start).with(hash_including(role: 'db_server'))
|
44
|
-
subject
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'returns true' do
|
48
|
-
expect(subject).to be_truthy
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'failed tests' do
|
53
|
-
let(:test_result) { false }
|
54
|
-
|
55
|
-
it 'returns false' do
|
56
|
-
expect(subject).to be_falsey
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe '#parse_tags' do
|
62
|
-
it 'parses a single key/value pair' do
|
63
|
-
expect(described_class.parse_tags("Name=AmiSpec")).to eq( { "Name"=>"AmiSpec" } )
|
64
|
-
end
|
65
|
-
|
66
|
-
it 'parses multiple key/value pairs' do
|
67
|
-
expect(described_class.parse_tags("Name=AmiSpec,Owner=Me")).to eq( { "Name"=>"AmiSpec", "Owner"=>"Me" } )
|
68
|
-
end
|
69
|
-
|
70
|
-
it 'parses an empty string' do
|
71
|
-
expect(described_class.parse_tags("")).to eq({})
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
data/spec/aws_instance_spec.rb
DELETED
@@ -1,142 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'base64'
|
3
|
-
require 'tempfile'
|
4
|
-
|
5
|
-
describe AmiSpec::AwsInstance do
|
6
|
-
let(:role) { 'web_server' }
|
7
|
-
let(:sec_group_id) { nil }
|
8
|
-
let(:region) { nil }
|
9
|
-
let(:client_double) { instance_double(Aws::EC2::Client) }
|
10
|
-
let(:new_ec2_double) { instance_double(Aws::EC2::Types::Instance) }
|
11
|
-
let(:ec2_double) { instance_double(Aws::EC2::Instance) }
|
12
|
-
let(:tags) { {} }
|
13
|
-
let(:iam_instance_profile_arn) { nil }
|
14
|
-
let(:user_data_file) { nil }
|
15
|
-
|
16
|
-
subject(:aws_instance) do
|
17
|
-
described_class.new(
|
18
|
-
role: role,
|
19
|
-
ami: 'ami',
|
20
|
-
subnet_id: 'subnet',
|
21
|
-
key_name: 'key',
|
22
|
-
aws_instance_type: 't2.micro',
|
23
|
-
aws_public_ip: false,
|
24
|
-
aws_security_groups: sec_group_id,
|
25
|
-
aws_region: region,
|
26
|
-
tags: tags,
|
27
|
-
user_data_file: user_data_file,
|
28
|
-
iam_instance_profile_arn: iam_instance_profile_arn
|
29
|
-
)
|
30
|
-
end
|
31
|
-
|
32
|
-
before do
|
33
|
-
allow(Aws::EC2::Client).to receive(:new).and_return(client_double)
|
34
|
-
allow(client_double).to receive(:run_instances).and_return(double(instances: [new_ec2_double]))
|
35
|
-
allow(ec2_double).to receive(:create_tags).and_return(double)
|
36
|
-
allow(Aws::EC2::Instance).to receive(:new).and_return(ec2_double)
|
37
|
-
allow(new_ec2_double).to receive(:instance_id)
|
38
|
-
allow(ec2_double).to receive(:instance_id)
|
39
|
-
allow(ec2_double).to receive(:wait_until_running)
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '#start' do
|
43
|
-
subject(:start) { aws_instance.start }
|
44
|
-
context 'without optional values' do
|
45
|
-
it 'does not include the security group' do
|
46
|
-
expect(client_double).to receive(:run_instances).with(
|
47
|
-
hash_excluding(:network_interfaces=>array_including(hash_including(:groups)))
|
48
|
-
)
|
49
|
-
start
|
50
|
-
end
|
51
|
-
|
52
|
-
it 'does include the region' do
|
53
|
-
expect(Aws::EC2::Client).to receive(:new).with(
|
54
|
-
hash_excluding(:region => region)
|
55
|
-
)
|
56
|
-
start
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
context 'with security group' do
|
61
|
-
let(:sec_group_id) { ['1234'] }
|
62
|
-
|
63
|
-
it 'does include security groups' do
|
64
|
-
expect(client_double).to receive(:run_instances).with(
|
65
|
-
hash_including(:network_interfaces=>array_including(hash_including(:groups)))
|
66
|
-
)
|
67
|
-
start
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'with region' do
|
72
|
-
let(:region) { 'us-east-1' }
|
73
|
-
|
74
|
-
it 'does include the region in the intial connection' do
|
75
|
-
expect(Aws::EC2::Client).to receive(:new).with(
|
76
|
-
hash_including(:region => region)
|
77
|
-
)
|
78
|
-
start
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'does include the region in the subsequent connection' do
|
82
|
-
expect(Aws::EC2::Instance).to receive(:new).with(
|
83
|
-
anything,
|
84
|
-
hash_including(:region => region)
|
85
|
-
)
|
86
|
-
start
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'with tags' do
|
91
|
-
let(:tags) { {"Name" => "AmiSpec"} }
|
92
|
-
|
93
|
-
it 'tags the instance' do
|
94
|
-
expect(ec2_double).to receive(:create_tags).with(
|
95
|
-
{tags: [{ key: 'AmiSpec', value: role}, { key: "Name", value: "AmiSpec"}]}
|
96
|
-
)
|
97
|
-
start
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
context 'with user_data' do
|
102
|
-
let(:user_data_file) {
|
103
|
-
file = Tempfile.new('user_data.txt')
|
104
|
-
file.write("my file\ncontent")
|
105
|
-
file.close
|
106
|
-
file.path
|
107
|
-
}
|
108
|
-
|
109
|
-
it 'does include user_data' do
|
110
|
-
expect(client_double).to receive(:run_instances).with(
|
111
|
-
hash_including(:user_data => Base64.encode64("my file\ncontent"))
|
112
|
-
)
|
113
|
-
start
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
context 'with iam_instance_profile_arn' do
|
118
|
-
let(:iam_instance_profile_arn) { "my_arn" }
|
119
|
-
|
120
|
-
it 'does include iam_instance_profile_arn' do
|
121
|
-
expect(client_double).to receive(:run_instances).with(
|
122
|
-
hash_including(:iam_instance_profile => { arn: 'my_arn'})
|
123
|
-
)
|
124
|
-
start
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
it 'tags the instance with a role' do
|
129
|
-
expect(ec2_double).to receive(:create_tags).with(
|
130
|
-
hash_including(tags: [{ key: 'AmiSpec', value: role}])
|
131
|
-
)
|
132
|
-
start
|
133
|
-
end
|
134
|
-
|
135
|
-
it 'delegates some methods to the instance variable' do
|
136
|
-
expect(ec2_double).to receive(:instance_id)
|
137
|
-
start
|
138
|
-
aws_instance.instance_id
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
FROM ubuntu:xenial
|
2
|
-
|
3
|
-
RUN cd /lib/systemd/system/sysinit.target.wants/; ls | grep -v systemd-tmpfiles-setup | xargs rm -f $1 \
|
4
|
-
rm -f /lib/systemd/system/multi-user.target.wants/*;\
|
5
|
-
rm -f /etc/systemd/system/*.wants/*;\
|
6
|
-
rm -f /lib/systemd/system/local-fs.target.wants/*; \
|
7
|
-
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
|
8
|
-
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
|
9
|
-
rm -f /lib/systemd/system/basic.target.wants/*;\
|
10
|
-
rm -f /lib/systemd/system/anaconda.target.wants/*; \
|
11
|
-
rm -f /lib/systemd/system/plymouth*; \
|
12
|
-
rm -f /lib/systemd/system/systemd-update-utmp*;
|
13
|
-
|
14
|
-
RUN apt-get update && apt-get install -y openssh-server dbus && apt-get clean
|
15
|
-
|
16
|
-
RUN systemctl set-default multi-user.target
|
17
|
-
|
18
|
-
COPY ami-spec.pub /root/.ssh/authorized_keys
|
19
|
-
|
20
|
-
EXPOSE 22
|
21
|
-
|
22
|
-
CMD ["/bin/bash", "-c", "exec /sbin/init --log-target=journal 3>&1"]
|
data/spec/containers/README.md
DELETED
@@ -1,5 +0,0 @@
|
|
1
|
-
## Integration test containers
|
2
|
-
|
3
|
-
This directory is used to create containers that can be used to test the `WaitForRC` class. Because they require upstart/systemd to exist we have to install and start the init environment. We also setup SSH so that we can simple call the `wait` function and have it SSH to our container to execute.
|
4
|
-
|
5
|
-
Refer to the [README](../../README.md#running-tests) for how to execute them.
|
data/spec/containers/ami-spec
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
-----BEGIN RSA PRIVATE KEY-----
|
2
|
-
MIIEowIBAAKCAQEAwWn2++lylp8RcHzy7H9QpYli3nxLDh769DDbnb9cw2UDd9OH
|
3
|
-
6JZKaT3xe3IbMr39SmkGlOygkBmeH43VxAkiVJv3awDPRU0UvDyUvCsbaYj1/cOS
|
4
|
-
8Vxr7ENExoiKkengcg6k3mFj65ooJ1pf8RoXuj+0+YU0fgejuR/M4x6V8GKFCJhU
|
5
|
-
wFmRs3mcoCx0EiJtTx40IW87uOQUruDX5HcgTUInRhyRxltNrXJaap1weMGpIA/o
|
6
|
-
Bo8foOx1Os9o3YKQlkPF4iqk2AVJ4FZGbMay0cIq3075Jeig6bdlIhRpYA+w+SAI
|
7
|
-
y/yT/K3U1ciQqKtPgahGEyihrh7Ks2F2FSLhdwIDAQABAoIBABWt/QNLrY54kgnb
|
8
|
-
15buxmlntu9dW0Rf8J1ChLtv4cP9JKBf05IcloapbNH7flT3utaGYzh6NZ0xYeoD
|
9
|
-
ifyJUZHOUbNqydDozPQ0ji9xXYc81OX28Beh1m8LM0BVucKVRpVCUvSiUgLsqqeO
|
10
|
-
l8Z8uEAmN/DoH3QpAw8TI3Ip0YC6OHA2aRV9PXuDnR5OTdBPOBj33Fdtf0rUAk41
|
11
|
-
UFe/BHFyACfTK05+bcQz9DvRV/H+SnBeOCqDie1eNDnEgza4NS2cnBUCogKsaCrY
|
12
|
-
gV06pivS2aHsK5CuNB1lcZi1tVf3DnDwPvFWqLLG9PIHaevPDpDURECirCrpCWJT
|
13
|
-
VSHm7KECgYEA4K5jSna4Jzo9FlHzF+yGEju5QwEJTjnhunNw1FpcgPAddFQ4hs3w
|
14
|
-
0EhyPlZyf3vwhfdH4vBhTLjRTrOF2SIvSSPwrkWlAhaluVvpVRFd/ncYW4kAVwhQ
|
15
|
-
15/ZBtvu8OQnKeeztsLlkEi4ik3cKjeXyeDQReb2Guvc6IM4fr6ZrlkCgYEA3F/S
|
16
|
-
uJr04UgzX0cQuNLX7uXz6oeyJupwFkTuAhvLcHDsDHFkP1M9zfFzg5aEcQungz/l
|
17
|
-
5s/vFJmfLBrzhSoYY1T9PDdLwEL/JKaxhKNEV9lExF4exMui6QPWdTMA8ndvB7r5
|
18
|
-
Ur85X8scH1qJo99fsEmNmG5O72PGXmltOB0sNE8CgYEApeuCPYIweh+C7xGzkE5F
|
19
|
-
r/9Uz4tbYN5TuMn5X4gfWcR4K+jqGXrJxDZLz4ctZMGVHIlBF/DmGa8+On1OccvR
|
20
|
-
2ZRl73xU35bz6U9bn0uE+x7d6PLiQmNMt/8+WNdfu5rw5PxLdcK1nnhldxUKak7F
|
21
|
-
k/qmM4jc44Kcj0QgG1EL0nkCgYAFbV61KSvKuIp7WDazNo4W1hbxubHLf46PHdd2
|
22
|
-
udSCymUl0U0UuioVflLH9NcCKbVQaCxzSL+slDP1VByXNPgwyhEKgJoe/Adokaph
|
23
|
-
h9vRBgrJgz/ivNkgP/XyIPVvAz36xMILJaZ2E3x30TT+kiu7HbSdAmpzPtPN027b
|
24
|
-
KOzDxQKBgEv2OvEtpvpv9DgPHs9Mq4haTh2o8c8JW7kwHqbbZOZjZ/4daEh89FhH
|
25
|
-
gjvJV5NjaNhFqBWTnNfjSr4o09WFDoQyVwEUrWNJXXZmsjOHqMDT/kwVoAsld1tO
|
26
|
-
N+JW6/4M+EMYvF39yWzdQn/U3A1gZIfzAC6S3HUCi9BgKLBMKEN3
|
27
|
-
-----END RSA PRIVATE KEY-----
|
@@ -1 +0,0 @@
|
|
1
|
-
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBafb76XKWnxFwfPLsf1CliWLefEsOHvr0MNudv1zDZQN304folkppPfF7chsyvf1KaQaU7KCQGZ4fjdXECSJUm/drAM9FTRS8PJS8KxtpiPX9w5LxXGvsQ0TGiIqR6eByDqTeYWPrmignWl/xGhe6P7T5hTR+B6O5H8zjHpXwYoUImFTAWZGzeZygLHQSIm1PHjQhbzu45BSu4NfkdyBNQidGHJHGW02tclpqnXB4wakgD+gGjx+g7HU6z2jdgpCWQ8XiKqTYBUngVkZsxrLRwirfTvkl6KDpt2UiFGlgD7D5IAjL/JP8rdTVyJCoq0+BqEYTKKGuHsqzYXYVIuF3
|
@@ -1,28 +0,0 @@
|
|
1
|
-
version: '3'
|
2
|
-
services:
|
3
|
-
xenial:
|
4
|
-
build:
|
5
|
-
context: .
|
6
|
-
dockerfile: Dockerfile.xenial
|
7
|
-
ports:
|
8
|
-
- "1122:22"
|
9
|
-
# --security-opt seccomp=unconfined --tmpfs /run --tmpfs /run/lock -v /sys/fs/cgroup:/sys/fs/cgroup:ro
|
10
|
-
security_opt:
|
11
|
-
- seccomp:unconfined
|
12
|
-
tmpfs:
|
13
|
-
- /run
|
14
|
-
- /run/lock
|
15
|
-
volumes:
|
16
|
-
- /sys/fs/cgroup:/sys/fs/cgroup:ro
|
17
|
-
trusty:
|
18
|
-
build:
|
19
|
-
context: .
|
20
|
-
dockerfile: Dockerfile.trusty
|
21
|
-
ports:
|
22
|
-
- "1123:22"
|
23
|
-
amazon_linux:
|
24
|
-
build:
|
25
|
-
context: .
|
26
|
-
dockerfile: Dockerfile.amazon_linux
|
27
|
-
ports:
|
28
|
-
- "1124:22"
|
data/spec/containers/rc.conf
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# rc - System V runlevel compatibility
|
2
|
-
#
|
3
|
-
# This task runs the old sysv-rc runlevel scripts. It
|
4
|
-
# is usually started by the telinit compatibility wrapper.
|
5
|
-
#
|
6
|
-
# Do not edit this file directly. If you want to change the behaviour,
|
7
|
-
# please create a file rc.override and put your changes there.
|
8
|
-
|
9
|
-
start on runlevel [0123456]
|
10
|
-
|
11
|
-
stop on runlevel [!$RUNLEVEL]
|
12
|
-
|
13
|
-
task
|
14
|
-
|
15
|
-
export RUNLEVEL
|
16
|
-
console output
|
17
|
-
exec /etc/rc.d/rc $RUNLEVEL
|
data/spec/containers/sshd_config
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
HostKey /etc/ssh/ssh_host_rsa_key
|
2
|
-
HostKey /etc/ssh/ssh_host_ecdsa_key
|
3
|
-
HostKey /etc/ssh/ssh_host_ed25519_key
|
4
|
-
SyslogFacility AUTHPRIV
|
5
|
-
AuthorizedKeysFile .ssh/authorized_keys
|
6
|
-
PasswordAuthentication no
|
7
|
-
ChallengeResponseAuthentication no
|
8
|
-
UsePAM yes
|
9
|
-
X11Forwarding yes
|
10
|
-
PrintLastLog yes
|
11
|
-
UsePrivilegeSeparation sandbox
|
12
|
-
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
|
13
|
-
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
|
14
|
-
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
|
15
|
-
AcceptEnv XMODIFIERS
|
16
|
-
Subsystem sftp /usr/libexec/openssh/sftp-server
|
17
|
-
PermitRootLogin yes
|
data/spec/spec_helper.rb
DELETED
data/spec/wait_for_rc_spec.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe AmiSpec::WaitForRC, integration: true do
|
4
|
-
let(:private_key_file) { File.expand_path(File.join('..', 'containers', 'ami-spec'), __FILE__) }
|
5
|
-
context 'xenial server' do
|
6
|
-
let(:ssh_port) { 1122 }
|
7
|
-
it 'executes without printing any errors' do
|
8
|
-
expect { described_class.wait("localhost", "root", private_key_file, ssh_port) }.to_not output.to_stdout
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
context 'trusty server' do
|
13
|
-
let(:ssh_port) { 1123 }
|
14
|
-
it 'executes without printing any errors' do
|
15
|
-
expect { described_class.wait("localhost", "root", private_key_file, ssh_port) }.to_not output.to_stdout
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
context 'amazon linux server' do
|
20
|
-
let(:ssh_port) { 1124 }
|
21
|
-
it 'executes without printing any errors' do
|
22
|
-
expect { described_class.wait("localhost", "root", private_key_file, ssh_port) }.to_not output.to_stdout
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/spec/wait_for_ssh_spec.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe AmiSpec::WaitForSSH do
|
4
|
-
describe '#wait' do
|
5
|
-
let(:retries) { 30 }
|
6
|
-
subject { described_class.wait('127.0.0.1', 'ubuntu', 'key.pem', 30) }
|
7
|
-
|
8
|
-
before do
|
9
|
-
allow_any_instance_of(Object).to receive(:sleep)
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'returns after one attempt if ssh connection succeeds' do
|
13
|
-
expect(Net::SSH).to receive(:start)
|
14
|
-
|
15
|
-
subject
|
16
|
-
end
|
17
|
-
|
18
|
-
context 'ssh fails' do
|
19
|
-
before do
|
20
|
-
allow(Net::SSH).to receive(:start).and_raise(Errno::ECONNREFUSED, 'ssh failed')
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'raises an exception' do
|
24
|
-
expect{subject}.to raise_error(AmiSpec::InstanceConnectionTimeout)
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'returns the last error' do
|
28
|
-
expect(Net::SSH).to receive(:start).and_raise(Errno::ECONNREFUSED, 'some other error')
|
29
|
-
expect{subject}.to raise_error(AmiSpec::InstanceConnectionTimeout, /ssh failed/)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'tries the number of retries specified' do
|
33
|
-
expect(Net::SSH).to receive(:start).exactly(retries).times
|
34
|
-
|
35
|
-
expect{subject}.to raise_error(AmiSpec::InstanceConnectionTimeout)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|