kubernetes-deploy 0.29.0 → 1.0.0.pre.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.buildkite/pipeline.nightly.yml +7 -0
  3. data/.rubocop.yml +0 -12
  4. data/.shopify-build/{kubernetes-deploy.yml → krane.yml} +8 -2
  5. data/1.0-Upgrade.md +109 -0
  6. data/CHANGELOG.md +60 -0
  7. data/CONTRIBUTING.md +2 -2
  8. data/Gemfile +1 -0
  9. data/README.md +86 -2
  10. data/dev.yml +3 -1
  11. data/dev/flamegraph-from-tests +1 -1
  12. data/exe/kubernetes-deploy +12 -9
  13. data/exe/kubernetes-render +9 -7
  14. data/exe/kubernetes-restart +3 -3
  15. data/exe/kubernetes-run +1 -1
  16. data/kubernetes-deploy.gemspec +5 -5
  17. data/lib/krane.rb +5 -3
  18. data/lib/{kubernetes-deploy → krane}/bindings_parser.rb +1 -1
  19. data/lib/krane/cli/deploy_command.rb +25 -13
  20. data/lib/krane/cli/global_deploy_command.rb +55 -0
  21. data/lib/krane/cli/krane.rb +12 -3
  22. data/lib/krane/cli/render_command.rb +19 -9
  23. data/lib/krane/cli/restart_command.rb +4 -4
  24. data/lib/krane/cli/run_command.rb +4 -4
  25. data/lib/krane/cli/version_command.rb +1 -1
  26. data/lib/krane/cluster_resource_discovery.rb +113 -0
  27. data/lib/{kubernetes-deploy → krane}/common.rb +8 -9
  28. data/lib/krane/concerns/template_reporting.rb +29 -0
  29. data/lib/{kubernetes-deploy → krane}/concurrency.rb +1 -1
  30. data/lib/{kubernetes-deploy → krane}/container_logs.rb +3 -2
  31. data/lib/{kubernetes-deploy → krane}/deferred_summary_logging.rb +2 -2
  32. data/lib/{kubernetes-deploy → krane}/delayed_exceptions.rb +0 -0
  33. data/lib/krane/deploy_task.rb +16 -0
  34. data/lib/krane/deploy_task_config_validator.rb +29 -0
  35. data/lib/krane/deprecated_deploy_task.rb +404 -0
  36. data/lib/{kubernetes-deploy → krane}/duration_parser.rb +1 -3
  37. data/lib/{kubernetes-deploy → krane}/ejson_secret_provisioner.rb +10 -13
  38. data/lib/krane/errors.rb +28 -0
  39. data/lib/{kubernetes-deploy → krane}/formatted_logger.rb +2 -2
  40. data/lib/krane/global_deploy_task.rb +210 -0
  41. data/lib/krane/global_deploy_task_config_validator.rb +12 -0
  42. data/lib/{kubernetes-deploy → krane}/kubeclient_builder.rb +13 -5
  43. data/lib/{kubernetes-deploy → krane}/kubectl.rb +14 -16
  44. data/lib/{kubernetes-deploy → krane}/kubernetes_resource.rb +110 -27
  45. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/cloudsql.rb +1 -1
  46. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/config_map.rb +1 -1
  47. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/cron_job.rb +1 -1
  48. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/custom_resource.rb +2 -2
  49. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/custom_resource_definition.rb +1 -5
  50. data/lib/krane/kubernetes_resource/daemon_set.rb +90 -0
  51. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/deployment.rb +2 -2
  52. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/horizontal_pod_autoscaler.rb +1 -1
  53. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/ingress.rb +1 -1
  54. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/job.rb +1 -1
  55. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/network_policy.rb +1 -1
  56. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/persistent_volume_claim.rb +1 -1
  57. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod.rb +6 -2
  58. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_disruption_budget.rb +2 -2
  59. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_set_base.rb +3 -3
  60. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_template.rb +1 -1
  61. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/replica_set.rb +2 -2
  62. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/resource_quota.rb +1 -1
  63. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/role.rb +1 -1
  64. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/role_binding.rb +1 -1
  65. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/secret.rb +1 -1
  66. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/service.rb +2 -2
  67. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/service_account.rb +1 -1
  68. data/lib/{kubernetes-deploy → krane}/kubernetes_resource/stateful_set.rb +2 -2
  69. data/lib/{kubernetes-deploy → krane}/label_selector.rb +1 -1
  70. data/lib/{kubernetes-deploy → krane}/oj.rb +0 -0
  71. data/lib/{kubernetes-deploy → krane}/options_helper.rb +2 -2
  72. data/lib/{kubernetes-deploy → krane}/remote_logs.rb +2 -2
  73. data/lib/krane/render_task.rb +149 -0
  74. data/lib/{kubernetes-deploy → krane}/renderer.rb +1 -1
  75. data/lib/{kubernetes-deploy → krane}/resource_cache.rb +10 -9
  76. data/lib/krane/resource_deployer.rb +265 -0
  77. data/lib/{kubernetes-deploy → krane}/resource_watcher.rb +24 -25
  78. data/lib/krane/restart_task.rb +228 -0
  79. data/lib/{kubernetes-deploy → krane}/rollout_conditions.rb +1 -1
  80. data/lib/krane/runner_task.rb +212 -0
  81. data/lib/{kubernetes-deploy → krane}/runner_task_config_validator.rb +1 -1
  82. data/lib/{kubernetes-deploy → krane}/statsd.rb +13 -27
  83. data/lib/krane/task_config.rb +22 -0
  84. data/lib/{kubernetes-deploy → krane}/task_config_validator.rb +1 -1
  85. data/lib/{kubernetes-deploy → krane}/template_sets.rb +5 -5
  86. data/lib/krane/version.rb +4 -0
  87. data/lib/kubernetes-deploy/deploy_task.rb +6 -608
  88. data/lib/kubernetes-deploy/errors.rb +1 -26
  89. data/lib/kubernetes-deploy/render_task.rb +5 -122
  90. data/lib/kubernetes-deploy/rescue_krane_exceptions.rb +18 -0
  91. data/lib/kubernetes-deploy/restart_task.rb +6 -198
  92. data/lib/kubernetes-deploy/runner_task.rb +6 -184
  93. metadata +96 -70
  94. data/lib/kubernetes-deploy/cluster_resource_discovery.rb +0 -34
  95. data/lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb +0 -54
  96. data/lib/kubernetes-deploy/task_config.rb +0 -16
  97. data/lib/kubernetes-deploy/version.rb +0 -4
