kubernetes-deploy 0.18.0 → 0.18.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|