bosh_cli_plugin_aws 1.5.0.pre.1113

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.
Files changed (36) hide show
  1. data/lib/bosh/cli/commands/aws.rb +464 -0
  2. data/lib/bosh_cli_plugin_aws/aws_config.rb +141 -0
  3. data/lib/bosh_cli_plugin_aws/aws_provider.rb +53 -0
  4. data/lib/bosh_cli_plugin_aws/bat_manifest.rb +40 -0
  5. data/lib/bosh_cli_plugin_aws/bootstrap.rb +31 -0
  6. data/lib/bosh_cli_plugin_aws/bosh_bootstrap.rb +158 -0
  7. data/lib/bosh_cli_plugin_aws/bosh_manifest.rb +71 -0
  8. data/lib/bosh_cli_plugin_aws/ec2.rb +265 -0
  9. data/lib/bosh_cli_plugin_aws/elb.rb +132 -0
  10. data/lib/bosh_cli_plugin_aws/micro_bosh_bootstrap.rb +64 -0
  11. data/lib/bosh_cli_plugin_aws/microbosh_manifest.rb +117 -0
  12. data/lib/bosh_cli_plugin_aws/migration.rb +40 -0
  13. data/lib/bosh_cli_plugin_aws/migration_helper.rb +150 -0
  14. data/lib/bosh_cli_plugin_aws/migrator.rb +137 -0
  15. data/lib/bosh_cli_plugin_aws/rds.rb +182 -0
  16. data/lib/bosh_cli_plugin_aws/route53.rb +103 -0
  17. data/lib/bosh_cli_plugin_aws/s3.rb +93 -0
  18. data/lib/bosh_cli_plugin_aws/version.rb +5 -0
  19. data/lib/bosh_cli_plugin_aws/vpc.rb +181 -0
  20. data/lib/bosh_cli_plugin_aws.rb +31 -0
  21. data/migrations/20130412000811_create_key_pairs.rb +8 -0
  22. data/migrations/20130412004642_create_vpc.rb +65 -0
  23. data/migrations/20130412181302_create_route53_records.rb +37 -0
  24. data/migrations/20130412183544_create_rds_dbs.rb +35 -0
  25. data/migrations/20130412192351_create_s3.rb +4 -0
  26. data/migrations/20130529212130_create_more_unique_s3_buckets.rb +33 -0
  27. data/migrations/20130531180445_create_bosh_rds_db.rb +30 -0
  28. data/migrations/20130826150635_update_elb_for_websockets.rb +97 -0
  29. data/migrations/20130827000001_add_secondary_az_to_vpc.rb +34 -0
  30. data/templates/aws_configuration_template.yml.erb +187 -0
  31. data/templates/aws_migration.erb +5 -0
  32. data/templates/aws_migration_spec.erb +12 -0
  33. data/templates/bat.yml.erb +84 -0
  34. data/templates/bosh.yml.erb +198 -0
  35. data/templates/micro_bosh.yml.erb +82 -0
  36. metadata +163 -0
