krane 2.1.6 → 2.2.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: d496112ecd7ff05ab81ab6cad622632a7d169b38d1ad0340c5dc2088aa061677
4
- data.tar.gz: 16c47fa820e244a7d42080e3fdf81065eda9167dda898e12412ab5c72d5bd4be
3
+ metadata.gz: 46134385171fa351edf5611c3ecd248c299339d2134cf4ff64f6227d68470b96
4
+ data.tar.gz: c30ad94176b02c7c402fa50ec5b73aa0c2c289a64401eefcc95494feb64354b3
5
5
  SHA512:
6
- metadata.gz: 97eeceba4a6f132ec19bd40bfdcf9fb7ad94f0a791f784d6b3ec0e01eb78eddca2e6d5e6809fc2feb15d6f5a4aba9c7eee295e4660998d595d267700566d7ad5
7
- data.tar.gz: 1c364bd503c83eb902e548d68d06aed5235e1ef33c8e72e3fe2fd90871da76d8580be288b822ac768fb75a1dd7ed0c1c801634a0d7068a18d74b8263670b0f7c
6
+ metadata.gz: '0792b68c262c3c310b7c51801c206f5422a2b65832eda020a2c0d58c804bb14adc755f9db5ff335b084d982303fbd376b846c5ff6af46d489530ffe6cfe2ae4b'
7
+ data.tar.gz: 185a56c603ccbc5dfcdbd7d247d6b7bec512da59a69f2a3e873f41243e79a70f3281596d5346829a4cd26ac1453c8d458cf45f41f498d8e61f1c5cf4e77a13ad
data/.rubocop.yml CHANGED
@@ -1,5 +1,5 @@
1
- inherit_from:
2
- - http://shopify.github.io/ruby-style-guide/rubocop.yml
1
+ inherit_gem:
2
+ rubocop-shopify: rubocop.yml
3
3
 
4
4
  AllCops:
5
5
  TargetRubyVersion: 2.4
@@ -8,6 +8,14 @@ steps:
8
8
  run:
9
9
  - bundle: ~
10
10
  - bundle exec rubocop
11
+ - label: 'Run Test Suite (:kubernetes: 1.21-latest :ruby: 3.0)'
12
+ command: bin/ci
13
+ agents:
14
+ queue: k8s-ci
15
+ env:
16
+ LOGGING_LEVEL: "4"
17
+ KUBERNETES_VERSION: v1.21-latest
18
+ RUBY_VERSION: "3.0"
11
19
  - label: 'Run Test Suite (:kubernetes: 1.20-latest :ruby: 3.0)'
12
20
  command: bin/ci
13
21
  agents:
@@ -38,17 +46,3 @@ steps:
38
46
  env:
39
47
  LOGGING_LEVEL: "4"
40
48
  KUBERNETES_VERSION: v1.17-latest
41
- - label: 'Run Test Suite (:kubernetes: 1.16-latest)'
42
- command: bin/ci
43
- agents:
44
- queue: k8s-ci
45
- env:
46
- LOGGING_LEVEL: "4"
47
- KUBERNETES_VERSION: v1.16-latest
48
- - label: 'Run Test Suite (:kubernetes: 1.15-latest)'
49
- command: bin/ci
50
- agents:
51
- queue: k8s-ci
52
- env:
53
- LOGGING_LEVEL: "4"
54
- KUBERNETES_VERSION: v1.15-latest
data/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  ## next
2
2
 
