kubernetes-deploy 0.26.3 → 0.26.4

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: dbfc9ddacc1877541f3c3d580be4e4f8dd2e4bbb59f6bbe52232ccc77985df1e
4
- data.tar.gz: 1b15550b1ba3eada31790eb1b5521be3f6a9837108e379f97eb9791548b1478a
3
+ metadata.gz: a537ded5641b9cb4b9813594e6da9d7ea06876721b26ce4785e48f0b933536cf
4
+ data.tar.gz: 8ee185bc26198298d5ed8c18aba4736a67bc46bd2526a72d71b74c988ac9e657
5
5
  SHA512:
6
- metadata.gz: 3f7fe96e6737b17e2d979ca6cbf6c21b8f2524a4029d0a1756c859029a003688ad0b51e6d9f55ac34190ca635a9b071a657b3654d3c7f2793627505e0cd5942c
7
- data.tar.gz: 438181bf2d2e491c0ffb5565a96f98b4d2d9352b6a88958fae80cfaedc1590a11cd0a768f1e35878a59f13ec5e57b18f3d8ce89459ba16be014d41c706d5e5b4
6
+ metadata.gz: 33f35406225136f90fa99219f5545d9052e2d323055f87d0d42c81640549cda6f18c0748d661ae4ba4c4ab7a1bcb0e7c717e6c8081ded664849befd2bf571d3c
7
+ data.tar.gz: 3d3e78256ad83ea456f9a92106c81b03c0212fc3d0940cdfbb990a45214f7250af1110c4f3231dde8b828ab4113b68c3ce5dfc28d675a001d19eea686b2bb9ca
@@ -8,3 +8,6 @@ Naming/FileName:
8
8
  Enabled: true
9
9
  Exclude:
10
10
  - lib/kubernetes-deploy.rb
11
+
12
+ Sorbet/ConstantsFromStrings:
13
+ Enabled: false
@@ -1,5 +1,16 @@
1
1
  ## next
2
2
 
