kubernetes-deploy 0.21.0 → 0.21.1
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 +4 -4
- data/kubernetes-deploy.gemspec +1 -1
- data/lib/kubernetes-deploy/deploy_task.rb +2 -4
- data/lib/kubernetes-deploy/kubectl.rb +14 -4
- data/lib/kubernetes-deploy/kubernetes_resource/job.rb +13 -0
- data/lib/kubernetes-deploy/kubernetes_resource/pod.rb +25 -6
- data/lib/kubernetes-deploy/kubernetes_resource.rb +22 -6
- data/lib/kubernetes-deploy/sync_mediator.rb +18 -9
- data/lib/kubernetes-deploy/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7b6b27e2d19b9194f53092f3037a6597d0c098b
|
4
|
+
data.tar.gz: 5c1e656293fdb2f0e193bdb90419bd7629f683fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f6968be66492775b5edac4e2a30fe30c168aafd1db5766f5a97eae84c2fd42dd5603c608960d6b0aadc5dec6f717842c41b9dbbd6cd7f6dad69716b1106b33e3
|
7
|
+
data.tar.gz: 751eb781b35bdf1081c41d4f182b84cc97a897b7c199c5020ecd2691c1478fb48174a1527d9ad0e3a58103a4dff734f149b261682d52a923fdb516239db0f8f0
|
data/kubernetes-deploy.gemspec
CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.required_ruby_version = '>= 2.3.0'
|
26
26
|
spec.add_dependency "activesupport", ">= 5.0"
|
27
27
|
spec.add_dependency "kubeclient", "~> 3.0"
|
28
|
-
spec.add_dependency "googleauth", "
|
28
|
+
spec.add_dependency "googleauth", "~> 0.6.6" # https://github.com/google/google-auth-library-ruby/issues/153
|
29
29
|
spec.add_dependency "ejson", "~> 1.0"
|
30
30
|
spec.add_dependency "colorize", "~> 0.8"
|
31
31
|
spec.add_dependency "statsd-instrument", "~> 2.2"
|
@@ -93,8 +93,6 @@ module KubernetesDeploy
|
|
93
93
|
kubectl.server_version
|
94
94
|
end
|
95
95
|
|
96
|
-
NOT_FOUND_ERROR = 'NotFound'
|
97
|
-
|
98
96
|
def initialize(namespace:, context:, current_sha:, template_dir:, logger:, kubectl_instance: nil, bindings: {},
|
99
97
|
max_watch_seconds: nil)
|
100
98
|
@namespace = namespace
|
@@ -399,7 +397,7 @@ module KubernetesDeploy
|
|
399
397
|
|
400
398
|
def apply_all(resources, prune)
|
401
399
|
return unless resources.present?
|
402
|
-
command =
|
400
|
+
command = %w(apply)
|
403
401
|
|
404
402
|
Dir.mktmpdir do |tmp_dir|
|
405
403
|
resources.each do |r|
|
@@ -500,7 +498,7 @@ module KubernetesDeploy
|
|
500
498
|
st, err = nil
|
501
499
|
with_retries(2) do
|
502
500
|
_, err, st = kubectl.run("get", "namespace", @namespace, use_namespace: false, log_failure: true)
|
503
|
-
st.success? || err.include?(
|
501
|
+
st.success? || err.include?(KubernetesDeploy::Kubectl::NOT_FOUND_ERROR_TEXT)
|
504
502
|
end
|
505
503
|
raise FatalDeploymentError, "Failed to find namespace. #{err}" unless st.success?
|
506
504
|
@logger.info("Namespace #{@namespace} found")
|
@@ -3,6 +3,9 @@
|
|
3
3
|
module KubernetesDeploy
|
4
4
|
class Kubectl
|
5
5
|
DEFAULT_TIMEOUT = 30
|
6
|
+
NOT_FOUND_ERROR_TEXT = 'NotFound'
|
7
|
+
|
8
|
+
class ResourceNotFoundError < StandardError; end
|
6
9
|
|
7
10
|
def initialize(namespace:, context:, logger:, log_failure_by_default:, default_timeout: DEFAULT_TIMEOUT,
|
8
11
|
output_is_sensitive: false)
|
@@ -17,7 +20,7 @@ module KubernetesDeploy
|
|
17
20
|
raise ArgumentError, "context is required" if context.blank?
|
18
21
|
end
|
19
22
|
|
20
|
-
def run(*args, log_failure: nil, use_context: true, use_namespace: true)
|
23
|
+
def run(*args, log_failure: nil, use_context: true, use_namespace: true, raise_if_not_found: false)
|
21
24
|
log_failure = @log_failure_by_default if log_failure.nil?
|
22
25
|
|
23
26
|
args = args.unshift("kubectl")
|
@@ -29,10 +32,17 @@ module KubernetesDeploy
|
|
29
32
|
out, err, st = Open3.capture3(*args)
|
30
33
|
@logger.debug(out.shellescape) unless output_is_sensitive?
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
unless st.success?
|
36
|
+
if log_failure
|
37
|
+
@logger.warn("The following command failed: #{Shellwords.join(args)}")
|
38
|
+
@logger.warn(err) unless output_is_sensitive?
|
39
|
+
end
|
40
|
+
|
41
|
+
if raise_if_not_found && err.match(NOT_FOUND_ERROR_TEXT)
|
42
|
+
raise ResourceNotFoundError, err
|
43
|
+
end
|
35
44
|
end
|
45
|
+
|
36
46
|
[out.chomp, err.chomp, st]
|
37
47
|
end
|
38
48
|
|
@@ -12,6 +12,7 @@ module KubernetesDeploy
|
|
12
12
|
|
13
13
|
def deploy_failed?
|
14
14
|
return false unless deploy_started?
|
15
|
+
return true if failed_status_condition
|
15
16
|
return false unless @instance_data.dig("spec", "backoffLimit").present?
|
16
17
|
(@instance_data.dig("status", "failed") || 0) >= @instance_data.dig("spec", "backoffLimit")
|
17
18
|
end
|
@@ -30,8 +31,20 @@ module KubernetesDeploy
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
def failure_message
|
35
|
+
if (condition = failed_status_condition.presence)
|
36
|
+
"#{condition['reason']} (#{condition['message']})"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
33
40
|
private
|
34
41
|
|
42
|
+
def failed_status_condition
|
43
|
+
@instance_data.dig("status", "conditions")&.detect do |condition|
|
44
|
+
condition["type"] == 'Failed' && condition['status'] == "True"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
35
48
|
def done?
|
36
49
|
(@instance_data.dig("status", "succeeded") || 0) == @instance_data.dig("spec", "completions")
|
37
50
|
end
|
@@ -66,14 +66,10 @@ module KubernetesDeploy
|
|
66
66
|
return STANDARD_TIMEOUT_MESSAGE unless readiness_probe_failure?
|
67
67
|
probe_failure_msgs = @containers.map(&:readiness_fail_reason).compact
|
68
68
|
header = "The following containers have not passed their readiness probes on at least one pod:\n"
|
69
|
-
header + probe_failure_msgs.join("\n")
|
69
|
+
header + probe_failure_msgs.join("\n")
|
70
70
|
end
|
71
71
|
|
72
72
|
def failure_message
|
73
|
-
if phase == FAILED_PHASE_NAME && !TRANSIENT_FAILURE_REASONS.include?(reason)
|
74
|
-
phase_problem = "Pod status: #{status}. "
|
75
|
-
end
|
76
|
-
|
77
73
|
doomed_containers = @containers.select(&:doomed?)
|
78
74
|
if doomed_containers.present?
|
79
75
|
container_problems = if unmanaged?
|
@@ -86,7 +82,7 @@ module KubernetesDeploy
|
|
86
82
|
container_problems += "> #{red_name}: #{c.doom_reason}\n"
|
87
83
|
end
|
88
84
|
end
|
89
|
-
"#{
|
85
|
+
"#{phase_failure_message} #{container_problems}".strip.presence
|
90
86
|
end
|
91
87
|
|
92
88
|
def fetch_debug_logs(kubectl)
|
@@ -100,6 +96,29 @@ module KubernetesDeploy
|
|
100
96
|
|
101
97
|
private
|
102
98
|
|
99
|
+
def failed_phase?
|
100
|
+
phase == FAILED_PHASE_NAME
|
101
|
+
end
|
102
|
+
|
103
|
+
def transient_failure_reason?
|
104
|
+
return false if unmanaged?
|
105
|
+
TRANSIENT_FAILURE_REASONS.include?(reason)
|
106
|
+
end
|
107
|
+
|
108
|
+
def phase_failure_message
|
109
|
+
if failed_phase? && !transient_failure_reason?
|
110
|
+
return "Pod status: #{status}."
|
111
|
+
end
|
112
|
+
|
113
|
+
return unless unmanaged?
|
114
|
+
|
115
|
+
if terminating?
|
116
|
+
"Pod status: Terminating."
|
117
|
+
elsif disappeared?
|
118
|
+
"Pod status: Disappeared."
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
103
122
|
def logs
|
104
123
|
@logs ||= KubernetesDeploy::RemoteLogs.new(
|
105
124
|
logger: @logger,
|
@@ -85,6 +85,7 @@ module KubernetesDeploy
|
|
85
85
|
@logger = logger
|
86
86
|
@definition = definition
|
87
87
|
@statsd_report_done = false
|
88
|
+
@disappeared = false
|
88
89
|
@validation_errors = []
|
89
90
|
@instance_data = {}
|
90
91
|
end
|
@@ -121,12 +122,23 @@ module KubernetesDeploy
|
|
121
122
|
end
|
122
123
|
|
123
124
|
def sync(mediator)
|
124
|
-
@instance_data = mediator.get_instance(kubectl_resource_type, name)
|
125
|
+
@instance_data = mediator.get_instance(kubectl_resource_type, name, raise_if_not_found: true)
|
126
|
+
rescue KubernetesDeploy::Kubectl::ResourceNotFoundError
|
127
|
+
@disappeared = true if deploy_started?
|
128
|
+
@instance_data = {}
|
125
129
|
end
|
126
130
|
|
127
131
|
def after_sync
|
128
132
|
end
|
129
133
|
|
134
|
+
def terminating?
|
135
|
+
@instance_data.dig('metadata', 'deletionTimestamp').present?
|
136
|
+
end
|
137
|
+
|
138
|
+
def disappeared?
|
139
|
+
@disappeared
|
140
|
+
end
|
141
|
+
|
130
142
|
def deploy_failed?
|
131
143
|
false
|
132
144
|
end
|
@@ -189,22 +201,26 @@ module KubernetesDeploy
|
|
189
201
|
def debug_message(cause = nil, info_hash = {})
|
190
202
|
helpful_info = []
|
191
203
|
if cause == :gave_up
|
192
|
-
|
204
|
+
debug_heading = ColorizedString.new("#{id}: GLOBAL WATCH TIMEOUT (#{info_hash[:timeout]} seconds)").yellow
|
193
205
|
helpful_info << "If you expected it to take longer than #{info_hash[:timeout]} seconds for your deploy"\
|
194
206
|
" to roll out, increase --max-watch-seconds."
|
195
207
|
elsif deploy_failed?
|
196
|
-
|
208
|
+
debug_heading = ColorizedString.new("#{id}: FAILED").red
|
197
209
|
helpful_info << failure_message if failure_message.present?
|
198
210
|
elsif deploy_timed_out?
|
199
|
-
|
211
|
+
debug_heading = ColorizedString.new("#{id}: TIMED OUT (#{pretty_timeout_type})").yellow
|
200
212
|
helpful_info << timeout_message if timeout_message.present?
|
201
213
|
else
|
202
214
|
# Arriving in debug_message when we neither failed nor timed out is very unexpected. Dump all available info.
|
203
|
-
|
215
|
+
debug_heading = ColorizedString.new("#{id}: MONITORING ERROR").red
|
204
216
|
helpful_info << failure_message if failure_message.present?
|
205
217
|
helpful_info << timeout_message if timeout_message.present? && timeout_message != STANDARD_TIMEOUT_MESSAGE
|
206
218
|
end
|
207
|
-
|
219
|
+
|
220
|
+
final_status = " - Final status: #{status}"
|
221
|
+
final_status = "\n#{final_status}" if helpful_info.present? && !helpful_info.last.end_with?("\n")
|
222
|
+
helpful_info.prepend(debug_heading)
|
223
|
+
helpful_info << final_status
|
208
224
|
|
209
225
|
if @debug_events.present?
|
210
226
|
helpful_info << " - Events (common success events excluded):"
|
@@ -10,12 +10,16 @@ module KubernetesDeploy
|
|
10
10
|
clear_cache
|
11
11
|
end
|
12
12
|
|
13
|
-
def get_instance(kind, resource_name)
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
request_instance(kind, resource_name)
|
13
|
+
def get_instance(kind, resource_name, raise_if_not_found: false)
|
14
|
+
unless @cache.key?(kind)
|
15
|
+
return request_instance(kind, resource_name, raise_if_not_found: raise_if_not_found)
|
18
16
|
end
|
17
|
+
|
18
|
+
cached_instance = @cache[kind].fetch(resource_name, {})
|
19
|
+
if cached_instance.blank? && raise_if_not_found
|
20
|
+
raise KubernetesDeploy::Kubectl::ResourceNotFoundError, "Resource does not exist (used cache for kind #{kind})"
|
21
|
+
end
|
22
|
+
cached_instance
|
19
23
|
end
|
20
24
|
|
21
25
|
def get_all(kind, selector = nil)
|
@@ -55,17 +59,22 @@ module KubernetesDeploy
|
|
55
59
|
@cache = {}
|
56
60
|
end
|
57
61
|
|
58
|
-
def request_instance(kind, iname)
|
59
|
-
raw_json,
|
62
|
+
def request_instance(kind, iname, raise_if_not_found:)
|
63
|
+
raw_json, _err, st = kubectl.run("get", kind, iname, "-a", "--output=json",
|
64
|
+
raise_if_not_found: raise_if_not_found)
|
60
65
|
st.success? ? JSON.parse(raw_json) : {}
|
61
66
|
end
|
62
67
|
|
63
68
|
def fetch_by_kind(kind)
|
64
69
|
raw_json, _, st = kubectl.run("get", kind, "-a", "--output=json")
|
65
70
|
return unless st.success?
|
66
|
-
|
67
|
-
|
71
|
+
|
72
|
+
instances = {}
|
73
|
+
JSON.parse(raw_json)["items"].each do |resource|
|
74
|
+
resource_name = resource.dig("metadata", "name")
|
75
|
+
instances[resource_name] = resource
|
68
76
|
end
|
77
|
+
@cache[kind] = instances
|
69
78
|
end
|
70
79
|
end
|
71
80
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kubernetes-deploy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.21.
|
4
|
+
version: 0.21.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katrina Verey
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-10-
|
12
|
+
date: 2018-10-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -43,16 +43,16 @@ dependencies:
|
|
43
43
|
name: googleauth
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - "~>"
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 0.6.
|
48
|
+
version: 0.6.6
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 0.6.
|
55
|
+
version: 0.6.6
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: ejson
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|