kubes 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +6 -5
  4. data/docs/_config.yml +1 -1
  5. data/docs/_docs/ci/cloudbuild.md +2 -0
  6. data/docs/_docs/config/args.md +10 -0
  7. data/docs/_docs/config/args/docker.md +19 -0
  8. data/docs/_docs/config/args/kubectl.md +19 -0
  9. data/docs/_docs/config/docker.md +4 -40
  10. data/docs/_docs/config/env.md +1 -1
  11. data/docs/_docs/config/hooks.md +10 -0
  12. data/docs/_docs/config/hooks/docker.md +70 -0
  13. data/docs/_docs/config/hooks/kubectl.md +83 -0
  14. data/docs/_docs/config/hooks/kubes.md +67 -0
  15. data/docs/_docs/config/hooks/ruby.md +74 -0
  16. data/docs/_docs/config/kubectl.md +3 -54
  17. data/docs/_docs/config/reference.md +20 -0
  18. data/docs/_docs/config/skip.md +58 -0
  19. data/docs/_docs/dsl/resources.md +1 -1
  20. data/docs/_docs/dsl/resources/backend_config.md +1 -1
  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/migrations.md +121 -0
  28. data/docs/_includes/commands.html +2 -0
  29. data/docs/_includes/config/hooks/options.md +20 -0
  30. data/docs/_includes/sidebar.html +32 -10
  31. data/docs/_reference/kubes-exec.md +14 -6
  32. data/docs/_reference/kubes-init.md +1 -0
  33. data/docs/_reference/kubes-logs.md +1 -0
  34. data/docs/_sass/theme.scss +25 -1
  35. data/kubes.gemspec +3 -0
  36. data/lib/kubes.rb +3 -0
  37. data/lib/kubes/cli.rb +25 -6
  38. data/lib/kubes/cli/apply.rb +2 -1
  39. data/lib/kubes/cli/base.rb +11 -0
  40. data/lib/kubes/cli/compile.rb +8 -0
  41. data/lib/kubes/cli/delete.rb +1 -1
  42. data/lib/kubes/cli/exec.rb +37 -6
  43. data/lib/kubes/cli/get.rb +3 -2
  44. data/lib/kubes/cli/init.rb +39 -2
  45. data/lib/kubes/cli/logs.rb +50 -3
  46. data/lib/kubes/cli/prune.rb +95 -0
  47. data/lib/kubes/compiler.rb +11 -7
  48. data/lib/kubes/compiler/decorator/base.rb +7 -1
  49. data/lib/kubes/compiler/decorator/{resources/secret.rb → hashable.rb} +5 -4
  50. data/lib/kubes/compiler/decorator/hashable/field.rb +53 -0
  51. data/lib/kubes/compiler/decorator/hashable/storage.rb +19 -0
  52. data/lib/kubes/compiler/decorator/post.rb +77 -0
  53. data/lib/kubes/compiler/decorator/pre.rb +12 -0
  54. data/lib/kubes/compiler/strategy.rb +2 -2
  55. data/lib/kubes/compiler/strategy/base.rb +1 -1
  56. data/lib/kubes/compiler/strategy/result.rb +4 -6
  57. data/lib/kubes/config.rb +16 -11
  58. data/lib/kubes/docker/strategy/build/docker.rb +1 -1
  59. data/lib/kubes/docker/strategy/build/gcloud.rb +1 -1
  60. data/lib/kubes/docker/strategy/image_name.rb +1 -1
  61. data/lib/kubes/docker/strategy/push/docker.rb +1 -1
  62. data/lib/kubes/docker/strategy/push/gcloud.rb +1 -1
  63. data/lib/kubes/docker/strategy/utils.rb +1 -1
  64. data/lib/kubes/hooks/builder.rb +29 -15
  65. data/lib/kubes/hooks/concern.rb +10 -0
  66. data/lib/kubes/hooks/dsl.rb +2 -1
  67. data/lib/kubes/hooks/runner.rb +22 -0
  68. data/lib/kubes/kubectl.rb +21 -18
  69. data/lib/kubes/kubectl/batch.rb +8 -5
  70. data/lib/kubes/kubectl/{decider.rb → dispatcher.rb} +1 -1
  71. data/lib/kubes/kubectl/fetch/base.rb +13 -10
  72. data/lib/kubes/kubectl/fetch/deployment.rb +21 -11
  73. data/lib/kubes/kubectl/fetch/pods.rb +4 -15
  74. data/lib/kubes/kubectl/kustomize.rb +1 -1
  75. data/lib/kubes/kubectl/ordering.rb +12 -0
  76. data/lib/kubes/util/consider.rb +2 -1
  77. data/lib/kubes/util/sh.rb +1 -1
  78. data/lib/kubes/version.rb +1 -1
  79. data/lib/templates/dsl/.kubes/resources/base/all.rb.tt +6 -1
  80. data/lib/templates/dsl/.kubes/resources/shared/namespace.rb.tt +1 -1
  81. data/lib/templates/dsl/.kubes/resources/web/deployment.rb +1 -1
  82. data/lib/templates/yaml/.kubes/resources/base/all.yaml.tt +1 -1
  83. data/lib/templates/yaml/.kubes/resources/shared/namespace.yaml.tt +1 -1
  84. data/lib/templates/yaml/.kubes/resources/web/deployment.yaml.tt +1 -1
  85. data/spec/fixtures/decorators/deployment/both/envFrom.yaml +31 -0
  86. data/spec/fixtures/decorators/deployment/both/valueFrom.yaml +33 -0
  87. data/spec/fixtures/decorators/deployment/both/volumes.yaml +40 -0
  88. data/spec/fixtures/prune/capture.yaml +57 -0
  89. data/spec/fixtures/prune/fetch_items.yaml +268 -0
  90. data/spec/kubes/cli/prune_spec.rb +38 -0
  91. data/spec/kubes/compiler/decorator/{resources → post}/deployment_spec.rb +52 -6
  92. data/spec/kubes/compiler/decorator/{resources → post}/pod_spec.rb +2 -11
  93. metadata +56 -19
  94. data/lib/kubes/compiler/decorator.rb +0 -17
  95. data/lib/kubes/compiler/decorator/compile.rb +0 -12
  96. data/lib/kubes/compiler/decorator/resources/base.rb +0 -13
  97. data/lib/kubes/compiler/decorator/resources/container.rb +0 -76
  98. data/lib/kubes/compiler/decorator/resources/container/mapping.rb +0 -28
  99. data/lib/kubes/compiler/decorator/resources/deployment.rb +0 -10
  100. data/lib/kubes/compiler/decorator/resources/pod.rb +0 -10
  101. data/lib/kubes/compiler/decorator/write.rb +0 -14
  102. data/lib/kubes/docker/strategy/hooks.rb +0 -9
