kubernetes-deploy 0.7.3 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +7 -1
- data/Rakefile +1 -1
- data/exe/kubernetes-deploy +1 -1
- data/exe/kubernetes-run +1 -1
- data/kubernetes-deploy.gemspec +2 -2
- data/lib/kubernetes-deploy.rb +2 -0
- data/lib/kubernetes-deploy/deferred_summary_logging.rb +3 -3
- data/lib/kubernetes-deploy/kubernetes_resource.rb +2 -1
- data/lib/kubernetes-deploy/kubernetes_resource/pod.rb +5 -3
- data/lib/kubernetes-deploy/resource_watcher.rb +6 -6
- data/lib/kubernetes-deploy/runner.rb +33 -26
- data/lib/kubernetes-deploy/runner_task.rb +2 -5
- data/lib/kubernetes-deploy/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 255ea8b3ecd484e3867da8b34b4a61a927d99c1f
|
4
|
+
data.tar.gz: 79c4034b3460995f65fbeb54216688a5b1f448b6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c9b9c54dea86318add19d23123e00ec5f3195488ec0ecbf954cdfe126dd1ca780d2c7cea6687342f2df8410ef7c5a53fd8b3ebb0649b6e8488fb1f2be42f0ef
|
7
|
+
data.tar.gz: a2cc7698a3b9f9b66bf8a54b7c5ac2483946760cdb52612c8b73fbc3c57ad8f716e8cedb4e1a732ecda59a82f37815de4ba41eb212a1e75399bd461b06061ebc
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -123,7 +123,13 @@ To run the tests:
|
|
123
123
|
* Make sure you have a context named "minikube" in your kubeconfig. Minikube adds this context for you when you run `minikube start`; please do not rename it. You can check for it using `kubectl config get-contexts`.
|
124
124
|
* Run `bundle exec rake test`
|
125
125
|
|
126
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
126
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
127
|
+
|
128
|
+
To release a new version:
|
129
|
+
|
130
|
+
* Update the version number in `version.rb`
|
131
|
+
* Tag the version with `git tag vx.y.z && git push --tags`
|
132
|
+
* A Shopify employee can use the [Shipit Stack](https://shipit.shopify.io/shopify/kubernetes-deploy/rubygems) to build the `.gem` file and upload to [rubygems.org](https://rubygems.org/gems/kubernetes-deploy).
|
127
133
|
|
128
134
|
## CI
|
129
135
|
|
data/Rakefile
CHANGED
data/exe/kubernetes-deploy
CHANGED
@@ -15,7 +15,7 @@ verbose_log_prefix = false
|
|
15
15
|
ARGV.options do |opts|
|
16
16
|
opts.on("--bindings=BINDINGS", Array, "k1=v1,k2=v2") do |binds|
|
17
17
|
bindings = binds.each_with_object({}) do |bind, acc|
|
18
|
-
k,v = bind.split('=')
|
18
|
+
k, v = bind.split('=')
|
19
19
|
raise "key for value #{v} is blank!" if k.blank?
|
20
20
|
raise "value for key #{k} is blank!" if v.blank?
|
21
21
|
acc[k] = v
|
data/exe/kubernetes-run
CHANGED
@@ -11,7 +11,7 @@ env_vars = []
|
|
11
11
|
|
12
12
|
ARGV.options do |opts|
|
13
13
|
opts.on("--template=TEMPLATE") { |n| template = n }
|
14
|
-
opts.on("--env-vars=ENV_VARS") { |vars| env_vars =
|
14
|
+
opts.on("--env-vars=ENV_VARS") { |vars| env_vars = vars.split(",") }
|
15
15
|
opts.on("--entrypoint=ENTRYPOINT") { |c| entrypoint = [c] }
|
16
16
|
opts.parse!
|
17
17
|
end
|
data/kubernetes-deploy.gemspec
CHANGED
@@ -6,10 +6,10 @@ require 'kubernetes-deploy/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "kubernetes-deploy"
|
8
8
|
spec.version = KubernetesDeploy::VERSION
|
9
|
-
spec.authors = ["
|
9
|
+
spec.authors = ["Katrina Verey", "Kir Shatrov"]
|
10
10
|
spec.email = ["ops-accounts+shipit@shopify.com"]
|
11
11
|
|
12
|
-
spec.summary =
|
12
|
+
spec.summary = 'Kubernetes deploy scripts'
|
13
13
|
spec.description = spec.summary
|
14
14
|
spec.homepage = "https://github.com/Shopify/kubernetes-deploy"
|
15
15
|
spec.license = "MIT"
|
data/lib/kubernetes-deploy.rb
CHANGED
@@ -23,12 +23,12 @@ module KubernetesDeploy
|
|
23
23
|
heading("Phase #{@current_phase}: #{phase_name}")
|
24
24
|
end
|
25
25
|
|
26
|
-
def heading(text, secondary_msg = '', secondary_msg_color = :
|
26
|
+
def heading(text, secondary_msg = '', secondary_msg_color = :cyan)
|
27
27
|
padding = (100.0 - (text.length + secondary_msg.length)) / 2
|
28
28
|
blank_line
|
29
|
-
part1 = ColorizedString.new("#{'-' * padding.floor}#{text}").
|
29
|
+
part1 = ColorizedString.new("#{'-' * padding.floor}#{text}").cyan
|
30
30
|
part2 = ColorizedString.new(secondary_msg).colorize(secondary_msg_color)
|
31
|
-
part3 = ColorizedString.new('-' * padding.ceil).
|
31
|
+
part3 = ColorizedString.new('-' * padding.ceil).cyan
|
32
32
|
info(part1 + part2 + part3)
|
33
33
|
end
|
34
34
|
|
@@ -10,6 +10,7 @@ module KubernetesDeploy
|
|
10
10
|
attr_writer :type, :deploy_started
|
11
11
|
|
12
12
|
TIMEOUT = 5.minutes
|
13
|
+
LOG_LINE_COUNT = 250
|
13
14
|
|
14
15
|
DEBUG_RESOURCE_NOT_FOUND_MESSAGE = "None found. Please check your usual logging service (e.g. Splunk)."
|
15
16
|
UNUSUAL_FAILURE_MESSAGE = <<-MSG.strip_heredoc
|
@@ -138,7 +139,7 @@ module KubernetesDeploy
|
|
138
139
|
if container_logs.blank? || container_logs.values.all?(&:blank?)
|
139
140
|
helpful_info << " - Logs: #{DEBUG_RESOURCE_NOT_FOUND_MESSAGE}"
|
140
141
|
else
|
141
|
-
helpful_info << " - Logs:"
|
142
|
+
helpful_info << " - Logs (last #{LOG_LINE_COUNT} lines shown):"
|
142
143
|
container_logs.each do |identifier, logs|
|
143
144
|
logs.split("\n").each do |line|
|
144
145
|
helpful_info << " [#{identifier}]\t#{line}"
|
@@ -75,12 +75,14 @@ module KubernetesDeploy
|
|
75
75
|
return {} unless exists? && @containers.present?
|
76
76
|
|
77
77
|
@containers.each_with_object({}) do |container_name, container_logs|
|
78
|
-
|
78
|
+
cmd = [
|
79
79
|
"logs",
|
80
80
|
@name,
|
81
81
|
"--container=#{container_name}",
|
82
|
-
"--since-time=#{@deploy_started.to_datetime.rfc3339}"
|
83
|
-
|
82
|
+
"--since-time=#{@deploy_started.to_datetime.rfc3339}",
|
83
|
+
]
|
84
|
+
cmd << "--tail=#{LOG_LINE_COUNT}" unless unmanaged?
|
85
|
+
out, _err, _st = kubectl.run(*cmd)
|
84
86
|
container_logs["#{id}/#{container_name}"] = out
|
85
87
|
end
|
86
88
|
end
|
@@ -1,25 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module KubernetesDeploy
|
3
3
|
class ResourceWatcher
|
4
|
-
def initialize(resources, logger:)
|
4
|
+
def initialize(resources, logger:, deploy_started_at: Time.now.utc)
|
5
5
|
unless resources.is_a?(Enumerable)
|
6
|
-
raise ArgumentError, <<-MSG.
|
7
|
-
ResourceWatcher expects Enumerable collection, got `#{resources.class}` instead
|
8
|
-
MSG
|
6
|
+
raise ArgumentError, <<-MSG.strip_heredoc
|
7
|
+
ResourceWatcher expects Enumerable collection, got `#{resources.class}` instead
|
8
|
+
MSG
|
9
9
|
end
|
10
10
|
@resources = resources
|
11
11
|
@logger = logger
|
12
|
+
@deploy_started_at = deploy_started_at
|
12
13
|
end
|
13
14
|
|
14
15
|
def run(delay_sync: 3.seconds)
|
15
16
|
delay_sync_until = Time.now.utc
|
16
|
-
started_at = delay_sync_until
|
17
17
|
|
18
18
|
while @resources.present?
|
19
19
|
if Time.now.utc < delay_sync_until
|
20
20
|
sleep(delay_sync_until - Time.now.utc)
|
21
21
|
end
|
22
|
-
watch_time = (Time.now.utc -
|
22
|
+
watch_time = (Time.now.utc - @deploy_started_at).round(1)
|
23
23
|
delay_sync_until = Time.now.utc + delay_sync # don't pummel the API if the sync is fast
|
24
24
|
@resources.each(&:sync)
|
25
25
|
newly_finished_resources, @resources = @resources.partition(&:deploy_finished?)
|
@@ -111,21 +111,20 @@ module KubernetesDeploy
|
|
111
111
|
raise FatalDeploymentError, "Refusing to deploy to protected namespace '#{@namespace}' with pruning enabled"
|
112
112
|
end
|
113
113
|
|
114
|
-
|
115
|
-
|
116
|
-
|
114
|
+
if verify_result
|
115
|
+
deploy_resources(resources, prune: prune, verify: true)
|
116
|
+
record_statuses(resources)
|
117
|
+
success = resources.all?(&:deploy_succeeded?)
|
118
|
+
else
|
119
|
+
deploy_resources(resources, prune: prune, verify: false)
|
117
120
|
@logger.summary.add_action("deployed #{resources.length} #{'resource'.pluralize(resources.length)}")
|
118
121
|
warning = <<-MSG.strip_heredoc
|
119
122
|
Deploy result verification is disabled for this deploy.
|
120
123
|
This means the desired changes were communicated to Kubernetes, but the deploy did not make sure they actually succeeded.
|
121
124
|
MSG
|
122
125
|
@logger.summary.add_paragraph(ColorizedString.new(warning).yellow)
|
123
|
-
|
126
|
+
success = true
|
124
127
|
end
|
125
|
-
|
126
|
-
wait_for_completion(resources)
|
127
|
-
record_statuses(resources)
|
128
|
-
success = resources.all?(&:deploy_succeeded?)
|
129
128
|
rescue FatalDeploymentError => error
|
130
129
|
@logger.summary.add_action(error.message)
|
131
130
|
success = false
|
@@ -200,8 +199,7 @@ module KubernetesDeploy
|
|
200
199
|
PREDEPLOY_SEQUENCE.each do |resource_type|
|
201
200
|
matching_resources = resource_list.select { |r| r.type == resource_type }
|
202
201
|
next if matching_resources.empty?
|
203
|
-
deploy_resources(matching_resources)
|
204
|
-
wait_for_completion(matching_resources)
|
202
|
+
deploy_resources(matching_resources, verify: true)
|
205
203
|
|
206
204
|
failed_resources = matching_resources.reject(&:deploy_succeeded?)
|
207
205
|
fail_count = failed_resources.length
|
@@ -297,8 +295,8 @@ module KubernetesDeploy
|
|
297
295
|
@logger.summary.add_paragraph(debug_msg)
|
298
296
|
end
|
299
297
|
|
300
|
-
def wait_for_completion(watched_resources)
|
301
|
-
watcher = ResourceWatcher.new(watched_resources, logger: @logger)
|
298
|
+
def wait_for_completion(watched_resources, started_at)
|
299
|
+
watcher = ResourceWatcher.new(watched_resources, logger: @logger, deploy_started_at: started_at)
|
302
300
|
watcher.run
|
303
301
|
end
|
304
302
|
|
@@ -359,37 +357,46 @@ module KubernetesDeploy
|
|
359
357
|
@logger.info("All required parameters and files are present")
|
360
358
|
end
|
361
359
|
|
362
|
-
def deploy_resources(resources, prune: false)
|
363
|
-
|
360
|
+
def deploy_resources(resources, prune: false, verify:)
|
361
|
+
return if resources.empty?
|
362
|
+
deploy_started_at = Time.now.utc
|
363
|
+
|
364
|
+
if resources.length > 1
|
365
|
+
@logger.info("Deploying resources:")
|
366
|
+
else
|
367
|
+
resource = resources.first
|
368
|
+
@logger.info("Deploying #{resource.id} (timeout: #{resource.timeout}s)")
|
369
|
+
end
|
364
370
|
|
365
371
|
# Apply can be done in one large batch, the rest have to be done individually
|
366
372
|
applyables, individuals = resources.partition { |r| r.deploy_method == :apply }
|
367
373
|
|
368
374
|
individuals.each do |r|
|
369
|
-
@logger.info("- #{r.id}")
|
375
|
+
@logger.info("- #{r.id} (timeout: #{r.timeout}s)") if resources.length > 1
|
370
376
|
r.deploy_started = Time.now.utc
|
371
377
|
case r.deploy_method
|
372
378
|
when :replace
|
373
|
-
_, _,
|
379
|
+
_, _, replace_st = kubectl.run("replace", "-f", r.file.path, log_failure: false)
|
374
380
|
when :replace_force
|
375
|
-
_, _,
|
381
|
+
_, _, replace_st = kubectl.run("replace", "--force", "-f", r.file.path, log_failure: false)
|
376
382
|
else
|
377
383
|
# Fail Fast! This is a programmer mistake.
|
378
384
|
raise ArgumentError, "Unexpected deploy method! (#{r.deploy_method.inspect})"
|
379
385
|
end
|
380
386
|
|
381
|
-
next if
|
387
|
+
next if replace_st.success?
|
382
388
|
# it doesn't exist so we can't replace it
|
383
|
-
_, err,
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
389
|
+
_, err, create_st = kubectl.run("create", "-f", r.file.path, log_failure: false)
|
390
|
+
|
391
|
+
next if create_st.success?
|
392
|
+
raise FatalDeploymentError, <<-MSG.strip_heredoc
|
393
|
+
Failed to replace or create resource: #{r.id}
|
394
|
+
#{err}
|
395
|
+
MSG
|
390
396
|
end
|
391
397
|
|
392
398
|
apply_all(applyables, prune)
|
399
|
+
wait_for_completion(resources, deploy_started_at) if verify
|
393
400
|
end
|
394
401
|
|
395
402
|
def apply_all(resources, prune)
|
@@ -397,7 +404,7 @@ module KubernetesDeploy
|
|
397
404
|
|
398
405
|
command = ["apply"]
|
399
406
|
resources.each do |r|
|
400
|
-
@logger.info("- #{r.id} (timeout: #{r.timeout}s)")
|
407
|
+
@logger.info("- #{r.id} (timeout: #{r.timeout}s)") if resources.length > 1
|
401
408
|
command.push("-f", r.file.path)
|
402
409
|
r.deploy_started = Time.now.utc
|
403
410
|
end
|
@@ -68,11 +68,8 @@ module KubernetesDeploy
|
|
68
68
|
begin
|
69
69
|
@kubeclient.get_namespace(@namespace) if @namespace.present?
|
70
70
|
rescue KubeException => e
|
71
|
-
|
72
|
-
|
73
|
-
else
|
74
|
-
"Could not connect to kubernetes cluster"
|
75
|
-
end
|
71
|
+
msg = e.error_code == 404 ? "Namespace was not found" : "Could not connect to kubernetes cluster"
|
72
|
+
errors << msg
|
76
73
|
end
|
77
74
|
|
78
75
|
raise FatalTaskRunError, "Configuration invalid: #{errors.join(', ')}" unless errors.empty?
|
metadata
CHANGED
@@ -1,16 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kubernetes-deploy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Kir Shatrov
|
8
|
-
- Jean Boussier
|
9
7
|
- Katrina Verey
|
8
|
+
- Kir Shatrov
|
10
9
|
autorequire:
|
11
10
|
bindir: exe
|
12
11
|
cert_chain: []
|
13
|
-
date: 2017-06-
|
12
|
+
date: 2017-06-05 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: activesupport
|