krane 1.1.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -0
  3. data/{ISSUE_TEMPLATE.md → .github/ISSUE_TEMPLATE.md} +0 -0
  4. data/{pull_request_template.md → .github/pull_request_template.md} +0 -0
  5. data/.rubocop-http---shopify-github-io-ruby-style-guide-rubocop-yml +27 -33
  6. data/.rubocop.yml +0 -12
  7. data/.shopify-build/krane.yml +20 -6
  8. data/1.0-Upgrade.md +1 -1
  9. data/CHANGELOG.md +57 -0
  10. data/CONTRIBUTING.md +2 -2
  11. data/README.md +17 -14
  12. data/bin/ci +1 -1
  13. data/bin/test +2 -2
  14. data/dev.yml +3 -2
  15. data/krane.gemspec +6 -4
  16. data/lib/krane/annotation.rb +11 -0
  17. data/lib/krane/cli/deploy_command.rb +2 -3
  18. data/lib/krane/cli/global_deploy_command.rb +3 -3
  19. data/lib/krane/cli/render_command.rb +3 -3
  20. data/lib/krane/cluster_resource_discovery.rb +10 -6
  21. data/lib/krane/concerns/template_reporting.rb +0 -6
  22. data/lib/krane/container_logs.rb +1 -1
  23. data/lib/krane/container_overrides.rb +33 -0
  24. data/lib/krane/deploy_task.rb +21 -18
  25. data/lib/krane/ejson_secret_provisioner.rb +1 -2
  26. data/lib/krane/global_deploy_task.rb +8 -14
  27. data/lib/krane/kubeclient_builder.rb +4 -2
  28. data/lib/krane/kubectl.rb +18 -5
  29. data/lib/krane/kubernetes_resource.rb +32 -42
  30. data/lib/krane/kubernetes_resource/custom_resource.rb +2 -2
  31. data/lib/krane/kubernetes_resource/custom_resource_definition.rb +13 -10
  32. data/lib/krane/kubernetes_resource/deployment.rb +5 -7
  33. data/lib/krane/kubernetes_resource/persistent_volume_claim.rb +1 -0
  34. data/lib/krane/kubernetes_resource/pod.rb +12 -8
  35. data/lib/krane/psych_k8s_compatibility.rb +36 -0
  36. data/lib/krane/render_task.rb +2 -2
  37. data/lib/krane/renderer.rb +2 -0
  38. data/lib/krane/resource_deployer.rb +9 -5
  39. data/lib/krane/resource_watcher.rb +1 -1
  40. data/lib/krane/restart_task.rb +8 -8
  41. data/lib/krane/runner_task.rb +21 -24
  42. data/lib/krane/statsd.rb +2 -2
  43. data/lib/krane/task_config.rb +7 -2
  44. data/lib/krane/task_config_validator.rb +3 -3
  45. data/lib/krane/template_sets.rb +1 -1
  46. data/lib/krane/version.rb +1 -1
  47. metadata +17 -13
  48. data/lib/krane/kubernetes_resource/cloudsql.rb +0 -44
data/bin/ci CHANGED
@@ -17,5 +17,5 @@ docker run --rm \
17
17
  -e VERBOSE=1 \
18
18
  -e PARALLELISM=$PARALLELISM \
19
19
  -w /usr/src/app \
20
- ruby:2.4 \
20
+ ruby:"${RUBY_VERSION:-2.5}" \
21
21
  bin/test
data/bin/test CHANGED
@@ -41,7 +41,7 @@ bundle exec rake unit_test
41
41
  print_header "Run Non-Parallel Integration Tests"
42
42
  bundle exec rake serial_integration_test
43
43
 
44
- print_header "Run Parallel Integration Tests (N=$PARALLELISM)"
45
- PARALLELIZE_ME=1 N=$PARALLELISM bundle exec rake integration_test
44
+ print_header "Run Parallel Integration Tests (MT_CPU=$PARALLELISM)"
45
+ PARALLELIZE_ME=1 MT_CPU=$PARALLELISM bundle exec rake integration_test
46
46
 
47
47
  test $err -eq 0
data/dev.yml CHANGED
@@ -1,10 +1,11 @@
1
1
  ---
2
2
  name: krane
3
3
  up:
4
- - ruby: 2.4.6 # Matches gemspec
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
@@ -23,7 +23,9 @@ Gem::Specification.new do |spec|
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  spec.require_paths = %w(lib)
25
25
 
26
- spec.required_ruby_version = '>= 2.4.0'
26
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
27
+
28
+ spec.required_ruby_version = '>= 2.5.0'
27
29
  spec.add_dependency("activesupport", ">= 5.0")
