krane 1.1.2 → 2.1.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/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +11 -19
- data/.shopify-build/krane.yml +20 -6
- data/1.0-Upgrade.md +1 -1
- data/CHANGELOG.md +48 -1
- data/CONTRIBUTING.md +1 -1
- data/README.md +8 -5
- data/bin/ci +1 -1
- data/dev.yml +3 -2
- data/krane.gemspec +4 -2
- data/lib/krane/annotation.rb +11 -0
- data/lib/krane/cluster_resource_discovery.rb +9 -6
- data/lib/krane/concerns/template_reporting.rb +0 -6
- data/lib/krane/container_overrides.rb +33 -0
- data/lib/krane/deploy_task.rb +21 -17
- data/lib/krane/ejson_secret_provisioner.rb +2 -3
- data/lib/krane/global_deploy_task.rb +8 -14
- data/lib/krane/kubeclient_builder.rb +4 -2
- data/lib/krane/kubectl.rb +8 -4
- data/lib/krane/kubernetes_resource.rb +32 -42
- data/lib/krane/kubernetes_resource/custom_resource.rb +2 -2
- data/lib/krane/kubernetes_resource/custom_resource_definition.rb +13 -10
- data/lib/krane/kubernetes_resource/deployment.rb +5 -7
- data/lib/krane/kubernetes_resource/persistent_volume_claim.rb +1 -0
- data/lib/krane/psych_k8s_compatibility.rb +36 -0
- data/lib/krane/render_task.rb +2 -2
- data/lib/krane/renderer.rb +2 -0
- data/lib/krane/resource_deployer.rb +9 -5
- data/lib/krane/restart_task.rb +8 -8
- data/lib/krane/runner_task.rb +21 -24
- data/lib/krane/statsd.rb +2 -2
- data/lib/krane/task_config.rb +7 -2
- data/lib/krane/task_config_validator.rb +3 -3
- data/lib/krane/version.rb +1 -1
- metadata +10 -6
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'psych'
|
4
|
+
|
5
|
+
module PsychK8sCompatibility
|
6
|
+
def self.massage_node(n)
|
7
|
+
if n.is_a?(Psych::Nodes::Scalar) &&
|
8
|
+
(n.style == Psych::Nodes::Scalar::PLAIN) &&
|
9
|
+
n.value.is_a?(String) &&
|
10
|
+
n.value =~ /\A[+-]?\d+(?:\.\d+)?[eE][+-]?\d+\z/
|
11
|
+
n.style = Psych::Nodes::Scalar::DOUBLE_QUOTED
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
refine Psych.singleton_class do
|
16
|
+
def dump(o, io = nil, options = {})
|
17
|
+
if io.is_a?(Hash)
|
18
|
+
options = io
|
19
|
+
io = nil
|
20
|
+
end
|
21
|
+
visitor = Psych::Visitors::YAMLTree.create(options)
|
22
|
+
visitor << o
|
23
|
+
visitor.tree.each { |n| PsychK8sCompatibility.massage_node(n) }
|
24
|
+
visitor.tree.yaml(io, options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def dump_stream(*objects)
|
28
|
+
visitor = Psych::Visitors::YAMLTree.create({})
|
29
|
+
objects.each do |o|
|
30
|
+
visitor << o
|
31
|
+
end
|
32
|
+
visitor.tree.each { |n| PsychK8sCompatibility.massage_node(n) }
|
33
|
+
visitor.tree.yaml
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/krane/render_task.rb
CHANGED
data/lib/krane/renderer.rb
CHANGED
@@ -46,8 +46,13 @@ module Krane
|
|
46
46
|
bare_pods.first.stream_logs = true
|
47
47
|
end
|
48
48
|
|
49
|
-
predeploy_sequence.each do |resource_type|
|
50
|
-
matching_resources = resource_list.select
|
49
|
+
predeploy_sequence.each do |resource_type, attributes|
|
50
|
+
matching_resources = resource_list.select do |r|
|
51
|
+
r.type == resource_type &&
|
52
|
+
(!attributes[:group] || r.group == attributes[:group])
|
53
|
+
end
|
54
|
+
StatsD.client.gauge('priority_resources.count', matching_resources.size, tags: statsd_tags)
|
55
|
+
|
51
56
|
next if matching_resources.empty?
|
52
57
|
deploy_resources(matching_resources, verify: true, record_summary: false)
|
53
58
|
|
@@ -90,11 +95,10 @@ module Krane
|
|
90
95
|
applyables, individuals = resources.partition { |r| r.deploy_method == :apply }
|
91
96
|
# Prunable resources should also applied so that they can be pruned
|
92
97
|
pruneable_types = @prune_whitelist.map { |t| t.split("/").last }
|
93
|
-
applyables += individuals.select { |r| pruneable_types.include?(r.type) }
|
98
|
+
applyables += individuals.select { |r| pruneable_types.include?(r.type) && !r.deploy_method_override }
|
94
99
|
|
95
100
|
individuals.each do |individual_resource|
|
96
101
|
individual_resource.deploy_started_at = Time.now.utc
|
97
|
-
|
98
102
|
case individual_resource.deploy_method
|
99
103
|
when :create
|
100
104
|
err, status = create_resource(individual_resource)
|
@@ -148,7 +152,7 @@ module Krane
|
|
148
152
|
output_is_sensitive = resources.any?(&:sensitive_template_content?)
|
149
153
|
global_mode = resources.all?(&:global?)
|
150
154
|
out, err, st = kubectl.run(*command, log_failure: false, output_is_sensitive: output_is_sensitive,
|
151
|
-
use_namespace: !global_mode)
|
155
|
+
attempts: 2, use_namespace: !global_mode)
|
152
156
|
|
153
157
|
if st.success?
|
154
158
|
log_pruning(out) if prune
|
data/lib/krane/restart_task.rb
CHANGED
@@ -22,15 +22,19 @@ module Krane
|
|
22
22
|
HTTP_OK_RANGE = 200..299
|
23
23
|
ANNOTATION = "shipit.shopify.io/restart"
|
24
24
|
|
25
|
+
attr_reader :task_config
|
26
|
+
|
27
|
+
delegate :kubeclient_builder, to: :task_config
|
28
|
+
|
25
29
|
# Initializes the restart task
|
26
30
|
#
|
27
31
|
# @param context [String] Kubernetes context / cluster (*required*)
|
28
32
|
# @param namespace [String] Kubernetes namespace (*required*)
|
29
33
|
# @param logger [Object] Logger object (defaults to an instance of Krane::FormattedLogger)
|
30
34
|
# @param global_timeout [Integer] Timeout in seconds
|
31
|
-
def initialize(context:, namespace:, logger: nil, global_timeout: nil)
|
35
|
+
def initialize(context:, namespace:, logger: nil, global_timeout: nil, kubeconfig: nil)
|
32
36
|
@logger = logger || Krane::FormattedLogger.build(namespace, context)
|
33
|
-
@task_config = Krane::TaskConfig.new(context, namespace, @logger)
|
37
|
+
@task_config = Krane::TaskConfig.new(context, namespace, @logger, kubeconfig)
|
34
38
|
@context = context
|
35
39
|
@namespace = namespace
|
36
40
|
@global_timeout = global_timeout
|
@@ -39,8 +43,8 @@ module Krane
|
|
39
43
|
# Runs the task, returning a boolean representing success or failure
|
40
44
|
#
|
41
45
|
# @return [Boolean]
|
42
|
-
def run(
|
43
|
-
perform!(
|
46
|
+
def run(**args)
|
47
|
+
perform!(**args)
|
44
48
|
true
|
45
49
|
rescue FatalDeploymentError
|
46
50
|
false
|
@@ -220,9 +224,5 @@ module Krane
|
|
220
224
|
def v1beta1_kubeclient
|
221
225
|
@v1beta1_kubeclient ||= kubeclient_builder.build_v1beta1_kubeclient(@context)
|
222
226
|
end
|
223
|
-
|
224
|
-
def kubeclient_builder
|
225
|
-
@kubeclient_builder ||= KubeclientBuilder.new
|
226
|
-
end
|
227
227
|
end
|
228
228
|
end
|
data/lib/krane/runner_task.rb
CHANGED
@@ -9,13 +9,16 @@ require 'krane/resource_watcher'
|
|
9
9
|
require 'krane/kubernetes_resource'
|
10
10
|
require 'krane/kubernetes_resource/pod'
|
11
11
|
require 'krane/runner_task_config_validator'
|
12
|
+
require 'krane/container_overrides'
|
12
13
|
|
13
14
|
module Krane
|
14
15
|
# Run a pod that exits upon completing a task
|
15
16
|
class RunnerTask
|
16
17
|
class TaskTemplateMissingError < TaskConfigurationError; end
|
17
18
|
|
18
|
-
attr_reader :pod_name
|
19
|
+
attr_reader :pod_name, :task_config
|
20
|
+
|
21
|
+
delegate :kubeclient_builder, to: :task_config
|
19
22
|
|
20
23
|
# Initializes the runner task
|
21
24
|
#
|
@@ -23,9 +26,9 @@ module Krane
|
|
23
26
|
# @param context [String] Kubernetes context / cluster (*required*)
|
24
27
|
# @param logger [Object] Logger object (defaults to an instance of Krane::FormattedLogger)
|
25
28
|
# @param global_timeout [Integer] Timeout in seconds
|
26
|
-
def initialize(namespace:, context:, logger: nil, global_timeout: nil)
|
29
|
+
def initialize(namespace:, context:, logger: nil, global_timeout: nil, kubeconfig: nil)
|
27
30
|
@logger = logger || Krane::FormattedLogger.build(namespace, context)
|
28
|
-
@task_config = Krane::TaskConfig.new(context, namespace, @logger)
|
31
|
+
@task_config = Krane::TaskConfig.new(context, namespace, @logger, kubeconfig)
|
29
32
|
@namespace = namespace
|
30
33
|
@context = context
|
31
34
|
@global_timeout = global_timeout
|
@@ -34,8 +37,8 @@ module Krane
|
|
34
37
|
# Runs the task, returning a boolean representing success or failure
|
35
38
|
#
|
36
39
|
# @return [Boolean]
|
37
|
-
def run(
|
38
|
-
run!(
|
40
|
+
def run(**args)
|
41
|
+
run!(**args)
|
39
42
|
true
|
40
43
|
rescue DeploymentTimeoutError, FatalDeploymentError
|
41
44
|
false
|
@@ -50,7 +53,7 @@ module Krane
|
|
50
53
|
# @param verify_result [Boolean] Wait for completion and verify pod success
|
51
54
|
#
|
52
55
|
# @return [nil]
|
53
|
-
def run!(template:, command:, arguments:, env_vars: [], verify_result: true)
|
56
|
+
def run!(template:, command:, arguments:, env_vars: [], image_tag: nil, verify_result: true)
|
54
57
|
start = Time.now.utc
|
55
58
|
@logger.reset
|
56
59
|
|
@@ -59,8 +62,13 @@ module Krane
|
|
59
62
|
@logger.info("Validating configuration")
|
60
63
|
verify_config!(template)
|
61
64
|
@logger.info("Using namespace '#{@namespace}' in context '#{@context}'")
|
62
|
-
|
63
|
-
|
65
|
+
container_overrides = ContainerOverrides.new(
|
66
|
+
command: command,
|
67
|
+
arguments: arguments,
|
68
|
+
env_vars: env_vars,
|
69
|
+
image_tag: image_tag
|
70
|
+
)
|
71
|
+
pod = build_pod(template, container_overrides, verify_result)
|
64
72
|
validate_pod(pod)
|
65
73
|
|
66
74
|
@logger.phase_heading("Running pod")
|
@@ -98,11 +106,12 @@ module Krane
|
|
98
106
|
raise FatalDeploymentError, msg
|
99
107
|
end
|
100
108
|
|
101
|
-
def build_pod(template_name,
|
109
|
+
def build_pod(template_name, container_overrides, verify_result)
|
102
110
|
task_template = get_template(template_name)
|
103
111
|
@logger.info("Using template '#{template_name}'")
|
104
112
|
pod_template = build_pod_definition(task_template)
|
105
|
-
|
113
|
+
container = extract_task_runner_container(pod_template)
|
114
|
+
container_overrides.apply!(container)
|
106
115
|
ensure_valid_restart_policy!(pod_template, verify_result)
|
107
116
|
Pod.new(namespace: @namespace, context: @context, logger: @logger, stream_logs: true,
|
108
117
|
definition: pod_template.to_hash.deep_stringify_keys, statsd_tags: [])
|
@@ -165,7 +174,7 @@ module Krane
|
|
165
174
|
pod_definition
|
166
175
|
end
|
167
176
|
|
168
|
-
def
|
177
|
+
def extract_task_runner_container(pod_definition)
|
169
178
|
container = pod_definition.spec.containers.find { |cont| cont.name == 'task-runner' }
|
170
179
|
if container.nil?
|
171
180
|
message = "Pod spec does not contain a template container called 'task-runner'"
|
@@ -173,15 +182,7 @@ module Krane
|
|
173
182
|
raise TaskConfigurationError, message
|
174
183
|
end
|
175
184
|
|
176
|
-
container
|
177
|
-
container.args = args if args
|
178
|
-
|
179
|
-
env_args = env_vars.map do |env|
|
180
|
-
key, value = env.split('=', 2)
|
181
|
-
{ name: key, value: value }
|
182
|
-
end
|
183
|
-
container.env ||= []
|
184
|
-
container.env = container.env.map(&:to_h) + env_args
|
185
|
+
container
|
185
186
|
end
|
186
187
|
|
187
188
|
def ensure_valid_restart_policy!(template, verify)
|
@@ -201,10 +202,6 @@ module Krane
|
|
201
202
|
@kubeclient ||= kubeclient_builder.build_v1_kubeclient(@context)
|
202
203
|
end
|
203
204
|
|
204
|
-
def kubeclient_builder
|
205
|
-
@kubeclient_builder ||= KubeclientBuilder.new
|
206
|
-
end
|
207
|
-
|
208
205
|
def statsd_tags(status)
|
209
206
|
%W(namespace:#{@namespace} context:#{@context} status:#{status})
|
210
207
|
end
|
data/lib/krane/statsd.rb
CHANGED
@@ -35,10 +35,10 @@ module Krane
|
|
35
35
|
end
|
36
36
|
|
37
37
|
metric ||= "#{method_name}.duration"
|
38
|
-
self::InstrumentationProxy.send(:define_method, method_name) do |*args, &block|
|
38
|
+
self::InstrumentationProxy.send(:define_method, method_name) do |*args, **kwargs, &block|
|
39
39
|
begin
|
40
40
|
start_time = Time.now.utc
|
41
|
-
super(*args, &block)
|
41
|
+
super(*args, **kwargs, &block)
|
42
42
|
rescue
|
43
43
|
error = true
|
44
44
|
raise
|
data/lib/krane/task_config.rb
CHANGED
@@ -4,12 +4,13 @@ require 'krane/cluster_resource_discovery'
|
|
4
4
|
|
5
5
|
module Krane
|
6
6
|
class TaskConfig
|
7
|
-
attr_reader :context, :namespace, :logger
|
7
|
+
attr_reader :context, :namespace, :logger, :kubeconfig
|
8
8
|
|
9
|
-
def initialize(context, namespace, logger = nil)
|
9
|
+
def initialize(context, namespace, logger = nil, kubeconfig = nil)
|
10
10
|
@context = context
|
11
11
|
@namespace = namespace
|
12
12
|
@logger = logger || FormattedLogger.build(@namespace, @context)
|
13
|
+
@kubeconfig = kubeconfig || ENV['KUBECONFIG']
|
13
14
|
end
|
14
15
|
|
15
16
|
def global_kinds
|
@@ -18,5 +19,9 @@ module Krane
|
|
18
19
|
cluster_resource_discoverer.fetch_resources(namespaced: false).map { |g| g["kind"] }
|
19
20
|
end
|
20
21
|
end
|
22
|
+
|
23
|
+
def kubeclient_builder
|
24
|
+
@kubeclient_builder ||= KubeclientBuilder.new(kubeconfig: kubeconfig)
|
25
|
+
end
|
21
26
|
end
|
22
27
|
end
|
@@ -45,7 +45,7 @@ module Krane
|
|
45
45
|
end
|
46
46
|
|
47
47
|
_, err, st = @kubectl.run("config", "get-contexts", context, "-o", "name",
|
48
|
-
use_namespace: false, use_context: false, log_failure: false)
|
48
|
+
use_namespace: false, use_context: false, log_failure: false, attempts: 2)
|
49
49
|
|
50
50
|
unless st.success?
|
51
51
|
@errors << if err.match("error: context #{context} not found")
|
@@ -58,7 +58,7 @@ module Krane
|
|
58
58
|
|
59
59
|
def validate_context_reachable
|
60
60
|
_, err, st = @kubectl.run("get", "namespaces", "-o", "name",
|
61
|
-
use_namespace: false, log_failure: false)
|
61
|
+
use_namespace: false, log_failure: false, attempts: 2)
|
62
62
|
|
63
63
|
unless st.success?
|
64
64
|
@errors << "Something went wrong connecting to #{context}. #{err} "
|
@@ -71,7 +71,7 @@ module Krane
|
|
71
71
|
end
|
72
72
|
|
73
73
|
_, err, st = @kubectl.run("get", "namespace", "-o", "name", namespace,
|
74
|
-
use_namespace: false, log_failure: false)
|
74
|
+
use_namespace: false, log_failure: false, attempts: 3)
|
75
75
|
|
76
76
|
unless st.success?
|
77
77
|
@errors << if err.match("Error from server [(]NotFound[)]: namespace")
|
data/lib/krane/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: krane
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 2.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katrina Verey
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2020-
|
13
|
+
date: 2020-10-21 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -366,14 +366,14 @@ dependencies:
|
|
366
366
|
requirements:
|
367
367
|
- - "~>"
|
368
368
|
- !ruby/object:Gem::Version
|
369
|
-
version: 0.
|
369
|
+
version: 0.89.1
|
370
370
|
type: :development
|
371
371
|
prerelease: false
|
372
372
|
version_requirements: !ruby/object:Gem::Requirement
|
373
373
|
requirements:
|
374
374
|
- - "~>"
|
375
375
|
- !ruby/object:Gem::Version
|
376
|
-
version: 0.
|
376
|
+
version: 0.89.1
|
377
377
|
- !ruby/object:Gem::Dependency
|
378
378
|
name: codecov
|
379
379
|
requirement: !ruby/object:Gem::Requirement
|
@@ -423,6 +423,7 @@ files:
|
|
423
423
|
- exe/krane
|
424
424
|
- krane.gemspec
|
425
425
|
- lib/krane.rb
|
426
|
+
- lib/krane/annotation.rb
|
426
427
|
- lib/krane/bindings_parser.rb
|
427
428
|
- lib/krane/cli/deploy_command.rb
|
428
429
|
- lib/krane/cli/global_deploy_command.rb
|
@@ -436,6 +437,7 @@ files:
|
|
436
437
|
- lib/krane/concerns/template_reporting.rb
|
437
438
|
- lib/krane/concurrency.rb
|
438
439
|
- lib/krane/container_logs.rb
|
440
|
+
- lib/krane/container_overrides.rb
|
439
441
|
- lib/krane/deferred_summary_logging.rb
|
440
442
|
- lib/krane/delayed_exceptions.rb
|
441
443
|
- lib/krane/deploy_task.rb
|
@@ -475,6 +477,7 @@ files:
|
|
475
477
|
- lib/krane/label_selector.rb
|
476
478
|
- lib/krane/oj.rb
|
477
479
|
- lib/krane/options_helper.rb
|
480
|
+
- lib/krane/psych_k8s_compatibility.rb
|
478
481
|
- lib/krane/remote_logs.rb
|
479
482
|
- lib/krane/render_task.rb
|
480
483
|
- lib/krane/renderer.rb
|
@@ -498,7 +501,8 @@ files:
|
|
498
501
|
homepage: https://github.com/Shopify/krane
|
499
502
|
licenses:
|
500
503
|
- MIT
|
501
|
-
metadata:
|
504
|
+
metadata:
|
505
|
+
allowed_push_host: https://rubygems.org
|
502
506
|
post_install_message:
|
503
507
|
rdoc_options: []
|
504
508
|
require_paths:
|
@@ -507,7 +511,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
507
511
|
requirements:
|
508
512
|
- - ">="
|
509
513
|
- !ruby/object:Gem::Version
|
510
|
-
version: 2.
|
514
|
+
version: 2.5.0
|
511
515
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
512
516
|
requirements:
|
513
517
|
- - ">="
|