kubernetes-deploy 0.10.1 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kubernetes-deploy.rb +1 -0
- data/lib/kubernetes-deploy/concurrency.rb +18 -0
- data/lib/kubernetes-deploy/kubernetes_resource.rb +15 -1
- data/lib/kubernetes-deploy/kubernetes_resource/deployment.rb +2 -1
- data/lib/kubernetes-deploy/resource_watcher.rb +1 -1
- data/lib/kubernetes-deploy/runner.rb +15 -13
- data/lib/kubernetes-deploy/version.rb +1 -1
- metadata +3 -3
- data/lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb +0 -95
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1fcb28df3ae43f68d38b30177c5131f59c45c602
|
4
|
+
data.tar.gz: 26f3440846640400f726ef6fe9acc95460ddef3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e6d10b5aea6ffacfacf466d7d3255e7f22709432ea49924cee5ef173f371b13b9f7d04cfb86b9182610e7cb3207e2f526fefca37cd864bc83ffdd1fc559869a
|
7
|
+
data.tar.gz: 7909b9c4475a6151b4b1d286601b49188d4797e0d0174f9526ade8afa67134ecfff07e793b42392d004c2eb145b075b986556dd7f35e1f812e9414737c89924f
|
data/lib/kubernetes-deploy.rb
CHANGED
@@ -15,6 +15,7 @@ require 'kubernetes-deploy/errors'
|
|
15
15
|
require 'kubernetes-deploy/formatted_logger'
|
16
16
|
require 'kubernetes-deploy/runner'
|
17
17
|
require 'kubernetes-deploy/statsd'
|
18
|
+
require 'kubernetes-deploy/concurrency'
|
18
19
|
|
19
20
|
module KubernetesDeploy
|
20
21
|
KubernetesDeploy::StatsD.build
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module KubernetesDeploy
|
3
|
+
module Concurrency
|
4
|
+
MAX_THREADS = 8
|
5
|
+
|
6
|
+
def self.split_across_threads(all_work, &block)
|
7
|
+
return if all_work.empty?
|
8
|
+
raise ArgumentError, "Block of work is required" unless block_given?
|
9
|
+
|
10
|
+
slice_size = ((all_work.length + MAX_THREADS - 1) / MAX_THREADS)
|
11
|
+
threads = []
|
12
|
+
all_work.each_slice(slice_size) do |work_group|
|
13
|
+
threads << Thread.new { work_group.each(&block) }
|
14
|
+
end
|
15
|
+
threads.each(&:join)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,7 +6,7 @@ require 'kubernetes-deploy/kubectl'
|
|
6
6
|
|
7
7
|
module KubernetesDeploy
|
8
8
|
class KubernetesResource
|
9
|
-
attr_reader :name, :namespace, :file, :context
|
9
|
+
attr_reader :name, :namespace, :file, :context, :validation_error_msg
|
10
10
|
attr_writer :type, :deploy_started
|
11
11
|
|
12
12
|
TIMEOUT = 5.minutes
|
@@ -55,6 +55,20 @@ module KubernetesDeploy
|
|
55
55
|
@logger = logger
|
56
56
|
@definition = definition
|
57
57
|
@statsd_report_done = false
|
58
|
+
@validation_error_msg = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate_definition
|
62
|
+
@validation_error_msg = nil
|
63
|
+
command = ["create", "-f", file_path, "--dry-run", "--output=name"]
|
64
|
+
_, err, st = kubectl.run(*command, log_failure: false)
|
65
|
+
return true if st.success?
|
66
|
+
@validation_error_msg = err
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def validation_failed?
|
71
|
+
@validation_error_msg.present?
|
58
72
|
end
|
59
73
|
|
60
74
|
def id
|
@@ -14,7 +14,8 @@ module KubernetesDeploy
|
|
14
14
|
@rollout_data = { "replicas" => 0 }.merge(deployment_data["status"]
|
15
15
|
.slice("replicas", "updatedReplicas", "availableReplicas", "unavailableReplicas"))
|
16
16
|
@status = @rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
|
17
|
-
|
17
|
+
conditions = deployment_data.fetch("status", {}).fetch("conditions", [])
|
18
|
+
@progress = conditions.find { |condition| condition['type'] == 'Progressing' }
|
18
19
|
else # reset
|
19
20
|
@latest_rs = nil
|
20
21
|
@rollout_data = { "replicas" => 0 }
|
@@ -21,7 +21,7 @@ module KubernetesDeploy
|
|
21
21
|
end
|
22
22
|
delay_sync_until = Time.now.utc + delay_sync # don't pummel the API if the sync is fast
|
23
23
|
|
24
|
-
@resources
|
24
|
+
KubernetesDeploy::Concurrency.split_across_threads(@resources, &:sync)
|
25
25
|
newly_finished_resources, @resources = @resources.partition(&:deploy_finished?)
|
26
26
|
|
27
27
|
if newly_finished_resources.present?
|
@@ -20,7 +20,6 @@ require 'kubernetes-deploy/kubernetes_resource'
|
|
20
20
|
pod_disruption_budget
|
21
21
|
replica_set
|
22
22
|
service_account
|
23
|
-
daemon_set
|
24
23
|
).each do |subresource|
|
25
24
|
require "kubernetes-deploy/kubernetes_resource/#{subresource}"
|
26
25
|
end
|
@@ -89,9 +88,10 @@ module KubernetesDeploy
|
|
89
88
|
confirm_context_exists
|
90
89
|
confirm_namespace_exists
|
91
90
|
resources = discover_resources
|
91
|
+
validate_definitions(resources)
|
92
92
|
|
93
93
|
@logger.phase_heading("Checking initial resource statuses")
|
94
|
-
|
94
|
+
KubernetesDeploy::Concurrency.split_across_threads(resources, &:sync)
|
95
95
|
resources.each { |r| @logger.info(r.pretty_status) }
|
96
96
|
|
97
97
|
ejson = EjsonSecretProvisioner.new(
|
@@ -192,7 +192,7 @@ module KubernetesDeploy
|
|
192
192
|
# stderr often contains one or more lines like the following, from which we can extract the file path(s):
|
193
193
|
# Error from server (TypeOfError): error when creating "/path/to/service-gqq5oh.yml": Service "web" is invalid:
|
194
194
|
matches = stderr.scan(%r{"(/\S+\.ya?ml\S*)"})
|
195
|
-
matches
|
195
|
+
matches&.flatten
|
196
196
|
end
|
197
197
|
|
198
198
|
def deploy_has_priority_resources?(resources)
|
@@ -215,15 +215,26 @@ module KubernetesDeploy
|
|
215
215
|
end
|
216
216
|
end
|
217
217
|
|
218
|
+
def validate_definitions(resources)
|
219
|
+
KubernetesDeploy::Concurrency.split_across_threads(resources, &:validate_definition)
|
220
|
+
failed_resources = resources.select(&:validation_failed?)
|
221
|
+
return unless failed_resources.present?
|
222
|
+
|
223
|
+
failed_resources.each do |r|
|
224
|
+
record_invalid_template(r.validation_error_msg, file_paths: [r.file_path])
|
225
|
+
end
|
226
|
+
raise FatalDeploymentError, "Template validation failed"
|
227
|
+
end
|
228
|
+
|
218
229
|
def discover_resources
|
219
230
|
resources = []
|
220
231
|
@logger.info("Discovering templates:")
|
232
|
+
|
221
233
|
Dir.foreach(@template_dir) do |filename|
|
222
234
|
next unless filename.end_with?(".yml.erb", ".yml", ".yaml", ".yaml.erb")
|
223
235
|
|
224
236
|
split_templates(filename) do |r_def|
|
225
237
|
r = KubernetesResource.build(namespace: @namespace, context: @context, logger: @logger, definition: r_def)
|
226
|
-
validate_template_via_dry_run(r.file_path, filename)
|
227
238
|
resources << r
|
228
239
|
@logger.info " - #{r.id}"
|
229
240
|
end
|
@@ -231,14 +242,6 @@ module KubernetesDeploy
|
|
231
242
|
resources
|
232
243
|
end
|
233
244
|
|
234
|
-
def validate_template_via_dry_run(file_path, original_filename)
|
235
|
-
command = ["create", "-f", file_path, "--dry-run", "--output=name"]
|
236
|
-
_, err, st = kubectl.run(*command, log_failure: false)
|
237
|
-
return if st.success?
|
238
|
-
record_invalid_template(err, file_paths: [file_path], original_filenames: [original_filename])
|
239
|
-
raise FatalDeploymentError, "Template validation failed (command: #{Shellwords.join(command)})"
|
240
|
-
end
|
241
|
-
|
242
245
|
def split_templates(filename)
|
243
246
|
file_content = File.read(File.join(@template_dir, filename))
|
244
247
|
rendered_content = render_template(filename, file_content)
|
@@ -271,7 +274,6 @@ module KubernetesDeploy
|
|
271
274
|
if file_content.present?
|
272
275
|
debug_msg += "\n> Rendered template content:\n#{indent_four(file_content)}"
|
273
276
|
end
|
274
|
-
|
275
277
|
@logger.summary.add_paragraph(debug_msg)
|
276
278
|
end
|
277
279
|
|
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.
|
4
|
+
version: 0.11.0
|
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: 2017-07-
|
12
|
+
date: 2017-07-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -202,6 +202,7 @@ files:
|
|
202
202
|
- exe/kubernetes-run
|
203
203
|
- kubernetes-deploy.gemspec
|
204
204
|
- lib/kubernetes-deploy.rb
|
205
|
+
- lib/kubernetes-deploy/concurrency.rb
|
205
206
|
- lib/kubernetes-deploy/deferred_summary_logging.rb
|
206
207
|
- lib/kubernetes-deploy/ejson_secret_provisioner.rb
|
207
208
|
- lib/kubernetes-deploy/errors.rb
|
@@ -213,7 +214,6 @@ files:
|
|
213
214
|
- lib/kubernetes-deploy/kubernetes_resource/bugsnag.rb
|
214
215
|
- lib/kubernetes-deploy/kubernetes_resource/cloudsql.rb
|
215
216
|
- lib/kubernetes-deploy/kubernetes_resource/config_map.rb
|
216
|
-
- lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb
|
217
217
|
- lib/kubernetes-deploy/kubernetes_resource/deployment.rb
|
218
218
|
- lib/kubernetes-deploy/kubernetes_resource/ingress.rb
|
219
219
|
- lib/kubernetes-deploy/kubernetes_resource/persistent_volume_claim.rb
|
@@ -1,95 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module KubernetesDeploy
|
3
|
-
class DaemonSet < KubernetesResource
|
4
|
-
TIMEOUT = 5.minutes
|
5
|
-
|
6
|
-
def sync
|
7
|
-
raw_json, _err, st = kubectl.run("get", type, @name, "--output=json")
|
8
|
-
@found = st.success?
|
9
|
-
|
10
|
-
if @found
|
11
|
-
daemonset_data = JSON.parse(raw_json)
|
12
|
-
@current_generation = daemonset_data["metadata"]["generation"]
|
13
|
-
@observed_generation = daemonset_data["status"]["observedGeneration"]
|
14
|
-
@rollout_data = daemonset_data["status"]
|
15
|
-
.slice("currentNumberScheduled", "desiredNumberScheduled", "numberReady", "numberAvailable")
|
16
|
-
@status = @rollout_data.map { |state_replicas, num| "#{num} #{state_replicas}" }.join(", ")
|
17
|
-
@pods = find_pods(daemonset_data)
|
18
|
-
else # reset
|
19
|
-
@rollout_data = { "currentNumberScheduled" => 0 }
|
20
|
-
@current_generation = 1 # to make sure the current and observed generations are different
|
21
|
-
@observed_generation = 0
|
22
|
-
@status = nil
|
23
|
-
@pods = []
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def deploy_succeeded?
|
28
|
-
@rollout_data["desiredNumberScheduled"].to_i == @rollout_data["currentNumberScheduled"].to_i &&
|
29
|
-
@rollout_data["desiredNumberScheduled"].to_i == @rollout_data["numberAvailable"].to_i &&
|
30
|
-
@current_generation == @observed_generation
|
31
|
-
end
|
32
|
-
|
33
|
-
def deploy_failed?
|
34
|
-
@pods.present? && @pods.any?(&:deploy_failed?)
|
35
|
-
end
|
36
|
-
|
37
|
-
def failure_message
|
38
|
-
@pods.map(&:failure_message).compact.uniq.join("\n")
|
39
|
-
end
|
40
|
-
|
41
|
-
def timeout_message
|
42
|
-
@pods.map(&:timeout_message).compact.uniq.join("\n")
|
43
|
-
end
|
44
|
-
|
45
|
-
def deploy_timed_out?
|
46
|
-
super || @pods.present? && @pods.any?(&:deploy_timed_out?)
|
47
|
-
end
|
48
|
-
|
49
|
-
def exists?
|
50
|
-
@found
|
51
|
-
end
|
52
|
-
|
53
|
-
def fetch_events
|
54
|
-
own_events = super
|
55
|
-
return own_events unless @pods.present?
|
56
|
-
most_useful_pod = @pods.find(&:deploy_failed?) || @pods.find(&:deploy_timed_out?) || @pods.first
|
57
|
-
own_events.merge(most_useful_pod.fetch_events)
|
58
|
-
end
|
59
|
-
|
60
|
-
def fetch_logs
|
61
|
-
most_useful_pod = @pods.find(&:deploy_failed?) || @pods.find(&:deploy_timed_out?) || @pods.first
|
62
|
-
most_useful_pod.fetch_logs
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def find_pods(ds_data)
|
68
|
-
label_string = ds_data["spec"]["selector"]["matchLabels"].map { |k, v| "#{k}=#{v}" }.join(",")
|
69
|
-
raw_json, _err, st = kubectl.run("get", "pods", "-a", "--output=json", "--selector=#{label_string}")
|
70
|
-
return [] unless st.success?
|
71
|
-
|
72
|
-
all_pods = JSON.parse(raw_json)["items"]
|
73
|
-
current_generation = ds_data["metadata"]["generation"]
|
74
|
-
|
75
|
-
latest_pods = all_pods.find_all do |pods|
|
76
|
-
pods["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == ds_data["metadata"]["uid"] } &&
|
77
|
-
pods["metadata"]["labels"]["pod-template-generation"].to_i == current_generation.to_i
|
78
|
-
end
|
79
|
-
return unless latest_pods.present?
|
80
|
-
|
81
|
-
latest_pods.each_with_object([]) do |pod_data, relevant_pods|
|
82
|
-
pod = Pod.new(
|
83
|
-
namespace: namespace,
|
84
|
-
context: context,
|
85
|
-
definition: pod_data,
|
86
|
-
logger: @logger,
|
87
|
-
parent: "#{@name.capitalize} daemon set",
|
88
|
-
deploy_started: @deploy_started
|
89
|
-
)
|
90
|
-
pod.sync(pod_data)
|
91
|
-
relevant_pods << pod
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|