tobsch-krane 1.0.0
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 +7 -0
- data/.buildkite/pipeline.nightly.yml +43 -0
- data/.github/probots.yml +2 -0
- data/.gitignore +20 -0
- data/.rubocop.yml +17 -0
- data/.shopify-build/VERSION +1 -0
- data/.shopify-build/kubernetes-deploy.yml +53 -0
- data/1.0-Upgrade.md +185 -0
- data/CHANGELOG.md +431 -0
- data/CODE_OF_CONDUCT.md +46 -0
- data/CONTRIBUTING.md +164 -0
- data/Gemfile +16 -0
- data/ISSUE_TEMPLATE.md +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +655 -0
- data/Rakefile +36 -0
- data/bin/ci +21 -0
- data/bin/setup +16 -0
- data/bin/test +47 -0
- data/dev.yml +28 -0
- data/dev/flamegraph-from-tests +35 -0
- data/exe/krane +5 -0
- data/krane.gemspec +44 -0
- data/lib/krane.rb +7 -0
- data/lib/krane/bindings_parser.rb +88 -0
- data/lib/krane/cli/deploy_command.rb +75 -0
- data/lib/krane/cli/global_deploy_command.rb +54 -0
- data/lib/krane/cli/krane.rb +91 -0
- data/lib/krane/cli/render_command.rb +41 -0
- data/lib/krane/cli/restart_command.rb +34 -0
- data/lib/krane/cli/run_command.rb +54 -0
- data/lib/krane/cli/version_command.rb +13 -0
- data/lib/krane/cluster_resource_discovery.rb +113 -0
- data/lib/krane/common.rb +23 -0
- data/lib/krane/concerns/template_reporting.rb +29 -0
- data/lib/krane/concurrency.rb +18 -0
- data/lib/krane/container_logs.rb +106 -0
- data/lib/krane/deferred_summary_logging.rb +95 -0
- data/lib/krane/delayed_exceptions.rb +14 -0
- data/lib/krane/deploy_task.rb +363 -0
- data/lib/krane/deploy_task_config_validator.rb +29 -0
- data/lib/krane/duration_parser.rb +27 -0
- data/lib/krane/ejson_secret_provisioner.rb +154 -0
- data/lib/krane/errors.rb +28 -0
- data/lib/krane/formatted_logger.rb +57 -0
- data/lib/krane/global_deploy_task.rb +210 -0
- data/lib/krane/global_deploy_task_config_validator.rb +12 -0
- data/lib/krane/kubeclient_builder.rb +156 -0
- data/lib/krane/kubectl.rb +120 -0
- data/lib/krane/kubernetes_resource.rb +621 -0
- data/lib/krane/kubernetes_resource/cloudsql.rb +43 -0
- data/lib/krane/kubernetes_resource/config_map.rb +22 -0
- data/lib/krane/kubernetes_resource/cron_job.rb +18 -0
- data/lib/krane/kubernetes_resource/custom_resource.rb +87 -0
- data/lib/krane/kubernetes_resource/custom_resource_definition.rb +98 -0
- data/lib/krane/kubernetes_resource/daemon_set.rb +90 -0
- data/lib/krane/kubernetes_resource/deployment.rb +213 -0
- data/lib/krane/kubernetes_resource/horizontal_pod_autoscaler.rb +65 -0
- data/lib/krane/kubernetes_resource/ingress.rb +18 -0
- data/lib/krane/kubernetes_resource/job.rb +60 -0
- data/lib/krane/kubernetes_resource/network_policy.rb +22 -0
- data/lib/krane/kubernetes_resource/persistent_volume_claim.rb +80 -0
- data/lib/krane/kubernetes_resource/pod.rb +269 -0
- data/lib/krane/kubernetes_resource/pod_disruption_budget.rb +23 -0
- data/lib/krane/kubernetes_resource/pod_set_base.rb +71 -0
- data/lib/krane/kubernetes_resource/pod_template.rb +20 -0
- data/lib/krane/kubernetes_resource/replica_set.rb +92 -0
- data/lib/krane/kubernetes_resource/resource_quota.rb +22 -0
- data/lib/krane/kubernetes_resource/role.rb +22 -0
- data/lib/krane/kubernetes_resource/role_binding.rb +22 -0
- data/lib/krane/kubernetes_resource/secret.rb +24 -0
- data/lib/krane/kubernetes_resource/service.rb +104 -0
- data/lib/krane/kubernetes_resource/service_account.rb +22 -0
- data/lib/krane/kubernetes_resource/stateful_set.rb +70 -0
- data/lib/krane/label_selector.rb +42 -0
- data/lib/krane/oj.rb +4 -0
- data/lib/krane/options_helper.rb +39 -0
- data/lib/krane/remote_logs.rb +60 -0
- data/lib/krane/render_task.rb +118 -0
- data/lib/krane/renderer.rb +118 -0
- data/lib/krane/resource_cache.rb +68 -0
- data/lib/krane/resource_deployer.rb +265 -0
- data/lib/krane/resource_watcher.rb +171 -0
- data/lib/krane/restart_task.rb +228 -0
- data/lib/krane/rollout_conditions.rb +103 -0
- data/lib/krane/runner_task.rb +212 -0
- data/lib/krane/runner_task_config_validator.rb +18 -0
- data/lib/krane/statsd.rb +65 -0
- data/lib/krane/task_config.rb +22 -0
- data/lib/krane/task_config_validator.rb +96 -0
- data/lib/krane/template_sets.rb +173 -0
- data/lib/krane/version.rb +4 -0
- data/pull_request_template.md +8 -0
- data/screenshots/deploy-demo.gif +0 -0
- data/screenshots/migrate-logs.png +0 -0
- data/screenshots/missing-secret-fail.png +0 -0
- data/screenshots/success.png +0 -0
- data/screenshots/test-output.png +0 -0
- metadata +375 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class PodDisruptionBudget < KubernetesResource
|
4
|
+
TIMEOUT = 10.seconds
|
5
|
+
|
6
|
+
def status
|
7
|
+
exists? ? "Available" : "Not Found"
|
8
|
+
end
|
9
|
+
|
10
|
+
def deploy_succeeded?
|
11
|
+
exists? && observed_generation == current_generation
|
12
|
+
end
|
13
|
+
|
14
|
+
def deploy_method
|
15
|
+
# Required until https://github.com/kubernetes/kubernetes/issues/45398 changes
|
16
|
+
uses_generate_name? ? :create : :replace_force
|
17
|
+
end
|
18
|
+
|
19
|
+
def timeout_message
|
20
|
+
UNUSUAL_FAILURE_MESSAGE
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'krane/kubernetes_resource/pod'
|
3
|
+
|
4
|
+
module Krane
|
5
|
+
class PodSetBase < KubernetesResource
|
6
|
+
def failure_message
|
7
|
+
pods.map(&:failure_message).compact.uniq.join("\n")
|
8
|
+
end
|
9
|
+
|
10
|
+
def timeout_message
|
11
|
+
pods.map(&:timeout_message).compact.uniq.join("\n")
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch_events(kubectl)
|
15
|
+
own_events = super
|
16
|
+
return own_events unless pods.present?
|
17
|
+
most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
|
18
|
+
own_events.merge(most_useful_pod.fetch_events(kubectl))
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_debug_logs
|
22
|
+
logs = Krane::RemoteLogs.new(
|
23
|
+
logger: @logger,
|
24
|
+
parent_id: id,
|
25
|
+
container_names: container_names,
|
26
|
+
namespace: @namespace,
|
27
|
+
context: @context
|
28
|
+
)
|
29
|
+
logs.sync
|
30
|
+
logs
|
31
|
+
end
|
32
|
+
|
33
|
+
def print_debug_logs?
|
34
|
+
pods.present? # the kubectl command times out if no pods exist
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def pods
|
40
|
+
raise NotImplementedError, "Subclasses must define a `pods` accessor"
|
41
|
+
end
|
42
|
+
|
43
|
+
def parent_of_pod?(_)
|
44
|
+
raise NotImplementedError, "Subclasses must define a `parent_of_pod?` method"
|
45
|
+
end
|
46
|
+
|
47
|
+
def container_names
|
48
|
+
regular_containers = @definition["spec"]["template"]["spec"]["containers"].map { |c| c["name"] }
|
49
|
+
init_containers = @definition["spec"]["template"]["spec"].fetch("initContainers", {}).map { |c| c["name"] }
|
50
|
+
regular_containers + init_containers
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_pods(cache)
|
54
|
+
all_pods = cache.get_all(Pod.kind, @instance_data["spec"]["selector"]["matchLabels"])
|
55
|
+
|
56
|
+
all_pods.each_with_object([]) do |pod_data, relevant_pods|
|
57
|
+
next unless parent_of_pod?(pod_data)
|
58
|
+
pod = Pod.new(
|
59
|
+
namespace: namespace,
|
60
|
+
context: context,
|
61
|
+
definition: pod_data,
|
62
|
+
logger: @logger,
|
63
|
+
parent: "#{name.capitalize} #{type}",
|
64
|
+
deploy_started_at: @deploy_started_at
|
65
|
+
)
|
66
|
+
pod.sync(cache)
|
67
|
+
relevant_pods << pod
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class PodTemplate < KubernetesResource
|
4
|
+
def status
|
5
|
+
exists? ? "Available" : "Not Found"
|
6
|
+
end
|
7
|
+
|
8
|
+
def deploy_succeeded?
|
9
|
+
exists?
|
10
|
+
end
|
11
|
+
|
12
|
+
def deploy_failed?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def timeout_message
|
17
|
+
UNUSUAL_FAILURE_MESSAGE
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'krane/kubernetes_resource/pod_set_base'
|
3
|
+
|
4
|
+
module Krane
|
5
|
+
class ReplicaSet < PodSetBase
|
6
|
+
TIMEOUT = 5.minutes
|
7
|
+
attr_reader :pods
|
8
|
+
|
9
|
+
def initialize(namespace:, context:, definition:, logger:, statsd_tags: nil,
|
10
|
+
parent: nil, deploy_started_at: nil)
|
11
|
+
@parent = parent
|
12
|
+
@deploy_started_at = deploy_started_at
|
13
|
+
@pods = []
|
14
|
+
super(namespace: namespace, context: context, definition: definition,
|
15
|
+
logger: logger, statsd_tags: statsd_tags)
|
16
|
+
end
|
17
|
+
|
18
|
+
def sync(cache)
|
19
|
+
super
|
20
|
+
@pods = fetch_pods_if_needed(cache) || []
|
21
|
+
end
|
22
|
+
|
23
|
+
def status
|
24
|
+
return super unless rollout_data.present?
|
25
|
+
rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
26
|
+
end
|
27
|
+
|
28
|
+
def deploy_succeeded?
|
29
|
+
return false if stale_status?
|
30
|
+
desired_replicas == rollout_data["availableReplicas"].to_i &&
|
31
|
+
desired_replicas == rollout_data["readyReplicas"].to_i
|
32
|
+
end
|
33
|
+
|
34
|
+
def deploy_failed?
|
35
|
+
pods.present? &&
|
36
|
+
pods.all?(&:deploy_failed?) &&
|
37
|
+
!stale_status?
|
38
|
+
end
|
39
|
+
|
40
|
+
def desired_replicas
|
41
|
+
return -1 unless exists?
|
42
|
+
@instance_data["spec"]["replicas"].to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
def ready_replicas
|
46
|
+
return -1 unless exists?
|
47
|
+
rollout_data['readyReplicas'].to_i
|
48
|
+
end
|
49
|
+
|
50
|
+
def available_replicas
|
51
|
+
return -1 unless exists?
|
52
|
+
rollout_data["availableReplicas"].to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def stale_status?
|
58
|
+
observed_generation != current_generation
|
59
|
+
end
|
60
|
+
|
61
|
+
def fetch_pods_if_needed(cache)
|
62
|
+
# If the ReplicaSet doesn't exist, its pods won't either
|
63
|
+
return unless exists?
|
64
|
+
# If the status hasn't been updated yet, we're not going to make a determination anyway
|
65
|
+
return if stale_status?
|
66
|
+
# If we don't want any pods at all, we don't need to look for them
|
67
|
+
return if desired_replicas == 0
|
68
|
+
# We only need to fetch pods so that deploy_failed? can check that they aren't ALL bad.
|
69
|
+
# If we can already tell some pods are ok from the RS data, don't bother fetching them (which can be expensive)
|
70
|
+
# Lower numbers here make us more susceptible to being fooled by replicas without probes briefly appearing ready
|
71
|
+
return if ready_replicas > 1
|
72
|
+
|
73
|
+
find_pods(cache)
|
74
|
+
end
|
75
|
+
|
76
|
+
def rollout_data
|
77
|
+
return { "replicas" => 0 } unless exists?
|
78
|
+
{ "replicas" => 0 }.merge(
|
79
|
+
@instance_data["status"].slice("replicas", "availableReplicas", "readyReplicas")
|
80
|
+
)
|
81
|
+
end
|
82
|
+
|
83
|
+
def parent_of_pod?(pod_data)
|
84
|
+
return false unless pod_data.dig("metadata", "ownerReferences")
|
85
|
+
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] }
|
86
|
+
end
|
87
|
+
|
88
|
+
def unmanaged?
|
89
|
+
@parent.blank?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class ResourceQuota < KubernetesResource
|
4
|
+
TIMEOUT = 30.seconds
|
5
|
+
|
6
|
+
def status
|
7
|
+
exists? ? "In effect" : "Not Found"
|
8
|
+
end
|
9
|
+
|
10
|
+
def deploy_succeeded?
|
11
|
+
@instance_data.dig("spec", "hard") == @instance_data.dig("status", "hard")
|
12
|
+
end
|
13
|
+
|
14
|
+
def deploy_failed?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def timeout_message
|
19
|
+
UNUSUAL_FAILURE_MESSAGE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class Role < KubernetesResource
|
4
|
+
TIMEOUT = 30.seconds
|
5
|
+
|
6
|
+
def status
|
7
|
+
exists? ? "Created" : "Not Found"
|
8
|
+
end
|
9
|
+
|
10
|
+
def deploy_succeeded?
|
11
|
+
exists?
|
12
|
+
end
|
13
|
+
|
14
|
+
def deploy_failed?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def timeout_message
|
19
|
+
UNUSUAL_FAILURE_MESSAGE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class RoleBinding < KubernetesResource
|
4
|
+
TIMEOUT = 30.seconds
|
5
|
+
|
6
|
+
def status
|
7
|
+
exists? ? "Created" : "Not Found"
|
8
|
+
end
|
9
|
+
|
10
|
+
def deploy_succeeded?
|
11
|
+
exists?
|
12
|
+
end
|
13
|
+
|
14
|
+
def deploy_failed?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def timeout_message
|
19
|
+
UNUSUAL_FAILURE_MESSAGE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class Secret < KubernetesResource
|
4
|
+
TIMEOUT = 30.seconds
|
5
|
+
SENSITIVE_TEMPLATE_CONTENT = true
|
6
|
+
SERVER_DRY_RUNNABLE = true
|
7
|
+
|
8
|
+
def status
|
9
|
+
exists? ? "Available" : "Not Found"
|
10
|
+
end
|
11
|
+
|
12
|
+
def deploy_succeeded?
|
13
|
+
exists?
|
14
|
+
end
|
15
|
+
|
16
|
+
def deploy_failed?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
|
20
|
+
def timeout_message
|
21
|
+
UNUSUAL_FAILURE_MESSAGE
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'krane/kubernetes_resource/pod'
|
3
|
+
|
4
|
+
module Krane
|
5
|
+
class Service < KubernetesResource
|
6
|
+
TIMEOUT = 7.minutes
|
7
|
+
|
8
|
+
def sync(cache)
|
9
|
+
super
|
10
|
+
if exists? && selector.present?
|
11
|
+
@related_pods = cache.get_all(Pod.kind, selector)
|
12
|
+
@related_workloads = fetch_related_workloads(cache)
|
13
|
+
else
|
14
|
+
@related_pods = []
|
15
|
+
@related_workloads = []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def status
|
20
|
+
if !exists?
|
21
|
+
"Not found"
|
22
|
+
elsif requires_publishing? && !published?
|
23
|
+
"LoadBalancer IP address is not provisioned yet"
|
24
|
+
elsif !requires_endpoints?
|
25
|
+
"Doesn't require any endpoints"
|
26
|
+
elsif selects_some_pods?
|
27
|
+
"Selects at least 1 pod"
|
28
|
+
else
|
29
|
+
"Selects 0 pods"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def deploy_succeeded?
|
34
|
+
return false unless exists?
|
35
|
+
return published? if requires_publishing?
|
36
|
+
return exists? unless requires_endpoints?
|
37
|
+
# We can't use endpoints if we want the service to be able to fail fast when the pods are down
|
38
|
+
exposes_zero_replica_workload? || selects_some_pods?
|
39
|
+
end
|
40
|
+
|
41
|
+
def deploy_failed?
|
42
|
+
false
|
43
|
+
end
|
44
|
+
|
45
|
+
def timeout_message
|
46
|
+
"This service does not seem to select any pods and this is likely invalid. "\
|
47
|
+
"Please confirm the spec.selector is correct and the targeted workload is healthy."
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def fetch_related_workloads(cache)
|
53
|
+
related_deployments = cache.get_all(Deployment.kind)
|
54
|
+
related_statefulsets = cache.get_all(StatefulSet.kind)
|
55
|
+
(related_deployments + related_statefulsets).select do |workload|
|
56
|
+
selector.all? { |k, v| workload['spec']['template']['metadata']['labels'][k] == v }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def exposes_zero_replica_workload?
|
61
|
+
return false unless related_replica_count
|
62
|
+
related_replica_count == 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def requires_endpoints?
|
66
|
+
# services of type External don't have endpoints
|
67
|
+
return false if external_name_svc?
|
68
|
+
|
69
|
+
# problem counting replicas - by default, assume endpoints are required
|
70
|
+
return true if related_replica_count.blank?
|
71
|
+
|
72
|
+
related_replica_count > 0
|
73
|
+
end
|
74
|
+
|
75
|
+
def selects_some_pods?
|
76
|
+
return false unless selector.present?
|
77
|
+
@related_pods.present?
|
78
|
+
end
|
79
|
+
|
80
|
+
def selector
|
81
|
+
@definition["spec"].fetch("selector", {})
|
82
|
+
end
|
83
|
+
|
84
|
+
def related_replica_count
|
85
|
+
return 0 unless selector.present?
|
86
|
+
|
87
|
+
if @related_workloads.present?
|
88
|
+
@related_workloads.inject(0) { |sum, d| sum + d["spec"]["replicas"].to_i }
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def external_name_svc?
|
93
|
+
@definition["spec"]["type"] == "ExternalName"
|
94
|
+
end
|
95
|
+
|
96
|
+
def requires_publishing?
|
97
|
+
@definition["spec"]["type"] == "LoadBalancer"
|
98
|
+
end
|
99
|
+
|
100
|
+
def published?
|
101
|
+
@instance_data.dig('status', 'loadBalancer', 'ingress').present?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Krane
|
3
|
+
class ServiceAccount < KubernetesResource
|
4
|
+
TIMEOUT = 30.seconds
|
5
|
+
|
6
|
+
def status
|
7
|
+
exists? ? "Created" : "Not Found"
|
8
|
+
end
|
9
|
+
|
10
|
+
def deploy_succeeded?
|
11
|
+
exists?
|
12
|
+
end
|
13
|
+
|
14
|
+
def deploy_failed?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
def timeout_message
|
19
|
+
UNUSUAL_FAILURE_MESSAGE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'krane/kubernetes_resource/pod_set_base'
|
3
|
+
module Krane
|
4
|
+
class StatefulSet < PodSetBase
|
5
|
+
TIMEOUT = 10.minutes
|
6
|
+
ONDELETE = 'OnDelete'
|
7
|
+
attr_reader :pods
|
8
|
+
|
9
|
+
def sync(cache)
|
10
|
+
super
|
11
|
+
@pods = exists? ? find_pods(cache) : []
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
return super unless @instance_data["status"].present?
|
16
|
+
rollout_data = @instance_data["status"].slice("replicas", "readyReplicas", "currentReplicas")
|
17
|
+
rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
18
|
+
end
|
19
|
+
|
20
|
+
def deploy_succeeded?
|
21
|
+
if update_strategy == ONDELETE
|
22
|
+
# Gem cannot monitor update since it doesn't occur until delete
|
23
|
+
unless @success_assumption_warning_shown
|
24
|
+
@logger.warn("WARNING: Your StatefulSet's updateStrategy is set to OnDelete, "\
|
25
|
+
"which means updates will not be applied until its pods are deleted. "\
|
26
|
+
"Consider switching to rollingUpdate.")
|
27
|
+
@success_assumption_warning_shown = true
|
28
|
+
end
|
29
|
+
true
|
30
|
+
else
|
31
|
+
observed_generation == current_generation &&
|
32
|
+
status_data['currentRevision'] == status_data['updateRevision'] &&
|
33
|
+
desired_replicas == status_data['readyReplicas'].to_i &&
|
34
|
+
desired_replicas == status_data['currentReplicas'].to_i
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def deploy_failed?
|
39
|
+
return false if update_strategy == ONDELETE
|
40
|
+
pods.present? && pods.any?(&:deploy_failed?) &&
|
41
|
+
observed_generation == current_generation
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def update_strategy
|
47
|
+
if exists?
|
48
|
+
@instance_data['spec']['updateStrategy']['type']
|
49
|
+
else
|
50
|
+
'Unknown'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def status_data
|
55
|
+
return { 'readyReplicas' => '-1', 'currentReplicas' => '-2' } unless exists?
|
56
|
+
@instance_data["status"]
|
57
|
+
end
|
58
|
+
|
59
|
+
def desired_replicas
|
60
|
+
return -1 unless exists?
|
61
|
+
@instance_data["spec"]["replicas"].to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
def parent_of_pod?(pod_data)
|
65
|
+
return false unless pod_data.dig("metadata", "ownerReferences")
|
66
|
+
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
|
67
|
+
@instance_data["status"]["currentRevision"] == pod_data["metadata"]["labels"]["controller-revision-hash"]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|