@@ -0,0 +1,31 @@
1
+ module Bosh
2
+ module Aws
3
+ class Bootstrap
4
+ AWS_JENKINS_BUCKET = "bosh-jenkins-artifacts"
5
+
6
+ attr_accessor :options, :runner
7
+
8
+ def initialize(runner, options)
9
+ self.options = options
10
+ self.runner = runner
11
+ end
12
+
13
+ def create_user(username, password)
14
+ user = Bosh::Cli::Command::User.new(runner)
15
+ user.options = self.options
16
+ user.create(username, password)
17
+ login(username, password)
18
+ end
19
+
20
+ def login(username, password)
21
+ misc = Bosh::Cli::Command::Misc.new(runner)
22
+ misc.options = self.options
23
+ misc.login(username, password)
24
+ end
25
+
26
+ def manifest
27
+ raise NotImplementedError
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,158 @@
1
+ require_relative 'bootstrap'
2
+ require 'net/https'
3
+ require 'bosh/stemcell/archive'
4
+ require 'bosh/stemcell/archive_filename'
5
+ require 'bosh/stemcell/infrastructure'
6
+ require 'bosh/stemcell/operating_system'
7
+
8
+ module Bosh
9
+ module Aws
10
+ BootstrapError = Class.new(StandardError)
11
+
12
+ class BoshBootstrap < Bootstrap
13
+ attr_accessor :director, :s3
14
+
15
+ def initialize(director, s3, options)
16
+ self.options = options
17
+ self.options[:non_interactive] = true
18
+ self.director = director
19
+ self.s3 = s3
20
+ end
21
+
22
+ def validate_requirements
23
+
24
+ release_exist = director.list_releases.detect { |r| r['name'] == 'bosh' }
25
+ first_stemcell = director.list_stemcells.first
26
+
27
+ existing_deployments = director.list_deployments.map { |deployment| deployment['name'] }
28
+
29
+ if existing_deployments.include? manifest.bosh_deployment_name
30
+ raise BootstrapError, <<-MSG
31
+ Deployment `#{manifest.bosh_deployment_name}' already exists.
32
+ This command should be used for bootstrapping bosh from scratch.
33
+ MSG
34
+ end
35
+
36
+ return release_exist, first_stemcell
37
+ end
38
+
39
+ def start
40
+ release_exist, first_stemcell = validate_requirements
41
+
42
+ fetch_and_upload_release unless release_exist
43
+ if first_stemcell
44
+ manifest.stemcell_name = first_stemcell['name']
45
+ else
46
+ manifest.stemcell_name = Bosh::Stemcell::Archive.new(fetch_and_upload_stemcell).name
47
+ end
48
+ generate_manifest
49
+
50
+ deploy
51
+
52
+ target_bosh_and_log_in
53
+ end
54
+
55
+ private
56
+
57
+ def manifest
58
+ unless @manifest
59
+ vpc_receipt_filename = File.expand_path('aws_vpc_receipt.yml')
60
+ route53_receipt_filename = File.expand_path('aws_route53_receipt.yml')
61
+ bosh_rds_receipt_filename = File.expand_path('aws_rds_bosh_receipt.yml')
62
+
63
+ vpc_config = load_yaml_file(vpc_receipt_filename)
64
+ route53_config = load_yaml_file(route53_receipt_filename)
65
+ bosh_rds_config = load_yaml_file(bosh_rds_receipt_filename)
66
+ @manifest = Bosh::Aws::BoshManifest.new(vpc_config, route53_config, director.uuid, bosh_rds_config, options)
67
+ end
68
+
69
+ @manifest
70
+ end
71
+
72
+ def generate_manifest
73
+ deployment_folder = File.join('deployments', manifest.deployment_name)
74
+
75
+ FileUtils.mkdir_p deployment_folder
76
+ Dir.chdir(deployment_folder) do
77
+ write_yaml(manifest, manifest.file_name)
78
+ end
79
+
80
+ deployment_command = Bosh::Cli::Command::Deployment.new
81
+ deployment_command.options = self.options
82
+ deployment_command.set_current(File.join(deployment_folder, manifest.file_name))
83
+ end
84
+
85
+ def fetch_and_upload_release
86
+ release_command = Bosh::Cli::Command::Release.new
87
+ release_command.options = self.options
88
+ release_command.upload(bosh_release)
89
+ end
90
+
91
+ def target_bosh_and_log_in
92
+ misc_command = Bosh::Cli::Command::Misc.new
93
+ misc_command.options = self.options
94
+ misc_command.set_target(manifest.vip)
95
+ misc_command.options[:target] = manifest.vip
96
+ misc_command.login('admin', 'admin')
97
+
98
+ self.options[:target] = misc_command.config.target
99
+ end
100
+
101
+ def deploy
102
+ deployment_command = Bosh::Cli::Command::Deployment.new
103
+ deployment_command.options = self.options
104
+ deployment_command.perform
105
+
106
+ new_director = Bosh::Cli::Client::Director.new("https://#{manifest.vip}:25555", nil, nil,
107
+ num_retries: 12, retry_wait_interval: 5)
108
+ new_director.wait_until_ready
109
+ end
110
+
111
+ def fetch_and_upload_stemcell
112
+ stemcell_command = Bosh::Cli::Command::Stemcell.new
113
+ stemcell_command.options = self.options
114
+ stemcell_path = bosh_stemcell
115
+ stemcell_command.upload(stemcell_path)
116
+ stemcell_path
117
+ end
118
+
119
+ def bosh_stemcell
120
+ if bosh_stemcell_override
121
+ say("Using stemcell #{bosh_stemcell_override}")
122
+ return bosh_stemcell_override
123
+ end
124
+
125
+ s3.copy_remote_file(AWS_JENKINS_BUCKET,
126
+ "bosh-stemcell/aws/#{latest_aws_ubuntu_bosh_stemcell_filename}",
127
+ 'bosh_stemcell.tgz')
128
+ end
129
+
130
+ def latest_aws_ubuntu_bosh_stemcell_filename
131
+ infrastructure = Bosh::Stemcell::Infrastructure.for('aws')
132
+ operating_system = Bosh::Stemcell::OperatingSystem.for('ubuntu')
133
+ Bosh::Stemcell::ArchiveFilename.new('latest', infrastructure, operating_system, 'bosh-stemcell', true)
134
+ end
135
+
136
+ def bosh_release
137
+ if bosh_release_override
138
+ say("Using release #{bosh_release_override}")
139
+ return bosh_release_override
140
+ end
141
+ s3.copy_remote_file(AWS_JENKINS_BUCKET, "release/bosh-#{bosh_version}.tgz", 'bosh_release.tgz')
142
+ end
143
+
144
+ def bosh_stemcell_override
145
+ ENV['BOSH_OVERRIDE_LIGHT_STEMCELL_URL']
146
+ end
147
+
148
+ def bosh_release_override
149
+ ENV['BOSH_OVERRIDE_RELEASE_TGZ']
150
+ end
151
+
152
+ def bosh_version
153
+ ENV['BOSH_VERSION_OVERRIDE'] ||
154
+ Bosh::Aws::VERSION.split('.').last
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,71 @@
1
+ module Bosh
2
+ module Aws
3
+ class BoshManifest < MicroboshManifest
4
+
5
+ attr_reader :director_uuid, :rds_receipt
6
+
7
+ attr_accessor :stemcell_name
8
+
9
+ def initialize(vpc_receipt, route53_receipt, director_uuid, rds_receipt, options={})
10
+ super(vpc_receipt, route53_receipt, options)
11
+ @director_uuid = director_uuid
12
+ @rds_receipt = rds_receipt
13
+ @stemcell_name = 'bosh-aws-xen-ubuntu'
14
+ end
15
+
16
+ def file_name
17
+ "bosh.yml"
18
+ end
19
+
20
+ def deployment_name
21
+ "bosh"
22
+ end
23
+
24
+ def vip
25
+ route53_receipt['elastic_ips']['bosh']['ips'][0] || warning('Missing vip field')
26
+ end
27
+
28
+ def bosh_deployment_name
29
+ "vpc-bosh-#{name}"
30
+ end
31
+
32
+ def director_ssl_key
33
+ certificate.key.gsub("\n", "\n ")
34
+ end
35
+
36
+ def director_ssl_cert
37
+ certificate.certificate.gsub("\n", "\n ")
38
+ end
39
+
40
+ def bosh_rds_properties
41
+ rds_receipt['deployment_manifest']['properties']['bosh']
42
+ end
43
+
44
+ def bosh_rds_host
45
+ bosh_rds_properties['address']
46
+ end
47
+
48
+ def bosh_rds_port
49
+ bosh_rds_properties['port']
50
+ end
51
+
52
+ def bosh_rds_password
53
+ bosh_rds_properties['roles'].first['password']
54
+ end
55
+
56
+ def bosh_rds_user
57
+ bosh_rds_properties['roles'].first['name']
58
+ end
59
+
60
+ # RSpec overloads to_yaml when you set up expectations on an object;
61
+ # so to_y is just a way to get directly at the to_yaml implementation without fighting RSpec.
62
+ def to_y
63
+ ERB.new(File.read(get_template("bosh.yml.erb"))).result(binding)
64
+ end
65
+
66
+ def to_yaml
67
+ to_y
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,265 @@
1
+ module Bosh
2
+ module Aws
3
+ class EC2
4
+
5
+ NAT_AMI_ID = {
6
+ 'us-east-1' => 'ami-f619c29f', # ami-vpc-nat-1.1.0-beta
7
+ 'us-west-1' => 'ami-3bcc9e7e', # ami-vpc-nat-1.0.0-beta
8
+ 'us-west-2' => 'ami-52ff7262', # ami-vpc-nat-1.0.0-beta
9
+ 'eu-west-1' => 'ami-e5e2d991', # ami-vpc-nat-1.1.0-beta
10
+ 'ap-southeast-1' => 'ami-02eb9350', # ami-vpc-nat-1.0.0-beta
11
+ 'ap-northeast-1' => 'ami-14d86d15', # ami-vpc-nat-1.0.0-beta
12
+ 'ap-southeast-2' => 'ami-ab990e91', # ami-vpc-nat-1.0.0-beta
13
+ 'sa-east-1' => 'ami-0039e61d', # ami-vpc-nat-1.0.0-beta
14
+ }
15
+
16
+ attr_reader :elastic_ips
17
+
18
+ def initialize(credentials)
19
+ @aws_provider = AwsProvider.new(credentials)
20
+ @elastic_ips = []
21
+ end
22
+
23
+ def instances_count
24
+ terminatable_instances.size
25
+ end
26
+
27
+ def vpcs
28
+ aws_ec2.vpcs
29
+ end
30
+
31
+ def dhcp_options
32
+ aws_ec2.dhcp_options
33
+ end
34
+
35
+ def allocate_elastic_ips(count)
36
+ count.times do
37
+ @elastic_ips << allocate_elastic_ip.public_ip
38
+ end
39
+ #say "\tallocated #{eip.public_ip}".make_green
40
+ end
41
+
42
+ def allocate_elastic_ip
43
+ aws_ec2.elastic_ips.allocate(vpc: true)
44
+ end
45
+
46
+ def release_elastic_ips(ips)
47
+ aws_ec2.elastic_ips.each { |ip| ip.release if ips.include? ip.public_ip }
48
+ end
49
+
50
+ def release_all_elastic_ips
51
+ releasable_elastic_ips.map(&:release)
52
+ end
53
+
54
+ def create_internet_gateway
55
+ aws_ec2.internet_gateways.create
56
+ end
57
+
58
+ def internet_gateway_ids
59
+ aws_ec2.internet_gateways.map(&:id)
60
+ end
61
+
62
+ def delete_internet_gateways(ids)
63
+ Array(ids).each do |id|
64
+ gw = aws_ec2.internet_gateways[id]
65
+ gw.attachments.map(&:delete)
66
+ gw.delete
67
+ end
68
+ end
69
+
70
+ def create_instance(options)
71
+ aws_ec2.instances.create(options)
72
+ end
73
+
74
+ def create_nat_instance(options)
75
+ name = options["name"]
76
+ key_pair = select_key_pair_for_instance(name, options["key_name"])
77
+
78
+
79
+ instance_options = {
80
+ image_id: NAT_AMI_ID[aws_provider.region],
81
+ instance_type: options.fetch("instance_type", "m1.small"),
82
+ subnet: options["subnet_id"],
83
+ private_ip_address: options["ip"],
84
+ security_groups: [options["security_group"]],
85
+ key_name: key_pair
86
+ }
87
+
88
+ create_instance(instance_options).tap do |instance|
89
+ Bosh::AwsCloud::ResourceWait.for_instance(instance: instance, state: :running)
90
+ instance.add_tag("Name", {value: name})
91
+ elastic_ip = allocate_elastic_ip
92
+ Bosh::Common.retryable(tries: 30, on: AWS::EC2::Errors::InvalidAddress::NotFound) do
93
+ instance.associate_elastic_ip(elastic_ip)
94
+ true
95
+ end
96
+ disable_src_dest_checking(instance.id)
97
+ end
98
+ end
99
+
100
+ def disable_src_dest_checking(instance_id)
101
+ aws_ec2.client.modify_instance_attribute(
102
+ :instance_id => instance_id,
103
+ :source_dest_check => {:value => false}
104
+ )
105
+ end
106
+
107
+ def terminate_instances
108
+ terminatable_instances.each(&:terminate)
109
+ 1.upto(100) do
110
+ break if terminatable_instances.empty?
111
+ sleep 4
112
+ end
113
+ terminatable_instances.empty?
114
+ end
115
+
116
+ def instance_names
117
+ terminatable_instances.inject({}) do |memo, instance|
118
+ memo[instance.instance_id] = instance.tags["Name"] || '<unnamed instance>'
119
+ memo
120
+ end
121
+ end
122
+
123
+ def get_running_instance_by_name(name)
124
+ instances = aws_ec2.instances.select { |instance| instance.tags["Name"] == name && instance.status == :running }
125
+ raise "More than one running instance with name '#{name}'." if instances.count > 1
126
+ instances.first
127
+ end
128
+
129
+ def terminatable_instance_names
130
+ terminatable_instances.inject({}) do |memo, instance|
131
+ memo[instance.instance_id] = instance.tags["Name"]
132
+ memo
133
+ end
134
+ end
135
+
136
+ def delete_volumes
137
+ unattached_volumes.each do |vol|
138
+ begin
139
+ vol.delete
140
+ rescue AWS::EC2::Errors::InvalidVolume::NotFound
141
+ # ignored
142
+ end
143
+ end
144
+ end
145
+
146
+ def volume_count
147
+ unattached_volumes.count
148
+ end
149
+
150
+ def add_key_pair(name, path_to_public_private_key)
151
+ private_key_path = path_to_public_private_key.gsub(/\.pub$/, '')
152
+ public_key_path = "#{private_key_path}.pub"
153
+
154
+ if !File.exist?(private_key_path)
155
+ system "ssh-keygen", "-q", '-N', "", "-t", "rsa", "-f", private_key_path
156
+ end
157
+
158
+ unless key_pair_by_name(name).nil?
159
+ err "Key pair #{name} already exists on AWS"
160
+ end
161
+
162
+ aws_ec2.key_pairs.import(name, File.read(public_key_path))
163
+ end
164
+
165
+ def key_pair_by_name(name)
166
+ key_pairs.detect { |kp| kp.name == name }
167
+ end
168
+
169
+ def key_pairs
170
+ aws_ec2.key_pairs.to_a
171
+ end
172
+
173
+ def force_add_key_pair(name, path_to_public_private_key)
174
+ remove_key_pair(name)
175
+ add_key_pair(name, path_to_public_private_key)
176
+ end
177
+
178
+ def remove_key_pair(name)
179
+ key_pair = key_pair_by_name(name)
180
+ key_pair.delete unless key_pair.nil?
181
+ Bosh::Common.retryable(tries: 15) do
182
+ key_pair_by_name(name).nil?
183
+ end
184
+ end
185
+
186
+ def remove_all_key_pairs
187
+ aws_ec2.key_pairs.each(&:delete)
188
+
189
+ Bosh::Common.retryable(tries: 10) do
190
+ aws_ec2.key_pairs.to_a.empty?
191
+ end
192
+ end
193
+
194
+ def delete_all_security_groups
195
+ dsg = deletable_security_groups
196
+
197
+ # Revoke all permissions before deleting because a permission can reference
198
+ # another security group, causing a delete to fail
199
+ dsg.each do |sg|
200
+ sg.ingress_ip_permissions.map(&:revoke)
201
+ sg.egress_ip_permissions.map(&:revoke)
202
+ end
203
+
204
+ dsg.each do |sg|
205
+ sg.delete unless (sg.name == "default" && !sg.vpc_id)
206
+ end
207
+ end
208
+
209
+ private
210
+
211
+ attr_reader :aws_provider
212
+
213
+ def aws_ec2
214
+ aws_provider.ec2
215
+ end
216
+
217
+ def terminatable_instances
218
+ aws_ec2.instances.reject do |i|
219
+ begin
220
+ i.api_termination_disabled? || i.status.to_s == "terminated"
221
+ rescue AWS::Core::Resource::NotFound
222
+ # ignoring instances which disappear while we are going through them
223
+ end
224
+ end
225
+ end
226
+
227
+ def releasable_elastic_ips
228
+ ti = terminatable_instances.map(&:id)
229
+ aws_ec2.elastic_ips.select { |eip| eip.instance_id.nil? || ti.include?(eip.instance_id) }
230
+ end
231
+
232
+ def deletable_security_groups
233
+ aws_ec2.security_groups.reject { |sg| security_group_in_use?(sg) }
234
+ end
235
+
236
+ def security_group_in_use?(sg)
237
+ sg.instances.any? { |s| s.api_termination_disabled? }
238
+ end
239
+
240
+ def unattached_volumes
241
+ # only check volumes that don't have status 'deleting' and 'deleted'
242
+ aws_ec2.volumes.filter('status', %w[available creating in_use error]).
243
+ reject { |v| v.attachments.any? }
244
+ end
245
+
246
+ def select_key_pair_for_instance(name, key_pair)
247
+ if key_pair.nil?
248
+ if key_pairs.count > 1
249
+ raise "AWS key pair name unspecified for instance '#{name}', unable to select a default."
250
+ elsif key_pairs.count == 0
251
+ raise "AWS key pair name unspecified for instance '#{name}', no key pairs available to select a default."
252
+ else
253
+ key_pair = key_pairs.first.name
254
+ end
255
+ else
256
+ if key_pairs.none? { |kp| kp.name == key_pair }
257
+ raise "No such key pair '#{key_pair}' on AWS."
258
+ end
259
+ end
260
+
261
+ key_pair
262
+ end
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,132 @@
1
+ module Bosh::Aws
2
+ class ELB
3
+ class BadCertificateError < RuntimeError;
4
+ end
5
+
6
+ def initialize(credentials)
7
+ @aws_provider = AwsProvider.new(credentials)
8
+ end
9
+
10
+ def create(name, vpc, settings, certs)
11
+ subnet_names = settings['subnets']
12
+ subnet_ids = vpc.subnets.select { |k, v| subnet_names.include?(k) }.values
13
+ security_group_name = settings['security_group']
14
+ security_group_id = vpc.security_group_by_name(security_group_name).id
15
+ options = {
16
+ :listeners => [{
17
+ port: 80,
18
+ protocol: :http,
19
+ instance_port: 80,
20
+ instance_protocol: :http,
21
+ }],
22
+ :subnets => subnet_ids,
23
+ :security_groups => [security_group_id]
24
+ }
25
+
26
+ if settings['https']
27
+ domain = settings['domain']
28
+ cert_name = settings['ssl_cert']
29
+ cert = certs[cert_name]
30
+ dns_record = settings['dns_record']
31
+
32
+ certificate = Bosh::Ssl::Certificate.new(cert['private_key_path'],
33
+ cert['certificate_path'],
34
+ "#{dns_record}.#{domain}",
35
+ cert['certificate_chain_path']
36
+ ).load_or_create
37
+
38
+ uploaded_cert = upload_certificate(cert_name, certificate)
39
+
40
+ options[:listeners] << {
41
+ :port => 443,
42
+ :protocol => :https,
43
+ :instance_port => 80,
44
+ :instance_protocol => :http,
45
+ # passing through 'ssl_certificate_id' is undocumented, but we're
46
+ # working around a bug filed here: https://github.com/aws/aws-sdk-ruby/issues/216
47
+ :ssl_certificate_id => uploaded_cert.arn
48
+ }
49
+ end
50
+
51
+ Bosh::Common.retryable(tries: 15, on: AWS::ELB::Errors::CertificateNotFound) do
52
+ aws_elb.load_balancers.create(name, options).tap do |new_elb|
53
+ new_elb.configure_health_check({
54
+ :healthy_threshold => 5,
55
+ :unhealthy_threshold => 2,
56
+ :interval => 5,
57
+ :timeout => 2,
58
+ :target => 'TCP:80'
59
+ })
60
+ end
61
+ end
62
+ end
63
+
64
+ def names
65
+ aws_elb.load_balancers.map(&:name)
66
+ end
67
+
68
+ def server_certificate_names
69
+ aws_iam.server_certificates.map(&:name)
70
+ end
71
+
72
+ def delete_elbs
73
+ aws_elb.load_balancers.each(&:delete)
74
+ delete_server_certificates
75
+ end
76
+
77
+ def delete_server_certificates
78
+ Bosh::Common.retryable(tries: 5, sleep: 2) do
79
+ aws_iam.server_certificates.each(&:delete)
80
+ aws_iam.server_certificates.to_a.empty?
81
+ end
82
+ end
83
+
84
+ def find_by_name(name)
85
+ aws_elb.load_balancers.find { |lb| lb.name == name }
86
+ end
87
+
88
+ private
89
+
90
+ attr_reader :aws_provider
91
+
92
+ def aws_iam
93
+ aws_provider.iam
94
+ end
95
+
96
+ def aws_elb
97
+ aws_provider.elb
98
+ end
99
+
100
+ def upload_certificate(name, cert)
101
+ certificates = aws_iam.server_certificates
102
+ options = {
103
+ name: name,
104
+ certificate_body: cert.certificate,
105
+ private_key: cert.key
106
+ }
107
+
108
+ options[:certificate_chain] = cert.chain if cert.chain
109
+
110
+ begin
111
+ certificate = nil
112
+
113
+ Bosh::Common.retryable(on: AWS::IAM::Errors::MalformedCertificate, tries: 10, sleep: 2) do
114
+ begin
115
+ certificate = certificates.upload(options)
116
+ server_certificate_names.include? name
117
+ rescue AWS::IAM::Errors::EntityAlreadyExists
118
+ certificate = aws_iam.server_certificates[name]
119
+ true
120
+ end
121
+ end
122
+
123
+ certificate
124
+ rescue AWS::IAM::Errors::MalformedCertificate => e
125
+ certificate = cert.certificate
126
+ private_key = cert.key
127
+ message = "Certificate:\n#{certificate}\n\nPrivate Key:\n#{private_key}"
128
+ raise BadCertificateError.new("Unable to upload ELB SSL Certificate: #{e.message}\n#{message}")
129
+ end
130
+ end
131
+ end
132
+ end