kubernetes-deploy 0.18.0 → 0.18.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/CHANGELOG.md +5 -1
- data/ISSUE_TEMPLATE.md +25 -0
- data/README.md +2 -3
- data/lib/kubernetes-deploy.rb +1 -0
- data/lib/kubernetes-deploy/deploy_task.rb +8 -5
- data/lib/kubernetes-deploy/kubernetes_resource.rb +34 -32
- data/lib/kubernetes-deploy/kubernetes_resource/bucket.rb +2 -7
- data/lib/kubernetes-deploy/kubernetes_resource/cloudsql.rb +20 -49
- data/lib/kubernetes-deploy/kubernetes_resource/config_map.rb +4 -10
- data/lib/kubernetes-deploy/kubernetes_resource/cron_job.rb +0 -10
- data/lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb +36 -32
- data/lib/kubernetes-deploy/kubernetes_resource/deployment.rb +69 -66
- data/lib/kubernetes-deploy/kubernetes_resource/elasticsearch.rb +1 -16
- data/lib/kubernetes-deploy/kubernetes_resource/ingress.rb +2 -8
- data/lib/kubernetes-deploy/kubernetes_resource/memcached.rb +18 -31
- data/lib/kubernetes-deploy/kubernetes_resource/persistent_volume_claim.rb +4 -10
- data/lib/kubernetes-deploy/kubernetes_resource/pod.rb +28 -31
- data/lib/kubernetes-deploy/kubernetes_resource/pod_disruption_budget.rb +2 -8
- data/lib/kubernetes-deploy/kubernetes_resource/pod_set_base.rb +10 -12
- data/lib/kubernetes-deploy/kubernetes_resource/pod_template.rb +2 -8
- data/lib/kubernetes-deploy/kubernetes_resource/redis.rb +19 -48
- data/lib/kubernetes-deploy/kubernetes_resource/replica_set.rb +33 -35
- data/lib/kubernetes-deploy/kubernetes_resource/resource_quota.rb +3 -14
- data/lib/kubernetes-deploy/kubernetes_resource/service.rb +20 -34
- data/lib/kubernetes-deploy/kubernetes_resource/service_account.rb +2 -8
- data/lib/kubernetes-deploy/kubernetes_resource/stateful_set.rb +37 -31
- data/lib/kubernetes-deploy/kubernetes_resource/statefulservice.rb +1 -16
- data/lib/kubernetes-deploy/kubernetes_resource/topic.rb +1 -16
- data/lib/kubernetes-deploy/resource_watcher.rb +6 -3
- data/lib/kubernetes-deploy/restart_task.rb +3 -1
- data/lib/kubernetes-deploy/sync_mediator.rb +66 -0
- data/lib/kubernetes-deploy/version.rb +1 -1
- metadata +4 -3
- data/lib/kubernetes-deploy/kubernetes_resource/bugsnag.rb +0 -33
@@ -5,53 +5,57 @@ module KubernetesDeploy
|
|
5
5
|
TIMEOUT = 5.minutes
|
6
6
|
attr_reader :pods
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
.slice("currentNumberScheduled", "desiredNumberScheduled", "numberReady")
|
18
|
-
@status = @rollout_data.map { |state_replicas, num| "#{num} #{state_replicas}" }.join(", ")
|
19
|
-
@pods = find_pods(daemonset_data)
|
20
|
-
else # reset
|
21
|
-
@rollout_data = { "currentNumberScheduled" => 0 }
|
22
|
-
@current_generation = 1 # to make sure the current and observed generations are different
|
23
|
-
@observed_generation = 0
|
24
|
-
@status = nil
|
25
|
-
@pods = []
|
26
|
-
end
|
8
|
+
SYNC_DEPENDENCIES = %w(Pod)
|
9
|
+
def sync(mediator)
|
10
|
+
super
|
11
|
+
@pods = exists? ? find_pods(mediator) : []
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
return super unless exists?
|
16
|
+
rollout_data.map { |state_replicas, num| "#{num} #{state_replicas}" }.join(", ")
|
27
17
|
end
|
28
18
|
|
29
19
|
def deploy_succeeded?
|
30
|
-
|
31
|
-
|
32
|
-
|
20
|
+
return false unless exists?
|
21
|
+
rollout_data["desiredNumberScheduled"].to_i == rollout_data["currentNumberScheduled"].to_i &&
|
22
|
+
rollout_data["desiredNumberScheduled"].to_i == rollout_data["numberReady"].to_i &&
|
23
|
+
current_generation == observed_generation
|
33
24
|
end
|
34
25
|
|
35
26
|
def deploy_failed?
|
36
27
|
pods.present? && pods.any?(&:deploy_failed?)
|
37
28
|
end
|
38
29
|
|
39
|
-
def fetch_logs
|
40
|
-
return {} unless
|
41
|
-
most_useful_pod =
|
42
|
-
most_useful_pod.fetch_logs
|
30
|
+
def fetch_logs(kubectl)
|
31
|
+
return {} unless pods.present?
|
32
|
+
most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
|
33
|
+
most_useful_pod.fetch_logs(kubectl)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def current_generation
|
39
|
+
return -1 unless exists? # must be different default than observed_generation
|
40
|
+
@instance_data["metadata"]["generation"]
|
43
41
|
end
|
44
42
|
|
45
|
-
def
|
46
|
-
|
43
|
+
def observed_generation
|
44
|
+
return -2 unless exists?
|
45
|
+
@instance_data["status"]["observedGeneration"]
|
47
46
|
end
|
48
47
|
|
49
|
-
|
48
|
+
def rollout_data
|
49
|
+
return { "currentNumberScheduled" => 0 } unless exists?
|
50
|
+
@instance_data["status"]
|
51
|
+
.slice("currentNumberScheduled", "desiredNumberScheduled", "numberReady")
|
52
|
+
end
|
50
53
|
|
51
|
-
def parent_of_pod?(
|
54
|
+
def parent_of_pod?(pod_data)
|
52
55
|
return false unless pod_data.dig("metadata", "ownerReferences")
|
53
|
-
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] ==
|
54
|
-
pod_data["metadata"]["labels"]["pod-template-generation"].to_i ==
|
56
|
+
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
|
57
|
+
pod_data["metadata"]["labels"]["pod-template-generation"].to_i ==
|
58
|
+
@instance_data["spec"]["templateGeneration"].to_i
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|
@@ -6,53 +6,37 @@ module KubernetesDeploy
|
|
6
6
|
REQUIRED_ROLLOUT_TYPES = %w(maxUnavailable full none).freeze
|
7
7
|
DEFAULT_REQUIRED_ROLLOUT = 'full'
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
.slice("replicas", "updatedReplicas", "availableReplicas", "unavailableReplicas"))
|
20
|
-
@status = @rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
21
|
-
|
22
|
-
conditions = deployment_data.fetch("status", {}).fetch("conditions", [])
|
23
|
-
@progress_condition = conditions.find { |condition| condition['type'] == 'Progressing' }
|
24
|
-
@progress_deadline = deployment_data['spec']['progressDeadlineSeconds']
|
25
|
-
@max_unavailable = deployment_data.dig('spec', 'strategy', 'rollingUpdate', 'maxUnavailable')
|
26
|
-
else # reset
|
27
|
-
@latest_rs = nil
|
28
|
-
@rollout_data = { "replicas" => 0 }
|
29
|
-
@status = nil
|
30
|
-
@progress_condition = nil
|
31
|
-
@progress_deadline = @definition['spec']['progressDeadlineSeconds']
|
32
|
-
@desired_replicas = -1
|
33
|
-
@max_unavailable = @definition.dig('spec', 'strategy', 'rollingUpdate', 'maxUnavailable')
|
34
|
-
end
|
9
|
+
SYNC_DEPENDENCIES = %w(Pod ReplicaSet)
|
10
|
+
def sync(mediator)
|
11
|
+
super
|
12
|
+
@latest_rs = exists? ? find_latest_rs(mediator) : nil
|
13
|
+
@server_version ||= mediator.kubectl.server_version
|
14
|
+
end
|
15
|
+
|
16
|
+
def status
|
17
|
+
return super unless exists?
|
18
|
+
rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
35
19
|
end
|
36
20
|
|
37
|
-
def fetch_events
|
21
|
+
def fetch_events(kubectl)
|
38
22
|
own_events = super
|
39
23
|
return own_events unless @latest_rs.present?
|
40
|
-
own_events.merge(@latest_rs.fetch_events)
|
24
|
+
own_events.merge(@latest_rs.fetch_events(kubectl))
|
41
25
|
end
|
42
26
|
|
43
|
-
def fetch_logs
|
27
|
+
def fetch_logs(kubectl)
|
44
28
|
return {} unless @latest_rs.present?
|
45
|
-
@latest_rs.fetch_logs
|
29
|
+
@latest_rs.fetch_logs(kubectl)
|
46
30
|
end
|
47
31
|
|
48
32
|
def deploy_succeeded?
|
49
|
-
return false unless @latest_rs.present?
|
33
|
+
return false unless exists? && @latest_rs.present?
|
50
34
|
|
51
35
|
if required_rollout == 'full'
|
52
36
|
@latest_rs.deploy_succeeded? &&
|
53
|
-
@latest_rs.desired_replicas ==
|
54
|
-
|
55
|
-
|
37
|
+
@latest_rs.desired_replicas == desired_replicas && # latest RS fully scaled up
|
38
|
+
rollout_data["updatedReplicas"].to_i == desired_replicas &&
|
39
|
+
rollout_data["updatedReplicas"].to_i == rollout_data["availableReplicas"].to_i
|
56
40
|
elsif required_rollout == 'none'
|
57
41
|
true
|
58
42
|
elsif required_rollout == 'maxUnavailable' || percent?(required_rollout)
|
@@ -76,8 +60,8 @@ module KubernetesDeploy
|
|
76
60
|
end
|
77
61
|
|
78
62
|
def timeout_message
|
79
|
-
reason_msg = if
|
80
|
-
"Timeout reason: #{
|
63
|
+
reason_msg = if progress_condition.present?
|
64
|
+
"Timeout reason: #{progress_condition['reason']}"
|
81
65
|
else
|
82
66
|
"Timeout reason: hard deadline for #{type}"
|
83
67
|
end
|
@@ -86,19 +70,15 @@ module KubernetesDeploy
|
|
86
70
|
end
|
87
71
|
|
88
72
|
def pretty_timeout_type
|
89
|
-
|
73
|
+
progress_deadline.present? ? "progress deadline: #{progress_deadline}s" : super
|
90
74
|
end
|
91
75
|
|
92
76
|
def deploy_timed_out?
|
93
77
|
# Do not use the hard timeout if progress deadline is set
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
def exists?
|
98
|
-
@found
|
78
|
+
progress_condition.present? ? deploy_failing_to_progress? : super
|
99
79
|
end
|
100
80
|
|
101
|
-
def validate_definition
|
81
|
+
def validate_definition(_)
|
102
82
|
super
|
103
83
|
|
104
84
|
unless REQUIRED_ROLLOUT_TYPES.include?(required_rollout) || percent?(required_rollout)
|
@@ -116,39 +96,57 @@ module KubernetesDeploy
|
|
116
96
|
|
117
97
|
private
|
118
98
|
|
99
|
+
def desired_replicas
|
100
|
+
return -1 unless exists?
|
101
|
+
@instance_data["spec"]["replicas"].to_i
|
102
|
+
end
|
103
|
+
|
104
|
+
def rollout_data
|
105
|
+
return { "replicas" => 0 } unless exists?
|
106
|
+
{ "replicas" => 0 }.merge(@instance_data["status"]
|
107
|
+
.slice("replicas", "updatedReplicas", "availableReplicas", "unavailableReplicas"))
|
108
|
+
end
|
109
|
+
|
110
|
+
def progress_condition
|
111
|
+
return unless exists?
|
112
|
+
conditions = @instance_data.fetch("status", {}).fetch("conditions", [])
|
113
|
+
conditions.find { |condition| condition['type'] == 'Progressing' }
|
114
|
+
end
|
115
|
+
|
116
|
+
def progress_deadline
|
117
|
+
if exists?
|
118
|
+
@instance_data['spec']['progressDeadlineSeconds']
|
119
|
+
else
|
120
|
+
@definition['spec']['progressDeadlineSeconds']
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
119
124
|
def rollout_annotation_err_msg
|
120
125
|
"'#{REQUIRED_ROLLOUT_ANNOTATION}: #{required_rollout}' is invalid. "\
|
121
126
|
"Acceptable values: #{REQUIRED_ROLLOUT_TYPES.join(', ')}"
|
122
127
|
end
|
123
128
|
|
124
129
|
def deploy_failing_to_progress?
|
125
|
-
return false unless
|
130
|
+
return false unless progress_condition.present?
|
126
131
|
|
127
|
-
if
|
132
|
+
if @server_version < Gem::Version.new("1.7.7")
|
128
133
|
# Deployments were being updated prematurely with incorrect progress information
|
129
134
|
# https://github.com/kubernetes/kubernetes/issues/49637
|
130
|
-
return false unless Time.now.utc - @deploy_started_at >=
|
135
|
+
return false unless Time.now.utc - @deploy_started_at >= progress_deadline.to_i
|
131
136
|
else
|
132
137
|
return false unless deploy_started?
|
133
138
|
end
|
134
139
|
|
135
|
-
|
136
|
-
Time.parse(
|
140
|
+
progress_condition["status"] == 'False' &&
|
141
|
+
Time.parse(progress_condition["lastUpdateTime"]).to_i >= (@deploy_started_at - 5.seconds).to_i
|
137
142
|
end
|
138
143
|
|
139
|
-
def
|
140
|
-
|
141
|
-
|
142
|
-
return {} unless st.success?
|
144
|
+
def find_latest_rs(mediator)
|
145
|
+
all_rs_data = mediator.get_all(ReplicaSet.kind, @instance_data["spec"]["selector"]["matchLabels"])
|
146
|
+
current_revision = @instance_data["metadata"]["annotations"]["deployment.kubernetes.io/revision"]
|
143
147
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
def find_latest_rs(deployment_data)
|
148
|
-
current_revision = deployment_data["metadata"]["annotations"]["deployment.kubernetes.io/revision"]
|
149
|
-
|
150
|
-
latest_rs_data = all_rs_data(deployment_data["spec"]["selector"]["matchLabels"]).find do |rs|
|
151
|
-
rs["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == deployment_data["metadata"]["uid"] } &&
|
148
|
+
latest_rs_data = all_rs_data.find do |rs|
|
149
|
+
rs["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
|
152
150
|
rs["metadata"]["annotations"]["deployment.kubernetes.io/revision"] == current_revision
|
153
151
|
end
|
154
152
|
|
@@ -162,20 +160,25 @@ module KubernetesDeploy
|
|
162
160
|
parent: "#{@name.capitalize} deployment",
|
163
161
|
deploy_started_at: @deploy_started_at
|
164
162
|
)
|
165
|
-
rs.sync(
|
163
|
+
rs.sync(mediator)
|
166
164
|
rs
|
167
165
|
end
|
168
166
|
|
169
167
|
def min_available_replicas
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
(
|
168
|
+
if percent?(required_rollout)
|
169
|
+
(desired_replicas * required_rollout.to_i / 100.0).ceil
|
170
|
+
elsif max_unavailable =~ /%/
|
171
|
+
(desired_replicas * (100 - max_unavailable.to_i) / 100.0).ceil
|
174
172
|
else
|
175
|
-
|
173
|
+
desired_replicas - max_unavailable.to_i
|
176
174
|
end
|
177
175
|
end
|
178
176
|
|
177
|
+
def max_unavailable
|
178
|
+
source = exists? ? @instance_data : @definition
|
179
|
+
source.dig('spec', 'strategy', 'rollingUpdate', 'maxUnavailable')
|
180
|
+
end
|
181
|
+
|
179
182
|
def required_rollout
|
180
183
|
@definition.dig('metadata', 'annotations', REQUIRED_ROLLOUT_ANNOTATION).presence || DEFAULT_REQUIRED_ROLLOUT
|
181
184
|
end
|
@@ -1,23 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module KubernetesDeploy
|
3
3
|
class Elasticsearch < KubernetesResource
|
4
|
-
def sync
|
5
|
-
_, _err, st = kubectl.run("get", type, @name)
|
6
|
-
@found = st.success?
|
7
|
-
end
|
8
|
-
|
9
4
|
def deploy_succeeded?
|
10
|
-
|
11
|
-
|
12
|
-
unless @success_assumption_warning_shown
|
13
|
-
@logger.warn("Don't know how to monitor resources of type #{type}. Assuming #{id} deployed successfully.")
|
14
|
-
@success_assumption_warning_shown = true
|
15
|
-
end
|
16
|
-
true
|
17
|
-
end
|
18
|
-
|
19
|
-
def exists?
|
20
|
-
@found
|
5
|
+
super # success assumption, with warning
|
21
6
|
end
|
22
7
|
|
23
8
|
def deploy_failed?
|
@@ -3,10 +3,8 @@ module KubernetesDeploy
|
|
3
3
|
class Ingress < KubernetesResource
|
4
4
|
TIMEOUT = 30.seconds
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
@status = st.success? ? "Created" : "Unknown"
|
9
|
-
@found = st.success?
|
6
|
+
def status
|
7
|
+
exists? ? "Created" : "Unknown"
|
10
8
|
end
|
11
9
|
|
12
10
|
def deploy_succeeded?
|
@@ -16,9 +14,5 @@ module KubernetesDeploy
|
|
16
14
|
def deploy_failed?
|
17
15
|
false
|
18
16
|
end
|
19
|
-
|
20
|
-
def exists?
|
21
|
-
@found
|
22
|
-
end
|
23
17
|
end
|
24
18
|
end
|
@@ -4,58 +4,45 @@ module KubernetesDeploy
|
|
4
4
|
TIMEOUT = 5.minutes
|
5
5
|
CONFIGMAP_NAME = "memcached-url"
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@
|
11
|
-
@
|
12
|
-
@
|
7
|
+
SYNC_DEPENDENCIES = %w(Deployment Service ConfigMap)
|
8
|
+
def sync(mediator)
|
9
|
+
super
|
10
|
+
@deployment = mediator.get_instance(Deployment.kind, "memcached-#{@name}")
|
11
|
+
@service = mediator.get_instance(Service.kind, "memcached-#{@name}")
|
12
|
+
@configmap = mediator.get_instance(ConfigMap.kind, CONFIGMAP_NAME)
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
"Unknown"
|
18
|
-
end
|
15
|
+
def status
|
16
|
+
deploy_succeeded? ? "Provisioned" : "Unknown"
|
19
17
|
end
|
20
18
|
|
21
19
|
def deploy_succeeded?
|
22
|
-
|
20
|
+
deployment_ready? && service_ready? && configmap_ready?
|
23
21
|
end
|
24
22
|
|
25
23
|
def deploy_failed?
|
26
24
|
false
|
27
25
|
end
|
28
26
|
|
29
|
-
def exists?
|
30
|
-
@found
|
31
|
-
end
|
32
|
-
|
33
27
|
def deploy_method
|
34
28
|
:replace
|
35
29
|
end
|
36
30
|
|
37
31
|
private
|
38
32
|
|
39
|
-
def
|
40
|
-
|
41
|
-
return false unless st.success?
|
42
|
-
parsed = JSON.parse(deployment)
|
43
|
-
status = parsed.fetch("status", {})
|
33
|
+
def deployment_ready?
|
34
|
+
return false unless status = @deployment["status"]
|
44
35
|
status.fetch("availableReplicas", -1) == status.fetch("replicas", 0)
|
45
36
|
end
|
46
37
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
parsed = JSON.parse(service)
|
51
|
-
parsed.dig("spec", "clusterIP").present?
|
38
|
+
def service_ready?
|
39
|
+
return false unless @service.present?
|
40
|
+
@service.dig("spec", "clusterIP").present?
|
52
41
|
end
|
53
42
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
parsed = JSON.parse(secret)
|
58
|
-
parsed.dig("data", @name).present?
|
43
|
+
def configmap_ready?
|
44
|
+
return false unless @configmap.present?
|
45
|
+
@configmap.dig("data", @name).present?
|
59
46
|
end
|
60
47
|
end
|
61
48
|
end
|
@@ -3,22 +3,16 @@ module KubernetesDeploy
|
|
3
3
|
class PersistentVolumeClaim < KubernetesResource
|
4
4
|
TIMEOUT = 5.minutes
|
5
5
|
|
6
|
-
def
|
7
|
-
|
8
|
-
@found = st.success?
|
9
|
-
@status = out if @found
|
6
|
+
def status
|
7
|
+
exists? ? @instance_data["status"]["phase"] : "Unknown"
|
10
8
|
end
|
11
9
|
|
12
10
|
def deploy_succeeded?
|
13
|
-
|
11
|
+
status == "Bound"
|
14
12
|
end
|
15
13
|
|
16
14
|
def deploy_failed?
|
17
|
-
|
18
|
-
end
|
19
|
-
|
20
|
-
def exists?
|
21
|
-
@found
|
15
|
+
status == "Lost"
|
22
16
|
end
|
23
17
|
end
|
24
18
|
end
|