ami_spec 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bf865c78d589bc86d5e434dcb49d3ae8dcf301b1c07181d6a9d2f569ccd284e
4
- data.tar.gz: ace2b925ce79810075177b8e90113b84fc15f574be9c0cd782aa0ec90536c643
3
+ metadata.gz: 87eebf7f488963ef90dec3e34678f6c75bd36eaf896467edaeb1651271ad8d8a
4
+ data.tar.gz: 6425586e1732b2e78cb4099047f1ba6e5e99d08deb0dbd5aa4031daa24beb597
5
5
  SHA512:
6
- metadata.gz: 4263f8621665309d2de4859291b286a9bd3b5d5545ac47df20a1bbb56c589ae93585b55418121545afffdbf592c45a7594d171d403d1e94adde1bb0fe7357e73
7
- data.tar.gz: 8327271a25293d9cb08f1b9ae0e56983a39ac8d673acb1e7186183faf99d561c827a372251ba70e10100f80c287a6bf2a2578604e829611842bd76bbffaeec45
6
+ metadata.gz: 93dd91143f960c39da8cfdf24522a9daf271d4aaf3a69db345d4621e40c9a10ca43158abe19a0e2268cca9bbc699267fe9c54b954275d5e7bccb404eb2e487f7
7
+ data.tar.gz: 7e1a7762ed90bfd5266ab9a869fe1a78af16e065fe8d5c8ac0663376195e9029d7b23c8d8d7ab818cba7fdf5d2ee821eab723b8b176459880685851e4a055ebe
data/README.md CHANGED
@@ -36,21 +36,24 @@ Options:
36
36
  web_server,ami-id.
37
37
  -s, --specs=<s> The directory to find ServerSpecs
38
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
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
40
41
  -e, --key-file=<s> The SSH private key file associated to the key_name
41
42
  -h, --ssh-user=<s> The user to ssh to the instance as
42
43
  -w, --aws-region=<s> The AWS region, defaults to AWS_DEFAULT_REGION environment
43
44
  variable
44
45
  -i, --aws-instance-type=<s> The ec2 instance type, defaults to t2.micro (default:
45
46
  t2.micro)
46
- -c, --aws-security-groups=<s+> Security groups to associate to the launched instances. May be
47
- specified multiple times
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
48
50
  -p, --aws-public-ip Launch instances with a public IP
49
51
  -t, --ssh-retries=<i> The number of times we should try sshing to the ec2 instance
50
52
  before giving up. Defaults to 30 (default: 30)
51
53
  -g, --tags=<s> Additional tags to add to launched instances in the form of
52
54
  comma separated key=value pairs. i.e. Name=AmiSpec (default: )
53
55
  -d, --debug Don't terminate instances on exit
56
+ -b, --buildkite Output section separators for buildkite
54
57
  -f, --wait-for-rc Wait for oldschool SystemV scripts to run before conducting
55
58
  tests. Currently only supports Ubuntu with upstart
56
59
  -l, --user-data-file=<s> File path for aws ec2 user data
data/lib/ami_spec.rb CHANGED
@@ -1,10 +1,13 @@
1
1
  require 'ami_spec/aws_instance'
2
2
  require 'ami_spec/aws_instance_options'
3
+ require 'ami_spec/aws_key_pair'
4
+ require 'ami_spec/aws_security_group'
3
5
  require 'ami_spec/server_spec'
4
6
  require 'ami_spec/server_spec_options'
5
7
  require 'ami_spec/wait_for_ssh'
6
8
  require 'ami_spec/wait_for_rc'
7
9
  require 'optimist'
10
+ require 'logger'
8
11
 
9
12
  module AmiSpec
10
13
  class InstanceConnectionTimeout < StandardError; end
@@ -19,7 +22,7 @@ module AmiSpec
19
22
  # subnet_id::
20
23
  # The subnet_id to start instances in.
21
24
  # key_name::
22
- # The SSH key name to assign to instances. This key name must exist on the executing host for passwordless login.
25
+ # The SSH key name to assign to instances. If not provided a temporary key pair will be generated in AWS
23
26
  # key_file::
24
27
  # The SSH key file to use to connect to the host.
25
28
  # aws_region::
@@ -27,7 +30,9 @@ module AmiSpec
27
30
  # Defaults to AWS_DEFAULT_REGION
28
31
  # aws_security_group_ids::
