kubernetes-deploy 0.24.0 → 0.25.0

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: e8e6aa3c405f95c085c79e274915275e1d2a225803c4abf21813c11543bdfa8f
4
- data.tar.gz: ae315a1ce265d03205bd39972303d54d015a32210b43151c8f5d541377946143
3
+ metadata.gz: 2f35ca56d7b0ec844c55ff82157238409f2c21db3a9d83f870a73cc3305ee0b8
4
+ data.tar.gz: a9d9e566c21f699e9f066cd5bd505cd9c9ef9466a0180bb7563e9004211a8184
5
5
  SHA512:
6
- metadata.gz: 0111a5ab0e4959ff3e135311ed879f284cb9db70b5a510f0fa0e667385f35ab2c0c0323167f3ac30378d032e2daddeeab2f3afeacf6c36b5f0d3e7a5f5a21754
7
- data.tar.gz: 182840922280570f41aa6bff6918b24f8c0a34da631d01fe97c3b078a3ef376346118c0026ef3d4f6f1c96cc1d96e9789f483b00fb0ea35d19044129ea0830e2
6
+ metadata.gz: 2c3e2b2a8267d997e7d8859d6238d70f8a052bb423c8526f6e0f0490efe9de97da54ec1de0737f229f2682b04ba0b385ed2cc54caea3cc8276536026abb660cd
7
+ data.tar.gz: 98a21af1679cec75a00a6f81981b5a092888113c81f74d03eb967ba07da8b722db06c1394202a2de0fb943df5d784b3e95de15f653682e9c4555a4b595018a45
data/CHANGELOG.md CHANGED
@@ -1,4 +1,16 @@
1
- ## next
1
+ ## 0.25.0
2
+
3
+ *Features*
4
+ - Support timeout overrides on deployments ([#414](https://github.com/Shopify/kubernetes-deploy/pull/414))
5
+
6
+ *Bug fixes*
7
+ - Attempting to deploy from a directory that only contains `secrets.ejson` will no longer fail deploy ([#416](https://github.com/Shopify/kubernetes-deploy/pull/416))
8
+ - Remove the risk of sending decrypted EJSON secrets to output([#431](https://github.com/Shopify/kubernetes-deploy/pull/431))
9
+
10
+ *Other*
11
+ - Update kubeclient gem to 4.2.2. Note this replaces the `KubeclientBuilder::GoogleFriendlyConfig` class with `KubeclientBuilder::KubeConfig` ([#418](https://github.com/Shopify/kubernetes-deploy/pull/418)). This resolves [#396](https://github.com/Shopify/kubernetes-deploy/issues/396) and should allow us to support more authentication methods (e.g. `exec` for EKS).
12
+ - Invalid context when using `kubernetes-run` gives more descriptive error([#423](https://github.com/Shopify/kubernetes-deploy/pull/423))
13
+ - When resources are not found, instead of being `Unknown`, they are now labelled as `Not Found`([#427](https://github.com/Shopify/kubernetes-deploy/pull/427))
2
14
 
3
15
  ## 0.24.0
4
16
 
data/README.md CHANGED
@@ -227,7 +227,7 @@ This is a limitation of the current implementation.
227
227
  ### Customizing behaviour with annotations
228
228
  - `kubernetes-deploy.shopify.io/timeout-override`: Override the tool's hard timeout for one specific resource. Both full ISO8601 durations and the time portion of ISO8601 durations are valid. Value must be between 1 second and 24 hours.
229
229
  - _Example values_: 45s / 3m / 1h / PT0.25H
230
- - _Compatibility_: all resource types (Note: `Deployment` timeouts are based on `spec.progressDeadlineSeconds` if present, and that field has a default value as of the `apps/v1beta1` group version. Using this annotation will have no effect on `Deployment`s that time out with "Timeout reason: ProgressDeadlineExceeded".)
230
+ - _Compatibility_: all resource types
231
231
  - `kubernetes-deploy.shopify.io/required-rollout`: Modifies how much of the rollout needs to finish
232
232
  before the deployment is considered successful.
233
233
  - _Compatibility_: Deployment
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.required_ruby_version = '>= 2.3.0'
26
26
  spec.add_dependency("activesupport", ">= 5.0")
27
- spec.add_dependency("kubeclient", "~> 3.0")
27
+ spec.add_dependency("kubeclient", "~> 4.0")
28
28
  spec.add_dependency("googleauth", "~> 0.6.6") # https://github.com/google/google-auth-library-ruby/issues/153
29
29
  spec.add_dependency("ejson", "~> 1.0")
30
30
  spec.add_dependency("colorize", "~> 0.8")
@@ -324,8 +324,8 @@ module KubernetesDeploy
324
324
 
325
325
  if !File.directory?(@template_dir)
326
326
  errors << "Template directory `#{@template_dir}` doesn't exist"
327
- elsif Dir.entries(@template_dir).none? { |file| file =~ /\.ya?ml(\.erb)?$/ }
328
- errors << "`#{@template_dir}` doesn't contain valid templates (postfix .yml or .yml.erb)"
327
+ elsif Dir.entries(@template_dir).none? { |file| file =~ /(\.ya?ml(\.erb)?)$|(secrets\.ejson)$/ }
328
+ errors << "`#{@template_dir}` doesn't contain valid templates (secrets.ejson or postfix .yml, .yml.erb)"
329
329
  end
330
330
 
331
331
  if @namespace.blank?
@@ -74,7 +74,7 @@ module KubernetesDeploy
74
74
  prune_count += 1
75
75
  out, err, st = @kubectl.run("delete", "secret", secret_name)
76
76
  @logger.debug(out)
77
- raise EjsonSecretError, err unless st.success?
77
+ raise EjsonSecretError, "Failed to prune secrets" unless st.success?
78
78
  end
79
79
  @logger.summary.add_action("pruned #{prune_count} #{'secret'.pluralize(prune_count)}") if prune_count > 0
80
80
  end
@@ -121,7 +121,7 @@ module KubernetesDeploy
121
121
 
122
122
  out, err, st = @kubectl.run("apply", "--filename=#{file.path}")
123
123
  @logger.debug(out)
124
- raise EjsonSecretError, err unless st.success?
124
+ raise EjsonSecretError, "Failed to create or update secrets" unless st.success?
125
125
  ensure
126
126
  file&.unlink
127
127
  end
@@ -181,7 +181,7 @@ module KubernetesDeploy
181
181
  raise EjsonSecretError, out_err unless st.success?
182
182
  JSON.parse(out_err)
183
183
  rescue JSON::ParserError => e
184
- raise EjsonSecretError, "Failed to parse decrypted ejson:\n #{e}"
184
+ raise EjsonSecretError, "Failed to parse decrypted ejson"
185
185
  end
186
186
 
187
187
  def fetch_private_key_from_secret
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'googleauth'
4
+ module KubernetesDeploy
5
+ module KubeclientBuilder
6
+ class KubeConfig < Kubeclient::Config
7
+ def self.read(filename)
8
+ parsed = YAML.safe_load(File.read(filename), [Date, Time])
9
+ new(parsed, File.dirname(filename))
10
+ end
11
+
12
+ def fetch_user_auth_options(user)
13
+ if user.dig('auth-provider', 'name') == 'gcp'
14
+ { bearer_token: Kubeclient::GoogleApplicationDefaultCredentials.token }
15
+ else
16
+ super
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'kubeclient'
3
- require 'kubernetes-deploy/kubeclient_builder/google_friendly_config'
3
+ require 'kubernetes-deploy/kubeclient_builder/kube_config'
4
4
 
5
5
  module KubernetesDeploy
6
6
  module KubeclientBuilder
@@ -86,13 +86,12 @@ module KubernetesDeploy
86
86
 
87
87
  def _build_kubeclient(api_version:, context:, endpoint_path: nil)
88
88
  # Find a context defined in kube conf files that matches the input context by name
89
- friendly_configs = config_files.map { |f| GoogleFriendlyConfig.read(f) }
90
- config = friendly_configs.find { |c| c.contexts.include?(context) }
89
+ configs = config_files.map { |f| KubeConfig.read(f) }
90
+ config = configs.find { |c| c.contexts.include?(context) }
91
91
 
92
92
  raise ContextMissingError, context unless config
93
93
 
94
94
  kube_context = config.context(context)
95
-
96
95
  client = Kubeclient::Client.new(
97
96
  "#{kube_context.api_endpoint}#{endpoint_path}",
98
97
  api_version,
@@ -8,7 +8,7 @@ module KubernetesDeploy
8
8
  end
9
9
 
10
10
  def status
11
- exists? ? "Available" : "Unknown"
11
+ exists? ? "Available" : "Not Found"
12
12
  end
13
13
 
14
14
  def deploy_failed?
@@ -63,7 +63,9 @@ module KubernetesDeploy
63
63
  end
64
64
 
65
65
  def timeout_message
66
- reason_msg = if progress_condition.present?
66
+ reason_msg = if timeout_override
67
+ STANDARD_TIMEOUT_MESSAGE
68
+ elsif progress_condition.present?
67
69
  "Timeout reason: #{progress_condition['reason']}"
68
70
  else
69
71
  "Timeout reason: hard deadline for #{type}"
@@ -73,11 +75,19 @@ module KubernetesDeploy
73
75
  end
74
76
 
75
77
  def pretty_timeout_type
76
- progress_deadline.present? ? "progress deadline: #{progress_deadline}s" : super
78
+ if timeout_override
79
+ "timeout override: #{timeout_override}s"
80
+ elsif progress_deadline.present?
81
+ "progress deadline: #{progress_deadline}s"
82
+ else
83
+ super
84
+ end
77
85
  end
78
86
 
79
87
  def deploy_timed_out?
80
88
  return false if deploy_failed?
89
+ return super if timeout_override
90
+
81
91
  # Do not use the hard timeout if progress deadline is set
82
92
  progress_condition.present? ? deploy_failing_to_progress? : super
83
93
  end
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 30.seconds
5
5
 
6
6
  def status
7
- exists? ? "Created" : "Unknown"
7
+ exists? ? "Created" : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 5.minutes
5
5
 
6
6
  def status
7
- exists? ? @instance_data["status"]["phase"] : "Unknown"
7
+ exists? ? @instance_data["status"]["phase"] : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 10.seconds
5
5
 
6
6
  def status
7
- exists? ? "Available" : "Unknown"
7
+ exists? ? "Available" : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -2,7 +2,7 @@
2
2
  module KubernetesDeploy
3
3
  class PodTemplate < KubernetesResource
4
4
  def status
5
- exists? ? "Available" : "Unknown"
5
+ exists? ? "Available" : "Not Found"
6
6
  end
7
7
 
8
8
  def deploy_succeeded?
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 30.seconds
5
5
 
6
6
  def status
7
- exists? ? "In effect" : "Unknown"
7
+ exists? ? "In effect" : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 30.seconds
5
5
 
6
6
  def status
7
- exists? ? "Created" : "Unknown"
7
+ exists? ? "Created" : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 30.seconds
5
5
 
6
6
  def status
7
- exists? ? "Created" : "Unknown"
7
+ exists? ? "Created" : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -4,7 +4,7 @@ module KubernetesDeploy
4
4
  TIMEOUT = 30.seconds
5
5
 
6
6
  def status
7
- exists? ? "Created" : "Unknown"
7
+ exists? ? "Created" : "Not Found"
8
8
  end
9
9
 
10
10
  def deploy_succeeded?
@@ -179,7 +179,7 @@ module KubernetesDeploy
179
179
  end
180
180
 
181
181
  def status
182
- exists? ? "Exists" : "Unknown"
182
+ exists? ? "Exists" : "Not Found"
183
183
  end
184
184
 
185
185
  def type
@@ -112,12 +112,8 @@ module KubernetesDeploy
112
112
  def verify_namespace
113
113
  kubeclient.get_namespace(@namespace)
114
114
  @logger.info("Namespace #{@namespace} found in context #{@context}")
115
- rescue KubeException => error
116
- if error.error_code == 404
117
- raise NamespaceNotFoundError.new(@namespace, @context)
118
- else
119
- raise
120
- end
115
+ rescue Kubeclient::ResourceNotFoundError
116
+ raise NamespaceNotFoundError.new(@namespace, @context)
121
117
  end
122
118
 
123
119
  def patch_deployment_with_restart(record)
@@ -133,7 +129,7 @@ module KubernetesDeploy
133
129
  begin
134
130
  patch_deployment_with_restart(record)
135
131
  @logger.info("Triggered `#{record.metadata.name}` restart")
136
- rescue Kubeclient::ResourceNotFoundError, Kubeclient::HttpError => e
132
+ rescue Kubeclient::HttpError => e
137
133
  raise RestartAPIError.new(record.metadata.name, e.message)
138
134
  end
139
135
  end
@@ -144,12 +140,8 @@ module KubernetesDeploy
144
140
  record = nil
145
141
  begin
146
142
  record = v1beta1_kubeclient.get_deployment(name, @namespace)
147
- rescue KubeException => error
148
- if error.error_code == 404
149
- raise FatalRestartError, "Deployment `#{name}` not found in namespace `#{@namespace}`"
150
- else
151
- raise
152
- end
143
+ rescue Kubeclient::ResourceNotFoundError
144
+ raise FatalRestartError, "Deployment `#{name}` not found in namespace `#{@namespace}`"
153
145
  end
154
146
  record
155
147
  end
@@ -64,7 +64,7 @@ module KubernetesDeploy
64
64
  kubeclient.create_pod(pod.to_kubeclient_resource)
65
65
  @pod_name = pod.name
66
66
  @logger.info("Pod creation succeeded")
67
- rescue KubeException => e
67
+ rescue Kubeclient::HttpError => e
68
68
  msg = "Failed to create pod: #{e.class.name}: #{e.message}"
69
69
  @logger.summary.add_paragraph(msg)
70
70
  raise FatalDeploymentError, msg
@@ -122,9 +122,13 @@ module KubernetesDeploy
122
122
  begin
123
123
  kubeclient.get_namespace(@namespace) if @namespace.present?
124
124
  @logger.info("Using namespace '#{@namespace}' in context '#{@context}'")
125
- rescue KubeException => e
126
- msg = e.error_code == 404 ? "Namespace was not found" : "Could not connect to kubernetes cluster"
127
- errors << msg
125
+
126
+ rescue Kubeclient::ResourceNotFoundError
127
+ errors << "Namespace was not found"
128
+ rescue Kubeclient::HttpError
129
+ errors << "Could not connect to kubernetes cluster"
130
+ rescue KubernetesDeploy::KubeclientBuilder::ContextMissingError
131
+ errors << "Could not connect to kubernetes cluster - context invalid"
128
132
  end
129
133
 
130
134
  unless errors.empty?
@@ -140,16 +144,13 @@ module KubernetesDeploy
140
144
 
141
145
  def get_template(template_name)
142
146
  pod_template = kubeclient.get_pod_template(template_name, @namespace)
143
-
144
147
  pod_template.template
145
- rescue KubeException => error
146
- if error.error_code == 404
147
- msg = "Pod template `#{template_name}` not found in namespace `#{@namespace}`, context `#{@context}`"
148
- @logger.summary.add_paragraph(msg)
149
- raise TaskTemplateMissingError, msg
150
- else
151
- raise FatalKubeAPIError, "Error retrieving pod template: #{error.class.name}: #{error.message}"
152
- end
148
+ rescue Kubeclient::ResourceNotFoundError
149
+ msg = "Pod template `#{template_name}` not found in namespace `#{@namespace}`, context `#{@context}`"
150
+ @logger.summary.add_paragraph(msg)
151
+ raise TaskTemplateMissingError, msg
152
+ rescue Kubeclient::HttpError => error
153
+ raise FatalKubeAPIError, "Error retrieving pod template: #{error.class.name}: #{error.message}"
153
154
  end
154
155
 
155
156
  def build_pod_definition(base_template)
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
- VERSION = "0.24.0"
3
+ VERSION = "0.25.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kubernetes-deploy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.24.0
4
+ version: 0.25.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katrina Verey
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2019-01-23 00:00:00.000000000 Z
12
+ date: 2019-02-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -31,14 +31,14 @@ dependencies:
31
31
  requirements:
32
32
  - - "~>"
33
33
  - !ruby/object:Gem::Version
34
- version: '3.0'
34
+ version: '4.0'
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - "~>"
40
40
  - !ruby/object:Gem::Version
41
- version: '3.0'
41
+ version: '4.0'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: googleauth
44
44
  requirement: !ruby/object:Gem::Requirement
@@ -272,7 +272,7 @@ files:
272
272
  - lib/kubernetes-deploy/errors.rb
273
273
  - lib/kubernetes-deploy/formatted_logger.rb
274
274
  - lib/kubernetes-deploy/kubeclient_builder.rb
275
- - lib/kubernetes-deploy/kubeclient_builder/google_friendly_config.rb
275
+ - lib/kubernetes-deploy/kubeclient_builder/kube_config.rb
276
276
  - lib/kubernetes-deploy/kubectl.rb
277
277
  - lib/kubernetes-deploy/kubernetes_resource.rb
278
278
  - lib/kubernetes-deploy/kubernetes_resource/cloudsql.rb
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'googleauth'
4
- module KubernetesDeploy
5
- module KubeclientBuilder
6
- class GoogleFriendlyConfig < Kubeclient::Config
7
- def fetch_user_auth_options(user)
8
- if user.dig('auth-provider', 'name') == 'gcp'
9
- { bearer_token: new_token }
10
- else
11
- super
12
- end
13
- end
14
-
15
- def self.read(filename)
16
- new(YAML.safe_load(File.read(filename), [Time]), File.dirname(filename))
17
- end
18
-
19
- def new_token
20
- scopes = ['https://www.googleapis.com/auth/cloud-platform']
21
- authorization = Google::Auth.get_application_default(scopes)
22
-
23
- authorization.apply({})
24
-
25
- authorization.access_token
26
-
27
- rescue Signet::AuthorizationError => e
28
- err_message = json_error_message(e.response.body) || e.message
29
- raise KubeException.new(e.response.status, err_message, e.response.body)
30
- end
31
-
32
- private
33
-
34
- def json_error_message(body)
35
- err = JSON.parse(body || '') || {}
36
- err['message']
37
- rescue JSON::ParserError
38
- nil
39
- end
40
- end
41
- end
42
- end