krane 3.9.1 → 3.9.2
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/.github/CODEOWNERS +1 -1
- data/CHANGELOG.md +5 -0
- data/krane.gemspec +0 -1
- data/lib/krane/bindings_parser.rb +2 -2
- data/lib/krane/cli/krane.rb +1 -1
- data/lib/krane/cluster_resource_discovery.rb +3 -3
- data/lib/krane/container_logs.rb +3 -8
- data/lib/krane/deploy_task.rb +2 -2
- data/lib/krane/ejson_secret_provisioner.rb +4 -4
- data/lib/krane/kubectl.rb +1 -1
- data/lib/krane/kubernetes_resource/daemon_set.rb +2 -2
- data/lib/krane/kubernetes_resource/deployment.rb +2 -2
- data/lib/krane/kubernetes_resource/pod.rb +4 -4
- data/lib/krane/kubernetes_resource/pod_set_base.rb +2 -2
- data/lib/krane/kubernetes_resource.rb +10 -1
- data/lib/krane/remote_logs.rb +2 -2
- data/lib/krane/resource_cache.rb +3 -1
- data/lib/krane/resource_deployer.rb +1 -1
- data/lib/krane/rollout_conditions.rb +2 -2
- data/lib/krane/task_config_validator.rb +14 -0
- data/lib/krane/version.rb +1 -1
- metadata +2 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d13a5afaa821cb112292626c292ce30ffe425d807415376ae41ae7d277093122
|
|
4
|
+
data.tar.gz: a1e741ce9287d33e6f50efeea076363906e3e07e0de037984d626b4a963c9f41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 48523d3a94ca3efb25b04033d187bebf4c911e14fbbe1f7608d340b6f4087d2bee36e51fdfb3d89ea7fff33d74583870cb64ac1ee96be6332212c5e3ca40a8b3
|
|
7
|
+
data.tar.gz: 663967d67f1d685c3b2f7b34e156c7b33a052b78620c8e6a2f568a1998186677dc5cf470cc3a1f4fce73856988a44b17e70315de10eaf52a53405759350e1d00
|
data/.github/CODEOWNERS
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
* @Shopify/
|
|
1
|
+
* @Shopify/application-infrastructure
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
## next
|
|
2
2
|
|
|
3
|
+
## 3.9.2
|
|
4
|
+
- Parse JSON with `JSON.parse` instead of `MultiJson.load`, and drop the `multi_json` dependency. On Ruby 3.0 and 3.1, `multi_json` 1.19.1 (the newest those versions can install) crashes with `json` 3.x
|
|
5
|
+
- Pod log retrieval now uses the task's `kubeconfig` instead of falling back to `ENV['KUBECONFIG']`
|
|
6
|
+
- Reject namespaces, contexts and kinds that `kubectl` would parse as flags rather than as positional arguments
|
|
7
|
+
|
|
3
8
|
## 3.9.1
|
|
4
9
|
- Test against k8s 1.34
|
|
5
10
|
|
data/krane.gemspec
CHANGED
|
@@ -32,7 +32,6 @@ Gem::Specification.new do |spec|
|
|
|
32
32
|
spec.add_dependency("ejson", "~> 1.0")
|
|
33
33
|
spec.add_dependency("colorize", "~> 0.8")
|
|
34
34
|
spec.add_dependency("statsd-instrument", ['>= 2.8', "< 3.9"])
|
|
35
|
-
spec.add_dependency("multi_json")
|
|
36
35
|
spec.add_dependency("concurrent-ruby", "~> 1.1")
|
|
37
36
|
spec.add_dependency("jsonpath", "~> 1.0")
|
|
38
37
|
spec.add_dependency("thor", ">= 1.0", "< 2.0")
|
|
@@ -54,14 +54,14 @@ module Krane
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def parse_json(string)
|
|
57
|
-
bindings =
|
|
57
|
+
bindings = JSON.parse(string)
|
|
58
58
|
|
|
59
59
|
unless bindings.is_a?(Hash)
|
|
60
60
|
raise ArgumentError, "Expected JSON data to be a hash."
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
bindings
|
|
64
|
-
rescue
|
|
64
|
+
rescue JSON::ParserError
|
|
65
65
|
nil
|
|
66
66
|
end
|
|
67
67
|
|
data/lib/krane/cli/krane.rb
CHANGED
|
@@ -54,7 +54,7 @@ module Krane
|
|
|
54
54
|
@api_path_cache["/"] ||= begin
|
|
55
55
|
raw_json, err, st = kubectl.run("get", "--raw", base_api_path, attempts: 5, use_namespace: false)
|
|
56
56
|
paths = if st.success?
|
|
57
|
-
|
|
57
|
+
JSON.parse(raw_json)["paths"]
|
|
58
58
|
else
|
|
59
59
|
raise FatalKubeAPIError, "Error retrieving raw path /: #{err}"
|
|
60
60
|
end
|
|
@@ -66,7 +66,7 @@ module Krane
|
|
|
66
66
|
@api_path_cache[path] ||= begin
|
|
67
67
|
raw_json, err, st = kubectl.run("get", "--raw", path, attempts: 2, use_namespace: false)
|
|
68
68
|
if st.success?
|
|
69
|
-
|
|
69
|
+
JSON.parse(raw_json)
|
|
70
70
|
else
|
|
71
71
|
logger.warn("Error retrieving api path: #{err}")
|
|
72
72
|
{}
|
|
@@ -92,7 +92,7 @@ module Krane
|
|
|
92
92
|
raw_json, err, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
|
|
93
93
|
use_namespace: false)
|
|
94
94
|
if st.success?
|
|
95
|
-
|
|
95
|
+
JSON.parse(raw_json)["items"]
|
|
96
96
|
else
|
|
97
97
|
raise FatalKubeAPIError, "Error retrieving CustomResourceDefinition: #{err}"
|
|
98
98
|
end
|
data/lib/krane/container_logs.rb
CHANGED
|
@@ -16,8 +16,8 @@ module Krane
|
|
|
16
16
|
@printed_latest = false
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def sync
|
|
20
|
-
new_logs = fetch_latest
|
|
19
|
+
def sync(kubectl)
|
|
20
|
+
new_logs = fetch_latest(kubectl)
|
|
21
21
|
return unless new_logs.present?
|
|
22
22
|
@lines += sort_and_deduplicate(new_logs)
|
|
23
23
|
end
|
|
@@ -47,7 +47,7 @@ module Krane
|
|
|
47
47
|
|
|
48
48
|
private
|
|
49
49
|
|
|
50
|
-
def fetch_latest
|
|
50
|
+
def fetch_latest(kubectl)
|
|
51
51
|
cmd = ["logs", @parent_id, "--container=#{container_name}", "--timestamps"]
|
|
52
52
|
cmd << if @last_timestamp.present?
|
|
53
53
|
"--since-time=#{rfc3339_timestamp(@last_timestamp)}"
|
|
@@ -58,11 +58,6 @@ module Krane
|
|
|
58
58
|
out.encode('UTF-8', invalid: :replace, replace: '').split("\n")
|
|
59
59
|
end
|
|
60
60
|
|
|
61
|
-
def kubectl
|
|
62
|
-
task_config = TaskConfig.new(@context, @namespace, @logger)
|
|
63
|
-
@kubectl ||= Kubectl.new(task_config: task_config, log_failure_by_default: false)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
61
|
def rfc3339_timestamp(time)
|
|
67
62
|
time.strftime("%FT%T.%N%:z")
|
|
68
63
|
end
|
data/lib/krane/deploy_task.rb
CHANGED
|
@@ -318,7 +318,7 @@ module Krane
|
|
|
318
318
|
@namespace_definition ||= begin
|
|
319
319
|
definition, _err, st = kubectl.run("get", "namespace", @namespace, use_namespace: false,
|
|
320
320
|
log_failure: true, raise_if_not_found: true, attempts: 3, output: 'json')
|
|
321
|
-
st.success? ?
|
|
321
|
+
st.success? ? JSON.parse(definition, symbolize_names: true) : nil
|
|
322
322
|
end
|
|
323
323
|
rescue Kubectl::ResourceNotFoundError
|
|
324
324
|
nil
|
|
@@ -352,7 +352,7 @@ module Krane
|
|
|
352
352
|
unless st.success?
|
|
353
353
|
raise EjsonSecretError, "Error retrieving Secret/#{EjsonSecretProvisioner::EJSON_KEYS_SECRET}: #{err}"
|
|
354
354
|
end
|
|
355
|
-
|
|
355
|
+
JSON.parse(out)
|
|
356
356
|
end
|
|
357
357
|
end
|
|
358
358
|
|
|
@@ -117,8 +117,8 @@ module Krane
|
|
|
117
117
|
|
|
118
118
|
def load_ejson_from_file
|
|
119
119
|
return {} unless File.exist?(@ejson_file)
|
|
120
|
-
|
|
121
|
-
rescue
|
|
120
|
+
JSON.parse(File.read(@ejson_file))
|
|
121
|
+
rescue JSON::ParserError => e
|
|
122
122
|
raise EjsonSecretError, "Failed to parse encrypted ejson:\n #{e}"
|
|
123
123
|
end
|
|
124
124
|
|
|
@@ -139,8 +139,8 @@ module Krane
|
|
|
139
139
|
msg = err.presence || out
|
|
140
140
|
raise EjsonSecretError, msg
|
|
141
141
|
end
|
|
142
|
-
|
|
143
|
-
rescue
|
|
142
|
+
JSON.parse(out)
|
|
143
|
+
rescue JSON::ParserError
|
|
144
144
|
raise EjsonSecretError, "Failed to parse decrypted ejson"
|
|
145
145
|
end
|
|
146
146
|
|
data/lib/krane/kubectl.rb
CHANGED
|
@@ -86,7 +86,7 @@ module Krane
|
|
|
86
86
|
response, _, status = run("version", output: "json", use_namespace: false, log_failure: true, attempts: 2)
|
|
87
87
|
raise KubectlError, "Could not retrieve kubectl version info" unless status.success?
|
|
88
88
|
|
|
89
|
-
version_data =
|
|
89
|
+
version_data = JSON.parse(response)
|
|
90
90
|
client_version = platform_agnostic_version(version_data.dig("clientVersion", "gitVersion").to_s)
|
|
91
91
|
server_version = platform_agnostic_version(version_data.dig("serverVersion", "gitVersion").to_s)
|
|
92
92
|
unless client_version && server_version
|
|
@@ -30,9 +30,9 @@ module Krane
|
|
|
30
30
|
observed_generation == current_generation
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def fetch_debug_logs
|
|
33
|
+
def fetch_debug_logs(kubectl)
|
|
34
34
|
most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
|
|
35
|
-
most_useful_pod.fetch_debug_logs
|
|
35
|
+
most_useful_pod.fetch_debug_logs(kubectl)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def print_debug_logs?
|
|
@@ -28,12 +28,12 @@ module Krane
|
|
|
28
28
|
logger: logger, statsd_tags: statsd_tags)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
-
def sync(
|
|
31
|
+
def sync(cache)
|
|
32
32
|
super
|
|
33
33
|
raise_predates_deploy_error if exists? && unmanaged? && !deploy_started?
|
|
34
34
|
|
|
35
35
|
if exists?
|
|
36
|
-
logs.sync if unmanaged?
|
|
36
|
+
logs.sync(cache.kubectl) if unmanaged?
|
|
37
37
|
update_container_statuses(@instance_data["status"])
|
|
38
38
|
else # reset
|
|
39
39
|
@containers.each(&:reset_status)
|
|
@@ -93,8 +93,8 @@ module Krane
|
|
|
93
93
|
"#{phase_failure_message} #{container_problems}".strip.presence
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
def fetch_debug_logs
|
|
97
|
-
logs.sync
|
|
96
|
+
def fetch_debug_logs(kubectl)
|
|
97
|
+
logs.sync(kubectl)
|
|
98
98
|
logs
|
|
99
99
|
end
|
|
100
100
|
|
|
@@ -18,7 +18,7 @@ module Krane
|
|
|
18
18
|
own_events.merge(most_useful_pod.fetch_events(kubectl))
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def fetch_debug_logs
|
|
21
|
+
def fetch_debug_logs(kubectl)
|
|
22
22
|
logs = Krane::RemoteLogs.new(
|
|
23
23
|
logger: @logger,
|
|
24
24
|
parent_id: id,
|
|
@@ -26,7 +26,7 @@ module Krane
|
|
|
26
26
|
namespace: @namespace,
|
|
27
27
|
context: @context
|
|
28
28
|
)
|
|
29
|
-
logs.sync
|
|
29
|
+
logs.sync(kubectl)
|
|
30
30
|
logs
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -35,6 +35,11 @@ module Krane
|
|
|
35
35
|
If you have reason to believe it will succeed, retry the deploy to continue to monitor the rollout.
|
|
36
36
|
MSG
|
|
37
37
|
|
|
38
|
+
# Kubernetes kinds begin with a letter, and may contain dashes after that (the API server
|
|
39
|
+
# lowercases the kind and validates it as a DNS-1035 label). Only the first character matters
|
|
40
|
+
# here: a leading dash would make kubectl parse the kind as a flag rather than a resource kind.
|
|
41
|
+
KIND_FORMAT = /\A[A-Za-z]/
|
|
42
|
+
|
|
38
43
|
ALLOWED_DEPLOY_METHOD_OVERRIDES = %w(create replace replace-force)
|
|
39
44
|
DEPLOY_METHOD_OVERRIDE_ANNOTATION = "deploy-method-override"
|
|
40
45
|
TIMEOUT_OVERRIDE_ANNOTATION = "timeout-override"
|
|
@@ -91,6 +96,10 @@ module Krane
|
|
|
91
96
|
raise InvalidTemplateError.new("Template is missing required field 'kind'", content: debug_content)
|
|
92
97
|
end
|
|
93
98
|
|
|
99
|
+
unless definition["kind"].match?(KIND_FORMAT)
|
|
100
|
+
raise InvalidTemplateError.new("Template's 'kind' must begin with a letter", content: debug_content)
|
|
101
|
+
end
|
|
102
|
+
|
|
94
103
|
if definition.dig('metadata', 'name').blank? && definition.dig('metadata', 'generateName').blank?
|
|
95
104
|
raise InvalidTemplateError.new("Template must specify one of 'metadata.name' or 'metadata.generateName'",
|
|
96
105
|
content: debug_content)
|
|
@@ -250,7 +259,7 @@ module Krane
|
|
|
250
259
|
|
|
251
260
|
def sync_debug_info(kubectl)
|
|
252
261
|
@debug_events = fetch_events(kubectl) unless ENV[DISABLE_FETCHING_EVENT_INFO]
|
|
253
|
-
@debug_logs = fetch_debug_logs if print_debug_logs? && !ENV[DISABLE_FETCHING_LOG_INFO]
|
|
262
|
+
@debug_logs = fetch_debug_logs(kubectl) if print_debug_logs? && !ENV[DISABLE_FETCHING_LOG_INFO]
|
|
254
263
|
end
|
|
255
264
|
|
|
256
265
|
def debug_message(cause = nil, info_hash = {})
|
data/lib/krane/remote_logs.rb
CHANGED
data/lib/krane/resource_cache.rb
CHANGED
|
@@ -4,6 +4,8 @@ require 'concurrent/hash'
|
|
|
4
4
|
|
|
5
5
|
module Krane
|
|
6
6
|
class ResourceCache
|
|
7
|
+
attr_reader :kubectl
|
|
8
|
+
|
|
7
9
|
delegate :namespace, :context, :logger, to: :@task_config
|
|
8
10
|
|
|
9
11
|
def initialize(task_config)
|
|
@@ -64,7 +66,7 @@ module Krane
|
|
|
64
66
|
raise KubectlError unless st.success?
|
|
65
67
|
|
|
66
68
|
instances = {}
|
|
67
|
-
|
|
69
|
+
JSON.parse(raw_json)["items"].each do |resource|
|
|
68
70
|
resource_name = resource.dig("metadata", "name")
|
|
69
71
|
instances[resource_name] = resource
|
|
70
72
|
end
|
|
@@ -251,7 +251,7 @@ module Krane
|
|
|
251
251
|
# For resources that rely on a generateName attribute, we get the `name` from the result of the call to `create`
|
|
252
252
|
# We must explicitly set this name value so that the `apply` step for pruning can run successfully
|
|
253
253
|
if status.success? && resource.uses_generate_name?
|
|
254
|
-
resource.use_generated_name(
|
|
254
|
+
resource.use_generated_name(JSON.parse(out))
|
|
255
255
|
end
|
|
256
256
|
|
|
257
257
|
[err, status]
|
|
@@ -11,7 +11,7 @@ module Krane
|
|
|
11
11
|
def from_annotation(conditions_string)
|
|
12
12
|
return new(default_conditions) if conditions_string.downcase.strip == "true"
|
|
13
13
|
|
|
14
|
-
conditions =
|
|
14
|
+
conditions = JSON.parse(conditions_string).slice('success_conditions', 'failure_conditions')
|
|
15
15
|
conditions.deep_symbolize_keys!
|
|
16
16
|
|
|
17
17
|
# Create JsonPath objects
|
|
@@ -26,7 +26,7 @@ module Krane
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
new(conditions)
|
|
29
|
-
rescue
|
|
29
|
+
rescue JSON::ParserError => e
|
|
30
30
|
raise RolloutConditionsError, "Rollout conditions are not valid JSON: #{e}"
|
|
31
31
|
rescue StandardError => e
|
|
32
32
|
raise RolloutConditionsError,
|
|
@@ -9,6 +9,10 @@ module Krane
|
|
|
9
9
|
validate_namespace_exists
|
|
10
10
|
).freeze
|
|
11
11
|
|
|
12
|
+
# Kubernetes namespaces are DNS-1123 labels. Anything else, in particular a value starting with
|
|
13
|
+
# a dash, would be parsed by kubectl as a flag rather than as a resource name.
|
|
14
|
+
NAMESPACE_FORMAT = /\A[a-z0-9]([-a-z0-9]{0,61}[a-z0-9])?\z/
|
|
15
|
+
|
|
12
16
|
delegate :context, :namespace, :logger, to: :@task_config
|
|
13
17
|
|
|
14
18
|
def initialize(task_config, kubectl, kubeclient_builder, only: nil)
|
|
@@ -44,6 +48,12 @@ module Krane
|
|
|
44
48
|
return @errors << "Context can not be blank"
|
|
45
49
|
end
|
|
46
50
|
|
|
51
|
+
# A context is passed to kubectl as a bare positional argument; a leading dash would make
|
|
52
|
+
# kubectl parse it as a flag. Context names are otherwise too varied to constrain further.
|
|
53
|
+
if context.start_with?("-")
|
|
54
|
+
return @errors << "Context can not start with a dash"
|
|
55
|
+
end
|
|
56
|
+
|
|
47
57
|
_, err, st = @kubectl.run("config", "get-contexts", context, "-o", "name",
|
|
48
58
|
use_namespace: false, use_context: false, log_failure: false, attempts: 2)
|
|
49
59
|
|
|
@@ -70,6 +80,10 @@ module Krane
|
|
|
70
80
|
return @errors << "Namespace can not be blank"
|
|
71
81
|
end
|
|
72
82
|
|
|
83
|
+
unless namespace.match?(NAMESPACE_FORMAT)
|
|
84
|
+
return @errors << "Namespace is not a valid Kubernetes namespace name"
|
|
85
|
+
end
|
|
86
|
+
|
|
73
87
|
_, err, st = @kubectl.run("get", "namespace", "-o", "name", namespace,
|
|
74
88
|
use_namespace: false, log_failure: false, attempts: 3)
|
|
75
89
|
|
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: 3.9.
|
|
4
|
+
version: 3.9.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Katrina Verey
|
|
@@ -101,20 +101,6 @@ dependencies:
|
|
|
101
101
|
- - "<"
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
103
|
version: '3.9'
|
|
104
|
-
- !ruby/object:Gem::Dependency
|
|
105
|
-
name: multi_json
|
|
106
|
-
requirement: !ruby/object:Gem::Requirement
|
|
107
|
-
requirements:
|
|
108
|
-
- - ">="
|
|
109
|
-
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0'
|
|
111
|
-
type: :runtime
|
|
112
|
-
prerelease: false
|
|
113
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
114
|
-
requirements:
|
|
115
|
-
- - ">="
|
|
116
|
-
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0'
|
|
118
104
|
- !ruby/object:Gem::Dependency
|
|
119
105
|
name: concurrent-ruby
|
|
120
106
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -525,7 +511,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
525
511
|
- !ruby/object:Gem::Version
|
|
526
512
|
version: '0'
|
|
527
513
|
requirements: []
|
|
528
|
-
rubygems_version:
|
|
514
|
+
rubygems_version: 4.0.21
|
|
529
515
|
specification_version: 4
|
|
530
516
|
summary: A command line tool that helps you ship changes to a Kubernetes namespace
|
|
531
517
|
and understand the result
|