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.
- checksums.yaml +4 -4
- data/.buildkite/pipeline.nightly.yml +7 -0
- data/.rubocop.yml +0 -12
- data/.shopify-build/{kubernetes-deploy.yml → krane.yml} +8 -2
- data/1.0-Upgrade.md +109 -0
- data/CHANGELOG.md +60 -0
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +1 -0
- data/README.md +86 -2
- data/dev.yml +3 -1
- data/dev/flamegraph-from-tests +1 -1
- data/exe/kubernetes-deploy +12 -9
- data/exe/kubernetes-render +9 -7
- data/exe/kubernetes-restart +3 -3
- data/exe/kubernetes-run +1 -1
- data/kubernetes-deploy.gemspec +5 -5
- data/lib/krane.rb +5 -3
- data/lib/{kubernetes-deploy → krane}/bindings_parser.rb +1 -1
- data/lib/krane/cli/deploy_command.rb +25 -13
- data/lib/krane/cli/global_deploy_command.rb +55 -0
- data/lib/krane/cli/krane.rb +12 -3
- data/lib/krane/cli/render_command.rb +19 -9
- data/lib/krane/cli/restart_command.rb +4 -4
- data/lib/krane/cli/run_command.rb +4 -4
- data/lib/krane/cli/version_command.rb +1 -1
- data/lib/krane/cluster_resource_discovery.rb +113 -0
- data/lib/{kubernetes-deploy → krane}/common.rb +8 -9
- data/lib/krane/concerns/template_reporting.rb +29 -0
- data/lib/{kubernetes-deploy → krane}/concurrency.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/container_logs.rb +3 -2
- data/lib/{kubernetes-deploy → krane}/deferred_summary_logging.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/delayed_exceptions.rb +0 -0
- data/lib/krane/deploy_task.rb +16 -0
- data/lib/krane/deploy_task_config_validator.rb +29 -0
- data/lib/krane/deprecated_deploy_task.rb +404 -0
- data/lib/{kubernetes-deploy → krane}/duration_parser.rb +1 -3
- data/lib/{kubernetes-deploy → krane}/ejson_secret_provisioner.rb +10 -13
- data/lib/krane/errors.rb +28 -0
- data/lib/{kubernetes-deploy → krane}/formatted_logger.rb +2 -2
- data/lib/krane/global_deploy_task.rb +210 -0
- data/lib/krane/global_deploy_task_config_validator.rb +12 -0
- data/lib/{kubernetes-deploy → krane}/kubeclient_builder.rb +13 -5
- data/lib/{kubernetes-deploy → krane}/kubectl.rb +14 -16
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource.rb +110 -27
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/cloudsql.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/config_map.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/cron_job.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/custom_resource.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/custom_resource_definition.rb +1 -5
- data/lib/krane/kubernetes_resource/daemon_set.rb +90 -0
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/deployment.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/horizontal_pod_autoscaler.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/ingress.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/job.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/network_policy.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/persistent_volume_claim.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod.rb +6 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_disruption_budget.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_set_base.rb +3 -3
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/pod_template.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/replica_set.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/resource_quota.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/role.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/role_binding.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/secret.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/service.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/service_account.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/kubernetes_resource/stateful_set.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/label_selector.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/oj.rb +0 -0
- data/lib/{kubernetes-deploy → krane}/options_helper.rb +2 -2
- data/lib/{kubernetes-deploy → krane}/remote_logs.rb +2 -2
- data/lib/krane/render_task.rb +149 -0
- data/lib/{kubernetes-deploy → krane}/renderer.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/resource_cache.rb +10 -9
- data/lib/krane/resource_deployer.rb +265 -0
- data/lib/{kubernetes-deploy → krane}/resource_watcher.rb +24 -25
- data/lib/krane/restart_task.rb +228 -0
- data/lib/{kubernetes-deploy → krane}/rollout_conditions.rb +1 -1
- data/lib/krane/runner_task.rb +212 -0
- data/lib/{kubernetes-deploy → krane}/runner_task_config_validator.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/statsd.rb +13 -27
- data/lib/krane/task_config.rb +22 -0
- data/lib/{kubernetes-deploy → krane}/task_config_validator.rb +1 -1
- data/lib/{kubernetes-deploy → krane}/template_sets.rb +5 -5
- data/lib/krane/version.rb +4 -0
- data/lib/kubernetes-deploy/deploy_task.rb +6 -608
- data/lib/kubernetes-deploy/errors.rb +1 -26
- data/lib/kubernetes-deploy/render_task.rb +5 -122
- data/lib/kubernetes-deploy/rescue_krane_exceptions.rb +18 -0
- data/lib/kubernetes-deploy/restart_task.rb +6 -198
- data/lib/kubernetes-deploy/runner_task.rb +6 -184
- metadata +96 -70
- data/lib/kubernetes-deploy/cluster_resource_discovery.rb +0 -34
- data/lib/kubernetes-deploy/kubernetes_resource/daemon_set.rb +0 -54
- data/lib/kubernetes-deploy/task_config.rb +0 -16
- data/lib/kubernetes-deploy/version.rb +0 -4
data/exe/kubernetes-render
CHANGED
@@ -2,32 +2,34 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
4
|
require 'kubernetes-deploy/render_task'
|
5
|
-
require '
|
6
|
-
require '
|
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 =
|
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 =
|
27
|
+
logger = Krane::FormattedLogger.build(verbose_prefix: false)
|
26
28
|
|
27
29
|
begin
|
28
|
-
|
30
|
+
Krane::OptionsHelper.with_processed_template_paths(template_dir) do |dir|
|
29
31
|
runner = KubernetesDeploy::RenderTask.new(
|
30
|
-
current_sha:
|
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
|
40
|
+
rescue Krane::OptionsHelper::OptionsError => e
|
39
41
|
logger.error(e.message)
|
40
42
|
exit(1)
|
41
43
|
end
|
data/exe/kubernetes-restart
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
require 'optparse'
|
5
5
|
|
6
6
|
require 'kubernetes-deploy/restart_task'
|
7
|
-
require '
|
8
|
-
require '
|
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 =
|
17
|
+
selector = Krane::LabelSelector.parse(s)
|
18
18
|
end
|
19
19
|
opts.parse!
|
20
20
|
end
|
data/exe/kubernetes-run
CHANGED
data/kubernetes-deploy.gemspec
CHANGED
@@ -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 '
|
5
|
+
require 'krane/version'
|
6
6
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = "kubernetes-deploy"
|
9
|
-
spec.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
|
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", '
|
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", "
|
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")
|
data/lib/krane.rb
CHANGED
@@ -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: :
|
17
|
-
|
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 '
|
37
|
-
require '
|
38
|
-
require '
|
39
|
-
require '
|
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 =
|
46
|
+
bindings_parser = ::Krane::BindingsParser.new
|
42
47
|
options[:bindings]&.each { |binding_pair| bindings_parser.add(binding_pair) }
|
43
48
|
|
44
|
-
selector =
|
49
|
+
selector = ::Krane::LabelSelector.parse(options[:selector]) if options[:selector]
|
45
50
|
|
46
|
-
logger =
|
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
|
-
|
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 =
|
68
|
+
deploy = ::Krane::DeployTask.new(
|
57
69
|
namespace: namespace,
|
58
70
|
context: context,
|
59
|
-
current_sha:
|
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:
|
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
|
data/lib/krane/cli/krane.rb
CHANGED
@@ -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
|
78
|
+
rescue ::Krane::DeploymentTimeoutError
|
70
79
|
exit(TIMEOUT_EXIT_CODE)
|
71
|
-
rescue
|
80
|
+
rescue ::Krane::FatalDeploymentError
|
72
81
|
exit(FAILURE_EXIT_CODE)
|
73
|
-
rescue
|
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
|
8
|
-
filenames
|
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 '
|
13
|
-
require '
|
14
|
-
require '
|
15
|
+
require 'krane/render_task'
|
16
|
+
require 'krane/bindings_parser'
|
17
|
+
require 'krane/options_helper'
|
15
18
|
|
16
|
-
bindings_parser =
|
19
|
+
bindings_parser = ::Krane::BindingsParser.new
|
17
20
|
options[:bindings]&.each { |b| bindings_parser.add(b) }
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
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 '
|
20
|
-
selector =
|
21
|
-
restart =
|
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:
|
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
|
-
|
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 "
|
37
|
-
runner =
|
36
|
+
require "krane/runner_task"
|
37
|
+
runner = ::Krane::RunnerTask.new(
|
38
38
|
namespace: namespace,
|
39
39
|
context: context,
|
40
|
-
max_watch_seconds:
|
40
|
+
max_watch_seconds: ::Krane::DurationParser.new(options["global-timeout"]).parse!.to_i,
|
41
41
|
)
|
42
42
|
|
43
43
|
runner.run!(
|
@@ -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
|