@@ -1,5 +1,6 @@
1
1
  module Kubes
2
2
  class Compiler
3
+ include Kubes::Hooks::Concern
3
4
  include Kubes::Logging
4
5
  include Kubes::Util::Consider
5
6
 
@@ -8,13 +9,16 @@ module Kubes
8
9
  end
9
10
 
10
11
  def run
11
- results = resources.map do |path|
12
- strategy = Strategy.new(@options.merge(path: path))
13
- strategy.compile
14
- end.compact
12
+ Kubes.config # trigger config load. So can set ENV['VAR'] in config/envs/dev.rb etc
13
+ run_hooks("kubes.rb", name: "compile") do
14
+ results = resources.map do |path|
15
+ strategy = Strategy.new(@options.merge(path: path))
16
+ strategy.compile
17
+ end.compact
15
18
 
16
- results.each do |result|
17
- write(result)
19
+ results.each do |result|
20
+ write(result)
21
+ end
18
22
  end
19
23
 
20
24
  puts "Compiled .kubes/resources files to .kubes/output" if show_compiled_message?
@@ -49,7 +53,7 @@ module Kubes
49
53
  end
50
54
 
51
55
  def write(result)
52
- result.write_decorate!
56
+ result.decorate!(:post)
53
57
  filename, content = result.filename, result.content
