kubernetes-deploy 0.24.0 → 0.25.0

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: 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