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,32 +2,34 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'kubernetes-deploy/render_task'
5
- require 'kubernetes-deploy/options_helper'
6
- require 'kubernetes-deploy/bindings_parser'
5
+ require 'krane/options_helper'
6
+ require 'krane/bindings_parser'
7
7
 
8
8
  require 'optparse'
9
9
 
10
10
  template_dir = []
11
11
  bindings = {}
12
+ current_sha = ENV["REVISION"]
12
13
 
13
14
  ARGV.options do |opts|
14
- parser = KubernetesDeploy::BindingsParser.new
15
+ parser = Krane::BindingsParser.new
15
16
  opts.on("--bindings=BINDINGS", "Expose additional variables to ERB templates " \
16
17
  "(format: k1=v1,k2=v2, JSON string or file (JSON or YAML) path prefixed by '@')") { |b| parser.add(b) }
17
18
  opts.on("--template-dir=DIR", "Set the template dir (default: config/deploy/$ENVIRONMENT).") do |d|
18
19
  template_dir = [d]
19
20
  end
21
+ opts.on("--current-sha=CURRENT_SHA", "Expose SHA `current_sha` in ERB bindings") { |r| current_sha = r }
20
22
  opts.parse!
21
23
  bindings = parser.parse
22
24
  end
23
25
 
24
26
  templates = ARGV
25
- logger = KubernetesDeploy::FormattedLogger.build(verbose_prefix: false)
27
+ logger = Krane::FormattedLogger.build(verbose_prefix: false)
26
28
 
27
29
  begin
28
- KubernetesDeploy::OptionsHelper.with_processed_template_paths(template_dir) do |dir|
30
+ Krane::OptionsHelper.with_processed_template_paths(template_dir) do |dir|
29
31
  runner = KubernetesDeploy::RenderTask.new(
30
- current_sha: ENV["REVISION"],
32
+ current_sha: current_sha,
31
33
  template_dir: dir.first,
32
34
  bindings: bindings,
33
35
  )
@@ -35,7 +37,7 @@ begin
35
37
  success = runner.run(STDOUT, templates)
36
38
  exit(1) unless success
37
39
  end
38
- rescue KubernetesDeploy::OptionsHelper::OptionsError => e
40
+ rescue Krane::OptionsHelper::OptionsError => e
39
41
  logger.error(e.message)
40
42
  exit(1)
41
43
  end
@@ -4,8 +4,8 @@
4
4
  require 'optparse'
5
5
 
6
6
  require 'kubernetes-deploy/restart_task'
7
- require 'kubernetes-deploy/options_helper'
8
- require 'kubernetes-deploy/label_selector'
7
+ require 'krane/options_helper'
8
+ require 'krane/label_selector'
9
9
 
10
10
  raw_deployments = nil
11
11
  max_watch_seconds = nil
@@ -14,7 +14,7 @@ ARGV.options do |opts|
14
14
  opts.on("--deployments=LIST") { |v| raw_deployments = v.split(",") }
15
15
  opts.on("--max-watch-seconds=seconds") { |t| max_watch_seconds = t.to_i }
16
16
  opts.on("--selector=SELECTOR", "Restarts deployments matching selector (format: k1=v1,k2=v2)") do |s|
17
- selector = KubernetesDeploy::LabelSelector.parse(s)
17
+ selector = Krane::LabelSelector.parse(s)
18
18
  end
19
19
  opts.parse!
20
20
  end
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'kubernetes-deploy/runner_task'
5
- require 'kubernetes-deploy/options_helper'
5
+ require 'krane/options_helper'
6
6
  require 'optparse'
7
7
 
8
8
  template = "task-runner-template"
@@ -2,11 +2,11 @@
2
2
  # frozen_string_literal: true