54
58
  dest = "#{Kubes.root}/.kubes/output/#{filename}"
55
59
 
@@ -1,14 +1,20 @@
1
1
  module Kubes::Compiler::Decorator
2
2
  class Base
3
+ attr_reader :data
3
4
  def initialize(data)
4
5
  @data = data
5
6
  end
6
7
 
8
+ def run
9
+ return @data unless Kubes.config.suffix_hash
10
+ process
11
+ end
12
+
7
13
  def result
8
14
  if @data.is_a?(Kubes::Compiler::Dsl::Core::Blocks)
9
15
  @data.results.each { |k,v| process(v) } # returns nil
10
16
  else
11
- process(@data) # returns Hash
17
+ process # processes and returns @data
12
18
  end
13
19
  @data # important to return @data so we keep the original @data structure: Blocks or Hash
14
20
  end
@@ -1,17 +1,18 @@
1
1
  require 'digest'
2
2
 
3
- module Kubes::Compiler::Decorator::Resources
4
- class Secret < Base
3
+ module Kubes::Compiler::Decorator
4
+ class Hashable < Base
5
5
  include Kubes::Compiler::Util::YamlDump
6
6
 
7
- def perform
7
+ def store
8
8
  # even though name is required, will allow logic to get the kubectl apply and kubectl to surface the required name error
9
9
  name = @data.dig('metadata','name')
10
10
  return @data unless name
11
11
 
12
+ # puts "name #{name}" # TODO: scope Kind so Secret and ConfigMap can have the same name...
12
13
  md5 = md5(@data)
13
14
  @data['metadata']['name'] = "#{name}-#{md5}"
14
- Kubes::Compiler::Decorator.store(name, md5)
15
+ Storage.store(@data['kind'], name, md5)
15
16
  @data
16
17
  end
17
18
 