3
+ ## 2.2.0
4
+
5
+ *Enhancements*
6
+
7
+ - Add a new option `--selector-as-filter` to command `krane deploy` and `krane global-deploy` [#831](https://github.com/Shopify/krane/pull/831)
8
+
9
+ ## 2.1.10
10
+
11
+ *Bug Fixes*
12
+
13
+ - Don't gather prunable resources by calling uniq only on `kind`: use `group` as well. Otherwise certain resources may not be added to the prune whitelist if the same kind exists across multiple groups [#825](https://github.com/Shopify/krane/pull/825)
14
+ - Fix resource discovery failures when API paths are not located at the root of the API server (this occurs, for example, when using Rancher proxy) [#827](https://github.com/Shopify/krane/pull/827)
15
+
16
+ *Other*
17
+
18
+ - Fix ERB deprecation of positional arguments [#828](https://github.com/Shopify/krane/pull/828)
19
+
20
+ ## 2.1.9
21
+
22
+ *Other*
23
+
24
+ - Don't package screenshots in the built gem to reduce size [#817](https://github.com/Shopify/krane/pull/817)
25
+
26
+ ## 2.1.8
27
+
28
+ *Other*
29
+
30
+ - Change `statsd-instrument` dependency constraint to `< 4` [#815](https://github.com/Shopify/krane/pull/815)
31
+
32
+ ## 2.1.7
33
+
34
+ *Enhancements*
35
+ - ENV["KRANE_LOG_LINE_LIMIT"] allows the number of container logs printed for failures to be configurable from the 25 line default [#803](https://github.com/Shopify/krane/pull/803).
36
+
37
+ *Other*
38
+ - Remove the overly tight timeout on cluster resource discovery, which was causing too many timeouts in high latency environments [#813](https://github.com/Shopify/krane/pull/813)
39
+
40
+ ## 2.1.6
41
+
3
42
  *Enhancements*
4
43
  - Remove the need for a hard coded GVK overide list via improvements to cluster discovery [#778](https://github.com/Shopify/krane/pull/778)
5
44
 
data/README.md CHANGED
@@ -117,6 +117,7 @@ Refer to `krane help` for the authoritative set of options.
117
117
  - `--global-timeout=duration`: Raise a timeout error if it takes longer than _duration_ for any
118
118
  resource to deploy.
119
119
  - `--selector`: Instructs krane 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.
120
+ - `--selector-as-filter`: Instructs krane to only deploy resources that are filtered by the specified labels in `--selector`. The deploy will not fail if not all resources match the labels. This is useful if you only want to deploy a subset of resources within a given YAML file. See [Sharing a namespace](#sharing-a-namespace) below.
120
121
  - `--no-verify-result`: Skip verification that workloads correctly deployed.
121
122
  - `--protected-namespaces=default kube-system kube-public`: Fail validation if a deploy is targeted at a protected namespace.
122
123
  - `--verbose-log-prefix`: Add [context][namespace] to the log prefix
@@ -132,6 +133,8 @@ If you need to, you may specify `--no-prune` to disable all pruning behaviour, b
132
133
 
133
134
  If you need to share a namespace with resources which are managed by other tools or indeed other krane deployments, you can supply the `--selector` option, such that only resources with labels matching the selector are considered for pruning.
134
135
 
136
+ If you need to share a namespace with different set of resources using the same YAML file, you can supply the `--selector` and `--selector-as-filter` options, such that only the resources that match with the labels will be deployed. In each run of deploy, you can use different labels in `--selector` to deploy a different set of resources. Only the deployed resources in each run are considered for pruning.
137
+
135
138
  ### Using templates
136
139
 
137
140
  All templates must be YAML formatted.
@@ -441,6 +444,7 @@ Refer to `krane global-deploy help` for the authoritative set of options.
441
444
  - `--filenames / -f [PATHS]`: Accepts a list of directories and/or filenames to specify the set of directories/files that will be deployed. Use `-` to specify STDIN.
442
445
  - `--no-prune`: Skips pruning of resources that are no longer in your Kubernetes template set. Not recommended, as it allows your namespace to accumulate cruft that is not reflected in your deploy directory.
443
446
  - `--selector`: Instructs krane to only prune resources which match the specified label selector, such as `environment=staging`. By using this option, all resource templates must specify matching labels. See [Sharing a namespace](#sharing-a-namespace) below.
447
+ - `--selector-as-filter`: Instructs krane to only deploy resources that are filtered by the specified labels in `--selector`. The deploy will not fail if not all resources match the labels. This is useful if you only want to deploy a subset of resources within a given YAML file. See [Sharing a namespace](#sharing-a-namespace) below.
444
448
  - `--global-timeout=duration`: Raise a timeout error if it takes longer than _duration_ for any
445
449
  resource to deploy.
446
450
  - `--no-verify-result`: Skip verification that resources correctly deployed.
data/dev.yml CHANGED
@@ -4,7 +4,7 @@ up:
4
4
  - ruby: 2.6.6 # Matches gemspec
5
5
  - bundler
6
6
  - homebrew:
7
- - homebrew/cask/minikube
7
+ - minikube
8
8
  - hyperkit
9
9
  - custom:
10
10
  name: Install the minikube fork of driver-hyperkit
@@ -13,7 +13,7 @@ up:
13
13
  - custom:
14
14
  name: Minikube Cluster
15
15
  met?: test $(minikube status | grep Running | wc -l) -ge 2 && $(minikube status | grep -q 'Configured')
16
- meet: minikube start --kubernetes-version=v1.15.12 --vm-driver=hyperkit
16
+ meet: minikube start --kubernetes-version=v1.18.18 --vm-driver=hyperkit
17
17
  down: minikube stop
18
18
  commands:
19
19
  reset-minikube: minikube delete && rm -rf ~/.minikube
data/krane.gemspec CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |spec|
17
17
  spec.license = "MIT"
18
18
 
19
19
  spec.files = %x(git ls-files -z).split("\x0").reject do |f|
20
- f.match(%r{^(test|spec|features)/})
20
+ f.match(%r{^(test|spec|features|screenshots)/})
21
21
  end
22
22
  spec.bindir = "exe"
23
23
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
31
31
  spec.add_dependency("googleauth", "~> 0.8")
32
32
  spec.add_dependency("ejson", "~> 1.0")
33
33
  spec.add_dependency("colorize", "~> 0.8")
34
- spec.add_dependency("statsd-instrument", ['>= 2.8', "< 3.1"])
34
+ spec.add_dependency("statsd-instrument", ['>= 2.8', "< 4"])
35
35
  spec.add_dependency("oj", "~> 3.0")
36
36
  spec.add_dependency("concurrent-ruby", "~> 1.1")
37
37
  spec.add_dependency("jsonpath", "~> 0.9.6")
@@ -57,5 +57,6 @@ Gem::Specification.new do |spec|
57
57
  spec.add_development_dependency("ruby-prof")
58
58
  spec.add_development_dependency("ruby-prof-flamegraph")
59
59
  spec.add_development_dependency("rubocop", "~> 0.89.1")
60
+ spec.add_development_dependency("rubocop-shopify", "~> 1.0.5")
60
61
  spec.add_development_dependency("simplecov")
61
62
  end
@@ -25,6 +25,10 @@ module Krane
25
25
  default: true },
26
26
  "selector" => { type: :string, banner: "'label=value'",
27
27
  desc: "Select workloads by selector(s)" },
28
+ "selector-as-filter" => { type: :boolean,
29
+ desc: "Use --selector as a label filter to deploy only a subset "\
30
+ "of the provided resources",
31
+ default: false },
28
32
  "verbose-log-prefix" => { type: :boolean, desc: "Add [context][namespace] to the log prefix",
29
33
  default: false },
30
34
  "verify-result" => { type: :boolean, default: true,
@@ -37,6 +41,11 @@ module Krane
37
41
  require 'krane/label_selector'
38
42
 
39
43
  selector = ::Krane::LabelSelector.parse(options[:selector]) if options[:selector]
44
+ selector_as_filter = options['selector-as-filter']
45
+
46
+ if selector_as_filter && !selector
47
+ raise(Thor::RequiredArgumentMissingError, '--selector must be set when --selector-as-filter is set')
48
+ end
40
49
 
41
50
  logger = ::Krane::FormattedLogger.build(namespace, context,
42
51
  verbose_prefix: options['verbose-log-prefix'])
@@ -60,6 +69,7 @@ module Krane
60
69
  logger: logger,
61
70
  global_timeout: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
62
71
  selector: selector,
72
+ selector_as_filter: selector_as_filter,
63
73
  protected_namespaces: protected_namespaces,
64
74
  )
65
75
 
@@ -16,6 +16,10 @@ module Krane
16
16
  desc: "Verify workloads correctly deployed" },
17
17
  "selector" => { type: :string, banner: "'label=value'", required: true,
18
18
  desc: "Select workloads owned by selector(s)" },
19
+ "selector-as-filter" => { type: :boolean,
20
+ desc: "Use --selector as a label filter to deploy only a subset "\
21
+ "of the provided resources",
22
+ default: false },
19
23
  "prune" => { type: :boolean, desc: "Enable deletion of resources that match"\
20
24
  " the provided selector and do not appear in the provided templates",
21
25
  default: true },
@@ -28,6 +32,11 @@ module Krane
28
32
  require 'krane/duration_parser'
29
33
 
30
34
  selector = ::Krane::LabelSelector.parse(options[:selector])
35
+ selector_as_filter = options['selector-as-filter']
36
+
37
+ if selector_as_filter && !selector
38
+ raise(Thor::RequiredArgumentMissingError, '--selector must be set when --selector-as-filter is set')
39
+ end
31
40
 
32
41
  filenames = options[:filenames].dup
33
42
  filenames << "-" if options[:stdin]
@@ -41,6 +50,7 @@ module Krane
41
50
  filenames: paths,
42
51
  global_timeout: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
43
52
  selector: selector,
53
+ selector_as_filter: selector_as_filter,
44
54
  )
45
55
 
46
56
  deploy.run!(
@@ -34,7 +34,7 @@ module Krane
34
34
  end
35
35
  responses.flat_map do |path, resources|
36
36
  resources.map { |r| resource_hash(path, namespaced, r) }
37
- end.compact.uniq { |r| r["kind"] }
37
+ end.compact.uniq { |r| "#{r['apigroup']}/#{r['kind']}" }
38
38
  end
39
39
 
40
40
  def fetch_mutating_webhook_configurations
@@ -52,9 +52,20 @@ module Krane
52
52
 
53
53
  private
54
54
 
55
+ # During discovery, the api paths may not actually be at the root, so we must programatically find it.
56
+ def base_api_path
57
+ @base_api_path ||= begin
58
+ raw_response, err, st = kubectl.run("config", "view", "--minify", "--output",
59
+ "jsonpath={.clusters[*].cluster.server}", attempts: 5, use_namespace: false)
60
+ raise FatalKubeAPIError, "Error retrieving cluster url: #{err}" unless st.success?
61
+
62
+ URI(raw_response).path.blank? ? "/" : URI(raw_response).path
63
+ end
64
+ end
65
+
55
66
  def api_paths
56
67
  @api_path_cache["/"] ||= begin
57
- raw_json, err, st = kubectl.run("get", "--raw", "/", attempts: 5, use_namespace: false)
68
+ raw_json, err, st = kubectl.run("get", "--raw", base_api_path, attempts: 5, use_namespace: false)
58
69
  paths = if st.success?
59
70
  JSON.parse(raw_json)["paths"]
60
71
  else
@@ -101,7 +112,7 @@ module Krane
101
112
  end
102
113
 
103
114
  def kubectl
104
- @kubectl ||= Kubectl.new(task_config: @task_config, log_failure_by_default: true, default_timeout: 1)
115
+ @kubectl ||= Kubectl.new(task_config: @task_config, log_failure_by_default: true)
105
116
  end
106
117
  end
107
118
  end
@@ -3,7 +3,7 @@ module Krane
3
3
  class ContainerLogs
4
4
  attr_reader :lines, :container_name
5
5
 
6
- DEFAULT_LINE_LIMIT = 25
6
+ DEFAULT_LINE_LIMIT = Integer(ENV.fetch('KRANE_LOG_LINE_LIMIT', 25))
7
7
 
8
8
  def initialize(parent_id:, container_name:, namespace:, context:, logger:)
9
9
  @parent_id = parent_id
@@ -100,12 +100,13 @@ module Krane
100
100
  # @param bindings [Hash] Bindings parsed by Krane::BindingsParser
101
101
  # @param global_timeout [Integer] Timeout in seconds
102
102
  # @param selector [Hash] Selector(s) parsed by Krane::LabelSelector
103
+ # @param selector_as_filter [Boolean] Allow selecting a subset of Kubernetes resource templates to deploy
103
104
  # @param filenames [Array<String>] An array of filenames and/or directories containing templates (*required*)
104
105
  # @param protected_namespaces [Array<String>] Array of protected Kubernetes namespaces (defaults
105
106
  # to Krane::DeployTask::PROTECTED_NAMESPACES)
106
107
  # @param render_erb [Boolean] Enable ERB rendering
107
108
  def initialize(namespace:, context:, current_sha: nil, logger: nil, kubectl_instance: nil, bindings: {},
108
- global_timeout: nil, selector: nil, filenames: [], protected_namespaces: nil,
109
+ global_timeout: nil, selector: nil, selector_as_filter: false, filenames: [], protected_namespaces: nil,
109
110
  render_erb: false, kubeconfig: nil)
110
111
  @logger = logger || Krane::FormattedLogger.build(namespace, context)
111
112
  @template_sets = TemplateSets.from_dirs_and_files(paths: filenames, logger: @logger, render_erb: render_erb)
@@ -118,6 +119,7 @@ module Krane
118
119
  @kubectl = kubectl_instance
119
120
  @global_timeout = global_timeout
120
121
  @selector = selector
122
+ @selector_as_filter = selector_as_filter
121
123
  @protected_namespaces = protected_namespaces || PROTECTED_NAMESPACES
122
124
  @render_erb = render_erb
123
125
  end
@@ -273,6 +275,7 @@ module Krane
273
275
 
274
276
  confirm_ejson_keys_not_prunable if prune
275
277
  @logger.info("Using resource selector #{@selector}") if @selector
278
+ @logger.info("Only deploying resources filtered by labels in selector") if @selector && @selector_as_filter
276
279
  @namespace_tags |= tags_from_namespace_labels
277
280
  @logger.info("All required parameters and files are present")
278
281
  end
@@ -295,6 +298,7 @@ module Krane
295
298
  batchable_resources, individuals = partition_dry_run_resources(resources.dup)
296
299
  batch_dry_run_success = kubectl.server_dry_run_enabled? && validate_dry_run(batchable_resources)
297
300
  individuals += batchable_resources unless batch_dry_run_success
301
+ resources.select! { |r| r.selected?(@selector) } if @selector_as_filter
298
302
  Krane::Concurrency.split_across_threads(resources) do |r|
299
303
  r.validate_definition(kubectl: kubectl, selector: @selector, dry_run: individuals.include?(r))
300
304
  end
@@ -33,8 +33,10 @@ module Krane
33
33
  # @param context [String] Kubernetes context (*required*)
34
34
  # @param global_timeout [Integer] Timeout in seconds
35
35
  # @param selector [Hash] Selector(s) parsed by Krane::LabelSelector (*required*)
36
+ # @param selector_as_filter [Boolean] Allow selecting a subset of Kubernetes resource templates to deploy
36
37
  # @param filenames [Array<String>] An array of filenames and/or directories containing templates (*required*)
37
- def initialize(context:, global_timeout: nil, selector: nil, filenames: [], logger: nil, kubeconfig: nil)
38
+ def initialize(context:, global_timeout: nil, selector: nil, selector_as_filter: false,
39
+ filenames: [], logger: nil, kubeconfig: nil)
38
40
  template_paths = filenames.map { |path| File.expand_path(path) }
39
41
 
40
42
  @task_config = TaskConfig.new(context, nil, logger, kubeconfig)
@@ -42,6 +44,7 @@ module Krane
42
44
  logger: @task_config.logger, render_erb: false)
43
45
  @global_timeout = global_timeout
44
46
  @selector = selector
47
+ @selector_as_filter = selector_as_filter
45
48
  end
46
49
 
47
50
  # Runs the task, returning a boolean representing success or failure
@@ -130,6 +133,7 @@ module Krane
130
133
  def validate_resources(resources)
131
134
  validate_globals(resources)
132
135
 
136
+ resources.select! { |r| r.selected?(@selector) } if @selector_as_filter
133
137
  Concurrency.split_across_threads(resources) do |r|
134
138
  r.validate_definition(kubectl: @kubectl, selector: @selector)
135
139
  end
@@ -499,6 +499,10 @@ module Krane
499
499
  @global || self.class::GLOBAL
500
500
  end
501
501
 
502
+ def selected?(selector)
503
+ selector.nil? || selector.to_h <= labels
504
+ end
505
+
502
506
  private
503
507
 
504
508
  def validate_timeout_annotation
@@ -39,7 +39,7 @@ module Krane
39
39
  erb_binding = TemplateContext.new(self).template_binding
40
40
  bind_template_variables(erb_binding, template_variables)
41
41
 
42
- ERB.new(raw_template, nil, '-').result(erb_binding)
42
+ ERB.new(raw_template, trim_mode: '-').result(erb_binding)
43
43
  rescue InvalidPartialError => err
44
44
  err.parents = err.parents.dup.unshift(filename)
45
45
  err.filename = "#{err.filename} (partial included from: #{err.parents.join(' -> ')})"
@@ -56,7 +56,7 @@ module Krane
56
56
 
57
57
  partial_path = find_partial(partial)
58
58
  template = File.read(partial_path)
59
- expanded_template = ERB.new(template, nil, '-').result(erb_binding)
59
+ expanded_template = ERB.new(template, trim_mode: '-').result(erb_binding)
60
60
 
61
61
  docs = Psych.parse_stream(expanded_template, partial_path)
62
62
  # If the partial contains multiple documents or has an explicit document header,
data/lib/krane/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Krane
3
- VERSION = "2.1.6"
3
+ VERSION = "2.2.0"
4
4
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: krane
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.6
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Katrina Verey
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-02-11 00:00:00.000000000 Z
13
+ date: 2021-06-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -91,7 +91,7 @@ dependencies:
91
91
  version: '2.8'
92
92
  - - "<"
93
93
  - !ruby/object:Gem::Version
94
- version: '3.1'
94
+ version: '4'
95
95
  type: :runtime
96
96
  prerelease: false
97
97
  version_requirements: !ruby/object:Gem::Requirement
@@ -101,7 +101,7 @@ dependencies:
101
101
  version: '2.8'
102
102
  - - "<"
103
103
  - !ruby/object:Gem::Version
104
- version: '3.1'
104
+ version: '4'
105
105
  - !ruby/object:Gem::Dependency
106
106
  name: oj
107
107
  requirement: !ruby/object:Gem::Requirement
@@ -374,6 +374,20 @@ dependencies:
374
374
  - - "~>"
375
375
  - !ruby/object:Gem::Version
376
376
  version: 0.89.1
377
+ - !ruby/object:Gem::Dependency
378
+ name: rubocop-shopify
379
+ requirement: !ruby/object:Gem::Requirement
380
+ requirements:
381
+ - - "~>"
382
+ - !ruby/object:Gem::Version
383
+ version: 1.0.5
384
+ type: :development
385
+ prerelease: false
386
+ version_requirements: !ruby/object:Gem::Requirement
387
+ requirements:
388
+ - - "~>"
389
+ - !ruby/object:Gem::Version
390
+ version: 1.0.5
377
391
  - !ruby/object:Gem::Dependency
378
392
  name: simplecov
379
393
  requirement: !ruby/object:Gem::Requirement
@@ -494,11 +508,6 @@ files:
494
508
  - lib/krane/task_config_validator.rb
495
509
  - lib/krane/template_sets.rb
496
510
  - lib/krane/version.rb
497
- - screenshots/deploy-demo.gif
498
- - screenshots/migrate-logs.png
499
- - screenshots/missing-secret-fail.png
500
- - screenshots/success.png
501
- - screenshots/test-output.png
502
511
  homepage: https://github.com/Shopify/krane
503
512
  licenses:
504
513
  - MIT
@@ -519,7 +528,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
519
528
  - !ruby/object:Gem::Version
520
529
  version: '0'
521
530
  requirements: []
522
- rubygems_version: 3.0.3
531
+ rubygems_version: 3.2.17
523
532
  signing_key:
524
533
  specification_version: 4
525
534
  summary: A command line tool that helps you ship changes to a Kubernetes namespace
Binary file
Binary file
Binary file
Binary file
Binary file