krane 2.1.3 → 2.1.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a6ce0892fb00b50930df4316057572b08245e946d7eb9e60d938c76e4ba826e
4
- data.tar.gz: 903999c3121acbd58162ebfffedb10fbfa22a347262c7108bc3c499527654a22
3
+ metadata.gz: 7ae8bb4b0c7f920136990847601e19c7767075a89ead99781213feaf309d623d
4
+ data.tar.gz: 90ad58122ca788ebd4010e62e624c0d73a0c153e48c942673e244e7fbb4ccb32
5
5
  SHA512:
6
- metadata.gz: ca770038ea36996205013d9baae03f4acb6da0b37e57aa7f261d1c95a5846999135bb147392c1016a9c0c04377258e07e155f5f3a994e4a3e1fd8bffb69f296d
7
- data.tar.gz: b1c3fc517252cd4be20cf11a7dbf2381591890aeb6074859a6058f2dfd0becd060039800704106451dff116494a4725af93c2d0175550437df1a9c94428453b1
6
+ metadata.gz: 7e09bdb58b4bca4380b77bb10589a2e5b57aa31e9d8838c4b6efc68ccbf58b5af84c360a1a2710564164e4d6f19419cd38d7b3952559c56d5f7dc026d432a194
7
+ data.tar.gz: 659649bf0d0cfbe04d3fad0f32f3a05cc6ed9e117ca0755bf808d898b60dbf0d5be88cad609de1589cd93eb2b808e6cc9994a73f7b21267920b679b71afd138e
@@ -195,6 +195,7 @@ Style/FrozenStringLiteralComment:
195
195
  SupportedStyles:
196
196
  - always
197
197
  - never
198
+ SafeAutoCorrect: true
198
199
 
199
200
  Style/GlobalVars:
200
201
  AllowedVariables: []
@@ -1,6 +1,6 @@
1
1
  containers:
2
2
  default:
3
- docker: circleci/ruby:2.5.7
3
+ docker: circleci/ruby:2.6.6
4
4
 
5
5
  steps:
6
6
  - label: Lint
@@ -8,14 +8,29 @@ steps:
8
8
  run:
9
9
  - bundle: ~
10
10
  - bundle exec rubocop
11
- - label: 'Run Test Suite (:kubernetes: 1.18-latest :ruby: 2.7)'
11
+ - label: 'Run Test Suite (:kubernetes: 1.20-latest :ruby: 3.0)'
12
12
  command: bin/ci
13
13
  agents:
14
14
  queue: k8s-ci
15
15
  env:
16
16
  LOGGING_LEVEL: "4"
17
- KUBERNETES_VERSION: v1.18-latest
17
+ KUBERNETES_VERSION: v1.20-latest
18
+ RUBY_VERSION: "3.0"
19
+ - label: 'Run Test Suite (:kubernetes: 1.19-latest :ruby: 2.7)'
20
+ command: bin/ci
21
+ agents:
22
+ queue: k8s-ci
23
+ env:
24
+ LOGGING_LEVEL: "4"
25
+ KUBERNETES_VERSION: v1.19-latest
18
26
  RUBY_VERSION: "2.7"
27
+ - label: 'Run Test Suite (:kubernetes: 1.18-latest)'
28
+ command: bin/ci
29
+ agents:
30
+ queue: k8s-ci
31
+ env:
32
+ LOGGING_LEVEL: "4"
33
+ KUBERNETES_VERSION: v1.18-latest
19
34
  - label: 'Run Test Suite (:kubernetes: 1.17-latest)'
20
35
  command: bin/ci
21
36
  agents:
@@ -23,14 +38,13 @@ steps:
23
38
  env:
24
39
  LOGGING_LEVEL: "4"
25
40
  KUBERNETES_VERSION: v1.17-latest
26
- - label: 'Run Test Suite (:kubernetes: 1.16.12)'
41
+ - label: 'Run Test Suite (:kubernetes: 1.16-latest)'
27
42
  command: bin/ci
28
43
  agents:
29
44
  queue: k8s-ci
30
45
  env:
