ami_spec 1.2.0 → 1.4.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: 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