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
@@ -3,18 +3,18 @@ module KubernetesDeploy
|
|
3
3
|
class Service < KubernetesResource
|
4
4
|
TIMEOUT = 7.minutes
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
@
|
10
|
-
@
|
6
|
+
SYNC_DEPENDENCIES = %w(Pod Deployment)
|
7
|
+
def sync(mediator)
|
8
|
+
super
|
9
|
+
@related_deployments = selector.present? ? mediator.get_all(Deployment.kind, selector) : []
|
10
|
+
@related_pods = selector.present? ? mediator.get_all(Pod.kind, selector) : []
|
11
11
|
end
|
12
12
|
|
13
13
|
def status
|
14
|
-
if !
|
15
|
-
"
|
16
|
-
elsif
|
17
|
-
"
|
14
|
+
if !exists?
|
15
|
+
"Not found"
|
16
|
+
elsif !requires_endpoints?
|
17
|
+
"Doesn't require any endpoints"
|
18
18
|
elsif selects_some_pods?
|
19
19
|
"Selects at least 1 pod"
|
20
20
|
else
|
@@ -23,6 +23,7 @@ module KubernetesDeploy
|
|
23
23
|
end
|
24
24
|
|
25
25
|
def deploy_succeeded?
|
26
|
+
return false unless exists?
|
26
27
|
return exists? unless requires_endpoints?
|
27
28
|
# We can't use endpoints if we want the service to be able to fail fast when the pods are down
|
28
29
|
exposes_zero_replica_deployment? || selects_some_pods?
|
@@ -36,15 +37,11 @@ module KubernetesDeploy
|
|
36
37
|
"This service does not seem to select any pods. This means its spec.selector is probably incorrect."
|
37
38
|
end
|
38
39
|
|
39
|
-
def exists?
|
40
|
-
@found
|
41
|
-
end
|
42
|
-
|
43
40
|
private
|
44
41
|
|
45
42
|
def exposes_zero_replica_deployment?
|
46
|
-
return false unless
|
47
|
-
|
43
|
+
return false unless related_replica_count
|
44
|
+
related_replica_count == 0
|
48
45
|
end
|
49
46
|
|
50
47
|
def requires_endpoints?
|
@@ -52,35 +49,24 @@ module KubernetesDeploy
|
|
52
49
|
return false if external_name_svc?
|
53
50
|
|
54
51
|
# problem counting replicas - by default, assume endpoints are required
|
55
|
-
return true if
|
52
|
+
return true if related_replica_count.blank?
|
56
53
|
|
57
|
-
|
54
|
+
related_replica_count > 0
|
58
55
|
end
|
59
56
|
|
60
57
|
def selects_some_pods?
|
61
|
-
return false unless
|
62
|
-
@
|
58
|
+
return false unless selector.present?
|
59
|
+
@related_pods.present?
|
63
60
|
end
|
64
61
|
|
65
62
|
def selector
|
66
|
-
@
|
67
|
-
end
|
68
|
-
|
69
|
-
def fetch_related_pod_count
|
70
|
-
return 0 unless selector.present?
|
71
|
-
raw_json, _err, st = kubectl.run("get", "pods", "--selector=#{selector}", "--output=json")
|
72
|
-
return unless st.success?
|
73
|
-
JSON.parse(raw_json)["items"].length
|
63
|
+
@definition["spec"].fetch("selector", {})
|
74
64
|
end
|
75
65
|
|
76
|
-
def
|
66
|
+
def related_replica_count
|
77
67
|
return 0 unless selector.present?
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
deployments = JSON.parse(raw_json)["items"]
|
82
|
-
return unless deployments.length == 1
|
83
|
-
deployments.first["spec"]["replicas"].to_i
|
68
|
+
return unless @related_deployments.length == 1
|
69
|
+
@related_deployments.first["spec"]["replicas"].to_i
|
84
70
|
end
|
85
71
|
|
86
72
|
def external_name_svc?
|
@@ -3,10 +3,8 @@ module KubernetesDeploy
|
|
3
3
|
class ServiceAccount < 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?
|
@@ -17,10 +15,6 @@ module KubernetesDeploy
|
|
17
15
|
false
|
18
16
|
end
|
19
17
|
|
20
|
-
def exists?
|
21
|
-
@found
|
22
|
-
end
|
23
|
-
|
24
18
|
def timeout_message
|
25
19
|
UNUSUAL_FAILURE_MESSAGE
|
26
20
|
end
|
@@ -6,31 +6,21 @@ module KubernetesDeploy
|
|
6
6
|
ONDELETE = 'OnDelete'
|
7
7
|
attr_reader :pods
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
SYNC_DEPENDENCIES = %w(Pod)
|
10
|
+
def sync(mediator)
|
11
|
+
super
|
12
|
+
@pods = exists? ? find_pods(mediator) : []
|
13
|
+
@server_version ||= mediator.kubectl.server_version
|
14
|
+
end
|
12
15
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
rollout_data = stateful_data["status"].slice("replicas", "readyReplicas", "currentReplicas")
|
18
|
-
@update_strategy = if kubectl.server_version < Gem::Version.new("1.7.0")
|
19
|
-
ONDELETE
|
20
|
-
else
|
21
|
-
stateful_data['spec']['updateStrategy']['type']
|
22
|
-
end
|
23
|
-
@status = rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
24
|
-
@pods = find_pods(stateful_data)
|
25
|
-
else # reset
|
26
|
-
@status_data = { 'readyReplicas' => '-1', 'currentReplicas' => '-2' }
|
27
|
-
@status = nil
|
28
|
-
@pods = []
|
29
|
-
end
|
16
|
+
def status
|
17
|
+
return super unless @instance_data["status"].present?
|
18
|
+
rollout_data = @instance_data["status"].slice("replicas", "readyReplicas", "currentReplicas")
|
19
|
+
rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
30
20
|
end
|
31
21
|
|
32
22
|
def deploy_succeeded?
|
33
|
-
if
|
23
|
+
if update_strategy == ONDELETE
|
34
24
|
# Gem cannot monitor update since it doesn't occur until delete
|
35
25
|
unless @success_assumption_warning_shown
|
36
26
|
@logger.warn("WARNING: Your StatefulSet's updateStrategy is set to OnDelete, "\
|
@@ -40,27 +30,43 @@ module KubernetesDeploy
|
|
40
30
|
end
|
41
31
|
true
|
42
32
|
else
|
43
|
-
|
44
|
-
|
45
|
-
|
33
|
+
status_data['currentRevision'] == status_data['updateRevision'] &&
|
34
|
+
desired_replicas == status_data['readyReplicas'].to_i &&
|
35
|
+
desired_replicas == status_data['currentReplicas'].to_i
|
46
36
|
end
|
47
37
|
end
|
48
38
|
|
49
39
|
def deploy_failed?
|
50
|
-
return false if
|
40
|
+
return false if update_strategy == ONDELETE
|
51
41
|
pods.present? && pods.any?(&:deploy_failed?)
|
52
42
|
end
|
53
43
|
|
54
|
-
|
55
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
def update_strategy
|
47
|
+
if @server_version < Gem::Version.new("1.7.0")
|
48
|
+
ONDELETE
|
49
|
+
elsif exists?
|
50
|
+
@instance_data['spec']['updateStrategy']['type']
|
51
|
+
else
|
52
|
+
'Unknown'
|
53
|
+
end
|
56
54
|
end
|
57
55
|
|
58
|
-
|
56
|
+
def status_data
|
57
|
+
return { 'readyReplicas' => '-1', 'currentReplicas' => '-2' } unless exists?
|
58
|
+
@instance_data["status"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def desired_replicas
|
62
|
+
return -1 unless exists?
|
63
|
+
@instance_data["spec"]["replicas"].to_i
|
64
|
+
end
|
59
65
|
|
60
|
-
def parent_of_pod?(
|
66
|
+
def parent_of_pod?(pod_data)
|
61
67
|
return false unless pod_data.dig("metadata", "ownerReferences")
|
62
|
-
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] ==
|
63
|
-
|
68
|
+
pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
|
69
|
+
@instance_data["status"]["currentRevision"] == pod_data["metadata"]["labels"]["controller-revision-hash"]
|
64
70
|
end
|
65
71
|
end
|
66
72
|
end
|
@@ -1,23 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module KubernetesDeploy
|
3
3
|
class Statefulservice < 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?
|
@@ -1,23 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module KubernetesDeploy
|
3
3
|
class Topic < 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?
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
module KubernetesDeploy
|
3
3
|
class ResourceWatcher
|
4
|
-
def initialize(resources
|
4
|
+
def initialize(resources:, sync_mediator:, logger:, deploy_started_at: Time.now.utc, operation_name: "deploy")
|
5
5
|
unless resources.is_a?(Enumerable)
|
6
6
|
raise ArgumentError, <<~MSG
|
7
7
|
ResourceWatcher expects Enumerable collection, got `#{resources.class}` instead
|
@@ -9,6 +9,7 @@ module KubernetesDeploy
|
|
9
9
|
end
|
10
10
|
@resources = resources
|
11
11
|
@logger = logger
|
12
|
+
@sync_mediator = sync_mediator
|
12
13
|
@deploy_started_at = deploy_started_at
|
13
14
|
@operation_name = operation_name
|
14
15
|
end
|
@@ -23,7 +24,7 @@ module KubernetesDeploy
|
|
23
24
|
end
|
24
25
|
delay_sync_until = Time.now.utc + delay_sync # don't pummel the API if the sync is fast
|
25
26
|
|
26
|
-
|
27
|
+
@sync_mediator.sync(remainder)
|
27
28
|
new_successes, remainder = remainder.partition(&:deploy_succeeded?)
|
28
29
|
new_failures, remainder = remainder.partition(&:deploy_failed?)
|
29
30
|
new_timeouts, remainder = remainder.partition(&:deploy_timed_out?)
|
@@ -93,7 +94,9 @@ module KubernetesDeploy
|
|
93
94
|
"failed to #{@operation_name} #{failures.length} #{'resource'.pluralize(failures.length)}"
|
94
95
|
)
|
95
96
|
end
|
96
|
-
KubernetesDeploy::Concurrency.split_across_threads(failed_resources
|
97
|
+
KubernetesDeploy::Concurrency.split_across_threads(failed_resources) do |r|
|
98
|
+
r.sync_debug_info(@sync_mediator.kubectl)
|
99
|
+
end
|
97
100
|
failed_resources.each { |r| @logger.summary.add_paragraph(r.debug_message) }
|
98
101
|
end
|
99
102
|
end
|
@@ -24,6 +24,7 @@ module KubernetesDeploy
|
|
24
24
|
@context = context
|
25
25
|
@namespace = namespace
|
26
26
|
@logger = logger
|
27
|
+
@sync_mediator = SyncMediator.new(namespace: @namespace, context: @context, logger: @logger)
|
27
28
|
end
|
28
29
|
|
29
30
|
def perform(*args)
|
@@ -48,7 +49,8 @@ module KubernetesDeploy
|
|
48
49
|
|
49
50
|
@logger.phase_heading("Waiting for rollout")
|
50
51
|
resources = build_watchables(deployments, start)
|
51
|
-
ResourceWatcher.new(resources,
|
52
|
+
ResourceWatcher.new(resources: resources, sync_mediator: @sync_mediator,
|
53
|
+
logger: @logger, operation_name: "restart").run
|
52
54
|
failed_resources = resources.reject(&:deploy_succeeded?)
|
53
55
|
success = failed_resources.empty?
|
54
56
|
if !success && failed_resources.all?(&:deploy_timed_out?)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module KubernetesDeploy
|
3
|
+
class SyncMediator
|
4
|
+
def initialize(namespace:, context:, logger:)
|
5
|
+
@namespace = namespace
|
6
|
+
@context = context
|
7
|
+
@logger = logger
|
8
|
+
clear_cache
|
9
|
+
end
|
10
|
+
|
11
|
+
def get_instance(kind, resource_name)
|
12
|
+
if @cache.key?(kind)
|
13
|
+
@cache.dig(kind, resource_name) || {}
|
14
|
+
else
|
15
|
+
request_instance(kind, resource_name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_all(kind, selector = nil)
|
20
|
+
fetch_by_kind(kind) unless @cache.key?(kind)
|
21
|
+
instances = @cache.fetch(kind, {}).values
|
22
|
+
return instances unless selector
|
23
|
+
|
24
|
+
instances.select do |r|
|
25
|
+
labels = r.dig("metadata", "labels") || {}
|
26
|
+
labels >= selector
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def sync(resources)
|
31
|
+
clear_cache
|
32
|
+
dependencies = resources.map(&:class).uniq.flat_map do |c|
|
33
|
+
c::SYNC_DEPENDENCIES if c.const_defined?('SYNC_DEPENDENCIES')
|
34
|
+
end
|
35
|
+
kinds = (resources.map(&:type) + dependencies).compact.uniq
|
36
|
+
kinds.each { |kind| fetch_by_kind(kind) }
|
37
|
+
|
38
|
+
KubernetesDeploy::Concurrency.split_across_threads(resources) do |r|
|
39
|
+
r.sync(dup)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def kubectl
|
44
|
+
@kubectl ||= Kubectl.new(namespace: @namespace, context: @context, logger: @logger, log_failure_by_default: false)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def clear_cache
|
50
|
+
@cache = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def request_instance(kind, iname)
|
54
|
+
raw_json, _, st = kubectl.run("get", kind, iname, "-a", "--output=json")
|
55
|
+
st.success? ? JSON.parse(raw_json) : {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def fetch_by_kind(kind)
|
59
|
+
raw_json, _, st = kubectl.run("get", kind, "-a", "--output=json")
|
60
|
+
return unless st.success?
|
61
|
+
@cache[kind] = JSON.parse(raw_json)["items"].each_with_object({}) do |r, instances|
|
62
|
+
instances[r.dig("metadata", "name")] = r
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
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.18.
|
4
|
+
version: 0.18.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-04-
|
12
|
+
date: 2018-04-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -210,6 +210,7 @@ files:
|
|
210
210
|
- CHANGELOG.md
|
211
211
|
- CODE_OF_CONDUCT.md
|
212
212
|
- Gemfile
|
213
|
+
- ISSUE_TEMPLATE.md
|
213
214
|
- LICENSE.txt
|
214
215
|
- NO-BINAUTH
|
215
216
|
- README.md
|
@@ -237,7 +238,6 @@ files:
|
|
237
238
|
- lib/kubernetes-deploy/kubectl.rb
|
238
239
|
- lib/kubernetes-deploy/kubernetes_resource.rb
|
239
240
|
- lib/kubernetes-deploy/kubernetes_resource/bucket.rb
|
240
|
-
- lib/kubernetes-deploy/kubernetes_resource/bugsnag.rb
|
241
241
|
- lib/kubernetes-deploy/kubernetes_resource/cloudsql.rb
|
242
242
|
- lib/kubernetes-deploy/kubernetes_resource/config_map.rb
|
243
243
|
- lib/kubernetes-deploy/kubernetes_resource/cron_job.rb
|
@@ -264,6 +264,7 @@ files:
|
|
264
264
|
- lib/kubernetes-deploy/restart_task.rb
|
265
265
|
- lib/kubernetes-deploy/runner_task.rb
|
266
266
|
- lib/kubernetes-deploy/statsd.rb
|
267
|
+
- lib/kubernetes-deploy/sync_mediator.rb
|
267
268
|
- lib/kubernetes-deploy/version.rb
|
268
269
|
- screenshots/deploy-demo.gif
|
269
270
|
- screenshots/migrate-logs.png
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module KubernetesDeploy
|
3
|
-
class Bugsnag < KubernetesResource
|
4
|
-
TIMEOUT = 1.minute
|
5
|
-
|
6
|
-
def sync
|
7
|
-
@secret_found = false
|
8
|
-
_, _err, st = kubectl.run("get", type, @name)
|
9
|
-
@found = st.success?
|
10
|
-
if @found
|
11
|
-
secrets, _err, _st = kubectl.run("get", "secrets", "--output=name")
|
12
|
-
@secret_found = secrets.split.any? { |s| s.end_with?("-bugsnag") }
|
13
|
-
end
|
14
|
-
@status = @secret_found ? "Available" : "Unknown"
|
15
|
-
end
|
16
|
-
|
17
|
-
def deploy_succeeded?
|
18
|
-
@secret_found
|
19
|
-
end
|
20
|
-
|
21
|
-
def deploy_failed?
|
22
|
-
false
|
23
|
-
end
|
24
|
-
|
25
|
-
def exists?
|
26
|
-
@found
|
27
|
-
end
|
28
|
-
|
29
|
-
def deploy_method
|
30
|
-
:replace
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|