kubes 0.3.4 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +28 -0
  3. data/README.md +6 -5
  4. data/docs/_docs/ci/cloudbuild.md +2 -0
  5. data/docs/_docs/config/args.md +10 -0
  6. data/docs/_docs/config/args/docker.md +19 -0
  7. data/docs/_docs/config/args/kubectl.md +19 -0
  8. data/docs/_docs/config/docker.md +4 -40
  9. data/docs/_docs/config/env.md +1 -1
  10. data/docs/_docs/config/hooks.md +10 -0
  11. data/docs/_docs/config/hooks/docker.md +70 -0
  12. data/docs/_docs/config/hooks/kubectl.md +83 -0
  13. data/docs/_docs/config/hooks/kubes.md +67 -0
  14. data/docs/_docs/config/hooks/ruby.md +76 -0
  15. data/docs/_docs/config/kubectl.md +3 -54
  16. data/docs/_docs/config/reference.md +20 -0
  17. data/docs/_docs/config/skip.md +58 -0
  18. data/docs/_docs/dsl/resources.md +1 -1
  19. data/docs/_docs/dsl/resources/backend_config.md +1 -1
  20. data/docs/_docs/helpers.md +3 -2
  21. data/docs/_docs/intro.md +6 -3
  22. data/docs/_docs/learn/dsl/review-project.md +4 -2
  23. data/docs/_docs/learn/yaml/review-project.md +4 -2
  24. data/docs/_docs/{auto-context.md → misc/auto-context.md} +0 -0
  25. data/docs/_docs/{kustomize.md → misc/kustomize.md} +0 -0
  26. data/docs/_docs/misc/separate-steps.md +21 -0
  27. data/docs/_docs/patterns.md +4 -1
  28. data/docs/_docs/patterns/clock-web-worker.md +2 -0
  29. data/docs/_docs/patterns/migrations.md +123 -0
  30. data/docs/_docs/patterns/secrets.md +82 -0
  31. data/docs/_includes/config/hooks/options.md +20 -0
  32. data/docs/_includes/sidebar.html +35 -11
  33. data/docs/_reference/kubes-exec.md +14 -6
  34. data/docs/_reference/kubes-init.md +1 -0
  35. data/docs/_reference/kubes-logs.md +1 -0
  36. data/docs/_sass/theme.scss +25 -1
  37. data/kubes.gemspec +3 -0
  38. data/lib/kubes.rb +3 -0
  39. data/lib/kubes/cli.rb +20 -5
  40. data/lib/kubes/cli/apply.rb +2 -1
  41. data/lib/kubes/cli/base.rb +11 -0
  42. data/lib/kubes/cli/compile.rb +8 -0
  43. data/lib/kubes/cli/delete.rb +1 -1
  44. data/lib/kubes/cli/exec.rb +37 -6
  45. data/lib/kubes/cli/get.rb +3 -2
  46. data/lib/kubes/cli/init.rb +7 -2
  47. data/lib/kubes/cli/logs.rb +27 -3
  48. data/lib/kubes/cli/prune.rb +95 -0
  49. data/lib/kubes/compiler.rb +11 -7
  50. data/lib/kubes/compiler/decorator/base.rb +7 -1
  51. data/lib/kubes/compiler/decorator/{resources/secret.rb → hashable.rb} +5 -4
  52. data/lib/kubes/compiler/decorator/hashable/field.rb +53 -0
  53. data/lib/kubes/compiler/decorator/hashable/storage.rb +19 -0
  54. data/lib/kubes/compiler/decorator/post.rb +77 -0
  55. data/lib/kubes/compiler/decorator/pre.rb +12 -0
  56. data/lib/kubes/compiler/shared/helpers.rb +7 -2
  57. data/lib/kubes/compiler/strategy.rb +2 -2
  58. data/lib/kubes/compiler/strategy/base.rb +1 -1
  59. data/lib/kubes/compiler/strategy/result.rb +4 -6
  60. data/lib/kubes/config.rb +16 -11
  61. data/lib/kubes/docker/strategy/build/docker.rb +1 -1
  62. data/lib/kubes/docker/strategy/build/gcloud.rb +1 -1
  63. data/lib/kubes/docker/strategy/image_name.rb +1 -1
  64. data/lib/kubes/docker/strategy/push/docker.rb +1 -1
  65. data/lib/kubes/docker/strategy/push/gcloud.rb +1 -1
  66. data/lib/kubes/docker/strategy/utils.rb +1 -1
  67. data/lib/kubes/hooks/builder.rb +29 -15
  68. data/lib/kubes/hooks/concern.rb +10 -0
  69. data/lib/kubes/hooks/dsl.rb +2 -1
  70. data/lib/kubes/hooks/runner.rb +22 -0
  71. data/lib/kubes/kubectl.rb +21 -18
  72. data/lib/kubes/kubectl/batch.rb +8 -5
  73. data/lib/kubes/kubectl/{decider.rb → dispatcher.rb} +1 -1
  74. data/lib/kubes/kubectl/fetch/base.rb +13 -10
  75. data/lib/kubes/kubectl/fetch/deployment.rb +12 -13
  76. data/lib/kubes/kubectl/fetch/pods.rb +4 -15
  77. data/lib/kubes/kubectl/kustomize.rb +1 -1
  78. data/lib/kubes/kubectl/ordering.rb +12 -0
  79. data/lib/kubes/util/consider.rb +2 -1
  80. data/lib/kubes/util/sh.rb +1 -1
  81. data/lib/kubes/version.rb +1 -1
  82. data/lib/templates/dsl/.kubes/resources/web/deployment.rb +1 -1
  83. data/lib/templates/yaml/.kubes/resources/web/deployment.yaml.tt +1 -1
  84. data/spec/fixtures/decorators/deployment/both/envFrom.yaml +31 -0
  85. data/spec/fixtures/decorators/deployment/both/valueFrom.yaml +33 -0
  86. data/spec/fixtures/decorators/deployment/both/volumes.yaml +40 -0
  87. data/spec/fixtures/prune/capture.yaml +57 -0
  88. data/spec/fixtures/prune/fetch_items.yaml +268 -0
  89. data/spec/kubes/cli/prune_spec.rb +38 -0
  90. data/spec/kubes/compiler/decorator/{resources → post}/deployment_spec.rb +52 -6
  91. data/spec/kubes/compiler/decorator/{resources → post}/pod_spec.rb +2 -11
  92. metadata +57 -19
  93. data/lib/kubes/compiler/decorator.rb +0 -17
  94. data/lib/kubes/compiler/decorator/compile.rb +0 -12
  95. data/lib/kubes/compiler/decorator/resources/base.rb +0 -13
  96. data/lib/kubes/compiler/decorator/resources/container.rb +0 -76
  97. data/lib/kubes/compiler/decorator/resources/container/mapping.rb +0 -28
  98. data/lib/kubes/compiler/decorator/resources/deployment.rb +0 -10
  99. data/lib/kubes/compiler/decorator/resources/pod.rb +0 -10
  100. data/lib/kubes/compiler/decorator/write.rb +0 -14
  101. data/lib/kubes/docker/strategy/hooks.rb +0 -9