31
46
  LOGGING_LEVEL: "4"
32
- # Flip this back to v1.16-latest when 1.16.14 comes out (see #733)
33
- KUBERNETES_VERSION: v1.16.12
47
+ KUBERNETES_VERSION: v1.16-latest
34
48
  - label: 'Run Test Suite (:kubernetes: 1.15-latest)'
35
49
  command: bin/ci
36
50
  agents:
@@ -1,5 +1,15 @@
1
1
  ## next
2
2
 
3
+ ## 2.1.4
4
+
5
+ *Enhancements*
6
+ - Attempt to batch run server-side apply in validation phase instead of dry-running each resource individually [#781](https://github.com/Shopify/krane/pull/781).
7
+ - Evaluate progress condition only after progress deadline seconds have passed since deploy invocation [#765](https://github.com/Shopify/krane/pull/765).
8
+
9
+ *Other*
10
+ - Dropped support for Ruby 2.5 due to EoL. [#782](https://github.com/Shopify/krane/pull/782).
11
+ - Only patch JSON when run as CLI, not as library [#779](https://github.com/Shopify/krane/pull/779).
12
+
3
13
  ## 2.1.3
4
14
 
5
15
  - Add version exception for ServiceNetworkEndpointGroup (GKE resource) [#768](https://github.com/Shopify/krane/pull/768)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # krane [![Build status](https://badge.buildkite.com/35c56e797c3bbd6ba50053aefdded0715898cd8e8c86f7e462.svg?branch=master)](https://buildkite.com/shopify/krane) [![codecov](https://codecov.io/gh/Shopify/kubernetes-deploy/branch/master/graph/badge.svg)](https://codecov.io/gh/Shopify/kubernetes-deploy)
1
+ # krane [![Build status](https://badge.buildkite.com/35c56e797c3bbd6ba50053aefdded0715898cd8e8c86f7e462.svg?branch=master)](https://buildkite.com/shopify/krane)
2
2
 
3
3
  > This project used to be called `kubernetes-deploy`. Check out our [migration guide](https://github.com/Shopify/krane/blob/master/1.0-Upgrade.md) for more information including details about breaking changes.
4
4
 
@@ -73,7 +73,7 @@ If you need the ability to render dynamic values in templates before deploying,
73
73
 
74
74
  ## Prerequisites
75
75
 
76
- * Ruby 2.5+
76
+ * Ruby 2.6+
77
77
  * Your cluster must be running Kubernetes v1.15.0 or higher<sup>1</sup>
78
78
 
79
79
  <sup>1</sup> We run integration tests against these Kubernetes versions. You can find our
data/bin/ci CHANGED
@@ -12,10 +12,9 @@ docker run --rm \
12
12
  -v "$PWD":/usr/src/app \
13
13
  -v "/usr/bin/kubectl":"/usr/bin/kubectl" \
14
14
  -e CI=1 \
15
- -e CODECOV_TOKEN=$CODECOV_TOKEN \
16
15
  -e COVERAGE=1 \
17
16
  -e VERBOSE=1 \
18
17
  -e PARALLELISM=$PARALLELISM \
19
18
  -w /usr/src/app \
20
- ruby:"${RUBY_VERSION:-2.5}" \
19
+ ruby:"${RUBY_VERSION:-2.6.6}" \
21
20
  bin/test
data/dev.yml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: krane
3
3
  up:
4
- - ruby: 2.5.7 # Matches gemspec
4
+ - ruby: 2.6.6 # Matches gemspec
5
5
  - bundler
6
6
  - homebrew:
7
7
  - homebrew/cask/minikube
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
 
26
26
  spec.metadata['allowed_push_host'] = "https://rubygems.org"
27
27
 
28
- spec.required_ruby_version = '>= 2.5.0'
28
+ spec.required_ruby_version = '>= 2.6.0'
29
29
  spec.add_dependency("activesupport", ">= 5.0")
30
30
  spec.add_dependency("kubeclient", "~> 4.3")
31
31
  spec.add_dependency("googleauth", "~> 0.8")
@@ -57,5 +57,5 @@ Gem::Specification.new do |spec|
57
57
  spec.add_development_dependency("ruby-prof")
58
58
  spec.add_development_dependency("ruby-prof-flamegraph")
59
59
  spec.add_development_dependency("rubocop", "~> 0.89.1")
60
- spec.add_development_dependency("codecov")
60
+ spec.add_development_dependency("simplecov")
61
61
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
-
3
2
  require 'krane'
3
+ require 'krane/oj'
4
4
  require 'thor'
5
5
  require 'krane/cli/version_command'
6
6
  require 'krane/cli/restart_command'
@@ -23,9 +23,7 @@ module Krane
23
23
  fetch_resources(namespaced: namespaced).uniq { |r| r['kind'] }.map do |resource|
24
24
  next unless resource['verbs'].one? { |v| v == "delete" }
25
25
  next if black_list.include?(resource['kind'])
26
- group_versions = api_versions[resource['apigroup'].to_s]
27
- version = version_for_kind(group_versions, resource['kind'])
28
- [resource['apigroup'], version, resource['kind']].compact.join("/")
26
+ gvk_string(api_versions, resource)
29
27
  end.compact
30
28
  end
31
29
 
@@ -90,7 +88,9 @@ module Krane
90
88
  "CSIDriver" => "v1beta1", "Ingress" => "v1beta1",
91
89
  "CSINode" => "v1beta1", "Job" => "v1",
92
90
  "IngressClass" => "v1beta1", "FrontendConfig" => "v1beta1",
93
- "ServiceNetworkEndpointGroup" => "v1beta1" }
91
+ "ServiceNetworkEndpointGroup" => "v1beta1",
92
+ "EnvoyFilter" => "v1alpha3",
93
+ "TCPIngress" => "v1beta1" }
94
94
 
95
95
  pattern = /v(?<major>\d+)(?<pre>alpha|beta)?(?<minor>\d+)?/
96
96
  latest = versions.sort_by do |version|
@@ -101,6 +101,23 @@ module Krane
101
101
  version_override.fetch(kind, latest)
102
102
  end
103
103
 
104
+ def gvk_string(api_versions, resource)
105
+ apiversion = resource['apiversion'].to_s
106
+
107
+ ## In kubectl 1.20 APIGroups was replaced by APIVersions
108
+ if apiversion.empty?
109
+ apigroup = resource['apigroup'].to_s
110
+ group_versions = api_versions[apigroup]
111
+
112
+ version = version_for_kind(group_versions, resource['kind'])
113
+ apigroup = 'core' if apigroup.empty?
114
+ apiversion = "#{apigroup}/#{version}"
115
+ end
116
+
117
+ apiversion = "core/#{apiversion}" unless apiversion.include?("/")
118
+ [apiversion, resource['kind']].compact.join("/")
119
+ end
120
+
104
121
  def fetch_crds
105
122
  raw_json, err, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
106
123
  use_namespace: false)
@@ -11,7 +11,6 @@ require 'active_support/core_ext/array/conversions'
11
11
  require 'colorized_string'
12
12
 
13
13
  require 'krane/version'
14
- require 'krane/oj'
15
14
  require 'krane/errors'
16
15
  require 'krane/formatted_logger'
17
16
  require 'krane/statsd'
@@ -279,13 +279,17 @@ module Krane
279
279
 
280
280
  def validate_resources(resources)
281
281
  validate_globals(resources)
282
+ batch_dry_run_success = validate_dry_run(resources)
282
283
  Krane::Concurrency.split_across_threads(resources) do |r|
283
- r.validate_definition(kubectl, selector: @selector)
284
+ # No need to pass in kubectl (and do per-resource dry run apply) if batch dry run succeeded
285
+ if batch_dry_run_success
286
+ r.validate_definition(kubectl: nil, selector: @selector, dry_run: false)
287
+ else
288
+ r.validate_definition(kubectl: kubectl, selector: @selector, dry_run: true)
289
+ end
284
290
  end
285
-
286
291
  failed_resources = resources.select(&:validation_failed?)
287
292
  if failed_resources.present?
288
-
289
293
  failed_resources.each do |r|
290
294
  content = File.read(r.file_path) if File.file?(r.file_path) && !r.sensitive_template_content?
291
295
  record_invalid_template(logger: @logger, err: r.validation_error_msg,
@@ -308,6 +312,10 @@ module Krane
308
312
  "Use GlobalDeployTask instead."
309
313
  end
310
314
 
315
+ def validate_dry_run(resources)
316
+ resource_deployer.dry_run(resources)
317
+ end
318
+
311
319
  def namespace_definition
312
320
  @namespace_definition ||= begin
313
321
  definition, _err, st = kubectl.run("get", "namespace", @namespace, use_namespace: false,
@@ -53,7 +53,7 @@ module Krane
53
53
  secrets.map do |secret_name, secret_spec|
54
54
  validate_secret_spec(secret_name, secret_spec)
55
55
  resource = generate_secret_resource(secret_name, secret_spec["_type"], secret_spec["data"])
56
- resource.validate_definition(@kubectl)
56
+ resource.validate_definition(kubectl: @kubectl)
57
57
  if resource.validation_failed?
58
58
  raise EjsonSecretError, "Resulting resource Secret/#{secret_name} failed validation"
59
59
  end
@@ -131,7 +131,7 @@ module Krane
131
131
  validate_globals(resources)
132
132
 
133
133
  Concurrency.split_across_threads(resources) do |r|
134
- r.validate_definition(@kubectl, selector: @selector)
134
+ r.validate_definition(kubectl: @kubectl, selector: @selector)
135
135
  end
136
136
 
137
137
  failed_resources = resources.select(&:validation_failed?)
@@ -100,6 +100,14 @@ module Krane
100
100
  server_version >= Gem::Version.new(SERVER_DRY_RUN_MIN_VERSION)
101
101
  end
102
102
 
103
+ def dry_run_flag
104
+ if server_version >= Gem::Version.new("1.18")
105
+ "--dry-run=server"
106
+ else
107
+ "--server-dry-run"
108
+ end
109
+ end
110
+
103
111
  private
104
112
 
105
113
  def build_command_from_options(args, use_namespace, use_context, output)
@@ -134,12 +134,12 @@ module Krane
134
134
  Kubeclient::Resource.new(@definition)
135
135
  end
136
136
 
137
- def validate_definition(kubectl, selector: nil)
137
+ def validate_definition(kubectl:, selector: nil, dry_run: true)
138
138
  @validation_errors = []
139
139
  validate_selector(selector) if selector
140
140
  validate_timeout_annotation
141
141
  validate_deploy_method_override_annotation
142
- validate_spec_with_kubectl(kubectl)
142
+ validate_spec_with_kubectl(kubectl) if dry_run
143
143
  @validation_errors.present?
144
144
  end
145
145
 
@@ -560,7 +560,12 @@ module Krane
560
560
 
561
561
  # Server side dry run is only supported on apply
562
562
  def validate_with_server_side_dry_run(kubectl)
563
- command = ["apply", "-f", file_path, "--server-dry-run", "--output=name"]
563
+ command = if kubectl.server_version >= Gem::Version.new('1.18')
564
+ ["apply", "-f", file_path, "--dry-run=server", "--output=name"]
565
+ else
566
+ ["apply", "-f", file_path, "--server-dry-run", "--output=name"]
567
+ end
568
+
564
569
  kubectl.run(*command, log_failure: false, output_is_sensitive: sensitive_template_content?,
565
570
  retry_whitelist: [:client_timeout, :empty, :context_deadline], attempts: 3)
566
571
  end
@@ -155,6 +155,8 @@ module Krane
155
155
 
156
156
  def deploy_failing_to_progress?
157
157
  return false unless progress_condition.present?
158
+ # Ensure at least progress_deadline wall clock time has passed before before examining progress_condition
159
+ return false if deploy_started? && Time.now.utc - @deploy_started_at < progress_deadline
158
160
 
159
161
  # This assumes that when the controller bumps the observed generation, it also updates/clears all the status
160
162
  # conditions. Specifically, it assumes the progress condition is immediately set to True if a rollout is starting.
@@ -20,6 +20,13 @@ module Krane
20
20
  @statsd_tags = statsd_tags
21
21
  end
22
22
 
23
+ def dry_run(resources)
24
+ apply_all(resources, true, dry_run: true)
25
+ true
26
+ rescue FatalDeploymentError
27
+ false
28
+ end
29
+
23
30
  def deploy!(resources, verify_result, prune)
24
31
  if verify_result
25
32
  deploy_all_resources(resources, prune: prune, verify: true)
@@ -128,17 +135,17 @@ module Krane
128
135
  end
129
136
  end
130
137
 
131
- def apply_all(resources, prune)
138
+ def apply_all(resources, prune, dry_run: false)
132
139
  return unless resources.present?
133
- command = %w(apply)
140
+ start = Time.now.utc
134
141
 
142
+ command = %w(apply)
135
143
  Dir.mktmpdir do |tmp_dir|
136
144
  resources.each do |r|
137
145
  FileUtils.symlink(r.file_path, tmp_dir)
138
- r.deploy_started_at = Time.now.utc
146
+ r.deploy_started_at = Time.now.utc unless dry_run
139
147
  end
140
148
  command.push("-f", tmp_dir)
141
-
142
149
  if prune && @prune_whitelist.present?
143
150
  command.push("--prune")
144
151
  if @selector
@@ -149,20 +156,22 @@ module Krane
149
156
  @prune_whitelist.each { |type| command.push("--prune-whitelist=#{type}") }
150
157
  end
151
158
 
159
+ command.push(kubectl.dry_run_flag) if dry_run
152
160
  output_is_sensitive = resources.any?(&:sensitive_template_content?)
153
161
  global_mode = resources.all?(&:global?)
154
162
  out, err, st = kubectl.run(*command, log_failure: false, output_is_sensitive: output_is_sensitive,
155
163
  attempts: 2, use_namespace: !global_mode)
156
164
 
165
+ tags = statsd_tags + (dry_run ? ['dry_run:true'] : ['dry_run:false'])
166
+ Krane::StatsD.client.distribution('apply_all.duration', Krane::StatsD.duration(start), tags: tags)
157
167
  if st.success?
158
168
  log_pruning(out) if prune
159
169
  else
160
- record_apply_failure(err, resources: resources)
170
+ record_apply_failure(err, resources: resources) unless dry_run
161
171
  raise FatalDeploymentError, "Command failed: #{Shellwords.join(command)}"
162
172
  end
163
173
  end
164
174
  end
165
- measure_method(:apply_all)
166
175
 
167
176
  def log_pruning(kubectl_output)
168
177
  pruned = kubectl_output.scan(/^(.*) pruned$/)
@@ -118,7 +118,7 @@ module Krane
118
118
  end
119
119
 
120
120
  def validate_pod(pod)
121
- pod.validate_definition(kubectl)
121
+ pod.validate_definition(kubectl: kubectl)
122
122
  end
123
123
 
124
124
  def watch_pod(pod)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Krane
3
- VERSION = "2.1.3"
3
+ VERSION = "2.1.4"
4
4
  end
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: 2.1.3
4
+ version: 2.1.4
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-12-14 00:00:00.000000000 Z
13
+ date: 2021-01-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -375,7 +375,7 @@ dependencies:
375
375
  - !ruby/object:Gem::Version
376
376
  version: 0.89.1
377
377
  - !ruby/object:Gem::Dependency
378
- name: codecov
378
+ name: simplecov
379
379
  requirement: !ruby/object:Gem::Requirement
380
380
  requirements:
381
381
  - - ">="
@@ -511,7 +511,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
511
511
  requirements:
512
512
  - - ">="
513
513
  - !ruby/object:Gem::Version
514
- version: 2.5.0
514
+ version: 2.6.0
515
515
  required_rubygems_version: !ruby/object:Gem::Requirement
516
516
  requirements:
517
517
  - - ">="