travis_dpl_test 2.0.3.beta.4.ror
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 +7 -0
- data/CHANGELOG.md +172 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +392 -0
- data/Gemfile +32 -0
- data/Gemfile.lock +611 -0
- data/LICENSE +19 -0
- data/README.md +2744 -0
- data/Rakefile +210 -0
- data/bin/dpl +11 -0
- data/config/transliterate.yml +733 -0
- data/dpl.gemspec +23 -0
- data/lib/dpl/assets/atlas/install +19 -0
- data/lib/dpl/assets/convox/install +11 -0
- data/lib/dpl/assets/dpl/README.erb.md +138 -0
- data/lib/dpl/assets/dpl/git_ssh +8 -0
- data/lib/dpl/assets/git/detect_private_key +8 -0
- data/lib/dpl/assets/hephy/filter_log +3 -0
- data/lib/dpl/assets/pypi/install +4 -0
- data/lib/dpl/assets/scalingo/install +6 -0
- data/lib/dpl/cli.rb +100 -0
- data/lib/dpl/ctx/bash.rb +549 -0
- data/lib/dpl/ctx/test.rb +255 -0
- data/lib/dpl/ctx.rb +4 -0
- data/lib/dpl/helper/assets.rb +38 -0
- data/lib/dpl/helper/cmd.rb +169 -0
- data/lib/dpl/helper/config_file.rb +49 -0
- data/lib/dpl/helper/cookbook_site_streaming_uploader.rb +249 -0
- data/lib/dpl/helper/env.rb +92 -0
- data/lib/dpl/helper/github.rb +22 -0
- data/lib/dpl/helper/interpolate.rb +160 -0
- data/lib/dpl/helper/memoize.rb +23 -0
- data/lib/dpl/helper/squiggle.rb +24 -0
- data/lib/dpl/helper/transliterate.rb +13 -0
- data/lib/dpl/helper/wrap.rb +11 -0
- data/lib/dpl/helper/zip.rb +71 -0
- data/lib/dpl/provider/dsl.rb +410 -0
- data/lib/dpl/provider/examples.rb +132 -0
- data/lib/dpl/provider/status.rb +61 -0
- data/lib/dpl/provider.rb +651 -0
- data/lib/dpl/providers/anynines.rb +71 -0
- data/lib/dpl/providers/azure_web_apps.rb +63 -0
- data/lib/dpl/providers/bintray.rb +324 -0
- data/lib/dpl/providers/bluemixcloudfoundry.rb +98 -0
- data/lib/dpl/providers/boxfuse.rb +52 -0
- data/lib/dpl/providers/cargo.rb +32 -0
- data/lib/dpl/providers/chef_supermarket.rb +132 -0
- data/lib/dpl/providers/cloud66.rb +46 -0
- data/lib/dpl/providers/cloudfiles.rb +62 -0
- data/lib/dpl/providers/cloudformation.rb +281 -0
- data/lib/dpl/providers/cloudfoundry.rb +89 -0
- data/lib/dpl/providers/codedeploy.rb +190 -0
- data/lib/dpl/providers/convox.rb +130 -0
- data/lib/dpl/providers/datica.rb +64 -0
- data/lib/dpl/providers/ecr.rb +129 -0
- data/lib/dpl/providers/elasticbeanstalk.rb +207 -0
- data/lib/dpl/providers/engineyard.rb +113 -0
- data/lib/dpl/providers/firebase.rb +45 -0
- data/lib/dpl/providers/flynn.rb +35 -0
- data/lib/dpl/providers/gae.rb +78 -0
- data/lib/dpl/providers/gcs.rb +132 -0
- data/lib/dpl/providers/git_push.rb +273 -0
- data/lib/dpl/providers/gleis.rb +74 -0
- data/lib/dpl/providers/hackage.rb +53 -0
- data/lib/dpl/providers/hephy.rb +107 -0
- data/lib/dpl/providers/heroku/api.rb +123 -0
- data/lib/dpl/providers/heroku/git.rb +54 -0
- data/lib/dpl/providers/heroku.rb +111 -0
- data/lib/dpl/providers/lambda.rb +211 -0
- data/lib/dpl/providers/launchpad.rb +80 -0
- data/lib/dpl/providers/netlify.rb +38 -0
- data/lib/dpl/providers/npm.rb +130 -0
- data/lib/dpl/providers/nuget.rb +41 -0
- data/lib/dpl/providers/openshift.rb +52 -0
- data/lib/dpl/providers/opsworks.rb +146 -0
- data/lib/dpl/providers/packagecloud.rb_ +194 -0
- data/lib/dpl/providers/pages/api.rb +106 -0
- data/lib/dpl/providers/pages/git.rb +262 -0
- data/lib/dpl/providers/pages.rb +18 -0
- data/lib/dpl/providers/puppetforge.rb +50 -0
- data/lib/dpl/providers/pypi.rb +125 -0
- data/lib/dpl/providers/releases.rb +234 -0
- data/lib/dpl/providers/rubygems.rb +97 -0
- data/lib/dpl/providers/s3.rb +251 -0
- data/lib/dpl/providers/scalingo.rb +69 -0
- data/lib/dpl/providers/script.rb +32 -0
- data/lib/dpl/providers/snap.rb +68 -0
- data/lib/dpl/providers/surge.rb +59 -0
- data/lib/dpl/providers/testfairy.rb +101 -0
- data/lib/dpl/providers/transifex.rb +72 -0
- data/lib/dpl/providers.rb +48 -0
- data/lib/dpl/string_ext.rb +23 -0
- data/lib/dpl/support/aws_sdk_patch.rb +26 -0
- data/lib/dpl/support/gems.rb +73 -0
- data/lib/dpl/support/gstore_patch.rb +8 -0
- data/lib/dpl/support/version.rb +84 -0
- data/lib/dpl/version.rb +5 -0
- data/lib/dpl.rb +23 -0
- data/status.json +237 -0
- 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
|