@@ -3,7 +3,7 @@ module Kubes::Docker::Strategy::Build
3
3
  def perform
4
4
  params = args.flatten.join(' ')
5
5
  command = "docker build #{params}"
6
- run_hooks "build" do
6
+ run_hooks("docker.rb", name: "build") do
7
7
  sh(command)
8
8
  end
9
9
  end
@@ -2,7 +2,7 @@ module Kubes::Docker::Strategy::Build
2
2
  class Gcloud < Base
3
3
  def perform
4
4
  command = "gcloud builds submit --tag #{@@image_name}"
5
- run_hooks "build" do
5
+ run_hooks("docker.rb", name: "build") do
6
6
  sh(command)
7
7
  end
8
8
  end
@@ -61,7 +61,7 @@ module Kubes::Docker::Strategy
61
61
  end
62
62
 
63
63
  def custom
64
- custom = Kubes::Args::Custom.new(@name, "#{Kubes.root}/.kubes/config/docker/args.rb")
64
+ custom = Kubes::Args::Custom.new(@name, "#{Kubes.root}/.kubes/config/args/docker.rb")
65
65
  custom.build
66
66
  custom
67
67
  end
@@ -17,7 +17,7 @@ module Kubes::Docker::Strategy::Push
17
17
  def push
18
18
  params = args.flatten.join(' ')