@@ -2,13 +2,11 @@
2
2
 
3
3
  require 'active_support/duration'
4
4
 
5
- module KubernetesDeploy
6
- ##
5
+ module Krane
7
6
  # This class is a less strict extension of ActiveSupport::Duration::ISO8601Parser.
8
7
  # In addition to full ISO8601 durations, it can parse unprefixed ISO8601 time components (e.g. '1H').
9
8
  # It is also case-insensitive.
10
9
  # For example, this class considers the values "1H", "1h" and "PT1H" to be valid and equivalent.
11
-
12
10
  class DurationParser
13
11
  class ParsingError < ArgumentError; end
14
12
 
@@ -2,9 +2,9 @@
2
2
  require 'json'
3
3
  require 'base64'
4
4
  require 'open3'
5
- require 'kubernetes-deploy/kubectl'
5
+ require 'krane/kubectl'
6
6
 
7
- module KubernetesDeploy
7
+ module Krane
8
8
  class EjsonSecretError < FatalDeploymentError
9
9
  def initialize(msg)
10
10
  super("Generation of Kubernetes secrets from ejson failed: #{msg}")
@@ -16,19 +16,16 @@ module KubernetesDeploy
16
16
  EJSON_SECRET_KEY = "kubernetes_secrets"
17
17
  EJSON_SECRETS_FILE = "secrets.ejson"
18
18
  EJSON_KEYS_SECRET = "ejson-keys"
19
+ delegate :namespace, :context, :logger, to: :@task_config
19
20
 
20
- def initialize(namespace:, context:, ejson_keys_secret:, ejson_file:, logger:, statsd_tags:, selector: nil)
21
- @namespace = namespace
22
- @context = context
21
+ def initialize(task_config:, ejson_keys_secret:, ejson_file:, statsd_tags:, selector: nil)
23
22
  @ejson_keys_secret = ejson_keys_secret
24
23
  @ejson_file = ejson_file
25
- @logger = logger
26
24
  @statsd_tags = statsd_tags
27
25
  @selector = selector
26
+ @task_config = task_config
28
27
  @kubectl = Kubectl.new(
29
- namespace: @namespace,
30
- context: @context,
31
- logger: @logger,
28
+ task_config: @task_config,
32
29
  log_failure_by_default: false,
33
30
  output_is_sensitive_default: true # output may contain ejson secrets
34
31
  )
