travis_dpl_test 2.0.3.beta.4.ror

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +172 -0
  3. data/CODE_OF_CONDUCT.md +74 -0
  4. data/CONTRIBUTING.md +392 -0
  5. data/Gemfile +32 -0
  6. data/Gemfile.lock +611 -0
  7. data/LICENSE +19 -0
  8. data/README.md +2744 -0
  9. data/Rakefile +210 -0
  10. data/bin/dpl +11 -0
  11. data/config/transliterate.yml +733 -0
  12. data/dpl.gemspec +23 -0
  13. data/lib/dpl/assets/atlas/install +19 -0
  14. data/lib/dpl/assets/convox/install +11 -0
  15. data/lib/dpl/assets/dpl/README.erb.md +138 -0
  16. data/lib/dpl/assets/dpl/git_ssh +8 -0
  17. data/lib/dpl/assets/git/detect_private_key +8 -0
  18. data/lib/dpl/assets/hephy/filter_log +3 -0
  19. data/lib/dpl/assets/pypi/install +4 -0
  20. data/lib/dpl/assets/scalingo/install +6 -0
  21. data/lib/dpl/cli.rb +100 -0
  22. data/lib/dpl/ctx/bash.rb +549 -0
  23. data/lib/dpl/ctx/test.rb +255 -0
  24. data/lib/dpl/ctx.rb +4 -0
  25. data/lib/dpl/helper/assets.rb +38 -0
  26. data/lib/dpl/helper/cmd.rb +169 -0
  27. data/lib/dpl/helper/config_file.rb +49 -0
  28. data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
  29. data/lib/dpl/helper/env.rb +92 -0
  30. data/lib/dpl/helper/github.rb +22 -0
  31. data/lib/dpl/helper/interpolate.rb +160 -0
  32. data/lib/dpl/helper/memoize.rb +23 -0
  33. data/lib/dpl/helper/squiggle.rb +24 -0
  34. data/lib/dpl/helper/transliterate.rb +13 -0
  35. data/lib/dpl/helper/wrap.rb +11 -0
  36. data/lib/dpl/helper/zip.rb +71 -0
  37. data/lib/dpl/provider/dsl.rb +410 -0
  38. data/lib/dpl/provider/examples.rb +132 -0
  39. data/lib/dpl/provider/status.rb +61 -0
  40. data/lib/dpl/provider.rb +651 -0
  41. data/lib/dpl/providers/anynines.rb +71 -0
  42. data/lib/dpl/providers/azure_web_apps.rb +63 -0
  43. data/lib/dpl/providers/bintray.rb +324 -0
  44. data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
  45. data/lib/dpl/providers/boxfuse.rb +52 -0
  46. data/lib/dpl/providers/cargo.rb +32 -0
  47. data/lib/dpl/providers/chef_supermarket.rb +132 -0
  48. data/lib/dpl/providers/cloud66.rb +46 -0
  49. data/lib/dpl/providers/cloudfiles.rb +62 -0
  50. data/lib/dpl/providers/cloudformation.rb +281 -0
  51. data/lib/dpl/providers/cloudfoundry.rb +89 -0
  52. data/lib/dpl/providers/codedeploy.rb +190 -0
  53. data/lib/dpl/providers/convox.rb +130 -0
  54. data/lib/dpl/providers/datica.rb +64 -0
  55. data/lib/dpl/providers/ecr.rb +129 -0
  56. data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
  57. data/lib/dpl/providers/engineyard.rb +113 -0
  58. data/lib/dpl/providers/firebase.rb +45 -0
  59. data/lib/dpl/providers/flynn.rb +35 -0
  60. data/lib/dpl/providers/gae.rb +78 -0
  61. data/lib/dpl/providers/gcs.rb +132 -0
  62. data/lib/dpl/providers/git_push.rb +273 -0
  63. data/lib/dpl/providers/gleis.rb +74 -0
  64. data/lib/dpl/providers/hackage.rb +53 -0
  65. data/lib/dpl/providers/hephy.rb +107 -0
  66. data/lib/dpl/providers/heroku/api.rb +123 -0
  67. data/lib/dpl/providers/heroku/git.rb +54 -0
  68. data/lib/dpl/providers/heroku.rb +111 -0
  69. data/lib/dpl/providers/lambda.rb +211 -0
  70. data/lib/dpl/providers/launchpad.rb +80 -0
  71. data/lib/dpl/providers/netlify.rb +38 -0
  72. data/lib/dpl/providers/npm.rb +130 -0
  73. data/lib/dpl/providers/nuget.rb +41 -0
  74. data/lib/dpl/providers/openshift.rb +52 -0
  75. data/lib/dpl/providers/opsworks.rb +146 -0
  76. data/lib/dpl/providers/packagecloud.rb_ +194 -0
  77. data/lib/dpl/providers/pages/api.rb +106 -0
  78. data/lib/dpl/providers/pages/git.rb +262 -0
  79. data/lib/dpl/providers/pages.rb +18 -0
  80. data/lib/dpl/providers/puppetforge.rb +50 -0
  81. data/lib/dpl/providers/pypi.rb +125 -0
  82. data/lib/dpl/providers/releases.rb +234 -0
  83. data/lib/dpl/providers/rubygems.rb +97 -0
  84. data/lib/dpl/providers/s3.rb +251 -0
  85. data/lib/dpl/providers/scalingo.rb +69 -0
  86. data/lib/dpl/providers/script.rb +32 -0
  87. data/lib/dpl/providers/snap.rb +68 -0
  88. data/lib/dpl/providers/surge.rb +59 -0
  89. data/lib/dpl/providers/testfairy.rb +101 -0
  90. data/lib/dpl/providers/transifex.rb +72 -0
  91. data/lib/dpl/providers.rb +48 -0
  92. data/lib/dpl/string_ext.rb +23 -0
  93. data/lib/dpl/support/aws_sdk_patch.rb +26 -0
  94. data/lib/dpl/support/gems.rb +73 -0
  95. data/lib/dpl/support/gstore_patch.rb +8 -0
  96. data/lib/dpl/support/version.rb +84 -0
  97. data/lib/dpl/version.rb +5 -0
  98. data/lib/dpl.rb +23 -0
  99. data/status.json +237 -0
  100. metadata +161 -0
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class ChefSupermarket < Provider
6
+ register :'chef-supermarket'
7
+ register :chef_supermarket
8
+
9
+ status :alpha
10
+
11
+ full_name 'Chef Supermarket'
12
+
13
+ description sq(<<-STR)
14
+ tbd
15
+ STR
16
+
17
+ gem 'chef', '~> 18', require: %w[
18
+ chef/cookbook/cookbook_version_loader
19
+ chef/cookbook_uploader
20
+ ]
21
+
22
+ gem 'json'
23
+ gem 'mime-types', '~> 3.4.1'
24
+ gem 'net-telnet', '~> 0.1.0' if ruby_pre?('2.3')
25
+ gem 'rack'
26
+
27
+ env :chef
28
+
29
+ opt '--user_id ID', 'Chef Supermarket user name', required: true
30
+ opt '--name NAME', 'Cookbook name', note: 'defaults to the name given in metadata.json or metadata.rb', alias: :cookbook_name, deprecated: :cookbook_name
31
+ opt '--category CAT', 'Cookbook category in Supermarket', required: true, see: 'https://docs.getchef.com/knife_cookbook_site.html#id12', alias: :cookbook_category, deprecated: :cookbook_category
32
+ opt '--client_key KEY', 'Client API key file name', default: 'client.pem'
33
+ opt '--dir DIR', 'Directory containing the cookbook', default: '.'
34
+
35
+ URL = 'https://supermarket.chef.io/api/v1/cookbooks'
36
+
37
+ msgs validate: 'Validating cookbook',
38
+ upload: 'Uploading cookbook %{name} to %{url}',
39
+ missing_file: 'Missing file: %s',
40
+ unknown_error: 'Unknown error while sharing cookbook: %s',
41
+ version_exists: 'The same version of this cookbook already exists on the Opscode Cookbook Site.'
42
+
43
+ def setup
44
+ Chef::Config[:client_key] = client_key
45
+ chdir dir
46
+ end
47
+
48
+ def validate
49
+ info :validate
50
+ validate_file client_key
51
+ uploader.validate_cookbooks
52
+ end
53
+
54
+ def deploy
55
+ info :upload
56
+ upload
57
+ end
58
+
59
+ private
60
+
61
+ def upload
62
+ res = Chef::Knife::Core::CookbookSiteStreamingUploader.post(URL, user_id, client_key, params)
63
+ handle_error(res.body) if res.code.to_i != 201
64
+ end
65
+
66
+ def params
67
+ { cookbook: json(category: category), tarball: tarball}
68
+ end
69
+
70
+ def tarball
71
+ shell "tar -czf /tmp/#{name}.tgz -C #{build_dir} ."
72
+ shell "tar -tvf /tmp/#{name}.tgz"
73
+ open "/tmp/#{name}.tgz"
74
+ end
75
+
76
+ def name
77
+ @name ||= name_from_json || name_from_rb || error(:missing_file, 'metadata.json or metadata.rb')
78
+ end
79
+
80
+ def name_from_json
81
+ JSON.parse(read('metadata.json'))['name'] if file?('metadata.json')
82
+ end
83
+
84
+ def name_from_rb
85
+ Chef::Cookbook::Metadata.new.from_file('metadata.rb') if file?('metadata.rb')
86
+ end
87
+
88
+ def cookbook
89
+ @cookbook ||= loader.cookbook_version
90
+ end
91
+
92
+ def loader
93
+ Chef::Cookbook::CookbookVersionLoader.new('.').tap(&:load!)
94
+ end
95
+
96
+ def uploader
97
+ Chef::CookbookUploader.new(cookbook)
98
+ end
99
+
100
+ def build_dir
101
+ @build_dir ||= Chef::Knife::Core::CookbookSiteStreamingUploader.create_build_dir(cookbook)
102
+ end
103
+
104
+ def validate_file(path)
105
+ error :missing_file, path unless file?(path)
106
+ end
107
+
108
+ def url
109
+ URL
110
+ end
111
+
112
+ def handle_error(res)
113
+ res = JSON.parse(res)
114
+ unknown_error(res) unless res['error_messages']
115
+ version_exists if res['error_messages'][0].include?('Version already exists')
116
+ error (res['error_messages'][0]).to_s
117
+ end
118
+
119
+ def unknown_error(msg)
120
+ error :unknown_error, msg
121
+ end
122
+
123
+ def version_exists
124
+ error :version_exists
125
+ end
126
+
127
+ def json(obj)
128
+ JSON.dump(obj)
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Cloud66 < Provider
6
+ register :cloud66
7
+
8
+ status :alpha
9
+
10
+ description sq(<<-STR)
11
+ tbd
12
+ STR
13
+
14
+ env :cloud66
15
+
16
+ opt '--redeployment_hook URL', 'The redeployment hook URL', required: true, secret: true
17
+
18
+ msgs failed: 'Redeployment failed (%s)'
19
+
20
+ def deploy
21
+ response = client.request(request)
22
+ error :failed, response.code if response.code != '200'
23
+ end
24
+
25
+ private
26
+
27
+ def client
28
+ Net::HTTP.new(uri.host, uri.port).tap do |client|
29
+ client.use_ssl = use_ssl?
30
+ end
31
+ end
32
+
33
+ def request
34
+ Net::HTTP::Post.new(uri.path)
35
+ end
36
+
37
+ def uri
38
+ @uri ||= URI.parse(redeployment_hook)
39
+ end
40
+
41
+ def use_ssl?
42
+ uri.scheme.downcase == 'https'
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Cloudfiles < Provider
6
+ register :cloudfiles
7
+
8
+ status :alpha
9
+
10
+ full_name 'Cloud Files'
11
+
12
+ description sq(<<-STR)
13
+ tbd
14
+ STR
15
+
16
+ gem 'nokogiri', '~> 1.15'
17
+ gem 'fog-core', '~> 2.3', require: 'fog/core'
18
+ gem 'fog-rackspace', '~> 0.1.6', git: 'https://github.com/travis-oss/fog-rackspace', require: 'fog/rackspace'
19
+
20
+ env :cloudfiles
21
+
22
+ opt '--username USER', 'Rackspace username', required: true
23
+ opt '--api_key KEY', 'Rackspace API key', required: true, secret: true
24
+ opt '--region REGION', 'Cloudfiles region', required: true, enum: %w[ord dfw syd iad hkg]
25
+ opt '--container NAME', 'Name of the container that files will be uploaded to', required: true
26
+ opt '--glob GLOB', 'Paths to upload', default: '**/*'
27
+ opt '--dot_match', 'Upload hidden files starting a dot'
28
+
29
+ msgs missing_container: 'The specified container does not exist.'
30
+
31
+ def deploy
32
+ paths.each do |path|
33
+ container.files.create(key: path, body: File.open(path))
34
+ end
35
+ end
36
+
37
+ def paths
38
+ paths = Dir.glob(*glob)
39
+ paths.reject { |path| File.directory?(path) }
40
+ end
41
+
42
+ def glob
43
+ glob = [super]
44
+ glob << File::FNM_DOTMATCH if dot_match?
45
+ glob
46
+ end
47
+
48
+ def container
49
+ @container ||= api.directories.get(super) || error(:missing_container)
50
+ end
51
+
52
+ def api
53
+ @api ||= Fog::Storage.new(
54
+ provider: 'Rackspace',
55
+ rackspace_username: username,
56
+ rackspace_api_key: api_key,
57
+ rackspace_region: region
58
+ )
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,281 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Cloudformation < Provider
6
+ register :cloudformation
7
+ status :stable
8
+
9
+ full_name 'AWS CloudFormation'
10
+
11
+ description sq(<<-STR)
12
+ tbd
13
+ STR
14
+
15
+ gem 'aws-sdk-cloudformation', '~> 1.0'
16
+
17
+ env :aws, :cloudformation
18
+ config '~/.aws/credentials', prefix: 'aws'
19
+
20
+ opt '--access_key_id ID', 'AWS Access Key ID', required: true, secret: true
21
+ opt '--secret_access_key KEY', 'AWS Secret Key', required: true, secret: true
22
+ opt '--region REGION', 'AWS Region to deploy to', default: 'us-east-1'
23
+ opt '--template STR', 'CloudFormation template file', required: true, note: 'can be either a local path or an S3 URL'
24
+ opt '--stack_name NAME', 'CloudFormation Stack Name.', required: true
25
+ opt '--stack_name_prefix STR', 'CloudFormation Stack Name Prefix.'
26
+ opt '--promote', 'Deploy changes', default: true, note: 'otherwise a change set is created'
27
+ opt '--role_arn ARN', 'AWS Role ARN'
28
+ opt '--sts_assume_role ARN', 'AWS Role ARN for cross account deployments (assumed by travis using given AWS credentials).'
29
+ opt '--capabilities STR', 'CloudFormation allowed capabilities', type: :array, enum: %w[CAPABILITY_IAM CAPABILITY_NAMED_IAM CAPABILITY_AUTO_EXPAND], sep: ',', see: 'https://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_CreateStack.html'
30
+ opt '--wait', 'Wait for CloutFormation to finish the stack creation and update', default: true
31
+ opt '--wait_timeout SEC', 'How many seconds to wait for stack creation and update.', type: :integer, default: 3600
32
+ opt '--create_timeout SEC', 'How many seconds to wait before the stack status becomes CREATE_FAILED', type: :integer, default: 3600, note: 'valid only when creating a stack'
33
+ opt '--parameters STR', 'key=value pairs or ENV var names', type: :array, eg: 'one=1 or ENV_VAR_TWO'
34
+ opt '--output_file PATH', 'Path to output file to store CloudFormation outputs to'
35
+
36
+ msgs login: 'Using Access Key: %{access_key_id}',
37
+ create_stack: 'Creating stack ...',
38
+ promote_stack: 'Promoting stack ...',
39
+ create_change_set: 'Creating change set ...',
40
+ stack_up_to_date: 'Stack already up to date.',
41
+ delete_change_set: 'No changes in stack. Removing changeset.',
42
+ done: 'Done.',
43
+ missing_template: 'File does not exist: %{template}',
44
+ invalid_creds: 'Invalid credentials'
45
+
46
+ strs change_set_name: 'travis-ci-build-%{build_number}-%{now}',
47
+ change_set_desc: 'Changeset created by Travis CI job for build #%{build_number} (%{git_sha})'
48
+
49
+ def login
50
+ info :login
51
+ end
52
+
53
+ def deploy
54
+ stack_exists? ? update : create
55
+ store_events if output_file?
56
+ rescue Aws::CloudFormation::Errors::InvalidAccessKeyId
57
+ error :invalid_creds
58
+ end
59
+
60
+ private
61
+
62
+ def update
63
+ promote? ? promote : create_change_set(:update)
64
+ rescue Aws::CloudFormation::Errors::ValidationError => e
65
+ raise e unless e.message.start_with?('No updates are to be performed')
66
+
67
+ info :stack_up_to_date
68
+ end
69
+
70
+ def promote
71
+ info :promote_stack
72
+ client.update_stack(common_params)
73
+ stream_events(stack_name, :stack_update_complete) if wait?
74
+ info :done
75
+ end
76
+
77
+ def create
78
+ promote? ? create_stack : create_change_set(:create)
79
+ end
80
+
81
+ def create_stack
82
+ info :create_stack
83
+ params = { timeout_in_minutes: create_timeout, on_failure: 'ROLLBACK' }
84
+ client.create_stack(common_params.merge(params))
85
+ stream_events(stack_name, :stack_create_complete) if wait?
86
+ info :done
87
+ end
88
+
89
+ def create_change_set(type)
90
+ info :create_change_set
91
+ set = client.create_change_set(common_params.merge(change_set_params(type)))
92
+ wait_for(:change_set_create_complete, change_set_name: set.id) if wait? && !test?
93
+ info :done
94
+ rescue Aws::Waiters::Errors::FailureStateError => e
95
+ raise e unless change_set_contains_changes?(set)
96
+
97
+ info :delete_change_set
98
+ client.delete_change_set(change_set_name: set.id)
99
+ end
100
+
101
+ def change_set_params(type)
102
+ {
103
+ change_set_type: type.to_s.upcase,
104
+ change_set_name: interpolate(str(:change_set_name)),
105
+ description: interpolate(str(:change_set_desc))
106
+ }
107
+ end
108
+
109
+ def change_set_contains_changes?(change_set)
110
+ data = client.describe_change_set(change_set_name: change_set.id)
111
+ data.status_reason.start_with?(%(The submitted information didn't contain changes))
112
+ end
113
+
114
+ def stack_exists?
115
+ stack = last_stack
116
+ stack && stack.stack_status != 'REVIEW_IN_PROGRESS'
117
+ rescue Aws::CloudFormation::Errors::ValidationError => e
118
+ raise e unless e.message.include?('does not exist')
119
+
120
+ false
121
+ end
122
+
123
+ def stream_events(stack_name, condition)
124
+ stream = EventStream.new(client, stack_name, method(:info))
125
+ wait_for(condition, stack_name:) unless test? # hmm.
126
+ ensure
127
+ stream&.stop
128
+ end
129
+
130
+ def wait_for(cond, params)
131
+ started_at = Time.now
132
+ timeout = ->(*) { throw :failure if Time.now - started_at > wait_timeout }
133
+ # params = params.merge(max_attempts: nil, delay: 5, before_wait: timeout)
134
+ client.wait_until(cond, params) { |w| w.before_wait(&timeout) }
135
+ end
136
+
137
+ def store_events
138
+ logs = last_stack.outputs || {}
139
+ logs = logs.map { |log| "#{log[:output_key]}=#{log[:output_value]}" }
140
+ File.write(output_file, logs.join("\n"))
141
+ end
142
+
143
+ def last_stack
144
+ client.describe_stacks(stack_name:)[:stacks].first
145
+ end
146
+
147
+ def common_params
148
+ params = {
149
+ stack_name:,
150
+ role_arn:,
151
+ capabilities:,
152
+ parameters:
153
+ }
154
+ params.merge!(template_param)
155
+ @common_params ||= compact(params)
156
+ end
157
+
158
+ def parameters
159
+ @parameters ||= Array(super).map do |str|
160
+ key, value = str.split('=', 2)
161
+ { parameter_key: key, parameter_value: value || ENV[key] }
162
+ end
163
+ end
164
+
165
+ def create_timeout
166
+ super / 60
167
+ end
168
+
169
+ def stack_name
170
+ @stack_name ||= "#{stack_name_prefix}#{super}"
171
+ end
172
+
173
+ def template_param
174
+ str = template
175
+ return { template_url: str } if url?(str)
176
+ return { template_body: read(str) } if file?(str)
177
+
178
+ error(:missing_template)
179
+ end
180
+
181
+ def client
182
+ @client ||= Aws::CloudFormation::Client.new(client_options)
183
+ end
184
+
185
+ def client_options
186
+ params = { region:, credentials: }
187
+ params = params.merge(credentials: assume_role(params)) if sts_assume_role?
188
+ params
189
+ end
190
+
191
+ def credentials
192
+ Aws::Credentials.new(access_key_id, secret_access_key)
193
+ end
194
+
195
+ def assume_role(params)
196
+ assumed_role = Aws::STS::Client.new(params).assume_role(
197
+ role_arn: sts_assume_role,
198
+ role_session_name: "travis-build-#{build_number}"
199
+ )
200
+ Aws::Credentials.new(
201
+ assumed_role.credentials.access_key_id,
202
+ assumed_role.credentials.secret_access_key,
203
+ assumed_role.credentials.session_token
204
+ )
205
+ end
206
+
207
+ def now
208
+ Time.now.strftime('%Y%m%d%H%M%S')
209
+ end
210
+
211
+ def url?(str)
212
+ str =~ %r{^https?://}
213
+ end
214
+
215
+ class EventStream < Struct.new(:client, :stack_name, :handler)
216
+ attr_reader :thread
217
+
218
+ def initialize(*)
219
+ super
220
+ @event = describe_stack_events.stack_events.first
221
+ @thread = Thread.new(&method(:process))
222
+ end
223
+
224
+ def stop
225
+ mutex.synchronize { @stop = true }
226
+ thread.join
227
+ end
228
+
229
+ private
230
+
231
+ def process
232
+ until mutex.synchronize { @stop }
233
+ @event, events = events_since(@event)
234
+ events.each { |e| handler.call(format_event(e)) }
235
+ sleep 5 unless ENV['ENV'] == 'test'
236
+ end
237
+ end
238
+
239
+ # source: https://github.com/rvedotrc/cfn-events/blob/master/lib/cfn-events/runner.rb
240
+ def events_since(event)
241
+ described_stack = describe_stack_events
242
+ stack_events = described_stack.stack_events
243
+ return [event, []] if stack_events.first.event_id == event.event_id
244
+
245
+ events = []
246
+ described_stack.each_page do |page|
247
+ if (oldest_new = page.stack_events.index { |e| e.event_id == event.event_id })
248
+ events.concat(page.stack_events[0..oldest_new - 1])
249
+ return [events.first, events.reverse]
250
+ end
251
+ events.concat(page.stack_events)
252
+ end
253
+
254
+ warn %(Last-seen stack event is no longer returned by AWS. Please raise this as a provider's bug.)
255
+ [events.first, events.reverse]
256
+ end
257
+
258
+ def describe_stack_events
259
+ client.describe_stack_events(stack_name:)
260
+ end
261
+
262
+ def mutex
263
+ @mutex ||= Mutex.new
264
+ end
265
+
266
+ EVENT_KEYS = %i[timestamp resource_type resource_status logical_resource_id
267
+ physical_resource_id resource_status_reason].freeze
268
+
269
+ def format_event(event)
270
+ parts = EVENT_KEYS.map { |key| event.send(key) }
271
+ parts[0] = format_timestamp(parts[0])
272
+ parts.join(' ')
273
+ end
274
+
275
+ def format_timestamp(timestamp)
276
+ timestamp.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
277
+ end
278
+ end
279
+ end
280
+ end
281
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dpl
4
+ module Providers
5
+ class Cloudfoundry < Provider
6
+ register :cloudfoundry
7
+
8
+ status :stable
9
+
10
+ full_name 'Cloud Foundry'
11
+
12
+ description sq(<<-STR)
13
+ tbd
14
+ STR
15
+
16
+ env :cloudfoundry
17
+
18
+ opt '--username USER', 'Cloud Foundry username', required: true
19
+ opt '--password PASS', 'Cloud Foundry password', required: true, secret: true
20
+ opt '--organization ORG', 'Cloud Foundry organization', required: true
21
+ opt '--space SPACE', 'Cloud Foundry space', required: true
22
+ opt '--api URL', 'Cloud Foundry api URL', default: 'https://api.run.pivotal.io'
23
+ opt '--app_name APP', 'Application name'
24
+ opt '--buildpack PACK', 'Buildpack name or Git URL'
25
+ opt '--manifest FILE', 'Path to the manifest'
26
+ opt '--skip_ssl_validation', 'Skip SSL validation'
27
+ opt '--deployment_strategy STRATEGY', 'Deployment strategy, either rolling or null'
28
+ opt '--v3', 'Use the v3 API version to push the application'
29
+ opt '--logout', default: true, internal: true
30
+
31
+ cmds install: 'test $(uname) = "Linux" && rel="linux64-binary" || rel="macosx64"; wget "https://cli.run.pivotal.io/stable?release=${rel}&version=v7&source=github" -qO cf.tgz && tar -zxvf cf.tgz && rm cf.tgz',
32
+ api: './cf api %{api} %{skip_ssl_validation_opt}',
33
+ login: './cf login -u %{username} -p %{password} -o %{organization} -s %{space}',
34
+ push: './cf %{push_cmd} %{push_args}',
35
+ logout: './cf logout'
36
+
37
+ errs install: 'Failed to install CLI tools',
38
+ api: 'Failed to set api %{api}',
39
+ login: 'Failed to login',
40
+ push: 'Failed to push app',
41
+ logout: 'Failed to logout'
42
+
43
+ msgs manifest_missing: 'Application must have a manifest.yml for unattended deployment'
44
+
45
+ def install
46
+ shell :install
47
+ end
48
+
49
+ def validate
50
+ error :manifest_missing if manifest? && manifest_missing?
51
+ end
52
+
53
+ def login
54
+ shell :api
55
+ shell :login
56
+ end
57
+
58
+ def deploy
59
+ shell :push
60
+ end
61
+
62
+ def finish
63
+ shell :logout if logout?
64
+ end
65
+
66
+ private
67
+
68
+ def push_cmd
69
+ v3? ? 'v3-push' : 'push'
70
+ end
71
+
72
+ def push_args
73
+ args = []
74
+ args << quote(app_name) if app_name?
75
+ args << "-f #{manifest}" if manifest?
76
+ args << "--strategy #{deployment_strategy}" if deployment_strategy?
77
+ args.join(' ')
78
+ end
79
+
80
+ def skip_ssl_validation_opt
81
+ '--skip-ssl-validation' if skip_ssl_validation?
82
+ end
83
+
84
+ def manifest_missing?
85
+ !File.exist?(manifest)
86
+ end
87
+ end
88
+ end
89
+ end