19
19
  command = "docker push #{params}"
20
- run_hooks "push" do
20
+ run_hooks("docker.rb", name: "push") do
21
21
  sh(command)
22
22
  end
23
23
  end
@@ -1,7 +1,7 @@
1
1
  module Kubes::Docker::Strategy::Push
2
2
  class Gcloud < Base
3
3
  def run
4
- run_hooks "push" do
4
+ run_hooks("docker.rb", name: "push") do
5
5
  # noop, gcloud builds submit already pushes the image
6
6
  end
7
7
  end
@@ -4,6 +4,6 @@ module Kubes::Docker::Strategy
4
4
  include Kubes::Util::Sh
5
5
  include Kubes::Util::Time
6
6
  include Kubes::Docker::Strategy::ImageName
7
- include Kubes::Docker::Strategy::Hooks
7
+ include Kubes::Hooks::Concern
8
8
  end
9
9
  end
@@ -4,39 +4,53 @@ module Kubes::Hooks
4
4
  include Dsl
5
5
  include DslEvaluator
6
6
  include Kubes::Logging
7
- include Kubes::Util::Sh
8
7
 
9
8
  attr_accessor :name
10
- def initialize(name, file)
11
- @name = name.to_s
12
- @file = file # IE: .kubes/config/kubectl/hooks.rb
9
+ def initialize(dsl_file, options={})
10
+ @dsl_file, @options = dsl_file, options # IE: .kubes/config/hooks/kubectl.rb
11
+ @output_file = options[:file] # IE: .kubes/output/web/service.yaml
12
+ @name = options[:name].to_s
13
13
  @hooks = {before: {}, after: {}}
14
14
  end
15
15
 
16
16
  def build
17
- return @hooks unless File.exist?(@file)
18
- evaluate_file(@file)
17
+ return @hooks unless File.exist?(@dsl_file)
18
+ evaluate_file(@dsl_file)
19
19
  @hooks.deep_stringify_keys!
20
20
  end
21
21
  memoize :build
22
22
 
23
23
  def run_hooks
24
24
  build
25
- run_hook("before")
25
+ run_each_hook("before")
26
26
  out = yield if block_given?
27
- run_hook("after")
27
+ run_each_hook("after")
28
28
  out
29
29
  end
30
30
 
31
- def run_hook(type)
32
- execute = @hooks.dig(type, @name.to_s, "execute")
33
- return unless execute
31
+ def run_each_hook(type)
32
+ hooks = @hooks.dig(type, @name.to_s) || []
33
+ hooks.each do |hook|
34
+ run_hook(type, hook)
35
+ end
36
+ end
37
+
38
+ def run_hook(type, hook)
39
+ return unless run?(hook)
34
40
 
35
- exit_on_fail = @hooks.dig(type, @name.to_s, "exit_on_fail")
36
- exit_on_fail = exit_on_fail.nil? ? true : exit_on_fail
41
+ command = File.basename(@dsl_file).sub('.rb','') # IE: kubes, kubectl, docker
42
+ id = "#{command} #{type} #{@name}"
43
+ on = " on: #{hook["on"]}" if hook["on"]
44
+ label = " label: #{hook["label"]}" if hook["label"]
45
+ logger.info "Running #{id} hook.#{on}#{label}"
46
+ logger.debug "Hook options: #{hook}"
47
+ Runner.new(hook).run
48
+ end
37
49
 