@@ -48,7 +45,7 @@ module KubernetesDeploy
48
45
  with_decrypted_ejson do |decrypted|
49
46
  secrets = decrypted[EJSON_SECRET_KEY]
50
47
  unless secrets.present?
51
- @logger.warn("#{EJSON_SECRETS_FILE} does not have key #{EJSON_SECRET_KEY}."\
48
+ logger.warn("#{EJSON_SECRETS_FILE} does not have key #{EJSON_SECRET_KEY}."\
52
49
  "No secrets will be created.")
53
50
  return []
54
51
  end
@@ -108,14 +105,14 @@ module KubernetesDeploy
108
105
  'metadata' => {
109
106
  "name" => secret_name,
110
107
  "labels" => labels,
111
- "namespace" => @namespace,
108
+ "namespace" => namespace,
112
109
  "annotations" => { EJSON_SECRET_ANNOTATION => "true" },
113
110
  },
114
111
  "data" => encoded_data,
115
112
  }
116
113
 
117
- KubernetesDeploy::Secret.build(
118
- namespace: @namespace, context: @context, logger: @logger, definition: secret, statsd_tags: @statsd_tags,
114
+ Krane::Secret.build(
115
+ namespace: namespace, context: context, logger: logger, definition: secret, statsd_tags: @statsd_tags,
119
116
  )
120
117
  end
121
118
 
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Krane
4
+ class FatalDeploymentError < StandardError; end
5
+ class FatalKubeAPIError < FatalDeploymentError; end
6
+ class KubectlError < StandardError; end
7
+ class TaskConfigurationError < FatalDeploymentError; end
8
+
9
+ class InvalidTemplateError < FatalDeploymentError
10
+ attr_reader :content
11
+ attr_accessor :filename
12
+ def initialize(err, filename: nil, content: nil)
13
+ @filename = filename
14
+ @content = content
15
+ super(err)
16
+ end
17
+ end
18
+
19
+ class DeploymentTimeoutError < FatalDeploymentError; end
20
+
21
+ class EjsonPrunableError < FatalDeploymentError
22
+ def initialize
23
+ super("Found #{KubernetesResource::LAST_APPLIED_ANNOTATION} annotation on " \
24
+ "#{EjsonSecretProvisioner::EJSON_KEYS_SECRET} secret. " \
25
+ "krane will not continue since it is extremely unlikely that this secret should be pruned.")
26
+ end
27
+ end
28
+ end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  require 'logger'
3
3
  require 'colorized_string'
4
- require 'kubernetes-deploy/deferred_summary_logging'
4
+ require 'krane/deferred_summary_logging'
5
5
 
6
- module KubernetesDeploy
6
+ module Krane
7
7
  class FormattedLogger < Logger
8
8
  include DeferredSummaryLogging
9
9
 
@@ -0,0 +1,210 @@
1
+ # frozen_string_literal: true
2
+ require 'tempfile'
3
+
4
+ require 'krane/common'
5
+ require 'krane/concurrency'
6
+ require 'krane/resource_cache'
7
+ require 'krane/kubectl'
8
+ require 'krane/kubeclient_builder'
9
+ require 'krane/cluster_resource_discovery'
10
+ require 'krane/template_sets'
11
+ require 'krane/resource_deployer'
12
+ require 'krane/kubernetes_resource'
13
+ require 'krane/global_deploy_task_config_validator'
14
+ require 'krane/concerns/template_reporting'
15
+
16
+ %w(
17
+ custom_resource
18
+ custom_resource_definition
19
+ ).each do |subresource|
20
+ require "krane/kubernetes_resource/#{subresource}"
21
+ end
22
+
23
+ module Krane
24
+ # Ship global resources to a context
25
+ class GlobalDeployTask
26
+ extend Krane::StatsD::MeasureMethods
27
+ include TemplateReporting
28
+ delegate :context, :logger, :global_kinds, to: :@task_config
29
+
30
+ # Initializes the deploy task
31
+ #
32
+ # @param context [String] Kubernetes context
33
+ # @param global_timeout [Integer] Timeout in seconds
34
+ # @param selector [Hash] Selector(s) parsed by Krane::LabelSelector
35
+ # @param template_paths [Array<String>] An array of template paths
36
+ def initialize(context:, global_timeout: nil, selector: nil, filenames: [], logger: nil)
37
+ template_paths = filenames.map { |path| File.expand_path(path) }
38
+
39
+ @task_config = TaskConfig.new(context, nil, logger)
40
+ @template_sets = TemplateSets.from_dirs_and_files(paths: template_paths,
41
+ logger: @task_config.logger)
42
+ @global_timeout = global_timeout
43
+ @selector = selector
44
+ end
45
+
46
+ # Runs the task, returning a boolean representing success or failure
47
+ #
48
+ # @return [Boolean]
49
+ def run(*args)
50
+ run!(*args)
51
+ true
52
+ rescue FatalDeploymentError
53
+ false
54
+ end
55
+
56
+ # Runs the task, raising exceptions in case of issues
57
+ #
58
+ # @param verify_result [Boolean] Wait for completion and verify success
59
+ # @param prune [Boolean] Enable deletion of resources that match the provided
60
+ # selector and do not appear in the template dir
61
+ #
62
+ # @return [nil]
63
+ def run!(verify_result: true, prune: true)
64
+ start = Time.now.utc
65
+ logger.reset
66
+
67
+ logger.phase_heading("Initializing deploy")
68
+ validate_configuration
69
+ resources = discover_resources
70
+ validate_resources(resources)
71
+
72
+ logger.phase_heading("Checking initial resource statuses")
73
+ check_initial_status(resources)
74
+
75
+ logger.phase_heading("Deploying all resources")
76
+ deploy!(resources, verify_result, prune)
77
+
78
+ StatsD.client.event("Deployment succeeded",
79
+ "Successfully deployed all resources to #{context}",
80
+ alert_type: "success", tags: statsd_tags + %w(status:success))
81
+ StatsD.client.distribution('all_resources.duration', StatsD.duration(start),
82
+ tags: statsd_tags << "status:success")
83
+ logger.print_summary(:success)
84
+ rescue Krane::DeploymentTimeoutError
85
+ logger.print_summary(:timed_out)
86
+ StatsD.client.event("Deployment timed out",
87
+ "One or more resources failed to deploy to #{context} in time",
88
+ alert_type: "error", tags: statsd_tags + %w(status:timeout))
89
+ StatsD.client.distribution('all_resources.duration', StatsD.duration(start),
90
+ tags: statsd_tags << "status:timeout")
91
+ raise
92
+ rescue Krane::FatalDeploymentError => error
93
+ logger.summary.add_action(error.message) if error.message != error.class.to_s
94
+ logger.print_summary(:failure)
95
+ StatsD.client.event("Deployment failed",
96
+ "One or more resources failed to deploy to #{context}",
97
+ alert_type: "error", tags: statsd_tags + %w(status:failed))
98
+ StatsD.client.distribution('all_resources.duration', StatsD.duration(start),
99
+ tags: statsd_tags << "status:failed")
100
+ raise
101
+ end
102
+
103
+ private
104
+
105
+ def deploy!(resources, verify_result, prune)
106
+ resource_deployer = ResourceDeployer.new(task_config: @task_config,
107
+ prune_whitelist: prune_whitelist, max_watch_seconds: @global_timeout,
108
+ selector: @selector, statsd_tags: statsd_tags)
109
+ resource_deployer.deploy!(resources, verify_result, prune)
110
+ end
111
+
112
+ def validate_configuration
113
+ task_config_validator = GlobalDeployTaskConfigValidator.new(@task_config,
114
+ kubectl, kubeclient_builder)
115
+ errors = []
116
+ errors += task_config_validator.errors
117
+ errors += @template_sets.validate
118
+ errors << "Selector is required" unless @selector.to_h.present?
119
+ unless errors.empty?
120
+ add_para_from_list(logger: logger, action: "Configuration invalid", enum: errors)
121
+ raise TaskConfigurationError
122
+ end
123
+
124
+ logger.info("Using resource selector #{@selector}")
125
+ logger.info("All required parameters and files are present")
126
+ end
127
+ measure_method(:validate_configuration)
128
+
129
+ def validate_resources(resources)
130
+ validate_globals(resources)
131
+
132
+ Concurrency.split_across_threads(resources) do |r|
133
+ r.validate_definition(@kubectl, selector: @selector)
134
+ end
135
+
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
+ failed_resources = resources.select(&:validation_failed?)
142
+ if failed_resources.present?
143
+ failed_resources.each do |r|
144
+ content = File.read(r.file_path) if File.file?(r.file_path) && !r.sensitive_template_content?
145
+ record_invalid_template(logger: logger, err: r.validation_error_msg,
146
+ filename: File.basename(r.file_path), content: content)
147
+ end
148
+ raise FatalDeploymentError, "Template validation failed"
149
+ end
150
+ end
151
+ measure_method(:validate_resources)
152
+
153
+ def validate_globals(resources)
154
+ return unless (namespaced = resources.reject(&:global?).presence)
155
+ namespaced_names = namespaced.map do |resource|
156
+ "#{resource.name} (#{resource.type}) in #{File.basename(resource.file_path)}"
157
+ end
158
+ namespaced_names = FormattedLogger.indent_four(namespaced_names.join("\n"))
159
+
160
+ logger.summary.add_paragraph(ColorizedString.new("Namespaced resources:\n#{namespaced_names}").yellow)
161
+ raise FatalDeploymentError, "This command cannot deploy namespaced resources"
162
+ end
163
+
164
+ def discover_resources
165
+ logger.info("Discovering resources:")
166
+ resources = []
167
+ crds_by_kind = cluster_resource_discoverer.crds.map { |crd| [crd.name, crd] }.to_h
168
+ @template_sets.with_resource_definitions do |r_def|
169
+ crd = crds_by_kind[r_def["kind"]]&.first
170
+ r = KubernetesResource.build(context: context, logger: logger, definition: r_def,
171
+ crd: crd, global_names: global_kinds, statsd_tags: statsd_tags)
172
+ resources << r
173
+ logger.info(" - #{r.id}")
174
+ end
175
+
176
+ resources.sort
177
+ rescue InvalidTemplateError => e
178
+ record_invalid_template(logger: logger, err: e.message, filename: e.filename, content: e.content)
179
+ raise FatalDeploymentError, "Failed to parse template"
180
+ end
181
+ measure_method(:discover_resources)
182
+
183
+ def cluster_resource_discoverer
184
+ @cluster_resource_discoverer ||= ClusterResourceDiscovery.new(task_config: @task_config)
185
+ end
186
+
187
+ def statsd_tags
188
+ %W(context:#{context})
189
+ end
190
+
191
+ def kubectl
192
+ @kubectl ||= Kubectl.new(task_config: @task_config, log_failure_by_default: true)
193
+ end
194
+
195
+ def kubeclient_builder
196
+ @kubeclient_builder ||= KubeclientBuilder.new
197
+ end
198
+
199
+ def prune_whitelist
200
+ cluster_resource_discoverer.prunable_resources(namespaced: false)
201
+ end
202
+
203
+ def check_initial_status(resources)
204
+ cache = ResourceCache.new(@task_config)
205
+ Concurrency.split_across_threads(resources) { |r| r.sync(cache) }
206
+ resources.each { |r| logger.info(r.pretty_status) }
207
+ end
208
+ measure_method(:check_initial_status, "initial_status.duration")
209
+ end
210
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'krane/task_config_validator'
4
+
5
+ module Krane
6
+ class GlobalDeployTaskConfigValidator < Krane::TaskConfigValidator
7
+ def initialize(*arguments)
8
+ super(*arguments)
9
+ @validations -= [:validate_namespace_exists]
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'kubeclient'
3
3
 
4
- module KubernetesDeploy
4
+ module Krane
5
5
  class KubeclientBuilder
6
6
  class ContextMissingError < FatalDeploymentError
7
7
  def initialize(context_name, kubeconfig)
@@ -55,9 +55,9 @@ module KubernetesDeploy
55
55
  )
56
56
  end
57
57
 
58
- def build_apps_v1beta1_kubeclient(context)
58
+ def build_apps_v1_kubeclient(context)
59
59
  build_kubeclient(
60
- api_version: "v1beta1",
60
+ api_version: "v1",
61
61
  context: context,
62
62
  endpoint_path: "/apis/apps"
63
63
  )
@@ -103,6 +103,14 @@ module KubernetesDeploy
103
103
  )
