krane 1.1.3 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d24b8a158239b66ab2f5ee126937935c9942dfea2336fe213410326e446c30dc
4
- data.tar.gz: 44ee52b5123fd507c62f266b371f14edc606f2738550e47c39b5e2995b907b0b
3
+ metadata.gz: 5ad41e92d16ba261b260e69cef284e77c6a0849f3470f56de06b16cafc802d8b
4
+ data.tar.gz: 006ad4dc7823bff047095de7442c3aabf6cd8660f45f745f0d75d23e342fc483
5
5
  SHA512:
6
- metadata.gz: 37c9f7d48d131277dac55b357ef895b63e5822ab7428954a1d7b871ce50594908df47bfc82320dd4f01b4e6d790b3383e9eb214ba42f98f03afd0c5332ebcb4c
7
- data.tar.gz: e3f98ac0a1c55fdb63f88977cc9fad4d72c3ec437f7795ceaffbcc2664387b60d7f4ea6e75e3cc596f73b823371063d7dcc5d80ba63efe44b000dd9b02498551
6
+ metadata.gz: cdad877f2a08f48fd9f483795c4b8b90ec8b60661da4a52dfdad3f52d2f1faf913946a0fc714537f374efac635f0b541e2b79fe16f2bc66daee4e23871502e2f
7
+ data.tar.gz: 2024fc45779c55e0209de8d543379be64f7f42c94c58b084ce22021806f4680c142fb7d36b60c506d1cf53a63e1c0a8f3511f8674b1ab6d11fa05542d00a6f2e
@@ -264,7 +264,7 @@ Style/MethodCallWithArgsParentheses:
264
264
  - raise
265
265
  - puts
266
266
  Exclude:
267
- - Gemfile
267
+ - '**/Gemfile'
268
268
 
269
269
  Style/MethodDefParentheses:
270
270
  EnforcedStyle: require_parentheses
@@ -577,6 +577,7 @@ Layout/BlockEndNewline:
577
577
 
578
578
  Style/CaseEquality:
579
579
  Enabled: true
580
+ AllowOnConstant: true
580
581
 
581
582
  Style/CharacterLiteral:
582
583
  Enabled: true
@@ -659,6 +660,9 @@ Style/IfWithSemicolon:
659
660
  Style/IdenticalConditionalBranches:
660
661
  Enabled: true
661
662
 
663
+ Layout/IndentationStyle:
664
+ Enabled: true
665
+
662
666
  Style/InfiniteLoop:
663
667
  Enabled: true
664
668
 
@@ -671,7 +675,7 @@ Style/LineEndConcatenation:
671
675
  Style/MethodCallWithoutArgsParentheses:
672
676
  Enabled: true
673
677
 
674
- Style/MethodMissingSuper:
678
+ Lint/MissingSuper:
675
679
  Enabled: true
676
680
 
677
681
  Style/MissingRespondToMissing:
@@ -803,9 +807,6 @@ Layout/SpaceInsideRangeLiteral:
803
807
  Style/SymbolLiteral:
804
808
  Enabled: true
805
809
 
806
- Layout/Tab:
807
- Enabled: true
808
-
809
810
  Layout/TrailingWhitespace:
810
811
  Enabled: true
811
812
 
@@ -834,7 +835,7 @@ Style/ZeroLengthPredicate:
834
835
  Enabled: true
835
836
 
836
837
  Layout/HeredocIndentation:
837
- EnforcedStyle: squiggly
838
+ Enabled: true
838
839
 
839
840
  Lint/AmbiguousOperator:
840
841
  Enabled: true
@@ -872,9 +873,6 @@ Lint/EmptyEnsure:
872
873
  Lint/EmptyInterpolation:
873
874
  Enabled: true
874
875
 
875
- Lint/EndInMethod:
876
- Enabled: true
877
-
878
876
  Lint/EnsureReturn:
879
877
  Enabled: true
880
878
 
@@ -967,7 +965,7 @@ Lint/UselessAccessModifier:
967
965
  Lint/UselessAssignment:
968
966
  Enabled: true
969
967
 
970
- Lint/UselessComparison:
968
+ Lint/BinaryOperatorWithIdenticalOperands:
971
969
  Enabled: true
972
970
 
973
971
  Lint/UselessElseWithoutRescue:
@@ -1018,3 +1016,6 @@ Style/ModuleFunction:
1018
1016
 
1019
1017
  Lint/OrderedMagicComments:
1020
1018
  Enabled: true
1019
+
1020
+ Lint/DeprecatedOpenSSLConstant:
1021
+ Enabled: true
@@ -6,16 +6,15 @@ steps:
6
6
  - label: Lint
7
7
  timeout: 5m
8
8
  run:
9
+ - bundle: ~
9
10
  - bundle exec rubocop
10
- dependencies:
11
- - bundler
12
- - label: 'Run Test Suite (:kubernetes: 1.17-latest :ruby: 2.7)'
11
+ - label: 'Run Test Suite (:kubernetes: 1.18-latest :ruby: 2.7)'
13
12
  command: bin/ci
14
13
  agents:
15
14
  queue: k8s-ci
16
15
  env:
17
16
  LOGGING_LEVEL: "4"
18
- KUBERNETES_VERSION: v1.17-latest
17
+ KUBERNETES_VERSION: v1.18-latest
19
18
  RUBY_VERSION: "2.7"