38
- logger.info "Running #{type} hook"
39
- sh(execute, exit_on_fail: exit_on_fail)
50
+ def run?(hook)
51
+ return false unless hook["execute"]
52
+ return true unless hook["on"]
53
+ @output_file && @output_file.include?(hook["on"]) # output file is only passed
40
54
  end
41
55
  end
42
56
  end
@@ -0,0 +1,10 @@
1
+ module Kubes::Hooks
2
+ module Concern
3
+ # options example: {:name=>"apply", :file=>".kubes/output/web/service.yaml"}
4
+ def run_hooks(file, options={}, &block)
5
+ hooks = Kubes::Hooks::Builder.new("#{Kubes.root}/.kubes/config/hooks/#{file}", options)
6
+ hooks.build # build hooks
7
+ hooks.run_hooks(&block)
8
+ end
9
+ end
10
+ end
@@ -13,7 +13,8 @@ module Kubes::Hooks
13
13
  end
14
14
 
15
15
  def each_hook(type, name, props={})
16
- @hooks[type][name] = props
16
+ @hooks[type][name] ||= []
17
+ @hooks[type][name] << props
17
18
  end
18
19
  end
19
20
  end
@@ -0,0 +1,22 @@
1
+ module Kubes::Hooks
2
+ class Runner
3
+ include Kubes::Util::Sh
4
+ include Kubes::Logging
5
+
6
+ def initialize(hook)
7
+ @hook = hook
8
+ @execute = @hook["execute"]
9
+ end
10
+
11
+ def run
12
+ case @execute
13
+ when String
14
+ sh(@execute, exit_on_fail: @hook["exit_on_fail"])
15
+ when -> (e) { e.respond_to?(:public_instance_methods) && e.public_instance_methods.include?(:call) }
16
+ @execute.new.call
17
+ else
18
+ @execute.call
19
+ end
20
+ end
21
+ end
22
+ end
@@ -2,6 +2,7 @@ module Kubes
2
2
  class Kubectl
3
3
  extend Memoist
4
4
  include Kubes::Util::Sh
5
+ include Kubes::Hooks::Concern
5
6
 
6
7
  def initialize(name, options={})
7
8
  @name, @options = name, options
@@ -14,24 +15,19 @@ module Kubes
14
15
  options[:exit_on_fail] = exit_on_fail unless exit_on_fail.nil?
15
16
 
16
17
  params = args.flatten.join(' ')
17
- command = "kubectl #{@name} #{params}" # @name: apply or delete
18
+ args = "#{@name} #{params}" # @name: apply or delete
18
19
 
19
20
  switch_context do
20
- run_hooks(@name) do
21
+ run_hooks("kubectl.rb", name: @name, file: @options[:file]) do
21
22
  if options[:capture]
22
- capture(command, options)
23
+ self.class.capture(args, options) # already includes kubectl
23
24
  else
24
- sh(command, options)
25
+ self.class.execute(args, options)
25
26
  end
26
27
  end
27
28
  end
28
29
  end
29
30
 
30
- def execute(args, options={})
31
- command = "kubectl #{args}"
32
- capture(command)
33
- end
34
-
35
31
  # Useful for kustomize mode
36
32
  def validate!
37
33
  return true unless Kubes.kustomize?
@@ -44,9 +40,8 @@ module Kubes
44
40
  end
45
41
 
46
42
  def exit_on_fail
47
- kubectl = Kubes.config.kubectl
48
- exit_on_fail = kubectl.send("exit_on_fail_for_#{@name}")
49
- exit_on_fail.nil? ? kubectl.exit_on_fail : exit_on_fail
43
+ return false if ENV['KUBES_EXIT_ON_FAIL'] == '0'
44
+ Kubes.config.kubectl.exit_on_fail[@name]
50
45
  end
51
46
 
52
47
  def switch_context(&block)
@@ -65,12 +60,6 @@ module Kubes
65
60
  end
66
61
  end
67
62
 
68
- def run_hooks(name, &block)
69
- hooks = Kubes::Hooks::Builder.new(name, "#{Kubes.root}/.kubes/config/kubectl/hooks.rb")
70
- hooks.build # build hooks
71
- hooks.run_hooks(&block)
72
- end
73
-
74
63
  def args