28
30
  spec.add_dependency("kubeclient", "~> 4.3")
29
31
  spec.add_dependency("googleauth", "~> 0.8")
@@ -33,7 +35,7 @@ Gem::Specification.new do |spec|
33
35
  spec.add_dependency("oj", "~> 3.0")
34
36
  spec.add_dependency("concurrent-ruby", "~> 1.1")
35
37
  spec.add_dependency("jsonpath", "~> 0.9.6")
36
- spec.add_dependency("thor", ">= 0.20.3", "< 2.0")
38
+ spec.add_dependency("thor", ">= 1.0", "< 2.0")
37
39
 
38
40
  # Basics
39
41
  spec.add_development_dependency("bundler")
@@ -41,7 +43,7 @@ Gem::Specification.new do |spec|
41
43
  spec.add_development_dependency("yard")
42
44
 
43
45
  # Test framework
44
- spec.add_development_dependency("minitest", "~> 5.0")
46
+ spec.add_development_dependency("minitest", "~> 5.12")
45
47
  spec.add_development_dependency("minitest-stub-const", "~> 0.6")
46
48
  spec.add_development_dependency("minitest-reporters")
47
49
  spec.add_development_dependency("mocha", "~> 1.5")
@@ -54,6 +56,6 @@ Gem::Specification.new do |spec|
54
56
  spec.add_development_dependency("byebug")
55
57
  spec.add_development_dependency("ruby-prof")
56
58
  spec.add_development_dependency("ruby-prof-flamegraph")
57
- spec.add_development_dependency("rubocop", "~> 0.76.0")
59
+ spec.add_development_dependency("rubocop", "~> 0.89.1")
58
60
  spec.add_development_dependency("codecov")
59
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
@@ -14,7 +14,7 @@ module Krane
14
14
  aliases: :f, required: false, default: [],
15
15
  desc: "Directories and files that contains the configuration to apply" },
16
16
  "stdin" => { type: :boolean, default: false,
17
- desc: "Read resources from stdin" },
17
+ desc: "[DEPRECATED] Read resources from stdin" },
18
18
  "global-timeout" => { type: :string, banner: "duration", default: DEFAULT_DEPLOY_TIMEOUT,
19
19
  desc: "Max duration to monitor workloads correctly deployed" },