29
32
  # AWS Security groups to assign to the instances
30
- # Defaults to the default security group for the VPC
33
+ # If not provided a temporary security group will be generated in AWS
34
+ # allow_any_temporary_security_group::
35
+ # The temporary security group will allow SSH connections from any IP address (0.0.0.0/0)
31
36
  # aws_instance_type::
32
37
  # AWS ec2 instance type
33
38
  # aws_public_ip::
@@ -45,6 +50,26 @@ module AmiSpec
45
50
  # == Returns:
46
51
  # Boolean - The result of all the server specs.
47
52
  def self.run(options)
53
+ logger = Logger.new(STDOUT, formatter: proc { |_sev, _time, _name, message| "#{message}\n" })
54
+
55
+ ec2 = Aws::EC2::Resource.new(options[:aws_region] ? {region: options[:aws_region]} : {})
56
+
57
+ unless options[:key_name]
58
+ key_pair = AwsKeyPair.create(ec2: ec2, logger: logger)
59
+ options[:key_name] = key_pair.key_name
60
+ options[:key_file] = key_pair.key_file
61
+ end
62
+
63
+ if options[:aws_security_groups].nil? || options[:aws_security_groups].empty?
64
+ temporary_security_group = AwsSecurityGroup.create(
65
+ ec2: ec2,
66
+ subnet_id: options[:subnet_id],
67
+ allow_any_ip: options[:allow_any_temporary_security_group],
68
+ logger: logger
69
+ )
70
+ options[:aws_security_groups] = [temporary_security_group.group_id]
71
+ end
72
+
48
73
  instances = []
49
74
  options[:amis].each_pair do |role, ami|
50
75
  aws_instance_options = AwsInstanceOptions.new(options.merge(role: role, ami: ami))
@@ -64,6 +89,8 @@ module AmiSpec
64
89
  results.all?
65
90
  ensure
66
91
  stop_instances(instances, options[:debug])
92
+ key_pair.delete if key_pair
93
+ temporary_security_group.delete if temporary_security_group
67
94
  end
68
95
 
69
96
  def self.stop_instances(instances, debug)
@@ -90,13 +117,15 @@ module AmiSpec
90
117
  type: :string
91
118
  opt :specs, "The directory to find ServerSpecs", type: :string, required: true
92
119
  opt :subnet_id, "The subnet to start the instance in", type: :string, required: true
93
- opt :key_name, "The SSH key name to assign to instances", type: :string, required: true
94
- opt :key_file, "The SSH private key file associated to the key_name", 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
95
123
  opt :ssh_user, "The user to ssh to the instance as", type: :string, required: true
96
124
  opt :aws_region, "The AWS region, defaults to AWS_DEFAULT_REGION environment variable", type: :string
97
125
  opt :aws_instance_type, "The ec2 instance type, defaults to t2.micro", type: :string, default: 't2.micro'
98
- opt :aws_security_groups, "Security groups to associate to the launched instances. May be specified multiple times",
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",
99
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)"
100
129
  opt :aws_public_ip, "Launch instances with a public IP"
101
130
  opt :ssh_retries, "The number of times we should try sshing to the ec2 instance before giving up. Defaults to 30",
102
131
  type: :int, default: 30
@@ -121,9 +150,7 @@ module AmiSpec
121
150
  fail "You must specify either role and ami or role_ami_file"
122
151
  end
123
152
 
124
- unless File.exist? options[:key_file]
125
- fail "Key file #{options[:key_file]} not found"
126
- end
153
+ fail "Key file #{options[:key_file]} not found" if options[:key_name] && !File.exist?(options.fetch(:key_file))
127
154
 
128
155
  if options[:user_data_file] and !File.exist? options[:user_data_file]
129
156
  fail "User Data file #{options[:user_data_file]} not found"
@@ -131,6 +158,12 @@ module AmiSpec
131
158
 
132
159
  options[:tags] = parse_tags(options[:tags])
133
160
 
161
+ options[:amis].each_pair do |role, _|
162
+ unless Dir.exist?("#{options[:specs]}/#{role}")
163
+ 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)."
164
+ end
165
+ end
166
+
134
167
  exit run(options)
135
168
  end
136
169
 
@@ -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, :terminate, :private_ip_address, :public_ip_address
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
@@ -36,7 +36,7 @@ module AmiSpec
36
36
 
