awes_cli 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/ather +22 -2
- data/lib/build_app_image.rb +15 -5
- data/lib/deploy_app.rb +135 -0
- data/lib/deploy_config.rb +210 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: be520b19b3caa21d29f8208eab535c95e728180b7c74e77feb477263933587f4
|
4
|
+
data.tar.gz: 1233834f30464477ec72c618857d980bd68c2d750ce5c8cbd508a98db8876371
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 655d71c2e1f7563b8bd6f414f2e3ca73ee5a4c50713ab9009dc9a5709a30866682494cd1d2ddf6edd8bc8d8fa2e47d39cf2ae2c57c12bdf76ed45accd4671fa4
|
7
|
+
data.tar.gz: e4133b8c0233f5f89352e968daeaad5f31f837b1f0adeed939b83bd986e9d476dcf5e6f6ab8b52d6c56d4ad559754e44e656cad31dbdb21ae1203f15fc39d4b8
|
data/bin/ather
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require 'build_app_image'
|
3
|
+
require 'deploy_app'
|
4
|
+
require 'deploy_config'
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
primary_cmd = ARGV[0]
|
7
|
+
secondary_cmd = ARGV[1]
|
8
|
+
|
9
|
+
if primary_cmd.nil?
|
10
|
+
puts 'Run `ather build|deploy`'
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
if primary_cmd == 'deploy' && secondary_cmd.nil?
|
15
|
+
puts 'Run `ather deploy app|config`'
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
|
19
|
+
if primary_cmd == 'build'
|
20
|
+
BuildAppImage.new.main
|
21
|
+
elsif primary_cmd == 'deploy' && secondary_cmd == 'app'
|
22
|
+
DeployApp.new.main
|
23
|
+
elsif primary_cmd == 'deploy' && secondary_cmd == 'config'
|
24
|
+
DeployConfig.new.main
|
25
|
+
end
|
data/lib/build_app_image.rb
CHANGED
@@ -17,7 +17,11 @@ class BuildAppImage
|
|
17
17
|
require_options_keys = [:project_id, :app_name]
|
18
18
|
|
19
19
|
opt_parser = OptionParser.new do |opts|
|
20
|
-
opts.banner = "Usage:
|
20
|
+
opts.banner = "Usage: ather build [options]"
|
21
|
+
|
22
|
+
opts.on("--image-tag IMAGE_TAG", "Image tag to deploy [optional]") do |val|
|
23
|
+
options[:image_tag] = val
|
24
|
+
end
|
21
25
|
|
22
26
|
opts.on("--project-id PROJECT_ID", "GCP project id [required or set env var PROJECT_ID]") do |val|
|
23
27
|
options[:project_id] = val
|
@@ -60,13 +64,19 @@ class BuildAppImage
|
|
60
64
|
def build_image(gcr_url, options)
|
61
65
|
commit_image_tag = ENV['CI_COMMIT_SHORT_SHA'] || `git rev-parse --short HEAD`
|
62
66
|
time_image_tag=`TZ=IST-5:30 date +'%Y.%m.%d.%HH.%MM.%SS'`
|
63
|
-
|
67
|
+
commit_image_url = "#{gcr_url}:#{commit_image_tag}".strip
|
68
|
+
time_image_url = "#{gcr_url}:#{time_image_tag}".strip
|
69
|
+
|
64
70
|
project_id = options[:project_id]
|
65
71
|
|
66
|
-
shell_cmd("gcloud builds submit --project #{project_id} --timeout=20m --tag #{
|
72
|
+
shell_cmd("gcloud builds submit --project #{project_id} --timeout=20m --tag #{time_image_url} || true")
|
73
|
+
|
74
|
+
shell_cmd("gcloud container images add-tag --quiet #{time_image_url} #{commit_image_url}")
|
75
|
+
|
76
|
+
opt_image_url = options[:image_tag] && "#{gcr_url}:#{options[:image_tag]}"
|
67
77
|
|
68
|
-
shell_cmd("gcloud container images add-tag --quiet #{
|
78
|
+
opt_image_url && shell_cmd("gcloud container images add-tag --quiet #{time_image_url} #{opt_image_url}")
|
69
79
|
|
70
|
-
|
80
|
+
opt_image_url
|
71
81
|
end
|
72
82
|
end
|
data/lib/deploy_app.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
class DeployApp
|
5
|
+
def main
|
6
|
+
options = get_options
|
7
|
+
take_approval(options)
|
8
|
+
|
9
|
+
gcr_url = "gcr.io/#{options[:project_id]}/#{options[:app_name]}"
|
10
|
+
|
11
|
+
if(!options[:image_tag].nil?)
|
12
|
+
image_url = validate_image_tag_exists?(gcr_url, options[:image_tag])
|
13
|
+
else
|
14
|
+
image_url = build_image(gcr_url, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
deploy_image(gcr_url, image_url, options)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
def get_options
|
22
|
+
options = {}
|
23
|
+
|
24
|
+
require_options_keys = [:project_id, :app_name, :namespace, :cluster, :cluster_location]
|
25
|
+
|
26
|
+
opt_parser = OptionParser.new do |opts|
|
27
|
+
opts.banner = "Usage: ather deploy app [options]"
|
28
|
+
|
29
|
+
opts.on("-y", "Auto approve") do |flag|
|
30
|
+
options[:approve] = flag
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on("--image-tag IMAGE_TAG", "Image tag to deploy [optional]") do |val|
|
34
|
+
options[:image_tag] = val
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("--project-id PROJECT_ID", "GCP project id [required or set env var PROJECT_ID]") do |val|
|
38
|
+
options[:project_id] = val
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("--app-name APP_NAME", "K8s app name [required or set env var APP_NAME]") do |val|
|
42
|
+
options[:app_name] = val
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on("--namespace NAMESPACE", "K8s app namespace [required or set env var NAMESPACE]") do |val|
|
46
|
+
options[:namespace] = val
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on("--cluster CLUSTER", "App name [required or set env var CLUSTER]") do |val|
|
50
|
+
options[:cluster] = val
|
51
|
+
end
|
52
|
+
|
53
|
+
opts.on("--cluster-location CLUSTER_LOCATION", "App name [required or set env var CLUSTER_LOCATION]") do |val|
|
54
|
+
options[:cluster_location] = val
|
55
|
+
end
|
56
|
+
|
57
|
+
opts.on("-h", "--help", "Prints this help") do
|
58
|
+
puts opts
|
59
|
+
exit
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
opt_parser.parse!
|
64
|
+
|
65
|
+
require_options_keys.each do |key|
|
66
|
+
options[key] ||= ENV[key.to_s.upcase]
|
67
|
+
if options[key].nil?
|
68
|
+
puts "ERROR: Required option --#{key.to_s.gsub('_', '-')} or set env var #{key.to_s.upcase} \n"
|
69
|
+
opt_parser.parse! %w[--help]
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
options
|
75
|
+
end
|
76
|
+
|
77
|
+
def shell_cmd(cmd)
|
78
|
+
puts "EXECUTING : \e[32m\e[1m#{cmd}\e[22m\e[0m"
|
79
|
+
op = system(cmd)
|
80
|
+
if !op
|
81
|
+
puts "FAILED : \e[31m\e[1m#{cmd}\e[22m\e[0m"
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
puts "SUCCESS : \e[32m\e[1m#{cmd}\e[22m\e[0m"
|
85
|
+
end
|
86
|
+
|
87
|
+
def take_approval(options)
|
88
|
+
puts "\e[32m\e[1mDeployment parameter details \e[22m\e[0m"
|
89
|
+
options.each do |key, val|
|
90
|
+
puts "\e[1m#{key} = \e[31m#{val}\e[0m \e[22m"
|
91
|
+
end
|
92
|
+
|
93
|
+
return if(options[:approve])
|
94
|
+
puts 'Are you sure to want to deploy as per the above deployment parameters?'
|
95
|
+
confirmation = STDIN.gets.strip
|
96
|
+
return if(confirmation == 'y' || confirmation == 'Y')
|
97
|
+
exit
|
98
|
+
end
|
99
|
+
|
100
|
+
def validate_image_tag_exists?(gcr_url, image_tag)
|
101
|
+
image_url = "#{gcr_url}:#{image_tag}"
|
102
|
+
shell_cmd("gcloud container images describe #{image_url}")
|
103
|
+
image_url
|
104
|
+
end
|
105
|
+
|
106
|
+
def build_image(gcr_url, options)
|
107
|
+
commit_image_tag = ENV['CI_COMMIT_SHORT_SHA'] || `git rev-parse --short HEAD`
|
108
|
+
time_image_tag=`TZ=IST-5:30 date +'%Y.%m.%d.%HH.%MM.%SS'`
|
109
|
+
commit_image_url = "#{gcr_url}:#{commit_image_tag}".strip
|
110
|
+
time_image_url = "#{gcr_url}:#{time_image_tag}".strip
|
111
|
+
|
112
|
+
project_id = options[:project_id]
|
113
|
+
|
114
|
+
shell_cmd("gcloud builds submit --project #{project_id} --timeout=20m --tag #{time_image_url} || true")
|
115
|
+
|
116
|
+
shell_cmd("gcloud container images add-tag --quiet #{time_image_url} #{commit_image_url}")
|
117
|
+
|
118
|
+
commit_image_url
|
119
|
+
end
|
120
|
+
|
121
|
+
def deploy_image(gcr_url, image_url, options)
|
122
|
+
shell_cmd(
|
123
|
+
"gcloud container clusters get-credentials #{options[:cluster]} " \
|
124
|
+
"--region #{options[:cluster_location]} " \
|
125
|
+
"--project #{options[:project_id]} "
|
126
|
+
)
|
127
|
+
|
128
|
+
app_name = options[:app_name]
|
129
|
+
shell_cmd("kubectl set image deployment #{app_name} #{app_name}=#{image_url} -n #{options[:namespace]}")
|
130
|
+
|
131
|
+
shell_cmd("kubectl rollout status deployment/#{app_name} -n #{options[:namespace]}")
|
132
|
+
|
133
|
+
shell_cmd("gcloud container images add-tag --quiet #{image_url} #{gcr_url}:current")
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'json'
|
5
|
+
require 'yaml'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
class DeployConfig
|
9
|
+
def main
|
10
|
+
options = get_options
|
11
|
+
|
12
|
+
take_approval_deploy_params(options)
|
13
|
+
|
14
|
+
take_approval_app_env(options)
|
15
|
+
|
16
|
+
gcr_url = "gcr.io/#{options[:project_id]}/#{options[:app_name]}"
|
17
|
+
|
18
|
+
deploy_config(gcr_url, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def get_options
|
24
|
+
options = {}
|
25
|
+
|
26
|
+
require_options_keys = [:project_id, :app_name, :namespace, :cluster, :cluster_location, :kuztomize_env]
|
27
|
+
|
28
|
+
opt_parser = OptionParser.new do |opts|
|
29
|
+
opts.banner = "Usage: ather deploy config [options]"
|
30
|
+
|
31
|
+
opts.on("-y", "Auto approve") do |flag|
|
32
|
+
options[:approve] = flag
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("--image-tag IMAGE_TAG", "Image tag to deploy [optional]") do |val|
|
36
|
+
options[:image_tag] = val
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--project-id PROJECT_ID", "GCP project id [required or set env var PROJECT_ID]") do |val|
|
40
|
+
options[:project_id] = val
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("--app-name APP_NAME", "K8s app name [required or set env var APP_NAME]") do |val|
|
44
|
+
options[:app_name] = val
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--namespace NAMESPACE", "K8s app namespace [required or set env var NAMESPACE]") do |val|
|
48
|
+
options[:namespace] = val
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("--cluster CLUSTER", "App name [required or set env var CLUSTER]") do |val|
|
52
|
+
options[:cluster] = val
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("--cluster-location CLUSTER_LOCATION", "App name [required or set env var CLUSTER_LOCATION]") do |val|
|
56
|
+
options[:cluster_location] = val
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on("--kuztomize-env KUZTOMIZE_ENV", "App name [required or set env var KUZTOMIZE_ENV]") do |val|
|
60
|
+
options[:kuztomize_env] = val
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on("-h", "--help", "Prints this help") do
|
64
|
+
puts opts
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
opt_parser.parse!
|
70
|
+
|
71
|
+
require_options_keys.each do |key|
|
72
|
+
options[key] ||= ENV[key.to_s.upcase]
|
73
|
+
if options[key].nil?
|
74
|
+
puts "ERROR: Required option --#{key.to_s.gsub('_', '-')} or set env var #{key.to_s.upcase} \n"
|
75
|
+
opt_parser.parse! %w[--help]
|
76
|
+
exit 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
options
|
81
|
+
end
|
82
|
+
|
83
|
+
def shell_cmd(cmd)
|
84
|
+
puts "EXECUTING : \e[32m\e[1m#{cmd}\e[22m\e[0m"
|
85
|
+
op = system(cmd)
|
86
|
+
if !op
|
87
|
+
puts "FAILED : \e[31m\e[1m#{cmd}\e[22m\e[0m"
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
puts "SUCCESS : \e[32m\e[1m#{cmd}\e[22m\e[0m"
|
91
|
+
end
|
92
|
+
|
93
|
+
def take_approval_deploy_params(options)
|
94
|
+
puts "\e[32m\e[1mDeployment parameter details \e[22m\e[0m"
|
95
|
+
options.each do |key, val|
|
96
|
+
puts "\e[1m#{key} = \e[31m#{val}\e[0m \e[22m"
|
97
|
+
end
|
98
|
+
|
99
|
+
return if(options[:approve])
|
100
|
+
puts 'Are you sure to want to deploy as per the above deployment parameters? (y/N)'
|
101
|
+
confirmation = STDIN.gets.strip
|
102
|
+
return if(confirmation == 'y' || confirmation == 'Y')
|
103
|
+
exit
|
104
|
+
end
|
105
|
+
|
106
|
+
def take_approval_app_env(options)
|
107
|
+
override_env_filepath = "./deploy/env/#{options[:kuztomize_env]}.sh"
|
108
|
+
return if !File.file?(override_env_filepath)
|
109
|
+
puts "\e[32m\e[1mOverriding Application enviroment variables \e[22m\e[0m"
|
110
|
+
|
111
|
+
app_envs = `cat #{override_env_filepath}`.gsub('export ', "\n")
|
112
|
+
puts "\e[34m\e[1m#{app_envs}\e[22m\e[0m"
|
113
|
+
|
114
|
+
return if(options[:approve])
|
115
|
+
puts 'Are you sure to want to deploy the above application enviroment variables? (y/N)'
|
116
|
+
confirmation = STDIN.gets.strip
|
117
|
+
return if(confirmation == 'y' || confirmation == 'Y')
|
118
|
+
exit
|
119
|
+
end
|
120
|
+
|
121
|
+
def validate_image_tag_exists?(image_url)
|
122
|
+
shell_cmd("gcloud container images describe #{image_url}")
|
123
|
+
image_url
|
124
|
+
end
|
125
|
+
|
126
|
+
def deploy_config(gcr_url, options)
|
127
|
+
shell_cmd(
|
128
|
+
"gcloud container clusters get-credentials #{options[:cluster]} " \
|
129
|
+
"--region #{options[:cluster_location]} " \
|
130
|
+
"--project #{options[:project_id]} "
|
131
|
+
)
|
132
|
+
|
133
|
+
spec = app_container_spec(options[:app_name], options[:namespace]) || {}
|
134
|
+
|
135
|
+
override_envs = override_envs(options)
|
136
|
+
k8s_deployment_envs = k8s_deployment_envs(options)
|
137
|
+
|
138
|
+
yaml_file = `kustomize build deploy/overlays/#{options[:kuztomize_env]}`
|
139
|
+
|
140
|
+
yaml_file.scan(/\$[A-Z_]+/).each do |env_name|
|
141
|
+
env_key = env_name[1..-1]
|
142
|
+
env_val = override_envs[env_key] || ENV[env_key] || k8s_deployment_envs[env_key]
|
143
|
+
next if env_val.nil?
|
144
|
+
yaml_file[env_name] = env_val
|
145
|
+
end
|
146
|
+
|
147
|
+
gcr_url = "gcr.io/#{options[:project_id]}/#{options[:app_name]}"
|
148
|
+
|
149
|
+
opt_image_url = options[:image_tag] && "#{gcr_url}:#{options[:image_tag]}"
|
150
|
+
|
151
|
+
image_url = opt_image_url || spec[:image] || "#{gcr_url}:current"
|
152
|
+
validate_image_tag_exists?(image_url)
|
153
|
+
|
154
|
+
yaml_file['$IMAGE_URL'] = image_url
|
155
|
+
|
156
|
+
FileUtils.mkdir_p('./tmp')
|
157
|
+
|
158
|
+
File.write('./tmp/final.yaml', yaml_file)
|
159
|
+
|
160
|
+
puts "\e[36m\e[1m#{yaml_file}\e[22m\e[0m"
|
161
|
+
|
162
|
+
shell_cmd("kubectl apply -f ./tmp/final.yaml")
|
163
|
+
|
164
|
+
shell_cmd("kubectl rollout status deployment/#{options[:app_name]} -n #{options[:namespace]}")
|
165
|
+
end
|
166
|
+
|
167
|
+
def override_envs(options)
|
168
|
+
override_env_filepath = "./deploy/env/#{options[:kuztomize_env]}.yaml"
|
169
|
+
override_envs = {}
|
170
|
+
|
171
|
+
if File.file?(override_env_filepath)
|
172
|
+
env = YAML.load_file(override_env_filepath, symbolize_names: true)
|
173
|
+
env[:env].each do |env|
|
174
|
+
override_envs[env[:name]] = env[:value]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
override_envs
|
179
|
+
end
|
180
|
+
|
181
|
+
def k8s_deployment_envs(options)
|
182
|
+
spec = app_container_spec(options[:app_name], options[:namespace])
|
183
|
+
|
184
|
+
return {} if spec.nil?
|
185
|
+
|
186
|
+
k8s_deployment_envs = {}
|
187
|
+
|
188
|
+
spec[:env].each do |env|
|
189
|
+
k8s_deployment_envs[env[:name]] = env[:value]
|
190
|
+
end
|
191
|
+
|
192
|
+
k8s_deployment_envs
|
193
|
+
end
|
194
|
+
|
195
|
+
def app_container_spec(app_name, namespace)
|
196
|
+
deploy_containers = `kubectl get deployment -n #{namespace} #{app_name} --output=jsonpath={.spec.template.spec.containers}`
|
197
|
+
return nil if deploy_containers == ''
|
198
|
+
JSON.parse(deploy_containers, symbolize_names: true).find { |container| container[:name] == app_name }
|
199
|
+
end
|
200
|
+
|
201
|
+
def app_container_env_vars_cmd(spec)
|
202
|
+
env_export_cmd = ''
|
203
|
+
spec[:env].each do |env|
|
204
|
+
env_var_val = ENV.fetch(env[:name]) { env[:value] }
|
205
|
+
env_export_cmd += "export #{env[:name]}=#{env_var_val} && "
|
206
|
+
end
|
207
|
+
|
208
|
+
env_export_cmd
|
209
|
+
end
|
210
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: awes_cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Umar Siddiqui
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-07-
|
11
|
+
date: 2021-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Ather cli tool
|
14
14
|
email: umar.siddiqui@atherenergy.com
|
@@ -20,6 +20,8 @@ files:
|
|
20
20
|
- bin/ather
|
21
21
|
- lib/awes_cli.rb
|
22
22
|
- lib/build_app_image.rb
|
23
|
+
- lib/deploy_app.rb
|
24
|
+
- lib/deploy_config.rb
|
23
25
|
homepage: https://rubygems.org/gems/awes_cli
|
24
26
|
licenses:
|
25
27
|
- MIT
|