20
20
  "protected-namespaces" => { type: :array, banner: "namespace1 namespace2 namespaceN",
@@ -46,11 +46,10 @@ module Krane
46
46
  protected_namespaces = []
47
47
  end
48
48
 
49
- # never mutate options directly
50
49
  filenames = options[:filenames].dup
51
50
  filenames << "-" if options[:stdin]
52
51
  if filenames.empty?
53
- raise Thor::RequiredArgumentMissingError, 'At least one of --filenames or --stdin must be set'
52
+ raise(Thor::RequiredArgumentMissingError, '--filenames must be set and not empty')
54
53
  end
55
54
 
56
55
  ::Krane::OptionsHelper.with_processed_template_paths(filenames) do |paths|
@@ -8,7 +8,8 @@ module Krane
8
8
  "filenames" => { type: :array, banner: 'config/deploy/production config/deploy/my-extra-resource.yml',
9
9
  aliases: :f, required: false, default: [],
10
10
  desc: "Directories and files that contains the configuration to apply" },
11
- "stdin" => { type: :boolean, default: false, desc: "Read resources from stdin" },
11
+ "stdin" => { type: :boolean, default: false,
12
+ desc: "[DEPRECATED] Read resources from stdin" },
12
13
  "global-timeout" => { type: :string, banner: "duration", default: DEFAULT_DEPLOY_TIMEOUT,
13
14
  desc: "Max duration to monitor workloads correctly deployed" },
14
15
  "verify-result" => { type: :boolean, default: true,
@@ -28,11 +29,10 @@ module Krane
28
29
 
29
30
  selector = ::Krane::LabelSelector.parse(options[:selector])
30
31
 
31
- # never mutate options directly
32
32
  filenames = options[:filenames].dup
33
33
  filenames << "-" if options[:stdin]
34
34
  if filenames.empty?
35
- raise Thor::RequiredArgumentMissingError, 'At least one of --filenames or --stdin must be set'
35
+ raise(Thor::RequiredArgumentMissingError, '--filenames must be set and not empty')
36
36
  end
37
37
 
38
38
  ::Krane::OptionsHelper.with_processed_template_paths(filenames) do |paths|
@@ -7,7 +7,8 @@ module Krane
7
7
  "bindings" => { type: :array, banner: "foo=bar abc=def", desc: 'Bindings for erb' },
8
8
  "filenames" => { type: :array, banner: 'config/deploy/production config/deploy/my-extra-resource.yml',
9
9
  required: false, default: [], aliases: 'f', desc: 'Directories and files to render' },
10
- "stdin" => { type: :boolean, desc: "Read resources from stdin", default: false },
10
+ "stdin" => { type: :boolean, default: false,
11
+ desc: "[DEPRECATED] Read resources from stdin" },
11
12
  "current-sha" => { type: :string, banner: "SHA", desc: "Expose SHA `current_sha` in ERB bindings",
12
13
  lazy_default: '' },
13
14
  }
@@ -20,11 +21,10 @@ module Krane
20
21
  bindings_parser = ::Krane::BindingsParser.new
21
22
  options[:bindings]&.each { |b| bindings_parser.add(b) }
22
23
 
23
- # never mutate options directly
24
24
  filenames = options[:filenames].dup
25
25
  filenames << "-" if options[:stdin]
26
26
  if filenames.empty?
27
- raise Thor::RequiredArgumentMissingError, 'At least one of --filenames or --stdin must be set'
27
+ raise(Thor::RequiredArgumentMissingError, '--filenames must be set and not empty')
28
28
  end
29
29
 
30
30
  ::Krane::OptionsHelper.with_processed_template_paths(filenames, render_erb: true) do |paths|
@@ -37,7 +37,7 @@ module Krane
37
37
  def fetch_resources(namespaced: false)
38
38
  command = %w(api-resources)
39
39
  command << "--namespaced=#{namespaced}"
40
- raw, _, st = kubectl.run(*command, output: "wide", attempts: 5,
40
+ raw, err, st = kubectl.run(*command, output: "wide", attempts: 5,
41
41
  use_namespace: false)
42
42
  if st.success?
43
43
  rows = raw.split("\n")
@@ -59,7 +59,7 @@ module Krane
59
59
  resource
60
60
  end
61
61
  else
62
- []
62
+ raise FatalKubeAPIError, "Error retrieving api-resources: #{err}"
63
63
  end
64
64
  end
65
65
 
@@ -68,7 +68,7 @@ module Krane
68
68
  # kubectl api-versions returns a list of group/version strings e.g. autoscaling/v2beta2
69
69
  # A kind may not exist in all versions of the group.
70
70
  def fetch_api_versions
71
- raw, _, st = kubectl.run("api-versions", attempts: 5, use_namespace: false)
71
+ raw, err, st = kubectl.run("api-versions", attempts: 5, use_namespace: false)
72
72
  # The "core" group is represented by an empty string
73
73
  versions = { "" => %w(v1) }
74
74
  if st.success?
@@ -78,6 +78,8 @@ module Krane
78
78
  versions[group] ||= []
79
79
  versions[group] << version
80
80
  end
81
+ else
82
+ raise FatalKubeAPIError, "Error retrieving api-versions: #{err}"
81
83
  end
82
84
  versions
83
85
  end
@@ -85,7 +87,9 @@ module Krane
85
87
  def version_for_kind(versions, kind)
86
88
  # Override list for kinds that don't appear in the lastest version of a group
87
89
  version_override = { "CronJob" => "v1beta1", "VolumeAttachment" => "v1beta1",
88
- "CSIDriver" => "v1beta1", "Ingress" => "v1beta1", "CSINode" => "v1beta1" }
90
+ "CSIDriver" => "v1beta1", "Ingress" => "v1beta1",
91
+ "CSINode" => "v1beta1", "Job" => "v1",
92
+ "IngressClass" => "v1beta1" }
89
93
 
90
94
  pattern = /v(?<major>\d+)(?<pre>alpha|beta)?(?<minor>\d+)?/
91
95
  latest = versions.sort_by do |version|
@@ -97,12 +101,12 @@ module Krane
97
101
  end
98
102
 
99
103
  def fetch_crds
100
- raw_json, _, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
104
+ raw_json, err, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
101
105
  use_namespace: false)
102
106
  if st.success?
103
107
  JSON.parse(raw_json)["items"]
104
108
  else
105
- []
109
+ raise FatalKubeAPIError, "Error retrieving CustomResourceDefinition: #{err}"
106
110
  end
107
111
  end
108
112
 
@@ -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"))
@@ -3,7 +3,7 @@ module Krane
3
3
  class ContainerLogs
4
4
  attr_reader :lines, :container_name
5
5
 
6
- DEFAULT_LINE_LIMIT = 250
6
+ DEFAULT_LINE_LIMIT = 25
7
7
 
8
8
  def initialize(parent_id:, container_name:, namespace:, context:, logger:)