75
64
  # base at end in case of redirection. IE: command > /path
76
65
  custom.args + default.args
@@ -90,9 +79,23 @@ module Kubes
90
79
  memoize :default
91
80
 
92
81
  class << self
82
+ include Kubes::Util::Sh
93
83
  def run(name, options={})
94
84
  new(name, options).run
95
85
  end
86
+
87
+ def execute(args, options={})
88
+ sh("kubectl #{args}", options)
89
+ end
90
+
91
+ def capture(args, options={})
92
+ resp = sh_capture("kubectl #{args}", options)
93
+ if args.include?('-o json')
94
+ JSON.load(resp) # data
95
+ else
96
+ resp
97
+ end
98
+ end
96
99
  end
97
100
  end
98
101
  end
@@ -1,5 +1,6 @@
1
1
  class Kubes::Kubectl
2
2
  class Batch
3
+ include Kubes::Hooks::Concern
3
4
  include Kubes::Logging
4
5
  include Kubes::Util::Consider
5
6
  include Ordering
@@ -11,11 +12,13 @@ class Kubes::Kubectl
11
12
  def run
12
13
  # @options[:preview] is really only used for kubectl delete
13
14
  logger.info "Will run:" if @options[:preview]
14
- sorted_files.each do |file|
15
- if @options[:preview]
16
- logger.info " kubectl #{@name} -f #{file}"
17
- else
18
- Kubes::Kubectl.run(@name, @options.merge(file: file))
15
+ run_hooks("kubes.rb", name: @name) do
16
+ sorted_files.each do |file|
17
+ if @options[:preview]
18
+ logger.info " kubectl #{@name} -f #{file}"
19
+ else
20
+ Kubes::Kubectl.run(@name, @options.merge(file: file))
21
+ end
19
22
  end
20
23
  end
21
24
  end
@@ -1,5 +1,5 @@
1
1
  class Kubes::Kubectl
2
- class Decider
2
+ class Dispatcher
3
3
  def initialize(name, options={})
4
4
  @name, @options = name.to_s, options
5
5
  end
@@ -2,6 +2,7 @@ require "json"
2
2
 
3
3
  module Kubes::Kubectl::Fetch
4
4
  class Base
5
+ extend Memoist
5
6
  include Kubes::Logging
6
7
  include Kubes::Util::Sh
7
8
 
@@ -9,16 +10,18 @@ module Kubes::Kubectl::Fetch
9
10
  @options = options
10
11
  end
11
12
 
12
- def fetch_items
13
- o = {
14
- capture: true,
15
- output: "json",
16
- show_command: false,
17
- }
18
- kubectl = Kubes::Kubectl.new(:get, @options.merge(o)) # kubes get -f .kubes/output
19
- resp = kubectl.run
20
- data = JSON.load(resp)
21
- data['items']
13
+ def fetch(kind)
14
+ return [] unless namespace
15
+ data = Kubes::Kubectl.capture("get #{kind} -o json -n #{namespace}")
16
+ data['items'] || [] # Note: When fetching only 1 resource, items is not part of structure
22
17
  end
18
+
19
+ def namespace
20
+ path = ".kubes/output/shared/namespace.yaml"
21
+ return unless File.exist?(path)
22
+ data = Kubes::Kubectl.capture("get -f #{path} -o json")
23
+ data['metadata']['name']
24
+ end
25
+ memoize :namespace
23
26
  end
24
27
  end
@@ -3,40 +3,39 @@ module Kubes::Kubectl::Fetch
3
3
  extend Memoist
4
4
 
5
5
  def metadata
6
- deployment['metadata']
6
+ deployment['metadata'] if found
7
7
  end
8
8
 
9
9
  def spec
10
- deployment['spec']
10
+ deployment['spec'] if found
11
11
  end
12
12
 
13
13
  def deployment
14
- items = fetch_items
14
+ items = fetch(:deployment)
15
15
  # Not checking if deployment exists because kubes will error on `kubes get` from missing deployments already