@@ -0,0 +1,53 @@
1
+ class Kubes::Compiler::Decorator::Hashable
2
+ class Field
3
+ # item is full wrapper structure
4
+ #
5
+ # secretRef: <--- wrapper
6
+ # name: demo-secret
7
+ #
8
+ def initialize(item)
9
+ @item = item
10
+ end
11
+
12
+ def hashable?
13
+ x = @item.keys & map.keys
14
+ !x.empty?
15
+ end
16
+
17
+ def kind
18
+ wrapper =~ /configMap/ ? "ConfigMap" : "Secret"
19
+ end
20
+
21
+ # The key of the hashable value.
22
+ #
23
+ # envFrom:
24
+ # - secretRef:
25
+ # name: demo-secret <--- wrapper is 'name'
26
+ #
27
+ def key
28
+ map[wrapper]
29
+ end
30
+
31
+ # The wrapper field is nested right above the item with the hashable value.
32
+ #
33
+ # envFrom:
34
+ # - secretRef: <--- wrapper
35
+ # name: demo-secret
36
+ #
37
+ def wrapper
38
+ @item.keys.first
39
+ end
40
+
41
+ # wrapper element to key that stores the hashable value
42
+ def map
43
+ {
44
+ 'configMapRef' => 'name',
45
+ 'configMapKeyRef' => 'name',
46
+ 'configMap' => 'name',
47
+ 'secretRef' => 'name',
48
+ 'secretKeyRef' => 'name',
49
+ 'secret' => 'secretName',
50
+ }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,19 @@
1
+ class Kubes::Compiler::Decorator::Hashable
2
+ module Storage
3
+ @@md5s = {}
4
+ def store(kind, name, md5)
5
+ @@md5s[kind] ||= {}
6
+ @@md5s[kind][name] = md5
7
+ end
8
+
9
+ def fetch(kind, name)
10
+ @@md5s[kind] ||= {}
11
+ @@md5s[kind][name]
12
+ end
13
+
14
+ def md5s
15
+ @@md5s
16
+ end
17
+ extend self
18
+ end
19
+ end
@@ -0,0 +1,77 @@
1
+ module Kubes::Compiler::Decorator
2
+ class Post < Base
3
+ def process
4
+ add_hash(@data)
5
+ clean_namespace
6
+ @data
7
+ end
8
+
9
+ def clean_namespace
10
+ return unless @data['kind'] == 'Namespace'
11
+ @data['metadata'].delete('namespace')
12
+ @data
13
+ end
14
+
15
+ def add_hash(item, options={})
16
+ # hashable set from previous stack call
17
+ if options[:hashable_field] && item.is_a?(Hash)
18
+ field = options[:hashable_field]
19
+ value_without_md5 = item[field.key]
20
+ @reset_hashable_field = true unless value_without_md5
21
+ if field.hashable? && value_without_md5
22
+ md5 = Hashable::Storage.fetch(field.kind, value_without_md5)
23
+ v = [value_without_md5, md5].compact.join('-')
24
+ item[field.key] = v
25
+ end
26
+ end
27
+
28
+ options[:hashable_field] ||= hashable_field(item) # set for next stack call
29
+ # Pretty tricky case. Given:
30
+ #
31
+ # envFrom:
32
+ # - secretRef:
33
+ # name: demo-secret
34
+ # - configMapRef:
35
+ # name: demo-config-map
36
+ #
37
+ # Need to reset the stored hashable_field in the call stack.
38
+ # Else the field.kind is cached and the md5 look is incorrect
39
+ # The spec/fixtures/decorators/deployment/both/envFrom.yaml fixture covers this.
40
+ if @reset_hashable_field
41
+ options[:hashable_field] = hashable_field(item)
42
+ @reset_hashable_field = false
43
+ end
44
+ case item
45
+ when Array, Hash
46
+ item.each { |i| add_hash(i, options) }
47
+ end
48
+ item
49
+ end
50
+
51
+ # Returns the nested key name that will be hashable. Examples:
52
+ #
53
+ # 1. envFrom example
54
+ # envFrom:
55
+ # - secretRef:
56
+ # name: demo-secret
57
+ #
58
+ # 2. valueFrom example
59
+ # valueFrom:
60
+ # secretKeyRef:
61
+ # name: demo-secret
62
+ # key: password
63
+ #
64
+ # 3. volumes example
65
+ # volumes:
66
+ # - secret:
67
+ # secretName: demo-secret
68
+ #
69
+ # This is useful to capture for the next level of the stack call
70
+ #
71
+ def hashable_field(item)
72
+ return false unless item.is_a?(Hash)
73
+ field = Hashable::Field.new(item)
74
+ field if field.hashable?
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,12 @@
1
+ module Kubes::Compiler::Decorator
2
+ class Pre < Base
3
+ def process
4
+ case @data['kind']
5
+ when "ConfigMap", "Secret"
6
+ Hashable.new(@data).store
7
+ else
8
+ @data # passthrough
9
+ end
10
+ end
11
+ end
12
+ end
@@ -11,7 +11,7 @@ class Kubes::Compiler
11
11
 
12
12
  strategy = klass.new(@options.merge(path: @path)) # Dsl or Erb
13
13
  result = strategy.run
14
- result.compile_decorate! # compile phase decoration
14
+ result.decorate!(:pre) # compile pre phase decoration
15
15
  result
16
16
  end
17
17
 
@@ -19,7 +19,7 @@ class Kubes::Compiler
19
19
  ext = File.extname(@path)
20
20
  case ext
21
21
  when '.rb' then Dsl
22
- when '.yaml' then Erb
22
+ when '.yaml','.yml' then Erb
23
23
  else Pass
24
24
  end
25
25
  end
