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
@@ -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
|