3
+ ## 0.26.4
4
+
5
+ *Bug fixes*
6
+ - Adds several additional safeguards against the content of Secret resources being logged. [#474](https://github.com/Shopify/kubernetes-deploy/pull/474)
7
+
8
+ *Enhancements*
9
+ - Improves scalability by removing a check that caused recoverable registry problems to fail deploys. [#477](https://github.com/Shopify/kubernetes-deploy/pull/477)
10
+
11
+ *Other*
12
+ - Relaxes our dependency on the OJ gem. [#471](https://github.com/Shopify/kubernetes-deploy/pull/471)
13
+
3
14
  ## 0.26.3
4
15
 
5
16
  *Bug fixes*
@@ -113,8 +113,9 @@ If you work for Shopify, just run `dev up`, but otherwise:
113
113
 
114
114
  1. [Install kubectl version 1.10.0 or higher](https://kubernetes.io/docs/user-guide/prereqs/) and make sure it is in your path
115
115
  2. [Install minikube](https://kubernetes.io/docs/getting-started-guides/minikube/#installation) (required to run the test suite)
116
- 3. Check out the repo
117
- 4. Run `bin/setup` to install dependencies
116
+ 3. [Install any required minikube drivers](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md) (on OS X, you may need the [hyperkit driver](https://github.com/kubernetes/minikube/blob/master/docs/drivers.md#hyperkit-driver)
117
+ 4. Check out the repo
118
+ 5. Run `bin/setup` to install dependencies
118
119
 
119
120
  To install this gem onto your local machine, run `bundle exec rake install`.
120
121
 
@@ -144,14 +145,11 @@ To see the full-color output of a specific integration test, you can use `PRINT_
144
145
 
145
146
  ## Releasing a new version (Shopify employees)
146
147
 
147
- 1. Make sure all merged PRs are reflected in the changelog before creating the commit for the new version.
148
- 2. Update the version number in `version.rb` and commit that change with message "Version x.y.z". Don't push yet or you'll confuse Shipit.
149
- 3. Tag the version with `git tag vx.y.z -a -m "Version x.y.z"`
150
- 4. Push both your bump commit and its tag simultaneously with `git push origin master --follow-tags` (note that you can set `git config --global push.followTags true` to turn this flag on by default)
151
- 5. Use the [Shipit Stack](https://shipit.shopify.io/shopify/kubernetes-deploy/rubygems) to build the `.gem` file and upload to [rubygems.org](https://rubygems.org/gems/kubernetes-deploy).
152
-
153
- If you push your commit and the tag separately, Shipit usually fails with `You need to create the v0.7.9 tag first.`. To make it find your tag, go to `Settings` > `Resynchronize this stack` > `Clear git cache`.
154
-
148
+ 1. On a new branch, create a new heading in CHANGELOG.md for your version and move the entries from "Next" under it. Leave the "Next" heading in the file (this helps with the diff for rebases after the release).
149
+ 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.
150
+ 1. Update the version number in `version.rb`.
151
+ 1. Commit your changes with message "Version x.y.z" and open a PR.
152
+ 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/kubernetes-deploy).
155
153
 
156
154
  ## CI (External contributors)
157
155
 
data/README.md CHANGED
@@ -114,6 +114,8 @@ Refer to `kubernetes-deploy --help` for the authoritative set of options.
114
114
  resource to deploy.
115
115
  - `--selector`: Instructs kubernetes-deploy to only prune resources which match the specified label selector, such as `environment=staging`. If you use this option, all resource templates must specify matching labels. See [Sharing a namespace](#sharing-a-namespace) below.
116
116
 
117
+ > **NOTICE**: Deploy Secret resources at your own risk. Although we will fix any reported leak vectors with urgency, we cannot guarantee that sensitive information will never be logged.
118
+
117
119
  ### Sharing a namespace
118
120
 
119
121
  By default, kubernetes-deploy will prune any resources in the target namespace which have the `kubectl.kubernetes.io/last-applied-configuration` annotation and are not a result of the current deployment process, on the assumption that there is a one-to-one relationship between application deployment and namespace, and that a deployment provisions all relevant resources in the namespace.
data/dev.yml CHANGED
@@ -5,6 +5,10 @@ up:
5
5
  - bundler
6
6
  - homebrew:
7
7
  - Caskroom/cask/minikube
8
+ - custom:
9
+ name: Install the minikube fork of driver-hyperkit
10
+ met?: command -v docker-machine-driver-hyperkit
11
+ meet: curl -LO https://storage.googleapis.com/minikube/releases/latest/docker-machine-driver-hyperkit && sudo install -o root -g wheel -m 4755 docker-machine-driver-hyperkit /usr/local/bin/ && rm ./docker-machine-driver-hyperkit
8
12
  - custom:
9
13
  name: Minikube Cluster
10
14
  met?: test $(minikube status | grep Running | wc -l) -ge 2 && $(minikube status | grep -q 'Correctly Configured')
@@ -12,7 +12,7 @@ ARGV.options do |opts|
12
12
  parser = KubernetesDeploy::BindingsParser.new
13
13
  opts.on("--bindings=BINDINGS", "Expose additional variables to ERB templates " \
14
14
  "(format: k1=v1,k2=v2, JSON string or file (JSON or YAML) path prefixed by '@')") { |b| parser.add(b) }
15
- opts.on("--template-dir=DIR", "Set the template dir (default: config/deploy/$ENVIRONMENT)." ) do |d|
15
+ opts.on("--template-dir=DIR", "Set the template dir (default: config/deploy/$ENVIRONMENT).") do |d|
16
16
  template_dir = d
17
17
  end
18
18
  opts.parse!
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency("ejson", "~> 1.0")
30
30
  spec.add_dependency("colorize", "~> 0.8")
31
31
  spec.add_dependency("statsd-instrument", '~> 2.3', '>= 2.3.2')
32
- spec.add_dependency("oj", "~> 3.7")
32
+ spec.add_dependency("oj", "~> 3.0")
33
33
  spec.add_dependency("concurrent-ruby", "~> 1.1")
34
34
  spec.add_dependency("jsonpath", "~> 0.9.6")
35
35
 
@@ -47,7 +47,7 @@ module KubernetesDeploy
47
47
  level = :fatal
48
48
  end
49
49
 
50
- if actions_sentence = summary.actions_sentence.presence
50
+ if (actions_sentence = summary.actions_sentence.presence)
51
51
  public_send(level, actions_sentence)
52
52
  blank_line(level)
53
53
  end
@@ -252,7 +252,7 @@ module KubernetesDeploy
252
252
  return unless failed_resources.present?
253
253
 
254
254
  failed_resources.each do |r|
255
- content = File.read(r.file_path) if File.file?(r.file_path)
255
+ content = File.read(r.file_path) if File.file?(r.file_path) && !r.sensitive_template_content?
256
256
  record_invalid_template(err: r.validation_error_msg, filename: File.basename(r.file_path), content: content)
257
257
  end
258
258
  raise FatalDeploymentError, "Template validation failed"
@@ -319,7 +319,13 @@ module KubernetesDeploy
319
319
  def record_invalid_template(err:, filename:, content: nil)
320
320
  debug_msg = ColorizedString.new("Invalid template: #{filename}\n").red
321
321
  debug_msg += "> Error message:\n#{FormattedLogger.indent_four(err)}"
322
- debug_msg += "\n> Template content:\n#{FormattedLogger.indent_four(content)}" if content
322
+ if content
323
+ debug_msg += if content =~ /kind:\s*Secret/
324
+ "\n> Template content: Suppressed because it may contain a Secret"
325
+ else
326
+ "\n> Template content:\n#{FormattedLogger.indent_four(content)}"
327
+ end
328
+ end
323
329
  @logger.summary.add_paragraph(debug_msg)
324
330
  end
325
331
 
@@ -445,7 +451,7 @@ module KubernetesDeploy
445
451
  prune_whitelist.each { |type| command.push("--prune-whitelist=#{type}") }
446
452
  end
447
453
 
448
- output_is_sensitive = resources.any?(&:kubectl_output_is_sensitive?)
454
+ output_is_sensitive = resources.any?(&:sensitive_template_content?)
449
455
  out, err, st = kubectl.run(*command, log_failure: false, output_is_sensitive: output_is_sensitive)
450
456
 
451
457
  if st.success?
@@ -473,7 +479,7 @@ module KubernetesDeploy
473
479
 
474
480
  unidentified_errors = []
475
481
  filenames_with_sensitive_content = resources
476
- .select(&:kubectl_output_is_sensitive?)
482
+ .select(&:sensitive_template_content?)
477
483
  .map { |r| File.basename(r.file_path) }
478
484
 
479
485
  err.each_line do |line|
@@ -62,7 +62,11 @@ module KubernetesDeploy
62
62
 
63
63
  secrets.map do |secret_name, secret_spec|
64
64
  validate_secret_spec(secret_name, secret_spec)
65
- generate_secret_resource(secret_name, secret_spec["_type"], secret_spec["data"])
65
+ resource = generate_secret_resource(secret_name, secret_spec["_type"], secret_spec["data"])
66
+ unless resource.validate_definition(@kubectl)
67
+ raise EjsonSecretError, "Resulting resource Secret/#{secret_name} failed validation"
68
+ end
69
+ resource
66
70
  end
67
71
  end
68
72
  end
@@ -30,15 +30,13 @@ module KubernetesDeploy
30
30
 
31
31
  TIMEOUT_OVERRIDE_ANNOTATION = "kubernetes-deploy.shopify.io/timeout-override"
32
32
  LAST_APPLIED_ANNOTATION = "kubectl.kubernetes.io/last-applied-configuration"
33
- KUBECTL_OUTPUT_IS_SENSITIVE = false
33
+ SENSITIVE_TEMPLATE_CONTENT = false
34
34
 
35
35
  class << self
36
36
  def build(namespace:, context:, definition:, logger:, statsd_tags:, crd: nil)
37
+ validate_definition_essentials(definition)
37
38
  opts = { namespace: namespace, context: context, definition: definition, logger: logger,
38
39
  statsd_tags: statsd_tags }
39
- if definition["kind"].blank?
40
- raise InvalidTemplateError.new("Template missing 'Kind'", content: definition.to_yaml)
41
- end
42
40
  if (klass = class_for_kind(definition["kind"]))
43
41
  return klass.new(**opts)
44
42
  end
@@ -53,7 +51,7 @@ module KubernetesDeploy
53
51
 
54
52
  def class_for_kind(kind)
55
53
  if KubernetesDeploy.const_defined?(kind)
56
- KubernetesDeploy.const_get(kind) # rubocop:disable Sorbet/ConstantsFromStrings
54
+ KubernetesDeploy.const_get(kind)
57
55
  end
58
56
  rescue NameError
59
57
  nil
@@ -66,6 +64,24 @@ module KubernetesDeploy
66
64
  def kind
67
65
  name.demodulize
68
66
  end
67
+
68
+ private
69
+
70
+ def validate_definition_essentials(definition)
71
+ debug_content = <<~STRING
72
+ apiVersion: #{definition.fetch('apiVersion', '<missing>')}
73
+ kind: #{definition.fetch('kind', '<missing>')}
74
+ metadata: #{definition.fetch('metadata', {})}
75
+ <Template body suppressed because content sensitivity could not be determined.>
76
+ STRING
77
+ if definition["kind"].blank?
78
+ raise InvalidTemplateError.new("Template is missing required field 'kind'", content: debug_content)
79
+ end
80
+
81
+ if definition.dig('metadata', 'name').blank?
82
+ raise InvalidTemplateError.new("Template is missing required field 'metadata.name'", content: debug_content)
83
+ end
84
+ end
69
85
  end
70
86
 
71
87
  def timeout
@@ -86,12 +102,7 @@ module KubernetesDeploy
86
102
 
87
103
  def initialize(namespace:, context:, definition:, logger:, statsd_tags: [])
88
104
  # subclasses must also set these if they define their own initializer
89
- @name = definition.dig("metadata", "name")
90
- unless @name.present?
91
- logger.summary.add_paragraph("Rendered template content:\n#{definition.to_yaml}")
92
- raise FatalDeploymentError, "Template is missing required field metadata.name"
93
- end
94
-
105
+ @name = definition.dig("metadata", "name").to_s
95
106
  @optional_statsd_tags = statsd_tags
96
107
  @namespace = namespace
97
108
  @context = context
@@ -113,9 +124,9 @@ module KubernetesDeploy
113
124
  validate_timeout_annotation
114
125
 
115
126
  command = ["create", "-f", file_path, "--dry-run", "--output=name"]
116
- _, err, st = kubectl.run(*command, log_failure: false, output_is_sensitive: kubectl_output_is_sensitive?)
127
+ _, err, st = kubectl.run(*command, log_failure: false, output_is_sensitive: sensitive_template_content?)
117
128
  return true if st.success?
118
- if kubectl_output_is_sensitive?
129
+ if sensitive_template_content?
119
130
  @validation_errors << <<-EOS
120
131
  Validation for #{id} failed. Detailed information is unavailable as the raw error may contain sensitive data.
121
132
  EOS
@@ -322,8 +333,8 @@ module KubernetesDeploy
322
333
  end
323
334
  end
324
335
 
325
- def kubectl_output_is_sensitive?
326
- self.class::KUBECTL_OUTPUT_IS_SENSITIVE
336
+ def sensitive_template_content?
337
+ self.class::SENSITIVE_TEMPLATE_CONTENT
327
338
  end
328
339
 
329
340
  class Event
@@ -24,7 +24,7 @@ module KubernetesDeploy
24
24
  private
25
25
 
26
26
  def proxy_deployment_ready?
27
- return false unless status = @proxy_deployment["status"]
27
+ return false unless (status = @proxy_deployment["status"])
28
28
  # all cloudsql-proxy pods are running
29
29
  status.fetch("availableReplicas", -1) == status.fetch("replicas", 0)
30
30
  end
@@ -26,7 +26,7 @@ module KubernetesDeploy
26
26
  private
27
27
 
28
28
  def deployment_ready?
29
- return false unless status = @deployment["status"]
29
+ return false unless (status = @deployment["status"])
30
30
  status.fetch("availableReplicas", -1) == status.fetch("replicas", 0)
31
31
  end
32
32
 
@@ -146,7 +146,7 @@ module KubernetesDeploy
146
146
  end
147
147
 
148
148
  def ready?
149
- return false unless status_data = @instance_data["status"]
149
+ return false unless (status_data = @instance_data["status"])
150
150
  ready_condition = status_data.fetch("conditions", []).find { |condition| condition["type"] == "Ready" }
151
151
  ready_condition.present? && (ready_condition["status"] == "True")
152
152
  end
@@ -210,8 +210,7 @@ module KubernetesDeploy
210
210
  elsif limbo_reason == "CrashLoopBackOff"
211
211
  exit_code = @status.dig('lastState', 'terminated', 'exitCode')
212
212
  "Crashing repeatedly (exit #{exit_code}). See logs for more information."
213
- elsif %w(ImagePullBackOff ErrImagePull).include?(limbo_reason) &&
214
- limbo_message.match(/(?:not found)|(?:back-off)/i)
213
+ elsif limbo_reason == "ErrImagePull" && limbo_message.match(/not found/i)
215
214
  "Failed to pull image #{@image}. "\
216
215
  "Did you wait for it to be built and pushed to the registry before deploying?"
217
216
  elsif limbo_reason == "CreateContainerConfigError"
@@ -29,7 +29,7 @@ module KubernetesDeploy
29
29
  private
30
30
 
31
31
  def deployment_ready?
32
- return false unless status = @deployment["status"]
32
+ return false unless (status = @deployment["status"])
33
33
  # all redis pods are running
34
34
  status.fetch("availableReplicas", -1) == status.fetch("replicas", 0)
35
35
  end
@@ -2,7 +2,7 @@
2
2
  module KubernetesDeploy
3
3
  class Secret < KubernetesResource
4
4
  TIMEOUT = 30.seconds
5
- KUBECTL_OUTPUT_IS_SENSITIVE = true
5
+ SENSITIVE_TEMPLATE_CONTENT = true
6
6
 
7
7
  def status
8
8
  exists? ? "Available" : "Not Found"
@@ -51,7 +51,7 @@ module KubernetesDeploy
51
51
 
52
52
  def fetch_by_kind(kind)
53
53
  resource_class = KubernetesResource.class_for_kind(kind)
54
- output_is_sensitive = resource_class.nil? ? false : resource_class::KUBECTL_OUTPUT_IS_SENSITIVE
54
+ output_is_sensitive = resource_class.nil? ? false : resource_class::SENSITIVE_TEMPLATE_CONTENT
55
55
  raw_json, _, st = @kubectl.run("get", kind, "--chunk-size=0", attempts: 5, output: "json",
56
56
  output_is_sensitive: output_is_sensitive)
57
57
  raise KubectlError unless st.success?
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module KubernetesDeploy
3
- VERSION = "0.26.3"
3
+ VERSION = "0.26.4"
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.26.3
4
+ version: 0.26.4
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-04-22 00:00:00.000000000 Z
12
+ date: 2019-04-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -107,14 +107,14 @@ dependencies:
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '3.7'
110
+ version: '3.0'
111
111
  type: :runtime
112
112
  prerelease: false
113
113
  version_requirements: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '3.7'
117
+ version: '3.0'
118
118
  - !ruby/object:Gem::Dependency
119
119
  name: concurrent-ruby
120
120
  requirement: !ruby/object:Gem::Requirement