9
9
  @parent_id = parent_id
@@ -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,13 +4,13 @@ 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'
10
11
  require 'krane/kubernetes_resource'
11
12
  %w(
12
13
  custom_resource
13
- cloudsql
14
14
  config_map
15
15
  deployment
16
16
  ingress
@@ -57,6 +57,7 @@ module Krane
57
57
  )
58
58
 
59
59
  def predeploy_sequence
60
+ default_group = { group: nil }
60
61
  before_crs = %w(
61
62
  ResourceQuota
62
63
  NetworkPolicy
@@ -66,12 +67,14 @@ module Krane
66
67
  Role
67
68
  RoleBinding
68
69
  Secret
69
- )
70
+ ).map { |r| [r, default_group] }
71
+
70
72
  after_crs = %w(
71
73
  Pod
72
- )
74
+ ).map { |r| [r, default_group] }
73
75
 
74
- 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]
75
78
  end
76
79
 
77
80
  def prune_whitelist
@@ -82,6 +85,10 @@ module Krane
82
85
  kubectl.server_version
83
86
  end
84
87
 
88
+ attr_reader :task_config
89
+
90
+ delegate :kubeclient_builder, to: :task_config
91
+
85
92
  # Initializes the deploy task
86
93
  #
87
94
  # @param namespace [String] Kubernetes namespace (*required*)
@@ -98,10 +105,10 @@ module Krane
98
105
  # @param render_erb [Boolean] Enable ERB rendering
99
106
  def initialize(namespace:, context:, current_sha: nil, logger: nil, kubectl_instance: nil, bindings: {},
100
107
  global_timeout: nil, selector: nil, filenames: [], protected_namespaces: nil,
101
- render_erb: false)
108
+ render_erb: false, kubeconfig: nil)
102
109
  @logger = logger || Krane::FormattedLogger.build(namespace, context)
103
110
  @template_sets = TemplateSets.from_dirs_and_files(paths: filenames, logger: @logger, render_erb: render_erb)
104
- @task_config = Krane::TaskConfig.new(context, namespace, @logger)
111
+ @task_config = Krane::TaskConfig.new(context, namespace, @logger, kubeconfig)
105
112
  @bindings = bindings
106
113
  @namespace = namespace
107
114
  @namespace_tags = []
@@ -117,8 +124,8 @@ module Krane
117
124
  # Runs the task, returning a boolean representing success or failure
118
125
  #
119
126
  # @return [Boolean]
120
- def run(*args)
121
- run!(*args)
127
+ def run(**args)
128
+ run!(**args)
122
129
  true
123
130
  rescue FatalDeploymentError
124
131
  false
@@ -187,10 +194,6 @@ module Krane
187
194
  selector: @selector, statsd_tags: statsd_tags, current_sha: @current_sha)
188
195
  end
189
196
 
190
- def kubeclient_builder
191
- @kubeclient_builder ||= KubeclientBuilder.new
192
- end
193
-
194
197
  def cluster_resource_discoverer
195
198
  @cluster_resource_discoverer ||= ClusterResourceDiscovery.new(
196
199
  task_config: @task_config,
@@ -211,7 +214,10 @@ module Krane
211
214
  end
212
215
 
213
216
  def deploy_has_priority_resources?(resources)
214
- 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
215
221
  end
216
222
 
217
223
  def check_initial_status(resources)
@@ -243,6 +249,8 @@ module Krane
243
249
  @logger.info(" - #{secret.id} (from ejson)")
244
250
  end
245
251
 
252
+ StatsD.client.gauge('discover_resources.count', resources.size, tags: statsd_tags)
253
+
246
254
  resources.sort
247
255
  rescue InvalidTemplateError => e
248
256
  record_invalid_template(logger: @logger, err: e.message, filename: e.filename,
@@ -275,11 +283,6 @@ module Krane
275
283
  r.validate_definition(kubectl, selector: @selector)
276
284
  end
277
285
 
278
- resources.select(&:has_warnings?).each do |resource|
279
- record_warnings(logger: @logger, warning: resource.validation_warning_msg,
280
- filename: File.basename(resource.file_path))
281
- end
282
-
283
286
  failed_resources = resources.select(&:validation_failed?)
284
287
  if failed_resources.present?
285
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
  }
@@ -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
@@ -46,8 +47,8 @@ module Krane
46
47
  # Runs the task, returning a boolean representing success or failure
47
48
  #
48
49
  # @return [Boolean]
49
- def run(*args)
50
- run!(*args)
50
+ def run(**args)
51
+ run!(**args)
51
52
  true
52
53
  rescue FatalDeploymentError
53
54
  false
@@ -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