@@ -7,7 +7,7 @@ class Kubes::Compiler::Strategy
7
7
  @path = options[:path]
8
8
 
9
9
  @filename = @path.sub(%r{.*\.kubes/resources/},'') # IE: web/deployment.rb or web/deployment.yaml
10
- @save_file = @filename.sub('.rb','.yaml')
10
+ @save_file = @filename.sub('.yml','.yaml').sub('.rb','.yaml')
11
11
  end
12
12
  end
13
13
  end
@@ -11,12 +11,10 @@ class Kubes::Compiler::Strategy
11
11
  @data.respond_to?(:read)
12
12
  end
13
13
 
14
- def compile_decorate!
15
- @data = Kubes::Compiler::Decorator::Compile.new(@data).result
16
- end
17
-
18
- def write_decorate!
19
- @data = Kubes::Compiler::Decorator::Write.new(@data).result
14
+ # decorate(:pre) or decorate(:post)
15
+ def decorate!(phase)
16
+ klass = "Kubes::Compiler::Decorator::#{phase.to_s.camelize}".constantize
17
+ @data = klass.new(@data).result
20
18
  end
21
19
 
22
20
  def content
@@ -11,31 +11,36 @@ module Kubes
11
11
  def defaults
12
12
  config = ActiveSupport::OrderedOptions.new
13
13
 
14
- config.state = ActiveSupport::OrderedOptions.new
15
- config.state.docker_image_path = "#{Kubes.root}/.kubes/state/docker_image.txt"
14
+ config.auto_prune = true
16
15
 
17
- config.logger = Logger.new($stdout)
18
- config.logger.level = ENV['KUBES_LOG_LEVEL'] || :info
16
+ config.builder = "docker" # IE: docker or gcloud
19
17
 
20
18
  # Auto-switching options
21
19
  config.kubectl = ActiveSupport::OrderedOptions.new
22
20
  config.kubectl.context = nil
23
21
  config.kubectl.context_keep = true # after switching context keep it
24
- config.kubectl.exit_on_fail = nil # whether or not continue if the kubectl command fails
25
22
 
26
- config.kubectl.exit_on_fail_for_apply = true # whether or not continue if the kubectl apply command fails
27
- config.kubectl.exit_on_fail_for_delete = false # whether or not continue if the kubectl delete command fails
28
- # Note: not using config.kubectl.delete.exit_on_fail because delete a method internal to ActiveSupport::OrderedOptions
23
+ # whether or not continue if the kubectl command fails
24
+ config.kubectl.exit_on_fail = ActiveSupport::OrderedOptions.new
25
+ config.kubectl.exit_on_fail.apply = true # whether or not continue if the kubectl apply command fails
26
+ config.kubectl.exit_on_fail.delete = false # whether or not continue if the kubectl delete command fails
27
+ # Note: delete is a internal method to ActiveSupport::OrderedOptions so will have to access it with ['...']
29
28
 
30
29
  config.kubectl.order = ActiveSupport::OrderedOptions.new
31
30
  config.kubectl.order.roles = role_order
32
31
  config.kubectl.order.kinds = kind_order
33
32
 
34
- config.suffix_hash = true # append suffix has to ConfigMap and Secret
35
-
36
33
  config.repo = nil # expected to be set by .kubes/config.rb
37
34
 
38
- config.builder = "docker" # IE: docker or gcloud
35
+ config.logger = Logger.new($stdout)
36
+ config.logger.level = ENV['KUBES_LOG_LEVEL'] || :info
37
+
38
+ config.skip = []
39
+
40
+ config.state = ActiveSupport::OrderedOptions.new
41
+ config.state.docker_image_path = "#{Kubes.root}/.kubes/state/docker_image.txt"
42
+
43
+ config.suffix_hash = true # append suffix hash to ConfigMap and Secret
39
44
 
40
45
  config
41
46
  end
@@ -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.include?(hook["on"])
40
54
  end
41
55
  end
42
56
  end