bosh_cli_plugin_aws 1.5.0.pre.1113

Sign up to get free protection for your applications and to get access to all the features.
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