3
3
  lib = File.expand_path('../lib', __FILE__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
- require 'kubernetes-deploy/version'
5
+ require 'krane/version'
6
6
 
7
7
  Gem::Specification.new do |spec|
8
8
  spec.name = "kubernetes-deploy"
9
- spec.version = KubernetesDeploy::VERSION
9
+ spec.version = Krane::VERSION
10
10
  spec.authors = ["Katrina Verey", "Kir Shatrov"]
11
11
  spec.email = ["ops-accounts+shipit@shopify.com"]
12
12
 
@@ -25,14 +25,14 @@ Gem::Specification.new do |spec|
25
25
  spec.required_ruby_version = '>= 2.4.0'
26
26
  spec.add_dependency("activesupport", ">= 5.0")
27
27
  spec.add_dependency("kubeclient", "~> 4.3")
28
- spec.add_dependency("googleauth", "~> 0.8.0")
28
+ spec.add_dependency("googleauth", "~> 0.8")
29
29
  spec.add_dependency("ejson", "~> 1.0")
30
30
  spec.add_dependency("colorize", "~> 0.8")
31
- spec.add_dependency("statsd-instrument", '~> 2.3.2')
31
+ spec.add_dependency("statsd-instrument", ['>= 2.8', "< 3.1"])
32
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
- spec.add_dependency("thor", "~> 0.20.3")
35
+ spec.add_dependency("thor", [">= 0.20", "< 2.0"])
36
36
 
37
37
  spec.add_development_dependency("bundler")
38
38
  spec.add_development_dependency("rake", "~> 10.0")
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
- require 'kubernetes-deploy/common'
3
2
 
4
- module Krane
5
- end
3
+ require 'krane/deploy_task'
4
+ require 'krane/render_task'
5
+ require 'krane/restart_task'
6
+ require 'krane/runner_task'
7
+ require 'krane/global_deploy_task'
@@ -3,7 +3,7 @@ require 'json'
3
3
  require 'yaml'
4
4
  require 'csv'
5
5
 
6
- module KubernetesDeploy
6
+ module Krane
7
7
  class BindingsParser
8
8
  def self.parse(string)
9
9
  new(string).parse
@@ -13,8 +13,11 @@ module Krane
13
13
  "bindings" => { type: :array, banner: "foo=bar abc=def",
14
14
  desc: "Expose additional variables to ERB templates (format: k1=v1 k2=v2, JSON string or file "\
15
15
  "(JSON or YAML) path prefixed by '@')" },
16
- "filenames" => { type: :string, banner: '/tmp/my-resource.yml', aliases: :f, required: true,
17
- desc: "Path to file or directory that contains the configuration to apply" },
16
+ "filenames" => { type: :array, banner: 'config/deploy/production config/deploy/my-extra-resource.yml',
17
+ aliases: :f, required: false, default: [],
18
+ desc: "Directories and files that contains the configuration to apply" },
19
+ "stdin" => { type: :boolean, default: false,
20
+ desc: "Read resources from stdin" },
18
21
  "global-timeout" => { type: :string, banner: "duration", default: DEFAULT_DEPLOY_TIMEOUT,
19
22
  desc: "Max duration to monitor workloads correctly deployed" },
20
23
  "protected-namespaces" => { type: :array, banner: "namespace1 namespace2 namespaceN",
@@ -30,20 +33,22 @@ module Krane
30
33
  default: true },
31
34
  "verify-result" => { type: :boolean, default: true,
32
35
  desc: "Verify workloads correctly deployed" },
36
+ "current-sha" => { type: :string, banner: "SHA", desc: "Expose SHA `current_sha` in ERB bindings" },
37
+
33
38
  }
34
39
 
35
40
  def self.from_options(namespace, context, options)
36
- require 'kubernetes-deploy/deploy_task'
37
- require 'kubernetes-deploy/options_helper'
38
- require 'kubernetes-deploy/bindings_parser'
39
- require 'kubernetes-deploy/label_selector'
41
+ require 'krane/deploy_task'
42
+ require 'krane/options_helper'
43
+ require 'krane/bindings_parser'
44
+ require 'krane/label_selector'
40
45
 
41
- bindings_parser = KubernetesDeploy::BindingsParser.new
46
+ bindings_parser = ::Krane::BindingsParser.new
42
47
  options[:bindings]&.each { |binding_pair| bindings_parser.add(binding_pair) }
43
48
 
44
- selector = KubernetesDeploy::LabelSelector.parse(options[:selector]) if options[:selector]
49
+ selector = ::Krane::LabelSelector.parse(options[:selector]) if options[:selector]
45
50
 