104
104
  end
105
105
 
106
+ def build_scheduling_v1beta1_kubeclient(context)
107
+ build_kubeclient(
108
+ api_version: "v1beta1",
109
+ context: context,
110
+ endpoint_path: "/apis/scheduling.k8s.io"
111
+ )
112
+ end
113
+
106
114
  def validate_config_files
107
115
  errors = []
108
116
  if @kubeconfig_files.empty?
@@ -137,8 +145,8 @@ module KubernetesDeploy
137
145
  ssl_options: kube_context.ssl_options,
138
146
  auth_options: kube_context.auth_options,
139
147
  timeouts: {
140
- open: KubernetesDeploy::Kubectl::DEFAULT_TIMEOUT,
141
- read: KubernetesDeploy::Kubectl::DEFAULT_TIMEOUT,
148
+ open: Krane::Kubectl::DEFAULT_TIMEOUT,
149
+ read: Krane::Kubectl::DEFAULT_TIMEOUT,
142
150
  }
143
151
  )
144
152
  client.discover
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  require 'open3'
3
3
 
4
- module KubernetesDeploy
4
+ module Krane
5
5
  class Kubectl
6
6
  ERROR_MATCHERS = {
7
7
  not_found: /NotFound/,
@@ -13,30 +13,28 @@ module KubernetesDeploy
13
13
 
14
14
  class ResourceNotFoundError < StandardError; end
15
15
 
16
- def initialize(namespace:, context:, logger:, log_failure_by_default:, default_timeout: DEFAULT_TIMEOUT,
16
+ delegate :namespace, :context, :logger, to: :@task_config
17
+
18
+ def initialize(task_config:, log_failure_by_default:, default_timeout: DEFAULT_TIMEOUT,
17
19
  output_is_sensitive_default: false)
18
- @namespace = namespace
19
- @context = context
20
- @logger = logger
20
+ @task_config = task_config
21
21
  @log_failure_by_default = log_failure_by_default
22
22
  @default_timeout = default_timeout
23
23
  @output_is_sensitive_default = output_is_sensitive_default
24
-
25
- raise ArgumentError, "namespace is required" if namespace.blank?
26
- raise ArgumentError, "context is required" if context.blank?
27
24
  end
28
25
 
29
26
  def run(*args, log_failure: nil, use_context: true, use_namespace: true, output: nil,
30
27
  raise_if_not_found: false, attempts: 1, output_is_sensitive: nil, retry_whitelist: nil)
28
+ raise ArgumentError, "namespace is required" if namespace.blank? && use_namespace
31
29
  log_failure = @log_failure_by_default if log_failure.nil?
32
30
  output_is_sensitive = @output_is_sensitive_default if output_is_sensitive.nil?
33
31
  cmd = build_command_from_options(args, use_namespace, use_context, output)
34
32
  out, err, st = nil
35
33
 
36
34
  (1..attempts).to_a.each do |current_attempt|
37
- @logger.debug("Running command (attempt #{current_attempt}): #{cmd.join(' ')}")
35
+ logger.debug("Running command (attempt #{current_attempt}): #{cmd.join(' ')}")
38
36
  out, err, st = Open3.capture3(*cmd)
39
- @logger.debug("Kubectl out: " + out.gsub(/\s+/, ' ')) unless output_is_sensitive
37
+ logger.debug("Kubectl out: " + out.gsub(/\s+/, ' ')) unless output_is_sensitive
40
38
 
41
39
  break if st.success?
42
40
  raise(ResourceNotFoundError, err) if err.match(ERROR_MATCHERS[:not_found]) && raise_if_not_found
@@ -49,12 +47,12 @@ module KubernetesDeploy
49
47
  else
50
48
  "The following command failed and cannot be retried"
51
49
  end
52
- @logger.warn("#{warning}: #{Shellwords.join(cmd)}")
53
- @logger.warn(err) unless output_is_sensitive
50
+ logger.warn("#{warning}: #{Shellwords.join(cmd)}")
51
+ logger.warn(err) unless output_is_sensitive
54
52
  else
55
- @logger.debug("Kubectl err: #{output_is_sensitive ? '<suppressed sensitive output>' : err}")
53
+ logger.debug("Kubectl err: #{output_is_sensitive ? '<suppressed sensitive output>' : err}")
56
54
  end
57
- StatsD.increment('kubectl.error', 1, tags: { context: @context, namespace: @namespace, cmd: cmd[1] })
55
+ StatsD.client.increment('kubectl.error', 1, tags: { context: context, namespace: namespace, cmd: cmd[1] })
58
56
 
59
57
  break unless retriable_err?(err, retry_whitelist) && current_attempt < attempts
60
58
  sleep(retry_delay(current_attempt))
@@ -93,8 +91,8 @@ module KubernetesDeploy
93
91
 
94
92
  def build_command_from_options(args, use_namespace, use_context, output)
95
93
  cmd = ["kubectl"] + args
96
- cmd.push("--namespace=#{@namespace}") if use_namespace
97
- cmd.push("--context=#{@context}") if use_context
94
+ cmd.push("--namespace=#{namespace}") if use_namespace
95
+ cmd.push("--context=#{context}") if use_context
98
96
  cmd.push("--output=#{output}") if output
99
97
  cmd.push("--request-timeout=#{@default_timeout}") if @default_timeout
100
98
  cmd