37
37
  set :backend, :ssh
38
38
  set :host, @ip
39
- set :ssh_options, :user => @user, :keys => [@key_file], :paranoid => false
39
+ set :ssh_options, :user => @user, :keys => [@key_file], :verify_host_key => :never
40
40
 
41
41
  RSpec.configuration.fail_fast = true if @debug
42
42
 
@@ -1,3 +1,3 @@
1
1
  module AmiSpec
2
- VERSION = '1.2.0'
2
+ VERSION = '1.4.0'
3
3
  end
@@ -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], paranoid: false, port: port) do |ssh|
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,7 +8,7 @@ module AmiSpec
8
8
 
9
9
  while retries < max_retries
10
10
  begin
11
- Net::SSH.start(ip_address, user, keys: [key], paranoid: false) { |ssh| ssh.exec 'echo boo!' }
11
+ Net::SSH.start(ip_address, user, keys: [key], :verify_host_key => :never) { |ssh| ssh.exec 'echo boo!' }
12
12
  rescue Errno::ETIMEDOUT, Errno::ECONNREFUSED, Timeout::Error => error
13
13
  last_error = error
14
14
  sleep 1
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ami_spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrick Robinson
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-02-18 00:00:00.000000000 Z
12
+ date: 2019-07-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk-ec2
@@ -116,37 +116,19 @@ 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
129
123
  - lib/ami_spec/aws_instance.rb
130
124
  - lib/ami_spec/aws_instance_options.rb
125
+ - lib/ami_spec/aws_key_pair.rb
126
+ - lib/ami_spec/aws_security_group.rb
131
127
  - lib/ami_spec/server_spec.rb
132
128
  - lib/ami_spec/server_spec_options.rb
133
129
  - lib/ami_spec/version.rb
134
130
  - lib/ami_spec/wait_for_rc.rb
135
131
  - 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
132
  homepage: https://github.com/envato/ami-spec
151
133
  licenses: []
152
134
  metadata: {}
@@ -165,23 +147,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
147
  - !ruby/object:Gem::Version
166
148
  version: '0'
167
149
  requirements: []
168
- rubyforge_project:
169
- rubygems_version: 2.7.6
150
+ rubygems_version: 3.0.4
170
151
  signing_key:
171
152
  specification_version: 4
172
153
  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
154
+ test_files: []
data/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- Gemfile.lock
2
- *.gem
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.5.1
data/.travis.yml DELETED
@@ -1,7 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.2.6
4
- - 2.4.4
5
- - 2.5.1
6
- - 2.6.1
7
- script: bundle exec rake spec
data/Gemfile DELETED
@@ -1,3 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec) do |t|
5
- t.rspec_opts = "--tag ~integration"
6
- end
7
-
8
- task :default => :spec
data/ami_spec.gemspec DELETED
@@ -1,29 +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-ec2', '~> 1'
21
- gem.add_development_dependency 'rake'
22
- gem.add_dependency 'serverspec', '~> 2'
23
- gem.add_dependency 'specinfra', '>= 2.45'
24
- gem.add_dependency 'optimist', '~> 3'
25
- gem.add_dependency 'hashie'
26
- gem.add_dependency 'net-ssh', '~> 5'
27
-
28
- gem.required_ruby_version = '>= 2.2.6'
29
- end
@@ -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
@@ -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,9 +0,0 @@
1
- FROM amazonlinux:1
2
-
3
- RUN yum install -y upstart openssh-server && yum clean all
4
- ADD rc.conf /etc/init/rc.conf
5
-
6
- COPY ami-spec.pub /root/.ssh/authorized_keys
7
- COPY sshd_config /etc/ssh/sshd_config
8
-
9
- CMD ["/bin/bash", "-c", "exec /sbin/init"]
@@ -1,8 +0,0 @@
1
- FROM ubuntu-upstart:trusty
2
-
3
- ENV DEBIAN_FRONTEND noninteractive
4
- RUN apt-get update && apt-get install -y openssh-server && apt-get clean
5
-
6
- COPY ami-spec.pub /root/.ssh/authorized_keys
7
-
8
- EXPOSE 22
@@ -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"]
@@ -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.
@@ -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"
@@ -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
@@ -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
@@ -1,2 +0,0 @@
1
- $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
- require 'ami_spec'
@@ -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
@@ -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