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,190 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
# split this up to CodeDeploy::Github and CodeDeploy::S3 using the
|
|
6
|
+
# revision_type, in order to make opts more strict
|
|
7
|
+
class Codedeploy < Provider
|
|
8
|
+
register :codedeploy
|
|
9
|
+
|
|
10
|
+
status :stable
|
|
11
|
+
|
|
12
|
+
full_name 'AWS Code Deploy'
|
|
13
|
+
|
|
14
|
+
description sq(<<-STR)
|
|
15
|
+
tbd
|
|
16
|
+
STR
|
|
17
|
+
|
|
18
|
+
gem 'aws-sdk-codedeploy', '~> 1.0'
|
|
19
|
+
gem 'aws-sdk-s3', '~> 1'
|
|
20
|
+
|
|
21
|
+
env :aws, :codedeploy
|
|
22
|
+
config '~/.aws/credentials', '~/.aws/config', prefix: 'aws'
|
|
23
|
+
|
|
24
|
+
opt '--access_key_id ID', 'AWS access key', required: true, secret: true
|
|
25
|
+
opt '--secret_access_key KEY', 'AWS secret access key', required: true, secret: true
|
|
26
|
+
opt '--application NAME', 'CodeDeploy application name', required: true
|
|
27
|
+
opt '--deployment_group GROUP', 'CodeDeploy deployment group name'
|
|
28
|
+
opt '--revision_type TYPE', 'CodeDeploy revision type', enum: %w[s3 github], downcase: true
|
|
29
|
+
opt '--commit_id SHA', 'Commit ID in case of GitHub'
|
|
30
|
+
opt '--repository NAME', 'Repository name in case of GitHub'
|
|
31
|
+
opt '--bucket NAME', 'S3 bucket in case of S3'
|
|
32
|
+
opt '--region REGION', 'AWS availability zone', default: 'us-east-1'
|
|
33
|
+
opt '--file_exists_behavior STR', 'How to handle files that already exist in a deployment target location', enum: %w[disallow overwrite retain], default: 'disallow'
|
|
34
|
+
opt '--wait_until_deployed', 'Wait until the deployment has finished'
|
|
35
|
+
opt '--bundle_type TYPE', 'Bundle type of the revision'
|
|
36
|
+
opt '--key KEY', 'S3 bucket key of the revision'
|
|
37
|
+
opt '--description DESCR', 'Description of the revision', interpolate: true
|
|
38
|
+
opt '--endpoint ENDPOINT', 'S3 endpoint url'
|
|
39
|
+
|
|
40
|
+
msgs login: 'Using Access Key: %{access_key_id}',
|
|
41
|
+
deploy_triggered: 'Deployment triggered: %s',
|
|
42
|
+
register_revision: 'Registering app revision with version=%s, etag=%s',
|
|
43
|
+
waiting_for_deploy: 'Waiting for the deployment to finish ',
|
|
44
|
+
finished_deploy: 'done: %s.',
|
|
45
|
+
description: 'Deploy build %{build_number} via Travis CI',
|
|
46
|
+
missing_bucket: 'Missing required bucket for S3 deployment',
|
|
47
|
+
missing_key: 'Missing required key for S3 deployment',
|
|
48
|
+
unknown_revision_type: 'Unknown revision type %p',
|
|
49
|
+
unknown_bundle_type: 'Unknown bundle type'
|
|
50
|
+
|
|
51
|
+
vars :build_number
|
|
52
|
+
|
|
53
|
+
def login
|
|
54
|
+
info :login
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def deploy
|
|
58
|
+
register_revision if revision_info[:version]
|
|
59
|
+
id = create_deployment
|
|
60
|
+
info :deploy_triggered, id
|
|
61
|
+
wait_until_deployed(id) if wait_until_deployed?
|
|
62
|
+
rescue Aws::CodeDeploy::Errors::DeploymentLimitExceededException => e
|
|
63
|
+
error e.message
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def register_revision
|
|
67
|
+
info :register_revision, revision_info[:version], revision_info[:e_tag]
|
|
68
|
+
code_deploy.register_application_revision(
|
|
69
|
+
revision:,
|
|
70
|
+
application_name: application,
|
|
71
|
+
description:
|
|
72
|
+
)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def create_deployment
|
|
76
|
+
deployment = code_deploy.create_deployment(
|
|
77
|
+
revision:,
|
|
78
|
+
application_name: application,
|
|
79
|
+
deployment_group_name: deployment_group,
|
|
80
|
+
description:,
|
|
81
|
+
file_exists_behavior: file_exists_behavior.upcase
|
|
82
|
+
)
|
|
83
|
+
deployment.deployment_id
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def wait_until_deployed(id)
|
|
87
|
+
print :waiting_for_deploy
|
|
88
|
+
status = poll(id) until %w[Succeeded Failed Stopped].include?(status)
|
|
89
|
+
case status
|
|
90
|
+
when 'Succeeded'
|
|
91
|
+
info :finished_deploy, status
|
|
92
|
+
else
|
|
93
|
+
error :finished_deploy, status
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def poll(id)
|
|
98
|
+
sleep 5
|
|
99
|
+
print '.'
|
|
100
|
+
code_deploy.get_deployment(deployment_id: id)[:deployment_info][:status]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def revision_info
|
|
104
|
+
revision[:s3_location] || {}
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def revision
|
|
108
|
+
@revision ||= case revision_type
|
|
109
|
+
when 's3' then s3_revision
|
|
110
|
+
when 'github' then github_revision
|
|
111
|
+
when nil then bucket? ? s3_revision : github_revision
|
|
112
|
+
else error :unknown_revision_type, revision_type
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def s3_revision
|
|
117
|
+
{
|
|
118
|
+
revision_type: 'S3',
|
|
119
|
+
s3_location: compact(
|
|
120
|
+
bucket:,
|
|
121
|
+
bundle_type:,
|
|
122
|
+
version: revision_version_info[:version_id],
|
|
123
|
+
e_tag: revision_version_info[:etag],
|
|
124
|
+
key:
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def revision_version_info
|
|
130
|
+
s3.head_object(bucket:, key:)
|
|
131
|
+
rescue Aws::Errors::ServiceError => e
|
|
132
|
+
error e.message
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def github_revision
|
|
136
|
+
{
|
|
137
|
+
revision_type: 'GitHub',
|
|
138
|
+
git_hub_location: {
|
|
139
|
+
commit_id:,
|
|
140
|
+
repository:
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def commit_id
|
|
146
|
+
super || git_sha
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def repository
|
|
150
|
+
super || ENV['TRAVIS_REPO_SLUG'] || File.basename(Dir.pwd)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def bucket
|
|
154
|
+
super || error(:missing_bucket)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def key
|
|
158
|
+
super || error(:missing_key)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def bundle_type
|
|
162
|
+
super || key =~ /\.(tar|tgz|zip)$/ && ::Regexp.last_match(1) || error(:unknown_bundle_type)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def description
|
|
166
|
+
interpolate(super || msg(:description), vars:)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def build_number
|
|
170
|
+
ENV['TRAVIS_BUILD_NUMBER']
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def code_deploy
|
|
174
|
+
@code_deploy ||= Aws::CodeDeploy::Client.new(client_options)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def s3
|
|
178
|
+
@s3 ||= Aws::S3::Client.new(client_options)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def client_options
|
|
182
|
+
compact(region:, credentials:, endpoint:)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def credentials
|
|
186
|
+
Aws::Credentials.new(access_key_id, secret_access_key)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Convox < Provider
|
|
6
|
+
register :convox
|
|
7
|
+
|
|
8
|
+
status :dev
|
|
9
|
+
|
|
10
|
+
description sq(<<-STR)
|
|
11
|
+
tbd
|
|
12
|
+
STR
|
|
13
|
+
|
|
14
|
+
gem 'json'
|
|
15
|
+
|
|
16
|
+
env :convox
|
|
17
|
+
|
|
18
|
+
# needs descriptions
|
|
19
|
+
opt '--host HOST', default: 'console.convox.com'
|
|
20
|
+
opt '--app APP', required: true
|
|
21
|
+
opt '--rack RACK', required: true
|
|
22
|
+
opt '--password PASS', required: true
|
|
23
|
+
opt '--install_url URL', default: 'https://convox.com/cli/linux/convox'
|
|
24
|
+
opt '--update_cli'
|
|
25
|
+
opt '--create'
|
|
26
|
+
opt '--promote', default: true
|
|
27
|
+
opt '--env_names VARS', type: :array, sep: ','
|
|
28
|
+
opt '--env VARS', type: :array
|
|
29
|
+
opt '--env_file FILE'
|
|
30
|
+
opt '--description STR'
|
|
31
|
+
opt '--generation NUM', type: :int, default: '2'
|
|
32
|
+
opt '--prepare CMDS', 'Run commands with convox cli available just before deployment', type: :array
|
|
33
|
+
|
|
34
|
+
# if app and rack are exported to the env, do they need to be passed to these commands?
|
|
35
|
+
cmds login: 'convox version --rack %{rack}',
|
|
36
|
+
validate: 'convox apps info --rack %{rack} --app %{app}',
|
|
37
|
+
create: 'convox apps create %{app} --generation %{generation} --rack %{rack} --wait',
|
|
38
|
+
update: 'convox update',
|
|
39
|
+
set_env: 'convox env set %{env} --rack %{rack} --app %{app} --replace',
|
|
40
|
+
build: 'convox build --rack %{rack} --app %{app} --id --description %{escaped_description}',
|
|
41
|
+
deploy: 'convox deploy --rack %{rack} --app %{app} --wait --id --description %{escaped_description}'
|
|
42
|
+
|
|
43
|
+
msgs create: 'Application %{app} does not exist on rack %{rack}. Creating it ...',
|
|
44
|
+
missing: 'Application %{app} does not exist on rack %{rack}.',
|
|
45
|
+
env_file: 'The given env_file does not exist.',
|
|
46
|
+
deploy: 'Building and promoting application ...',
|
|
47
|
+
build: 'Building application ...'
|
|
48
|
+
|
|
49
|
+
errs login: 'Login failed.'
|
|
50
|
+
|
|
51
|
+
def install
|
|
52
|
+
script :install
|
|
53
|
+
shell :update if update_cli?
|
|
54
|
+
export
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def login
|
|
58
|
+
shell :login
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate
|
|
62
|
+
shell :validate, assert: false and return
|
|
63
|
+
error :missing unless create?
|
|
64
|
+
shell :create
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def prepare
|
|
68
|
+
Array(super).each do |cmd|
|
|
69
|
+
cmd.casecmp('restart').zero? ? restart : run_cmd(cmd)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def deploy
|
|
74
|
+
shell :set_env, echo: false unless env.empty?
|
|
75
|
+
shell promote? ? :deploy : :build, echo: false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def env_names
|
|
79
|
+
env = super || []
|
|
80
|
+
env = env.map { |str| "#{str}=#{ENV[str]}" }
|
|
81
|
+
env_file.concat(env)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def env
|
|
85
|
+
env = env_names.concat(super || [])
|
|
86
|
+
env.map { |str| escape(str) }.join(' ')
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def env_file
|
|
90
|
+
return [] unless env_file?
|
|
91
|
+
|
|
92
|
+
error :env_file unless file?(super)
|
|
93
|
+
lines = read(super).split("\n").map(&:strip)
|
|
94
|
+
lines.reject(&:empty?)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def description
|
|
98
|
+
if description?
|
|
99
|
+
super
|
|
100
|
+
else
|
|
101
|
+
JSON.dump(
|
|
102
|
+
repo_slug:,
|
|
103
|
+
git_commit_sha: git_sha,
|
|
104
|
+
git_commit_message: git_commit_msg,
|
|
105
|
+
git_commit_author: git_author_name,
|
|
106
|
+
git_tag:,
|
|
107
|
+
branch: git_branch,
|
|
108
|
+
travis_build_id: ENV['TRAVIS_BUILD_ID'],
|
|
109
|
+
travis_build_number: ENV['TRAVIS_BUILD_NUMBER'],
|
|
110
|
+
pull_request: ENV['TRAVIS_PULL_REQUEST']
|
|
111
|
+
)
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def export
|
|
116
|
+
env_vars.each { |key, value| ENV[key.to_s] = value.to_s }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def env_vars
|
|
120
|
+
{
|
|
121
|
+
CONVOX_HOST: host,
|
|
122
|
+
CONVOX_PASSWORD: password,
|
|
123
|
+
CONVOX_APP: app,
|
|
124
|
+
CONVOX_RACK: rack,
|
|
125
|
+
CONVOX_CLI: 'convox'
|
|
126
|
+
}
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Datica < Provider
|
|
6
|
+
register :datica
|
|
7
|
+
|
|
8
|
+
status :dev
|
|
9
|
+
|
|
10
|
+
register :datica, :catalyze
|
|
11
|
+
|
|
12
|
+
description sq(<<-STR)
|
|
13
|
+
tbd
|
|
14
|
+
STR
|
|
15
|
+
|
|
16
|
+
env :datica, :catalyze
|
|
17
|
+
|
|
18
|
+
opt '--target TARGET', 'The git remote repository to deploy to', required: true
|
|
19
|
+
opt '--path PATH', 'Path to files to deploy', default: '.'
|
|
20
|
+
|
|
21
|
+
needs :git
|
|
22
|
+
|
|
23
|
+
cmds checkout: 'git checkout HEAD',
|
|
24
|
+
add: 'git add %{path} --all --force',
|
|
25
|
+
commit: 'git commit -m "%{message}" --quiet',
|
|
26
|
+
push: 'git push --force %{target} HEAD:master'
|
|
27
|
+
|
|
28
|
+
msgs commit: 'Committing build files for deployment',
|
|
29
|
+
push: 'Deploying to Datica: %{target}'
|
|
30
|
+
|
|
31
|
+
def setup
|
|
32
|
+
commit if git_dirty? && !cleanup?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def deploy
|
|
36
|
+
shell :push
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def commit
|
|
42
|
+
info :commit
|
|
43
|
+
shell :checkout
|
|
44
|
+
shell :add
|
|
45
|
+
shell :commit
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def message
|
|
49
|
+
vars.empty? ? 'Local build' : 'Build #%s (%s) of %s@%s' % vars
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
VARS = %w[
|
|
53
|
+
TRAVIS_BUILD_NUMBER
|
|
54
|
+
TRAVIS_COMMIT
|
|
55
|
+
TRAVIS_REPO_SLUG
|
|
56
|
+
TRAVIS_BRANCH
|
|
57
|
+
].freeze
|
|
58
|
+
|
|
59
|
+
def vars
|
|
60
|
+
@vars ||= ENV.values_at(*VARS).compact
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Dpl
|
|
4
|
+
module Providers
|
|
5
|
+
class Ecr < Provider
|
|
6
|
+
status :alpha
|
|
7
|
+
|
|
8
|
+
full_name 'AWS ECR'
|
|
9
|
+
|
|
10
|
+
description sq(<<-STR)
|
|
11
|
+
tbd
|
|
12
|
+
STR
|
|
13
|
+
|
|
14
|
+
gem 'aws-sdk-ecr', '~> 1.0'
|
|
15
|
+
# gem 'docker-api', '~> 1.34'
|
|
16
|
+
gem 'json'
|
|
17
|
+
|
|
18
|
+
env :aws
|
|
19
|
+
|
|
20
|
+
opt '--access_key_id ID', 'AWS access key', required: true, secret: true
|
|
21
|
+
opt '--secret_access_key KEY', 'AWS secret access key', required: true, secret: true
|
|
22
|
+
opt '--account_id ID', 'AWS Account ID', note: 'Required if the repository is owned by a different account than the IAM user'
|
|
23
|
+
opt '--source SOURCE', 'Image to push', note: 'can be the id or the name and optional tag (e.g. mysql:5.6)', required: true
|
|
24
|
+
opt '--target TARGET', 'Comma separated list of partial repository names to push to', eg: 'image-one:tag,image-two', required: true
|
|
25
|
+
opt '--region REGION', 'Comma separated list of regions to push to', default: 'us-east-1'
|
|
26
|
+
|
|
27
|
+
msgs login: 'Using Access Key: %{access_key_id}',
|
|
28
|
+
auth_region: 'Authenticated with %{url}',
|
|
29
|
+
deploy: 'Pushing image %{source} to regions %{regions} as %{targets}',
|
|
30
|
+
image_pushed: 'Pushed image %{source} to region %{region} as %{target}'
|
|
31
|
+
|
|
32
|
+
cmds login: 'docker login -u %{user} -p %{pass} %{url}',
|
|
33
|
+
tag: 'docker tag %{source} %{url}/%{repo}:%{tag}',
|
|
34
|
+
push: 'docker push %{url}/%{repo}'
|
|
35
|
+
|
|
36
|
+
errs unknown_image: 'Image %{source} not found in the local Docker repository'
|
|
37
|
+
|
|
38
|
+
attr_reader :endpoints
|
|
39
|
+
|
|
40
|
+
def login
|
|
41
|
+
info :login
|
|
42
|
+
auth_regions
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def validate
|
|
46
|
+
# TODO: validate the image exists locally
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def deploy
|
|
50
|
+
info :deploy, regions: regions.join(', '), targets: targets.join(', ')
|
|
51
|
+
regions.product(targets).each do |region, target|
|
|
52
|
+
push(region, target)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def push(region, target)
|
|
59
|
+
url, repo, tag = endpoints[region], *target.split(':')
|
|
60
|
+
shell :tag, url:, repo:, tag: tag || 'latest'
|
|
61
|
+
shell(:push, url:, repo:)
|
|
62
|
+
info :image_pushed, region:, target:
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def auth_regions
|
|
66
|
+
@endpoints = regions.map { |region| [region, auth_region(region)] }.to_h
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def auth_region(region)
|
|
70
|
+
token = auth_token(region)
|
|
71
|
+
user, pass = parse_auth(token.authorization_token)
|
|
72
|
+
url = token.proxy_endpoint
|
|
73
|
+
shell :login, user:, pass:, url:, echo: false, silent: true
|
|
74
|
+
info(:auth_region, url:)
|
|
75
|
+
strip_protocol(url)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def auth_token(region)
|
|
79
|
+
ecr(region).get_authorization_token(registry_ids).authorization_data[0]
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def registry_ids
|
|
83
|
+
account_id? ? { registry_ids: [account_id] } : {}
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def regions
|
|
87
|
+
# not sure how this was meant to be normalized when being a YAML list
|
|
88
|
+
region.split(',')
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def targets
|
|
92
|
+
# not sure how this was meant to be normalized when being a YAML list
|
|
93
|
+
target.split(',')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def creds
|
|
97
|
+
@creds ||= only(opts, :access_key_id, :secret_access_key)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def ecr(region)
|
|
101
|
+
Aws::ECR::Client.new(region:, **creds)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def parse_auth(str)
|
|
105
|
+
user, pass = Base64.decode64(str).split(':')
|
|
106
|
+
[user, pass.chomp]
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def strip_protocol(url)
|
|
110
|
+
url.sub(%r{^https?://}, '')
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def progress(events)
|
|
114
|
+
events.split("\r\n").each do |event|
|
|
115
|
+
event = JSON.parse(event)
|
|
116
|
+
if e = event['error']
|
|
117
|
+
error e
|
|
118
|
+
elsif %w[Preparing Pushing].include?(event['status'])
|
|
119
|
+
nil
|
|
120
|
+
elsif event['id']
|
|
121
|
+
info "#{event['status']} [#{event['id']}]"
|
|
122
|
+
elsif event['status']
|
|
123
|
+
info event['status']
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|