46
- logger = KubernetesDeploy::FormattedLogger.build(namespace, context,
51
+ logger = ::Krane::FormattedLogger.build(namespace, context,
47
52
  verbose_prefix: options['verbose-log-prefix'])
48
53
 
49
54
  protected_namespaces = options['protected-namespaces']
@@ -51,16 +56,23 @@ module Krane
51
56
  protected_namespaces = []
52
57
  end
53
58
 
54
- KubernetesDeploy::OptionsHelper.with_processed_template_paths([options[:filenames]],
59
+ # never mutate options directly
60
+ filenames = options[:filenames].dup
61
+ filenames << "-" if options[:stdin]
62
+ if filenames.empty?
63
+ raise Thor::RequiredArgumentMissingError, 'At least one of --filenames or --stdin must be set'
64
+ end
65
+
66
+ ::Krane::OptionsHelper.with_processed_template_paths(filenames,
55
67
  require_explicit_path: true) do |paths|
56
- deploy = KubernetesDeploy::DeployTask.new(
68
+ deploy = ::Krane::DeployTask.new(
57
69
  namespace: namespace,
58
70
  context: context,
59
- current_sha: ENV["REVISION"],
71
+ current_sha: options['current-sha'],
60
72
  template_paths: paths,
61
73
  bindings: bindings_parser.parse,
62
74
  logger: logger,
63
- max_watch_seconds: KubernetesDeploy::DurationParser.new(options["global-timeout"]).parse!.to_i,
75
+ max_watch_seconds: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
64
76
  selector: selector,
65
77
  protected_namespaces: protected_namespaces,
66
78
  )
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Krane
4
+ module CLI
5
+ class GlobalDeployCommand
6
+ DEFAULT_DEPLOY_TIMEOUT = '300s'
7
+ OPTIONS = {
8
+ "filenames" => { type: :array, banner: 'config/deploy/production config/deploy/my-extra-resource.yml',
9
+ aliases: :f, required: false, default: [],
10
+ desc: "Directories and files that contains the configuration to apply" },
11
+ "stdin" => { type: :boolean, default: false, desc: "Read resources from stdin" },
12
+ "global-timeout" => { type: :string, banner: "duration", default: DEFAULT_DEPLOY_TIMEOUT,
13
+ desc: "Max duration to monitor workloads correctly deployed" },
14
+ "verify-result" => { type: :boolean, default: true,
15
+ desc: "Verify workloads correctly deployed" },
16
+ "selector" => { type: :string, banner: "'label=value'", required: true,
17
+ desc: "Select workloads owned by selector(s)" },
18
+ "prune" => { type: :boolean, desc: "Enable deletion of resources that match"\
19
+ " the provided selector and do not appear in the provided templates",
20
+ default: true },
21
+ }
22
+
23
+ def self.from_options(context, options)
24
+ require 'krane/global_deploy_task'
25
+ require 'krane/options_helper'
26
+ require 'krane/label_selector'
27
+ require 'krane/duration_parser'
28
+
29
+ selector = ::Krane::LabelSelector.parse(options[:selector])
30
+
31
+ # never mutate options directly
32
+ filenames = options[:filenames].dup
33
+ filenames << "-" if options[:stdin]
34
+ if filenames.empty?
35
+ raise Thor::RequiredArgumentMissingError, 'At least one of --filenames or --stdin must be set'
36
+ end
37
+
38
+ ::Krane::OptionsHelper.with_processed_template_paths(filenames,
39
+ require_explicit_path: true) do |paths|
40
+ deploy = ::Krane::GlobalDeployTask.new(
41
+ context: context,
42
+ filenames: paths,
43
+ global_timeout: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
44
+ selector: selector,
45
+ )
46
+
47
+ deploy.run!(
48
+ verify_result: options["verify-result"],
49
+ prune: options[:prune],
50
+ )
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -7,6 +7,7 @@ require 'krane/cli/restart_command'
7
7
  require 'krane/cli/run_command'
8
8
  require 'krane/cli/render_command'
9
9
  require 'krane/cli/deploy_command'
10
+ require 'krane/cli/global_deploy_command'
10
11
 
11
12
  module Krane
12
13
  module CLI
@@ -58,6 +59,14 @@ module Krane
58
59
  end
59
60
  end
60
61
 
62
+ desc("global-deploy CONTEXT", "Ship non-namespaced resources to a cluster")
63
+ expand_options(GlobalDeployCommand::OPTIONS)
64
+ def global_deploy(context)
65
+ rescue_and_exit do
66
+ GlobalDeployCommand.from_options(context, options)
67
+ end
68
+ end
69
+
61
70
  def self.exit_on_failure?
62
71
  true
63
72
  end
@@ -66,11 +75,11 @@ module Krane
66
75
 
67
76
  def rescue_and_exit
68
77
  yield
69
- rescue KubernetesDeploy::DeploymentTimeoutError
78
+ rescue ::Krane::DeploymentTimeoutError
70
79
  exit(TIMEOUT_EXIT_CODE)
71
- rescue KubernetesDeploy::FatalDeploymentError
80
+ rescue ::Krane::FatalDeploymentError
72
81
  exit(FAILURE_EXIT_CODE)
73
- rescue KubernetesDeploy::DurationParser::ParsingError => e
82
+ rescue ::Krane::DurationParser::ParsingError => e
74
83
  STDERR.puts(<<~ERROR_MESSAGE)
75
84
  Error parsing duration
76
85
  #{e.message}. Duration must be a full ISO8601 duration or time value (e.g. 300s, 10m, 1h)
@@ -4,21 +4,31 @@ module Krane
4
4
  module CLI
5
5
  class RenderCommand
6
6
  OPTIONS = {
7
- bindings: { type: :array, desc: 'Bindings for erb' },
8
- filenames: { type: :array, required: true, aliases: 'f', desc: 'Directories and files to render' },
7
+ "bindings" => { type: :array, banner: "foo=bar abc=def", desc: 'Bindings for erb' },
8
+ "filenames" => { type: :array, banner: 'config/deploy/production config/deploy/my-extra-resource.yml',
9
+ required: false, default: [], aliases: 'f', desc: 'Directories and files to render' },
10
+ "stdin" => { type: :boolean, desc: "Read resources from stdin", default: false },
11
+ "current-sha" => { type: :string, banner: "SHA", desc: "Expose SHA `current_sha` in ERB bindings" },
9
12
  }
10
13
 
11
14
  def self.from_options(options)
12
- require 'kubernetes-deploy/render_task'
13
- require 'kubernetes-deploy/bindings_parser'
14
- require 'kubernetes-deploy/options_helper'
15
+ require 'krane/render_task'
16
+ require 'krane/bindings_parser'
17
+ require 'krane/options_helper'
15
18
 
16
- bindings_parser = KubernetesDeploy::BindingsParser.new
19
+ bindings_parser = ::Krane::BindingsParser.new
17
20
  options[:bindings]&.each { |b| bindings_parser.add(b) }
18
21
 
19
- KubernetesDeploy::OptionsHelper.with_processed_template_paths(options[:filenames]) do |paths|
20
- runner = KubernetesDeploy::RenderTask.new(
21
- current_sha: ENV["REVISION"],
22
+ # never mutate options directly
23
+ filenames = options[:filenames].dup
24
+ filenames << "-" if options[:stdin]
25
+ if filenames.empty?
26
+ raise Thor::RequiredArgumentMissingError, 'At least one of --filenames or --stdin must be set'
27
+ end
28
+
29
+ ::Krane::OptionsHelper.with_processed_template_paths(filenames) do |paths|
30
+ runner = ::Krane::RenderTask.new(
31
+ current_sha: options['current-sha'],
22
32
  template_paths: paths,
23
33
  bindings: bindings_parser.parse,
24
34
  )
@@ -16,12 +16,12 @@ module Krane
16
16
  }
17
17
 
18
18
  def self.from_options(namespace, context, options)
19
- require 'kubernetes-deploy/restart_task'
20
- selector = KubernetesDeploy::LabelSelector.parse(options[:selector]) if options[:selector]
21
- restart = KubernetesDeploy::RestartTask.new(
19
+ require 'krane/restart_task'
20
+ selector = ::Krane::LabelSelector.parse(options[:selector]) if options[:selector]
21
+ restart = ::Krane::RestartTask.new(
22
22
  namespace: namespace,
23
23
  context: context,
24
- max_watch_seconds: KubernetesDeploy::DurationParser.new(options["global-timeout"]).parse!.to_i,
24
+ max_watch_seconds: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
25
25
  )
26
26
  restart.run!(
27
27
  options[:deployments],
@@ -22,7 +22,7 @@ module Krane
22
22
  "template" => {
23
23
  type: :string,
24
24
  desc: "The template file you'll be rendering",
25
- default: 'task-runner-template',
25
+ required: true,
26
26
  },
27
27
  "env-vars" => {
28
28
  type: :string,
@@ -33,11 +33,11 @@ module Krane
33
33
  }
34
34
 
35
35
  def self.from_options(namespace, context, options)
36
- require "kubernetes-deploy/runner_task"
37
- runner = KubernetesDeploy::RunnerTask.new(
36
+ require "krane/runner_task"
37
+ runner = ::Krane::RunnerTask.new(
38
38
  namespace: namespace,
39
39
  context: context,
40
- max_watch_seconds: KubernetesDeploy::DurationParser.new(options["global-timeout"]).parse!.to_i,
40
+ max_watch_seconds: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
41
41
  )
42
42
 
43
43
  runner.run!(
@@ -6,7 +6,7 @@ module Krane
6
6
  OPTIONS = {}
7
7
 
8
8
  def self.from_options(_)
9
- puts("krane #{KubernetesDeploy::VERSION}")
9
+ puts("krane #{::Krane::VERSION}")
10
10
  end
11
11
  end
12
12
  end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Krane
4
+ class ClusterResourceDiscovery
5
+ delegate :namespace, :context, :logger, to: :@task_config
6
+
7
+ def initialize(task_config:, namespace_tags: [])
8
+ @task_config = task_config
9
+ @namespace_tags = namespace_tags
10
+ end
11
+
12
+ def crds
13
+ @crds ||= fetch_crds.map do |cr_def|
14
+ CustomResourceDefinition.new(namespace: namespace, context: context, logger: logger,
15
+ definition: cr_def, statsd_tags: @namespace_tags)
16
+ end
17
+ end
18
+
19
+ def prunable_resources(namespaced:)
20
+ black_list = %w(Namespace Node ControllerRevision)
21
+ api_versions = fetch_api_versions
22
+
23
+ fetch_resources(namespaced: namespaced).map do |resource|
24
+ next unless resource['verbs'].one? { |v| v == "delete" }
25
+ next if black_list.include?(resource['kind'])
26
+ group_versions = api_versions[resource['apigroup'].to_s]
27
+ version = version_for_kind(group_versions, resource['kind'])
28
+ [resource['apigroup'], version, resource['kind']].compact.join("/")
29
+ end.compact
30
+ end
31
+
32
+ # kubectl api-resources -o wide returns 5 columns
33
+ # NAME SHORTNAMES APIGROUP NAMESPACED KIND VERBS
34
+ # SHORTNAMES and APIGROUP may be blank
35
+ # VERBS is an array
36
+ # serviceaccounts sa <blank> true ServiceAccount [create delete deletecollection get list patch update watch]
37
+ def fetch_resources(namespaced: false)
38
+ command = %w(api-resources)
39
+ command << "--namespaced=#{namespaced}"
40
+ raw, _, st = kubectl.run(*command, output: "wide", attempts: 5,
41
+ use_namespace: false)
42
+ if st.success?
43
+ rows = raw.split("\n")
44
+ header = rows[0]
45
+ resources = rows[1..-1]
46
+ full_width_field_names = header.downcase.scan(/[a-z]+[\W]*/)
47
+ cursor = 0
48
+ fields = full_width_field_names.each_with_object({}) do |name, hash|
49
+ start = cursor
50
+ cursor = start + name.length
51
+ # Last field should consume the remainder of the line
52
+ cursor = 0 if full_width_field_names.last == name.strip
53
+ hash[name.strip] = [start, cursor - 1]
54
+ end
55
+ resources.map do |resource|
56
+ resource = fields.map { |k, (s, e)| [k.strip, resource[s..e].strip] }.to_h
57
+ # Manually parse verbs: "[get list]" into %w(get list)
58
+ resource["verbs"] = resource["verbs"][1..-2].split
59
+ resource
60
+ end
61
+ else
62
+ []
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ # kubectl api-versions returns a list of group/version strings e.g. autoscaling/v2beta2
69
+ # A kind may not exist in all versions of the group.
70
+ def fetch_api_versions
71
+ raw, _, st = kubectl.run("api-versions", attempts: 5, use_namespace: false)
72
+ # The "core" group is represented by an empty string
73
+ versions = { "" => %w(v1) }
74
+ if st.success?
75
+ rows = raw.split("\n")
76
+ rows.each do |group_version|
77
+ group, version = group_version.split("/")
78
+ versions[group] ||= []
79
+ versions[group] << version
80
+ end
81
+ end
82
+ versions
83
+ end
84
+
85
+ def version_for_kind(versions, kind)
86
+ # Override list for kinds that don't appear in the lastest version of a group
87
+ version_override = { "CronJob" => "v1beta1", "VolumeAttachment" => "v1beta1",
88
+ "CSIDriver" => "v1beta1", "Ingress" => "v1beta1", "CSINode" => "v1beta1" }
89
+
90
+ pattern = /v(?<major>\d+)(?<pre>alpha|beta)?(?<minor>\d+)?/
91
+ latest = versions.sort_by do |version|
92
+ match = version.match(pattern)
93
+ pre = { "alpha" => 0, "beta" => 1, nil => 2 }.fetch(match[:pre])
94
+ [match[:major].to_i, pre, match[:minor].to_i]
95
+ end.last
96
+ version_override.fetch(kind, latest)
97
+ end
98
+
99
+ def fetch_crds
100
+ raw_json, _, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
101
+ use_namespace: false)
102
+ if st.success?
103
+ JSON.parse(raw_json)["items"]
104
+ else
105
+ []
106
+ end
107
+ end
108
+
109
+ def kubectl
110
+ @kubectl ||= Kubectl.new(task_config: @task_config, log_failure_by_default: true)
111
+ end
112
+ end
113
+ end