16
16
  deployments = items.select { |i| i['kind'] == "Deployment" }
17
17
 
18
- if deployments.size > 1 && !@options[:name]
18
+ if !@options[:deployment] && !@options[:pod] && deployments.size > 1
19
19
  names = deployments.map { |d| d['metadata']['name'] }
20
20
  logger.info <<~EOL
21
21
  INFO: More than one deployment found.
22
22
  Deployment names: #{names.join(', ')}
23
23
  Using #{names.first}
24
- Note: You can specify the deployment to use with --name or -n
24
+ Note: You can specify the deployment to use with --deployment or -d
25
25
  EOL
26
26
  end
27
27
 
28
- deployment = find_deployment(deployments)
29
- unless deployment
30
- logger.error "ERROR: No deployment found".color(:red)
31
- exit 1
32
- end
33
- deployment
28
+ find_deployment(deployments)
34
29
  end
35
30
  memoize :deployment
36
31
 
32
+ def found
33
+ !!deployment
34
+ end
35
+
37
36
  def find_deployment(deployments)
38
- if @options[:name]
39
- deployments.find { |d| d['metadata']['name'] == @options[:name] }
37
+ if @options[:deployment]
38
+ deployments.find { |d| d['metadata']['name'] == @options[:deployment] }
40
39
  else
41
40
  deployments.first
42
41
  end
@@ -1,21 +1,10 @@
1
1
  module Kubes::Kubectl::Fetch
2
2
  class Pods < Base
3
- def show
4
- items = fetch_items
5
- # Not checking if deployment exists because kubes will error on `kubes get` from missing deployments already
6
- deployments = items.select { |i| i['kind'] == "Deployment" }
7
-
8
- deployments.each do |deployment|
9
- logger.info "Pods for deployment #{deployment['metadata']['name']}:".color(:green)
10
- show_for(deployment)
11
- end
12
- end
3
+ extend Memoist
13
4
 
14
- def show_for(deployment)
15
- metadata = deployment['metadata']
16
- labels = metadata['labels'].map { |k,v| "#{k}=#{v}" }.join(',')
17
- ns = metadata['namespace']
18
- sh("kubectl get pod -l #{labels} -n #{ns}")
5
+ def show
6
+ return unless namespace
7
+ sh("kubectl get pod -n #{namespace}")
19
8
  end
20
9
  end
21
10
  end
@@ -5,7 +5,7 @@ class Kubes::Kubectl
5
5
 
6
6
  def detect?
7
7
  expr = "#{Kubes.root}/.kubes/resources/**/*"
8
- !!Dir.glob(expr).detect { |p| p.include?("kustomization.yaml") }
8
+ !!Dir.glob(expr).detect { |p| p.include?("kustomization.y") } # allow for both .yml and .yaml to work
9
9
  end
10
10
  memoize :detect?
11
11
  end
@@ -12,9 +12,21 @@ class Kubes::Kubectl
12
12
 
13
13
  "#{role_i}/#{kind_i}"
14
14
  end
15
+
16
+ sorted = filter_files(sorted)
17
+
15
18
  @name == "delete" ? sorted.reverse : sorted
16
19
  end
17
20
 
21
+ def filter_files(sorted)
22
+ skip = Kubes.config.skip
23
+ skip += ENV['KUBES_SKIP'].split(' ') if ENV['KUBES_SKIP']
24
+ return sorted if skip.empty?
25
+ sorted.reject do |file|
26
+ skip.detect { |text| file.include?(text) }
27
+ end
28
+ end
29
+
18
30
  # type: kinds or roles
19
31
  # value: Examples: kind: deployment, role: web
20
32
  def index_for(type, value)
@@ -3,7 +3,8 @@ module Kubes::Util
3
3
  def consider?(path)
4
4
  File.file?(path) &&
5
5
  !path.include?('/resources/base') &&
6
- !path.include?('/base.yaml')
6
+ !path.include?('/base.yaml') &&
7
+ !path.include?('/base.yml')
7
8
  end
8
9
  end
9
10
  end