20
19
  - label: 'Run Test Suite (:kubernetes: 1.17-latest)'
21
20
  command: bin/ci
@@ -24,13 +23,14 @@ steps:
24
23
  env:
25
24
  LOGGING_LEVEL: "4"
26
25
  KUBERNETES_VERSION: v1.17-latest
27
- - label: 'Run Test Suite (:kubernetes: 1.16-latest)'
26
+ - label: 'Run Test Suite (:kubernetes: 1.16.12)'
28
27
  command: bin/ci
29
28
  agents:
30
29
  queue: k8s-ci
31
30
  env:
32
31
  LOGGING_LEVEL: "4"
33
- KUBERNETES_VERSION: v1.16-latest
32
+ # Flip this back to v1.16-latest when 1.16.14 comes out (see #733)
33
+ KUBERNETES_VERSION: v1.16.12
34
34
  - label: 'Run Test Suite (:kubernetes: 1.15-latest)'
35
35
  command: bin/ci
36
36
  agents:
@@ -1,5 +1,42 @@
1
1
  ## next
2
2
 
3
+ ## 2.1.2
4
+
5
+ - Add version exception for FrontendConfig (GKE resource) [#761](https://github.com/Shopify/krane/pull/761)
6
+
7
+ ## 2.1.1
8
+
9
+ *Bug Fixes*
10
+ - Fix the way environment variables are passed into the EJSON decryption invocation [#759](https://github.com/Shopify/krane/pull/759)
11
+
12
+ ## 2.1.0
13
+
14
+ *Features*
15
+ - _(experimental)_ Override deploy method via annotation. This feature is considered alpha and should not be considered stable [#753](https://github.com/Shopify/krane/pull/753)
16
+
17
+ *Enhancements*
18
+ - Increased the number of attempts on kubectl commands during Initializing deploy phase [#749](https://github.com/Shopify/krane/pull/749)
19
+ - Increased attempts on kubectl apply command during deploy [#751](https://github.com/Shopify/krane/pull/751)
20
+ - Whitelist context deadline error during kubctl dry run [#754](https://github.com/Shopify/krane/pull/754)
21
+ - Allow specifying a kubeconfig per task in the internal API [#746](https://github.com/Shopify/krane/pull/746)
22
+
23
+ ## 2.0.0
24
+
25
+ *Breaking Changes*
26
+ - Remove kubernetes deploy annotation prefix [#738](https://github.com/Shopify/krane/pull/738)
27
+
28
+ *Bug Fixes*
29
+ - Always set a deployment_id, even if the current_sha isn't set [#730](https://github.com/Shopify/krane/pull/730)
30
+ - YAML string scalars with scientific/e-notation numeric format not properly quoted. [#740](https://github.com/Shopify/krane/pull/740)
31
+
32
+ ## 1.1.4
33
+
34
+ *Bug Fixes*
35
+ - Properly look up constant on Krane namespace. [#720](https://github.com/Shopify/krane/pull/720)
36
+
37
+ *Enhancements*
38
+ - Allow to configure `image_tag` when using task runner. [#719](https://github.com/Shopify/krane/pull/719)
39
+
3
40
  ## 1.1.3
4
41
 
5
42
  *Bug Fixes*
@@ -51,6 +88,12 @@
51
88
  We've renamed the gem and cli to Krane.
52
89
  See our [migration guide](https://github.com/Shopify/krane/blob/master/1.0-Upgrade.md) to help navigate the breaking changes.
53
90
 
91
+ ## 1.0.0.pre.2
92
+
93
+ *Enhancements*
94
+ - Relax thor version requirement. ([#731](https://github.com/Shopify/krane/pull/731))
95
+ - Relax googleauth restriction. ([#731](https://github.com/Shopify/krane/pull/731))
96
+
54
97
  ## 1.0.0.pre.1
55
98
 
56
99
  *Important!*
@@ -151,7 +151,7 @@ To see the full-color output of a specific integration test, you can use `PRINT_
151
151
  1. Make sure CHANGELOG.md includes all user-facing changes since the last release. Things like test changes or refactors do not need to be included.
152
152
  1. Update the version number in `version.rb`.
153
153
  1. Commit your changes with message "Version x.y.z" and open a PR.
154
- 1. After merging your PR, deploy via [Shipit](https://shipit.shopify.io/shopify/kubernetes-deploy/rubygems). Shipit will automatically tag the release and upload the gem to [rubygems.org](https://rubygems.org/gems/krane).
154
+ 1. After merging your PR, deploy via [Shipit](https://shipit.shopify.io/shopify/krane/rubygems). Shipit will automatically tag the release and upload the gem to [rubygems.org](https://rubygems.org/gems/krane).
155
155
 
156
156
  ## CI (External contributors)
157
157
 
data/README.md CHANGED
@@ -159,6 +159,11 @@ before the deployment is considered successful.
159
159
  - _Default_: `true`
160
160
  - `true`: The custom resource will be deployed in the pre-deploy phase.
161
161
  - All other values: The custom resource will be deployed in the main deployment phase.
162
+ - `krane.shopify.io/deploy-method-override`: Cause a resource to be deployed by the specified `kubectl` command, instead of the default `apply`.
163
+ - _Compatibility_: Cannot be used for `PodDisruptionBudget`, since it always uses `create/replace-force`
164
+ - _Accepted values_: `create`, `replace`, and `replace-force`
165
+ - _Warning_: Resources whose deploy method is overridden are no longer subject to pruning on deploy.
166
+ - This feature is _experimental_ and may be removed at any time.
162
167
 
163
168
 
164
169
  ### Running tasks at the beginning of a deploy
data/dev.yml CHANGED
@@ -4,7 +4,8 @@ up:
4
4
  - ruby: 2.5.7 # Matches gemspec
5
5
  - bundler
6
6
  - homebrew:
7
- - Caskroom/cask/minikube
7
+ - homebrew/cask/minikube
8
+ - hyperkit
8
9
  - custom:
9
10
  name: Install the minikube fork of driver-hyperkit
10
11
  met?: command -v docker-machine-driver-hyperkit
@@ -56,6 +56,6 @@ Gem::Specification.new do |spec|
56
56
  spec.add_development_dependency("byebug")
57
57
  spec.add_development_dependency("ruby-prof")
58
58
  spec.add_development_dependency("ruby-prof-flamegraph")
59
- spec.add_development_dependency("rubocop", "~> 0.78.0")
59
+ spec.add_development_dependency("rubocop", "~> 0.89.1")
60
60
  spec.add_development_dependency("codecov")
61
61
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Krane
4
+ module Annotation
5
+ class << self
6
+ def for(suffix)
7
+ "krane.shopify.io/#{suffix}"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -88,7 +88,8 @@ module Krane
88
88
  # Override list for kinds that don't appear in the lastest version of a group
89
89
  version_override = { "CronJob" => "v1beta1", "VolumeAttachment" => "v1beta1",
90
90
  "CSIDriver" => "v1beta1", "Ingress" => "v1beta1",
91
- "CSINode" => "v1beta1", "Job" => "v1" }
91
+ "CSINode" => "v1beta1", "Job" => "v1",
92
+ "IngressClass" => "v1beta1", "FrontendConfig" => "v1beta1" }
92
93
 
93
94
  pattern = /v(?<major>\d+)(?<pre>alpha|beta)?(?<minor>\d+)?/
94
95
  latest = versions.sort_by do |version|
@@ -15,12 +15,6 @@ module Krane
15
15
  logger.summary.add_paragraph(debug_msg)
16
16
  end
17
17
 
18
- def record_warnings(logger:, warning:, filename:)
19
- warn_msg = "Template warning: #{filename}\n"
20
- warn_msg += "> Warning message:\n#{Krane::FormattedLogger.indent_four(warning)}"
21
- logger.summary.add_paragraph(ColorizedString.new(warn_msg).yellow)
22
- end
23
-
24
18
  def add_para_from_list(logger:, action:, enum:)
25
19
  logger.summary.add_action(action)
26
20
  logger.summary.add_paragraph(enum.map { |e| "- #{e}" }.join("\n"))
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ module Krane
3
+ class ContainerOverrides
4
+ attr_reader :command, :arguments, :env_vars, :image_tag
5
+
6
+ def initialize(command: nil, arguments: nil, env_vars: [], image_tag: nil)
7
+ @command = command
8
+ @arguments = arguments
9
+ @env_vars = env_vars
10
+ @image_tag = image_tag
11
+ end
12
+
13
+ def apply!(container)
14
+ container.command = command if command
15
+ container.args = arguments if arguments
16
+
17
+ if image_tag
18
+ image = container.image
19
+ base_image, _old_tag = image.split(':')
20
+ new_image = "#{base_image}:#{image_tag}"
21
+
22
+ container.image = new_image
23
+ end
24
+
25
+ env_args = env_vars.map do |env|
26
+ key, value = env.split('=', 2)
27
+ { name: key, value: value }
28
+ end
29
+ container.env ||= []
30
+ container.env = container.env.map(&:to_h) + env_args
31
+ end
32
+ end
33
+ end
@@ -4,6 +4,7 @@ require 'shellwords'
4
4
  require 'tempfile'
5
5
  require 'fileutils'
6
6
 
7
+ require 'krane/annotation'
7
8
  require 'krane/common'
8
9
  require 'krane/concurrency'
9
10
  require 'krane/resource_cache'
@@ -56,6 +57,7 @@ module Krane
56
57
  )
57
58
 
58
59
  def predeploy_sequence
60
+ default_group = { group: nil }
59
61
  before_crs = %w(
60
62
  ResourceQuota
61
63
  NetworkPolicy
@@ -65,12 +67,14 @@ module Krane
65
67
  Role
66
68
  RoleBinding
67
69
  Secret
68
- )
70
+ ).map { |r| [r, default_group] }
71
+
69
72
  after_crs = %w(
70
73
  Pod
71
- )
74
+ ).map { |r| [r, default_group] }
72
75
 
73
- before_crs + cluster_resource_discoverer.crds.select(&:predeployed?).map(&:kind) + after_crs
76
+ crs = cluster_resource_discoverer.crds.select(&:predeployed?).map { |cr| [cr.kind, { group: cr.group }] }
77
+ Hash[before_crs + crs + after_crs]
74
78
  end
75
79
 
76
80
  def prune_whitelist
@@ -81,6 +85,10 @@ module Krane
81
85
  kubectl.server_version
82
86
  end
83
87
 
88
+ attr_reader :task_config
89
+
90
+ delegate :kubeclient_builder, to: :task_config
91
+
84
92
  # Initializes the deploy task
85
93
  #
86
94
  # @param namespace [String] Kubernetes namespace (*required*)
@@ -97,10 +105,10 @@ module Krane
97
105
  # @param render_erb [Boolean] Enable ERB rendering
98
106
  def initialize(namespace:, context:, current_sha: nil, logger: nil, kubectl_instance: nil, bindings: {},
99
107
  global_timeout: nil, selector: nil, filenames: [], protected_namespaces: nil,
100
- render_erb: false)
108
+ render_erb: false, kubeconfig: nil)
101
109
  @logger = logger || Krane::FormattedLogger.build(namespace, context)
102
110
  @template_sets = TemplateSets.from_dirs_and_files(paths: filenames, logger: @logger, render_erb: render_erb)
103
- @task_config = Krane::TaskConfig.new(context, namespace, @logger)
111
+ @task_config = Krane::TaskConfig.new(context, namespace, @logger, kubeconfig)
104
112
  @bindings = bindings
105
113
  @namespace = namespace
106
114
  @namespace_tags = []
@@ -186,10 +194,6 @@ module Krane
186
194
  selector: @selector, statsd_tags: statsd_tags, current_sha: @current_sha)
187
195
  end
188
196
 
189
- def kubeclient_builder
190
- @kubeclient_builder ||= KubeclientBuilder.new
191
- end
192
-
193
197
  def cluster_resource_discoverer
194
198
  @cluster_resource_discoverer ||= ClusterResourceDiscovery.new(
195
199
  task_config: @task_config,
@@ -210,7 +214,10 @@ module Krane
210
214
  end
211
215
 
212
216
  def deploy_has_priority_resources?(resources)
213
- resources.any? { |r| predeploy_sequence.include?(r.type) }
217
+ resources.any? do |r|
218
+ next unless (pr = predeploy_sequence[r.type])
219
+ !pr[:group] || pr[:group] == r.group
220
+ end
214
221
  end
215
222
 
216
223
  def check_initial_status(resources)
@@ -242,6 +249,8 @@ module Krane
242
249
  @logger.info(" - #{secret.id} (from ejson)")
243
250
  end
244
251
 
252
+ StatsD.client.gauge('discover_resources.count', resources.size, tags: statsd_tags)
253
+
245
254
  resources.sort
246
255
  rescue InvalidTemplateError => e
247
256
  record_invalid_template(logger: @logger, err: e.message, filename: e.filename,
@@ -274,11 +283,6 @@ module Krane
274
283
  r.validate_definition(kubectl, selector: @selector)
275
284
  end
276
285
 
277
- resources.select(&:has_warnings?).each do |resource|
278
- record_warnings(logger: @logger, warning: resource.validation_warning_msg,
279
- filename: File.basename(resource.file_path))
280
- end
281
-
282
286
  failed_resources = resources.select(&:validation_failed?)
283
287
  if failed_resources.present?
284
288
 
@@ -12,10 +12,10 @@ module Krane
12
12
  end
13
13
 
14
14
  class EjsonSecretProvisioner
15
- EJSON_SECRET_ANNOTATION = "kubernetes-deploy.shopify.io/ejson-secret"
16
15
  EJSON_SECRET_KEY = "kubernetes_secrets"
17
16
  EJSON_SECRETS_FILE = "secrets.ejson"
18
17
  EJSON_KEYS_SECRET = "ejson-keys"
18
+
19
19
  delegate :namespace, :context, :logger, to: :@task_config
20
20
 
21
21
  def initialize(task_config:, ejson_keys_secret:, ejson_file:, statsd_tags:, selector: nil)
@@ -106,7 +106,6 @@ module Krane
106
106
  "name" => secret_name,
107
107
  "labels" => labels,
108
108
  "namespace" => namespace,
109
- "annotations" => { EJSON_SECRET_ANNOTATION => "true" },
110
109
  },
111
110
  "data" => encoded_data,
112
111
  }
@@ -134,7 +133,7 @@ module Krane
134
133
  end
135
134
 
136
135
  def decrypt_ejson(key_dir)
137
- out, err, st = Open3.capture3("EJSON_KEYDIR=#{key_dir} ejson decrypt #{@ejson_file}")
136
+ out, err, st = Open3.capture3({ 'EJSON_KEYDIR' => key_dir.to_s }, 'ejson', 'decrypt', @ejson_file.to_s)
138
137
  unless st.success?
139
138
  # older ejson versions dump some errors to STDOUT
140
139
  msg = err.presence || out
@@ -25,7 +25,8 @@ module Krane
25
25
  class GlobalDeployTask
26
26
  extend Krane::StatsD::MeasureMethods
27
27
  include TemplateReporting
28
- delegate :context, :logger, :global_kinds, to: :@task_config
28
+ delegate :context, :logger, :global_kinds, :kubeclient_builder, to: :@task_config
29
+ attr_reader :task_config
29
30
 
30
31
  # Initializes the deploy task
31
32
  #
@@ -33,10 +34,10 @@ module Krane
33
34
  # @param global_timeout [Integer] Timeout in seconds
34
35
  # @param selector [Hash] Selector(s) parsed by Krane::LabelSelector (*required*)
35
36
  # @param filenames [Array<String>] An array of filenames and/or directories containing templates (*required*)
36
- def initialize(context:, global_timeout: nil, selector: nil, filenames: [], logger: nil)
37
+ def initialize(context:, global_timeout: nil, selector: nil, filenames: [], logger: nil, kubeconfig: nil)
37
38
  template_paths = filenames.map { |path| File.expand_path(path) }
38
39
 
39
- @task_config = TaskConfig.new(context, nil, logger)
40
+ @task_config = TaskConfig.new(context, nil, logger, kubeconfig)
40
41
  @template_sets = TemplateSets.from_dirs_and_files(paths: template_paths,
41
42
  logger: @task_config.logger, render_erb: false)
42
43
  @global_timeout = global_timeout
@@ -133,11 +134,6 @@ module Krane
133
134
  r.validate_definition(@kubectl, selector: @selector)
134
135
  end
135
136
 
136
- resources.select(&:has_warnings?).each do |resource|
137
- record_warnings(logger: logger, warning: resource.validation_warning_msg,
138
- filename: File.basename(resource.file_path))
139
- end
140
-
141
137
  failed_resources = resources.select(&:validation_failed?)
142
138
  if failed_resources.present?
143
139
  failed_resources.each do |r|
@@ -173,6 +169,8 @@ module Krane
173
169
  logger.info(" - #{r.id}")
174
170
  end
175
171
 
172
+ StatsD.client.gauge('discover_resources.count', resources.size, tags: statsd_tags)
173
+
176
174
  resources.sort
177
175
  rescue InvalidTemplateError => e
178
176
  record_invalid_template(logger: logger, err: e.message, filename: e.filename, content: e.content)
@@ -192,10 +190,6 @@ module Krane
192
190
  @kubectl ||= Kubectl.new(task_config: @task_config, log_failure_by_default: true)
193
191
  end
194
192
 
195
- def kubeclient_builder
196
- @kubeclient_builder ||= KubeclientBuilder.new
197
- end
198
-
199
193
  def prune_whitelist
200
194
  cluster_resource_discoverer.prunable_resources(namespaced: false)
201
195
  end
@@ -10,8 +10,10 @@ module Krane
10
10
  end
11
11
  end
12
12
 
13
- def initialize(kubeconfig: ENV["KUBECONFIG"])
14
- files = kubeconfig || "#{Dir.home}/.kube/config"
13
+ attr_reader :kubeconfig_files
14
+
15
+ def initialize(kubeconfig: nil)
16
+ files = kubeconfig || ENV["KUBECONFIG"] || "#{Dir.home}/.kube/config"
15
17
  # Split the list by colon for Linux and Mac, and semicolon for Windows.
16
18
  @kubeconfig_files = files.split(/[:;]/).map!(&:strip).reject(&:empty?)
17
19
  end
@@ -7,6 +7,7 @@ module Krane
7
7
  not_found: /NotFound/,
8
8
  client_timeout: /Client\.Timeout exceeded while awaiting headers/,
9
9
  empty: /\A\z/,
10
+ context_deadline: /context deadline exceeded/,
10
11
  }
11
12
  DEFAULT_TIMEOUT = 15
12
13
  MAX_RETRY_DELAY = 16
@@ -14,7 +15,7 @@ module Krane
14
15
 
15
16
  class ResourceNotFoundError < StandardError; end
16
17
 
17
- delegate :namespace, :context, :logger, to: :@task_config
18
+ delegate :namespace, :context, :logger, :kubeconfig, to: :@task_config
18
19
 
19
20
  def initialize(task_config:, log_failure_by_default:, default_timeout: DEFAULT_TIMEOUT,
20
21
  output_is_sensitive_default: false)
@@ -34,7 +35,8 @@ module Krane
34
35
 
35
36
  (1..attempts).to_a.each do |current_attempt|
36
37
  logger.debug("Running command (attempt #{current_attempt}): #{cmd.join(' ')}")
37
- out, err, st = Open3.capture3(*cmd)
38
+ env = { 'KUBECONFIG' => kubeconfig }
39
+ out, err, st = Open3.capture3(env, *cmd)
38
40
 
39
41
  # https://github.com/Shopify/krane/issues/395
40
42
  unless out.valid_encoding?
@@ -62,7 +64,8 @@ module Krane
62
64
  else
63
65
  logger.debug("Kubectl err: #{output_is_sensitive ? '<suppressed sensitive output>' : err}")
64
66
  end
65
- StatsD.client.increment('kubectl.error', 1, tags: { context: context, namespace: namespace, cmd: cmd[1] })
67
+ StatsD.client.increment('kubectl.error', 1, tags: { context: context, namespace: namespace, cmd: cmd[1],
68
+ max_attempt: attempts, current_attempt: current_attempt })
66
69
 
67
70
  break unless retriable_err?(err, retry_whitelist) && current_attempt < attempts
68
71
  sleep(retry_delay(current_attempt))
@@ -79,7 +82,7 @@ module Krane
79
82
  def version_info
80
83
  @version_info ||=
81
84
  begin
82
- response, _, status = run("version", use_namespace: false, log_failure: true)
85
+ response, _, status = run("version", use_namespace: false, log_failure: true, attempts: 2)
83
86
  raise KubectlError, "Could not retrieve kubectl version info" unless status.success?
84
87
  extract_version_info_from_kubectl_response(response)
85
88
  end
@@ -6,9 +6,12 @@ require 'krane/remote_logs'
6
6
  require 'krane/duration_parser'
7
7
  require 'krane/label_selector'
8
8
  require 'krane/rollout_conditions'
9
+ require 'krane/psych_k8s_compatibility'
9
10
 
10
11
  module Krane
11
12
  class KubernetesResource
13
+ using PsychK8sCompatibility
14
+
12
15
  attr_reader :name, :namespace, :context
13
16
  attr_writer :type, :deploy_started_at, :global
14
17
 
@@ -32,9 +35,9 @@ module Krane
32
35
  If you have reason to believe it will succeed, retry the deploy to continue to monitor the rollout.
33
36
  MSG
34
37
 
35
- TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX = "timeout-override"
36
- TIMEOUT_OVERRIDE_ANNOTATION_DEPRECATED = "kubernetes-deploy.shopify.io/#{TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX}"
37
- TIMEOUT_OVERRIDE_ANNOTATION = "krane.shopify.io/#{TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX}"
38
+ ALLOWED_DEPLOY_METHOD_OVERRIDES = %w(create replace replace-force)
39
+ DEPLOY_METHOD_OVERRIDE_ANNOTATION = "deploy-method-override"
40
+ TIMEOUT_OVERRIDE_ANNOTATION = "timeout-override"
38
41
  LAST_APPLIED_ANNOTATION = "kubectl.kubernetes.io/last-applied-configuration"
39
42
  SENSITIVE_TEMPLATE_CONTENT = false
40
43
  SERVER_DRY_RUNNABLE = false
@@ -60,8 +63,8 @@ module Krane
60
63
  end
61
64
 
62
65
  def class_for_kind(kind)
63
- if Krane.const_defined?(kind)
64
- Krane.const_get(kind)
66
+ if Krane.const_defined?(kind, false)
67
+ Krane.const_get(kind, false)
65
68
  end
66
69
  rescue NameError
67
70
  nil
@@ -103,7 +106,7 @@ module Krane
103
106
  def timeout_override
104
107
  return @timeout_override if defined?(@timeout_override)
105
108
 
106
- @timeout_override = DurationParser.new(krane_annotation_value(TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX)).parse!.to_i
109
+ @timeout_override = DurationParser.new(krane_annotation_value(TIMEOUT_OVERRIDE_ANNOTATION)).parse!.to_i
107
110
  rescue DurationParser::ParsingError
108
111
  @timeout_override = nil
109
112
  end
@@ -123,7 +126,6 @@ module Krane
123
126
  @statsd_report_done = false
124
127
  @disappeared = false
125
128
  @validation_errors = []
126
- @validation_warnings = []
127
129
  @instance_data = {}
128
130
  @server_dry_run_validated = false
129
131
  end
@@ -134,22 +136,13 @@ module Krane
134
136
 
135
137
  def validate_definition(kubectl, selector: nil)
136
138
  @validation_errors = []
137
- @validation_warnings = []
138
139
  validate_selector(selector) if selector
139
140
  validate_timeout_annotation
140
- validate_annotation_version
141
+ validate_deploy_method_override_annotation
141
142
  validate_spec_with_kubectl(kubectl)
142
143
  @validation_errors.present?
143
144
  end
144
145
 
145
- def validation_warning_msg
146
- @validation_warnings.join("\n")
147
- end
148
-
149
- def has_warnings?
150
- @validation_warnings.present?
151
- end
152
-
153
146
  def validation_error_msg
154
147
  @validation_errors.join("\n")
155
148
  end
@@ -228,6 +221,11 @@ module Krane
228
221
  @type || self.class.kind
229
222
  end
230
223
 
224
+ def group
225
+ grouping, version = @definition.dig("apiVersion").split("/")
226
+ version ? grouping : "core"
227
+ end
228
+
231
229
  def kubectl_resource_type
232
230
  type
233
231
  end
@@ -242,10 +240,14 @@ module Krane
242
240
  if @definition.dig("metadata", "name").blank? && uses_generate_name?
243
241
  :create
244
242
  else
245
- :apply
243
+ deploy_method_override || :apply
246
244
  end
247
245
  end
248
246
 
247
+ def deploy_method_override
248
+ krane_annotation_value(DEPLOY_METHOD_OVERRIDE_ANNOTATION)&.to_sym
249
+ end
250
+
249
251
  def sync_debug_info(kubectl)
250
252
  @debug_events = fetch_events(kubectl) unless ENV[DISABLE_FETCHING_EVENT_INFO]
251
253
  @debug_logs = fetch_debug_logs if print_debug_logs? && !ENV[DISABLE_FETCHING_LOG_INFO]
@@ -495,8 +497,8 @@ module Krane
495
497
  private
496
498
 
497
499
  def validate_timeout_annotation
498
- timeout_override_value = krane_annotation_value(TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX)
499
- timeout_annotation_key = krane_annotation_key(TIMEOUT_OVERRIDE_ANNOTATION_SUFFIX)
500
+ timeout_override_value = krane_annotation_value(TIMEOUT_OVERRIDE_ANNOTATION)
501
+ timeout_annotation_key = Annotation.for(TIMEOUT_OVERRIDE_ANNOTATION)
500
502
  return if timeout_override_value.nil?
501
503
 
502
504
  override = DurationParser.new(timeout_override_value).parse!
@@ -509,30 +511,18 @@ module Krane
509
511
  @validation_errors << "#{timeout_annotation_key} annotation is invalid: #{e}"
510
512
  end
511
513
 
512
- def validate_annotation_version
513
- return if validation_warning_msg.include?("annotations is deprecated")
514
- annotation_keys = @definition.dig("metadata", "annotations")&.keys
515
- annotation_keys&.each do |annotation|
516
- if annotation.include?("kubernetes-deploy.shopify.io")
517
- annotation_prefix = annotation.split('/').first
518
- @validation_warnings << "#{annotation_prefix} as a prefix for annotations is deprecated: "\
519
- "Use the 'krane.shopify.io' annotation prefix instead"
520
- return
521
- end
514
+ def validate_deploy_method_override_annotation
515
+ deploy_method_override_value = krane_annotation_value(DEPLOY_METHOD_OVERRIDE_ANNOTATION)
516
+ deploy_method_override_annotation_key = Annotation.for(DEPLOY_METHOD_OVERRIDE_ANNOTATION)
517
+ return unless deploy_method_override_value
518
+ unless ALLOWED_DEPLOY_METHOD_OVERRIDES.include?(deploy_method_override_value)
519
+ @validation_errors << "#{deploy_method_override_annotation_key} is invalid: Accepted values are: " \
520
+ "#{ALLOWED_DEPLOY_METHOD_OVERRIDES.join(', ')} but got #{deploy_method_override_value}"
522
521
  end
523
522
  end
524
523
 
525
524
  def krane_annotation_value(suffix)
526
- @definition.dig("metadata", "annotations", "kubernetes-deploy.shopify.io/#{suffix}") ||
527
- @definition.dig("metadata", "annotations", "krane.shopify.io/#{suffix}")
528
- end
529
-
530
- def krane_annotation_key(suffix)
531
- if @definition.dig("metadata", "annotations", "kubernetes-deploy.shopify.io/#{suffix}")
532
- "kubernetes-deploy.shopify.io/#{suffix}"
533
- elsif @definition.dig("metadata", "annotations", "krane.shopify.io/#{suffix}")
534
- "krane.shopify.io/#{suffix}"
535
- end
525
+ @definition.dig("metadata", "annotations", Annotation.for(suffix))
536
526
  end
537
527
 
538
528
  def validate_selector(selector)
@@ -572,7 +562,7 @@ module Krane
572
562
  def validate_with_server_side_dry_run(kubectl)
573
563
  command = ["apply", "-f", file_path, "--server-dry-run", "--output=name"]
574
564
  kubectl.run(*command, log_failure: false, output_is_sensitive: sensitive_template_content?,
575
- retry_whitelist: [:client_timeout, :empty], attempts: 3)
565
+ retry_whitelist: [:client_timeout, :empty, :context_deadline], attempts: 3)
576
566
  end
577
567
 
578
568
  # Local dry run is supported on only create and apply
@@ -582,7 +572,7 @@ module Krane
582
572
  verb = deploy_method == :apply ? "apply" : "create"
583
573
  command = [verb, "-f", file_path, "--dry-run", "--output=name"]
584
574
  kubectl.run(*command, log_failure: false, output_is_sensitive: sensitive_template_content?,
585
- retry_whitelist: [:client_timeout, :empty], attempts: 3, use_namespace: !global?)
575
+ retry_whitelist: [:client_timeout, :empty, :context_deadline], attempts: 3, use_namespace: !global?)
586
576
  end
587
577
 
588
578
  def labels
@@ -70,7 +70,7 @@ module Krane
70
70
  @validation_errors << "The CRD that specifies this resource is using invalid rollout conditions. " \
71
71
  "Krane will not be able to continue until those rollout conditions are fixed.\n" \
72
72
  "Rollout conditions can be found on the CRD that defines this resource (#{@crd.name}), " \
73
- "under the annotation #{CustomResourceDefinition::ROLLOUT_CONDITIONS_ANNOTATION}.\n" \
73
+ "under the annotation #{Annotation.for(CustomResourceDefinition::ROLLOUT_CONDITIONS_ANNOTATION)}.\n" \
74
74
  "Validation failed with: #{e}"
75
75
  end
76
76
 
@@ -2,9 +2,8 @@
2
2
  module Krane
3
3
  class CustomResourceDefinition < KubernetesResource
4
4
  TIMEOUT = 2.minutes
5
- ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX = "instance-rollout-conditions"
6
- ROLLOUT_CONDITIONS_ANNOTATION = "krane.shopify.io/#{ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX}"
7
- TIMEOUT_FOR_INSTANCE_ANNOTATION = "krane.shopify.io/instance-timeout"
5
+ ROLLOUT_CONDITIONS_ANNOTATION = "instance-rollout-conditions"
6
+ TIMEOUT_FOR_INSTANCE_ANNOTATION = "instance-timeout"
8
7
  GLOBAL = true
9
8
 
10
9
  def deploy_succeeded?
@@ -20,7 +19,7 @@ module Krane
20
19
  end
21
20
 
22
21
  def timeout_for_instance
23
- timeout = krane_annotation_value("instance-timeout")
22
+ timeout = krane_annotation_value(TIMEOUT_FOR_INSTANCE_ANNOTATION)
24
23
  DurationParser.new(timeout).parse!.to_i
25
24
  rescue DurationParser::ParsingError
26
25
  nil
@@ -46,6 +45,10 @@ module Krane
46
45
  @definition.dig("spec", "names", "kind")
47
46
  end
48
47
 
48
+ def group
49
+ @definition.dig("spec", "group")
50
+ end
51
+
49
52
  def prunable?
50
53
  prunable = krane_annotation_value("prunable")
51
54
  prunable == "true"
@@ -59,8 +62,8 @@ module Krane
59
62
  def rollout_conditions
60
63
  return @rollout_conditions if defined?(@rollout_conditions)
61
64
 
62
- @rollout_conditions = if krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX)
63
- RolloutConditions.from_annotation(krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX))
65
+ @rollout_conditions = if krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION)
66
+ RolloutConditions.from_annotation(krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION))
64
67
  end
65
68
  rescue RolloutConditionsError
66
69
  @rollout_conditions = nil
@@ -71,13 +74,13 @@ module Krane
71
74
 
72
75
  validate_rollout_conditions
73
76
  rescue RolloutConditionsError => e
74
- @validation_errors << "Annotation #{krane_annotation_key(ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX)} "\
77
+ @validation_errors << "Annotation #{Annotation.for(ROLLOUT_CONDITIONS_ANNOTATION)} " \
75
78
  "on #{name} is invalid: #{e}"
76
79
  end
77
80
 
78
81
  def validate_rollout_conditions
79
- if krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX) && @rollout_conditions_validated.nil?
80
- conditions = RolloutConditions.from_annotation(krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION_SUFFIX))
82
+ if krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION) && @rollout_conditions_validated.nil?
83
+ conditions = RolloutConditions.from_annotation(krane_annotation_value(ROLLOUT_CONDITIONS_ANNOTATION))
81
84
  conditions.validate!
82
85
  end
83
86
 
@@ -5,9 +5,7 @@ module Krane
5
5
  class Deployment < KubernetesResource
6
6
  TIMEOUT = 7.minutes
7
7
  SYNC_DEPENDENCIES = %w(Pod ReplicaSet)
8
- REQUIRED_ROLLOUT_ANNOTATION_SUFFIX = "required-rollout"
9
- REQUIRED_ROLLOUT_ANNOTATION_DEPRECATED = "kubernetes-deploy.shopify.io/#{REQUIRED_ROLLOUT_ANNOTATION_SUFFIX}"
10
- REQUIRED_ROLLOUT_ANNOTATION = "krane.shopify.io/#{REQUIRED_ROLLOUT_ANNOTATION_SUFFIX}"
8
+ REQUIRED_ROLLOUT_ANNOTATION = "required-rollout"
11
9
  REQUIRED_ROLLOUT_TYPES = %w(maxUnavailable full none).freeze
12
10
  DEFAULT_REQUIRED_ROLLOUT = 'full'
13
11
 
@@ -106,7 +104,7 @@ module Krane
106
104
 
107
105
  strategy = @definition.dig('spec', 'strategy', 'type').to_s
108
106
  if required_rollout.downcase == 'maxunavailable' && strategy.present? && strategy.downcase != 'rollingupdate'
109
- @validation_errors << "'#{krane_annotation_key(REQUIRED_ROLLOUT_ANNOTATION_SUFFIX)}: #{required_rollout}' "\
107
+ @validation_errors << "'#{Annotation.for(REQUIRED_ROLLOUT_ANNOTATION)}: #{required_rollout}' " \
110
108
  "is incompatible with strategy '#{strategy}'"
111
109
  end
112
110
 
@@ -151,7 +149,7 @@ module Krane
151
149
  end
152
150
 
153
151
  def rollout_annotation_err_msg
154
- "'#{krane_annotation_key(REQUIRED_ROLLOUT_ANNOTATION_SUFFIX)}: #{required_rollout}' is invalid. "\
152
+ "'#{Annotation.for(REQUIRED_ROLLOUT_ANNOTATION)}: #{required_rollout}' is invalid. " \
155
153
  "Acceptable values: #{REQUIRED_ROLLOUT_TYPES.join(', ')}"
156
154
  end
157
155
 
@@ -63,6 +63,7 @@ module Krane
63
63
  attr_reader :name
64
64
 
65
65
  def initialize(definition)
66
+ super(definition: definition, namespace: nil, context: nil, logger: nil)
66
67
  @definition = definition
67
68
  @name = definition.dig("metadata", "name").to_s
68
69
  end
@@ -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
@@ -28,6 +28,8 @@ module Krane
28
28
  ENV["TASK_ID"]
29
29
  elsif current_sha
30
30
  current_sha[0...8] + "-#{SecureRandom.hex(4)}"
31
+ else
32
+ SecureRandom.hex(8)
31
33
  end
32
34
  end
33
35
 
@@ -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 { |r| r.type == resource_type }
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
@@ -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
@@ -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
@@ -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
@@ -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
- pod = build_pod(template, command, arguments, env_vars, verify_result)
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, command, args, env_vars, verify_result)
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
- set_container_overrides!(pod_template, command, args, env_vars)
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 set_container_overrides!(pod_definition, command, args, env_vars)
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.command = command if command
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
@@ -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")
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Krane
3
- VERSION = "1.1.3"
3
+ VERSION = "2.1.2"
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: 1.1.3
4
+ version: 2.1.2
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-04-23 00:00:00.000000000 Z
13
+ date: 2020-10-29 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.78.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.78.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