kubernetes-deploy 0.29.0 → 1.0.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.buildkite/pipeline.nightly.yml +7 -0
- data/.rubocop.yml +0 -12
- data/.shopify-build/{kubernetes-deploy.yml → krane.yml} +8 -2
- data/1.0-Upgrade.md +109 -0
- data/CHANGELOG.md +60 -0
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +1 -0
- data/README.md +86 -2
- data/dev.yml +3 -1
- data/dev/flamegraph-from-tests +1 -1
- data/exe/kubernetes-deploy +12 -9
- data/exe/kubernetes-render +9 -7
- data/exe/kubernetes-restart +3 -3
- data/exe/kubernetes-run +1 -1
- data/kubernetes-deploy.gemspec +5 -5
- data/lib/krane.rb +5 -3
- data/lib/{kubernetes-deploy → krane}/bindings_parser.rb +1 -1
- data/lib/krane/cli/deploy_command.rb +25 -13
- data/lib/krane/cli/global_deploy_command.rb +55 -0
- data/lib/krane/cli/krane.rb +12 -3
- data/lib/krane/cli/render_command.rb +19 -9
- data/lib/krane/cli/restart_command.rb +4 -4
- data/lib/krane/cli/run_command.rb +4 -4
- data/lib/krane/cli/version_command.rb +1 -1
- data/lib/krane/cluster_resource_discovery.rb +113 -0
- data/lib/{kubernetes-deploy → krane}/common.rb +8 -9
- data/lib/krane/concerns/template_reporting.rb +29 -0
- data/lib/{kubernetes-deploy → krane}/concurrency.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/container_logs.rb +3 -2
- data/lib/{kubernetes-deploy → krane}/deferred_summary_logging.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/delayed_exceptions.rb +0 -0
- data/lib/krane/deploy_task.rb +16 -0
- data/lib/krane/deploy_task_config_validator.rb +29 -0
- data/lib/krane/deprecated_deploy_task.rb +404 -0
- data/lib/{kubernetes-deploy → krane}/duration_parser.rb +1 -3
- data/lib/{kubernetes-deploy → krane}/ejson_secret_provisioner.rb +10 -13
- data/lib/krane/errors.rb +28 -0
- data/lib/{kubernetes-deploy → krane}/formatted_logger.rb +2 -2
- data/lib/krane/global_deploy_task.rb +210 -0
- data/lib/krane/global_deploy_task_config_validator.rb +12 -0
- data/lib/{kubernetes-deploy → krane}/kubeclient_builder.rb +13 -5
- data/lib/{kubernetes-deploy → krane}/kubectl.rb +14 -16
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource.rb +110 -27
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/cloudsql.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/config_map.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/cron_job.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/custom_resource.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/custom_resource_definition.rb +1 -5
- data/lib/krane/kubernetes_resource/daemon_set.rb +90 -0
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/deployment.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/horizontal_pod_autoscaler.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/ingress.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/job.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/network_policy.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/persistent_volume_claim.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod.rb +6 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_disruption_budget.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_set_base.rb +3 -3
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_template.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/replica_set.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/resource_quota.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/role.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/role_binding.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/secret.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/service.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/service_account.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/stateful_set.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/label_selector.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/oj.rb +0 -0
- data/lib/{kubernetes-deploy → krane}/options_helper.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/remote_logs.rb +2 -2
- data/lib/krane/render_task.rb +149 -0
- data/lib/{kubernetes-deploy → krane}/renderer.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/resource_cache.rb +10 -9
- data/lib/krane/resource_deployer.rb +265 -0
- data/lib/{kubernetes-deploy → krane}/resource_watcher.rb +24 -25
- data/lib/krane/restart_task.rb +228 -0
- data/lib/{kubernetes-deploy → krane}/rollout_conditions.rb +1 -1
- data/lib/krane/runner_task.rb +212 -0
- data/lib/{kubernetes-deploy → krane}/runner_task_config_validator.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/statsd.rb +13 -27
- data/lib/krane/task_config.rb +22 -0
- data/lib/{kubernetes-deploy → krane}/task_config_validator.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/template_sets.rb +5 -5
- data/lib/krane/version.rb +4 -0
- data/lib/kubernetes-deploy/deploy_task.rb +6 -608
- data/lib/kubernetes-deploy/errors.rb +1 -26
- data/lib/kubernetes-deploy/render_task.rb +5 -122
- data/lib/kubernetes-deploy/rescue_krane_exceptions.rb +18 -0
- data/lib/kubernetes-deploy/restart_task.rb +6 -198
- data/lib/kubernetes-deploy/runner_task.rb +6 -184
- metadata +96 -70
- data/lib/kubernetes-deploy/cluster_resource_discovery.rb +0 -34
- data/lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb +0 -54
- data/lib/kubernetes-deploy/task_config.rb +0 -16
- data/lib/kubernetes-deploy/version.rb +0 -4
@@ -2,15 +2,15 @@
|
|
2
2
|
require 'json'
|
3
3
|
require 'shellwords'
|
4
4
|
|
5
|
-
require '
|
6
|
-
require '
|
7
|
-
require '
|
8
|
-
require '
|
5
|
+
require 'krane/remote_logs'
|
6
|
+
require 'krane/duration_parser'
|
7
|
+
require 'krane/label_selector'
|
8
|
+
require 'krane/rollout_conditions'
|
9
9
|
|
10
|
-
module
|
10
|
+
module Krane
|
11
11
|
class KubernetesResource
|
12
12
|
attr_reader :name, :namespace, :context
|
13
|
-
attr_writer :type, :deploy_started_at
|
13
|
+
attr_writer :type, :deploy_started_at, :global
|
14
14
|
|
15
15
|
GLOBAL = false
|
16
16
|
TIMEOUT = 5.minutes
|
@@ -40,7 +40,7 @@ module KubernetesDeploy
|
|
40
40
|
SERVER_DRY_RUNNABLE = false
|
41
41
|
|
42
42
|
class << self
|
43
|
-
def build(namespace
|
43
|
+
def build(namespace: nil, context:, definition:, logger:, statsd_tags:, crd: nil, global_names: [])
|
44
44
|
validate_definition_essentials(definition)
|
45
45
|
opts = { namespace: namespace, context: context, definition: definition, logger: logger,
|
46
46
|
statsd_tags: statsd_tags }
|
@@ -50,15 +50,17 @@ module KubernetesDeploy
|
|
50
50
|
if crd
|
51
51
|
CustomResource.new(crd: crd, **opts)
|
52
52
|
else
|
53
|
+
type = definition["kind"]
|
53
54
|
inst = new(**opts)
|
54
|
-
inst.type =
|
55
|
+
inst.type = type
|
56
|
+
inst.global = global_names.map(&:downcase).include?(type.downcase)
|
55
57
|
inst
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
59
61
|
def class_for_kind(kind)
|
60
|
-
if
|
61
|
-
|
62
|
+
if Krane.const_defined?(kind)
|
63
|
+
Krane.const_get(kind)
|
62
64
|
end
|
63
65
|
rescue NameError
|
64
66
|
nil
|
@@ -85,8 +87,9 @@ module KubernetesDeploy
|
|
85
87
|
raise InvalidTemplateError.new("Template is missing required field 'kind'", content: debug_content)
|
86
88
|
end
|
87
89
|
|
88
|
-
if definition.dig('metadata', 'name').blank?
|
89
|
-
raise InvalidTemplateError.new("Template
|
90
|
+
if definition.dig('metadata', 'name').blank? && definition.dig('metadata', 'generateName').blank?
|
91
|
+
raise InvalidTemplateError.new("Template must specify one of 'metadata.name' or 'metadata.generateName'",
|
92
|
+
content: debug_content)
|
90
93
|
end
|
91
94
|
end
|
92
95
|
end
|
@@ -110,7 +113,7 @@ module KubernetesDeploy
|
|
110
113
|
|
111
114
|
def initialize(namespace:, context:, definition:, logger:, statsd_tags: [])
|
112
115
|
# subclasses must also set these if they define their own initializer
|
113
|
-
@name = definition.dig("metadata", "name").to_s
|
116
|
+
@name = (definition.dig("metadata", "name") || definition.dig("metadata", "generateName")).to_s
|
114
117
|
@optional_statsd_tags = statsd_tags
|
115
118
|
@namespace = namespace
|
116
119
|
@context = context
|
@@ -168,7 +171,7 @@ module KubernetesDeploy
|
|
168
171
|
|
169
172
|
def sync(cache)
|
170
173
|
@instance_data = cache.get_instance(kubectl_resource_type, name, raise_if_not_found: true)
|
171
|
-
rescue
|
174
|
+
rescue Krane::Kubectl::ResourceNotFoundError
|
172
175
|
@disappeared = true if deploy_started?
|
173
176
|
@instance_data = {}
|
174
177
|
end
|
@@ -233,9 +236,13 @@ module KubernetesDeploy
|
|
233
236
|
!deploy_succeeded? && !deploy_failed? && (Time.now.utc - @deploy_started_at > timeout)
|
234
237
|
end
|
235
238
|
|
236
|
-
# Expected values: :apply, :replace, :replace_force
|
239
|
+
# Expected values: :apply, :create, :replace, :replace_force
|
237
240
|
def deploy_method
|
238
|
-
|
241
|
+
if @definition.dig("metadata", "name").blank? && uses_generate_name?
|
242
|
+
:create
|
243
|
+
else
|
244
|
+
:apply
|
245
|
+
end
|
239
246
|
end
|
240
247
|
|
241
248
|
def sync_debug_info(kubectl)
|
@@ -315,7 +322,7 @@ module KubernetesDeploy
|
|
315
322
|
def fetch_events(kubectl)
|
316
323
|
return {} unless exists?
|
317
324
|
out, _err, st = kubectl.run("get", "events", "--output=go-template=#{Event.go_template_for(type, name)}",
|
318
|
-
log_failure: false)
|
325
|
+
log_failure: false, use_namespace: !global?)
|
319
326
|
return {} unless st.success?
|
320
327
|
|
321
328
|
event_collector = Hash.new { |hash, key| hash[key] = [] }
|
@@ -338,7 +345,7 @@ module KubernetesDeploy
|
|
338
345
|
|
339
346
|
def report_status_to_statsd(watch_time)
|
340
347
|
unless @statsd_report_done
|
341
|
-
StatsD.distribution('resource.duration', watch_time, tags: statsd_tags)
|
348
|
+
StatsD.client.distribution('resource.duration', watch_time, tags: statsd_tags)
|
342
349
|
@statsd_report_done = true
|
343
350
|
end
|
344
351
|
end
|
@@ -348,13 +355,29 @@ module KubernetesDeploy
|
|
348
355
|
end
|
349
356
|
|
350
357
|
def server_dry_runnable_resource?
|
351
|
-
|
358
|
+
# generateName and server-side dry run are incompatible because the former only works with `create`
|
359
|
+
# and the latter only works with `apply`
|
360
|
+
self.class::SERVER_DRY_RUNNABLE && !uses_generate_name?
|
361
|
+
end
|
362
|
+
|
363
|
+
def uses_generate_name?
|
364
|
+
@definition.dig('metadata', 'generateName').present?
|
352
365
|
end
|
353
366
|
|
354
367
|
def server_dry_run_validated?
|
355
368
|
@server_dry_run_validated
|
356
369
|
end
|
357
370
|
|
371
|
+
# If a resource uses generateName, we don't know the full name of the resource until it's deployed to the cluster.
|
372
|
+
# In this case, we need to update our local definition with the realized name in order to accurately track the
|
373
|
+
# resource during deploy
|
374
|
+
def use_generated_name(instance_data)
|
375
|
+
@name = instance_data.dig('metadata', 'name')
|
376
|
+
@definition['metadata']['name'] = @name
|
377
|
+
@definition['metadata'].delete('generateName')
|
378
|
+
@file = create_definition_tempfile
|
379
|
+
end
|
380
|
+
|
358
381
|
class Event
|
359
382
|
EVENT_SEPARATOR = "ENDEVENT--BEGINEVENT"
|
360
383
|
FIELD_SEPARATOR = "ENDFIELD--BEGINFIELD"
|
@@ -365,7 +388,12 @@ module KubernetesDeploy
|
|
365
388
|
.lastTimestamp
|
366
389
|
.reason
|
367
390
|
.message
|
391
|
+
.eventTime
|
392
|
+
.deprecatedCount
|
393
|
+
.deprecatedLastTimestamp
|
394
|
+
.series
|
368
395
|
)
|
396
|
+
FIELD_EMPTY_VALUE = '<no value>'
|
369
397
|
|
370
398
|
def self.go_template_for(kind, name)
|
371
399
|
and_conditions = [
|
@@ -386,17 +414,61 @@ module KubernetesDeploy
|
|
386
414
|
def self.extract_all_from_go_template_blob(blob)
|
387
415
|
blob.split(EVENT_SEPARATOR).map do |event_blob|
|
388
416
|
pieces = event_blob.split(FIELD_SEPARATOR, FIELDS.length)
|
417
|
+
count = extract_event_count(pieces)
|
418
|
+
timestamp = extract_event_timestamp(pieces)
|
419
|
+
|
389
420
|
new(
|
390
421
|
subject_kind: pieces[FIELDS.index(".involvedObject.kind")],
|
391
422
|
subject_name: pieces[FIELDS.index(".involvedObject.name")],
|
392
|
-
count:
|
393
|
-
last_timestamp:
|
423
|
+
count: count,
|
424
|
+
last_timestamp: timestamp,
|
394
425
|
reason: pieces[FIELDS.index(".reason")],
|
395
426
|
message: pieces[FIELDS.index(".message")]
|
396
427
|
)
|
397
428
|
end
|
398
429
|
end
|
399
430
|
|
431
|
+
def self.extract_event_count(pieces)
|
432
|
+
series = pieces[FIELDS.index(".series")]
|
433
|
+
count = pieces[FIELDS.index(".count")]
|
434
|
+
deprecated_count = pieces[FIELDS.index(".deprecatedCount")]
|
435
|
+
|
436
|
+
# Find the right event count according to Kubernetes API and kubectl version
|
437
|
+
if count.present? && count != FIELD_EMPTY_VALUE
|
438
|
+
count # This is the default field, so let's try to use it first
|
439
|
+
elsif series.present? && series != FIELD_EMPTY_VALUE
|
440
|
+
# kubectl 1.16 uses Events/v1, which has the .series/.count field
|
441
|
+
count_regex = /count:(?<value>\S+?(?=\s))/
|
442
|
+
count_regex.match(series)['value']
|
443
|
+
elsif deprecated_count.present? && deprecated_count != FIELD_EMPTY_VALUE
|
444
|
+
# kubectl < 1.16 uses events.k8s.io/v1beta1, which has .deprecatedCount
|
445
|
+
deprecated_count
|
446
|
+
else
|
447
|
+
"1" # Fallback to 1 when all count fields are null
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
def self.extract_event_timestamp(pieces)
|
452
|
+
series = pieces[FIELDS.index(".series")]
|
453
|
+
last_timestamp = pieces[FIELDS.index(".lastTimestamp")]
|
454
|
+
deprecated_timestamp = pieces[FIELDS.index(".deprecatedLastTimestamp")]
|
455
|
+
|
456
|
+
# Find the right event timestamp according to Kubernetes API and kubectl version
|
457
|
+
if last_timestamp.present? && last_timestamp != FIELD_EMPTY_VALUE
|
458
|
+
last_timestamp # kubernetes 1.16 also exposes .last_timestamp field, so let's support it
|
459
|
+
elsif series.present? && series != FIELD_EMPTY_VALUE
|
460
|
+
# kubectl 1.16 uses Events/v1, which has the .series/.lastObservedTime field
|
461
|
+
timestamp_regex = /lastObservedTime:(?<value>\S+?(?=\]))/
|
462
|
+
timestamp_regex.match(series)['value']
|
463
|
+
elsif deprecated_timestamp.present? && deprecated_timestamp != FIELD_EMPTY_VALUE
|
464
|
+
# kubectl < 1.16 uses events.k8s.io/v1beta1, which has .deprecatedLastTimestamp
|
465
|
+
deprecated_timestamp
|
466
|
+
else
|
467
|
+
pieces[FIELDS.index(".eventTime")] # Fallback to eventTime when other timestamp fields are null
|
468
|
+
end
|
469
|
+
end
|
470
|
+
private_class_method :extract_event_timestamp, :extract_event_count
|
471
|
+
|
400
472
|
def initialize(subject_kind:, last_timestamp:, reason:, message:, count:, subject_name:)
|
401
473
|
@subject_kind = subject_kind
|
402
474
|
@subject_name = subject_name
|
@@ -416,7 +488,7 @@ module KubernetesDeploy
|
|
416
488
|
end
|
417
489
|
|
418
490
|
def global?
|
419
|
-
self.class::GLOBAL
|
491
|
+
@global || self.class::GLOBAL
|
420
492
|
end
|
421
493
|
|
422
494
|
private
|
@@ -478,13 +550,13 @@ module KubernetesDeploy
|
|
478
550
|
def validate_spec_with_kubectl(kubectl)
|
479
551
|
err = ""
|
480
552
|
if kubectl.server_dry_run_enabled? && server_dry_runnable_resource?
|
481
|
-
_, err, st =
|
553
|
+
_, err, st = validate_with_server_side_dry_run(kubectl)
|
482
554
|
@server_dry_run_validated = st.success?
|
483
555
|
return true if st.success?
|
484
556
|
end
|
485
557
|
|
486
558
|
if err.empty? || err.match(SERVER_DRY_RUN_DISABLED_ERROR)
|
487
|
-
_, err, st =
|
559
|
+
_, err, st = validate_with_local_dry_run(kubectl)
|
488
560
|
end
|
489
561
|
|
490
562
|
return true if st.success?
|
@@ -495,10 +567,21 @@ module KubernetesDeploy
|
|
495
567
|
end
|
496
568
|
end
|
497
569
|
|
498
|
-
|
499
|
-
|
570
|
+
# Server side dry run is only supported on apply
|
571
|
+
def validate_with_server_side_dry_run(kubectl)
|
572
|
+
command = ["apply", "-f", file_path, "--server-dry-run", "--output=name"]
|
573
|
+
kubectl.run(*command, log_failure: false, output_is_sensitive: sensitive_template_content?,
|
574
|
+
retry_whitelist: [:client_timeout], attempts: 3)
|
575
|
+
end
|
576
|
+
|
577
|
+
# Local dry run is supported on only create and apply
|
578
|
+
# If the deploy method is create, validating with apply will fail
|
579
|
+
# If the resource template uses generateName, validating with apply will fail
|
580
|
+
def validate_with_local_dry_run(kubectl)
|
581
|
+
verb = deploy_method == :apply ? "apply" : "create"
|
582
|
+
command = [verb, "-f", file_path, "--dry-run", "--output=name"]
|
500
583
|
kubectl.run(*command, log_failure: false, output_is_sensitive: sensitive_template_content?,
|
501
|
-
|
584
|
+
retry_whitelist: [:client_timeout], attempts: 3, use_namespace: !global?)
|
502
585
|
end
|
503
586
|
|
504
587
|
def labels
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'jsonpath'
|
3
3
|
|
4
|
-
module
|
4
|
+
module Krane
|
5
5
|
class CustomResource < KubernetesResource
|
6
6
|
TIMEOUT_MESSAGE_DIFFERENT_GENERATIONS = <<~MSG
|
7
7
|
This resource's status could not be used to determine rollout success because it is not up-to-date
|
@@ -68,7 +68,7 @@ module KubernetesDeploy
|
|
68
68
|
@crd.validate_rollout_conditions
|
69
69
|
rescue RolloutConditionsError => e
|
70
70
|
@validation_errors << "The CRD that specifies this resource is using invalid rollout conditions. " \
|
71
|
-
"
|
71
|
+
"Krane will not be able to continue until those rollout conditions are fixed.\n" \
|
72
72
|
"Rollout conditions can be found on the CRD that defines this resource (#{@crd.name}), " \
|
73
73
|
"under the annotation #{CustomResourceDefinition::ROLLOUT_CONDITIONS_ANNOTATION}.\n" \
|
74
74
|
"Validation failed with: #{e}"
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
module
|
2
|
+
module Krane
|
3
3
|
class CustomResourceDefinition < KubernetesResource
|
4
4
|
TIMEOUT = 2.minutes
|
5
5
|
ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX = "instance-rollout-conditions"
|
@@ -46,10 +46,6 @@ module KubernetesDeploy
|
|
46
46
|
@definition.dig("spec", "names", "kind")
|
47
47
|
end
|
48
48
|
|
49
|
-
def name
|
50
|
-
@definition.dig("metadata", "name")
|
51
|
-
end
|
52
|
-
|
53
49
|
def prunable?
|
54
50
|
prunable = krane_annotation_value("prunable")
|
55
51
|
prunable == "true"
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "krane/kubernetes_resource/pod_set_base"
|
3
|
+
module Krane
|
4
|
+
class DaemonSet < PodSetBase
|
5
|
+
TIMEOUT = 5.minutes
|
6
|
+
attr_reader :pods
|
7
|
+
|
8
|
+
def sync(cache)
|
9
|
+
super
|
10
|
+
@pods = exists? ? find_pods(cache) : []
|
11
|
+
@nodes = find_nodes(cache) if @nodes.blank?
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
return super unless exists?
|
16
|
+
rollout_data.map { |state_replicas, num| "#{num} #{state_replicas}" }.join(", ")
|
17
|
+
end
|
18
|
+
|
19
|
+
def deploy_succeeded?
|
20
|
+
return false unless exists?
|
21
|
+
current_generation == observed_generation &&
|
22
|
+
rollout_data["desiredNumberScheduled"].to_i == rollout_data["updatedNumberScheduled"].to_i &&
|
23
|
+
relevant_pods_ready?
|
24
|
+
end
|
25
|
+
|
26
|
+
def deploy_failed?
|
27
|
+
pods.present? && pods.any?(&:deploy_failed?) &&
|
28
|
+
observed_generation == current_generation
|
29
|
+
end
|
30
|
+
|
31
|
+
def fetch_debug_logs
|
32
|
+
most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
|
33
|
+
most_useful_pod.fetch_debug_logs
|
34
|
+
end
|
35
|
+
|
36
|
+
def print_debug_logs?
|
37
|
+
pods.present? # the kubectl command times out if no pods exist
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
class Node
|
43
|
+
attr_reader :name
|
44
|
+
|
45
|
+
class << self
|
46
|
+
def kind
|
47
|
+
name.demodulize
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize(definition:)
|
52
|
+
@name = definition.dig("metadata", "name").to_s
|
53
|
+
@definition = definition
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def relevant_pods_ready?
|
58
|
+
return true if rollout_data["desiredNumberScheduled"].to_i == rollout_data["numberReady"].to_i # all pods ready
|
59
|
+
relevant_node_names = @nodes.map(&:name)
|
60
|
+
considered_pods = @pods.select { |p| relevant_node_names.include?(p.node_name) }
|
61
|
+
@logger.debug("DaemonSet is reporting #{rollout_data['numberReady']} pods ready." \
|
62
|
+
" Considered #{considered_pods.size} pods out of #{@pods.size} for #{@nodes.size} nodes.")
|
63
|
+
considered_pods.present? &&
|
64
|
+
considered_pods.all?(&:deploy_succeeded?) &&
|
65
|
+
rollout_data["numberReady"].to_i >= considered_pods.length
|
66
|
+
end
|
67
|
+
|
68
|
+
def find_nodes(cache)
|
69
|
+
all_nodes = cache.get_all(Node.kind)
|
70
|
+
all_nodes.map { |node_data| Node.new(definition: node_data) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def rollout_data
|
74
|
+
return { "currentNumberScheduled" => 0 } unless exists?
|
75
|
+
@instance_data["status"]
|
76
|
+
.slice("updatedNumberScheduled", "desiredNumberScheduled", "numberReady")
|
77
|
+
end
|
78
|
+
|
79
|
+
def parent_of_pod?(pod_data)
|
80
|
+
return false unless pod_data.dig("metadata", "ownerReferences")
|
81
|
+
|
82
|
+
template_generation = @instance_data.dig("spec", "templateGeneration") ||
|
83
|
+
@instance_data.dig("metadata", "annotations", "deprecated.daemonset.template.generation")
|
84
|
+
return false unless template_generation.present?
|
85
|
+
|
86
|
+
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
|
87
|
+
pod_data["metadata"]["labels"]["pod-template-generation"].to_i == template_generation.to_i
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require '
|
2
|
+
require 'krane/kubernetes_resource/replica_set'
|
3
3
|
|
4
|
-
module
|
4
|
+
module Krane
|
5
5
|
class Deployment < KubernetesResource
|
6
6
|
TIMEOUT = 7.minutes
|
7
7
|
REQUIRED_ROLLOUT_ANNOTATION_SUFFIX = "required-rollout"
|