kubernetes-deploy 0.21.0 → 0.21.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1155a58879313f8d0466b9d551a5ba9fb4a0ccab
4
- data.tar.gz: a5856d1be46d1aef9a6393e4b5f95715e5832284
3
+ metadata.gz: b7b6b27e2d19b9194f53092f3037a6597d0c098b
4
+ data.tar.gz: 5c1e656293fdb2f0e193bdb90419bd7629f683fd
5
5
  SHA512:
6
- metadata.gz: 9eecadcbaa7709aae55ba3baac6e0afaf120277df59c1dae1a407bd49f6f07755757448de776b7f33ed30298cacad081276ba2afcee4927d2b56ae899ea9a196
7
- data.tar.gz: 75cc0f78449088f47d621da287477c77a01ef33a0d1460fc5739676fee82d5a6652542412c483cd2ad80c708ed72a8350573a57515e848100b5c27cb6b9a2129
6
+ metadata.gz: f6968be66492775b5edac4e2a30fe30c168aafd1db5766f5a97eae84c2fd42dd5603c608960d6b0aadc5dec6f717842c41b9dbbd6cd7f6dad69716b1106b33e3
7
+ data.tar.gz: 751eb781b35bdf1081c41d4f182b84cc97a897b7c199c5020ecd2691c1478fb48174a1527d9ad0e3a58103a4dff734f149b261682d52a923fdb516239db0f8f0
@@ -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", "= 0.6.2" # https://github.com/google/google-auth-library-ruby/issues/153
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 = ["apply"]
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?(NOT_FOUND_ERROR)
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
- if !st.success? && log_failure
33
- @logger.warn("The following command failed: #{Shellwords.join(args)}")
34
- @logger.warn(err) unless output_is_sensitive?
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") + "\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
- "#{phase_problem}#{container_problems}".presence
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
- helpful_info << ColorizedString.new("#{id}: GLOBAL WATCH TIMEOUT (#{info_hash[:timeout]} seconds)").yellow
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
- helpful_info << ColorizedString.new("#{id}: FAILED").red
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
- helpful_info << ColorizedString.new("#{id}: TIMED OUT (#{pretty_timeout_type})").yellow
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
- helpful_info << ColorizedString.new("#{id}: MONITORING ERROR").red
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
- helpful_info << " - Final status: #{status}"
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
- if @cache.key?(kind)
15
- @cache.dig(kind, resource_name) || {}
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, _, st = kubectl.run("get", kind, iname, "-a", "--output=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
- @cache[kind] = JSON.parse(raw_json)["items"].each_with_object({}) do |r, instances|
67
- instances[r.dig("metadata", "name")] = r
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
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
- VERSION = "0.21.0"
3
+ VERSION = "0.21.1"
4
4
  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.0
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-16 00:00:00.000000000 Z
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.2
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.2
55
+ version: 0.6.6
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: ejson
58
58
  requirement: !ruby/object:Gem::Requirement