kubes 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/docs/_docs/config/reference.md +1 -0
  4. data/docs/_docs/config/skip.md +1 -1
  5. data/docs/_docs/dsl/multiple-resources.md +3 -3
  6. data/docs/_docs/dsl/resources/job.md +62 -0
  7. data/docs/_docs/extra-env/dsl.md +2 -2
  8. data/docs/_docs/extra-env/yaml.md +1 -1
  9. data/docs/_docs/generators.md +41 -0
  10. data/docs/_docs/helpers.md +10 -3
  11. data/docs/_docs/helpers/aws.md +15 -0
  12. data/docs/_docs/helpers/aws/iam-role.md +91 -0
  13. data/docs/_docs/helpers/aws/secrets.md +129 -0
  14. data/docs/_docs/helpers/aws/ssm.md +76 -0
  15. data/docs/_docs/helpers/custom.md +40 -0
  16. data/docs/_docs/helpers/google.md +17 -0
  17. data/docs/_docs/helpers/google/secrets.md +76 -0
  18. data/docs/_docs/helpers/google/service-account.md +60 -0
  19. data/docs/_docs/intro.md +3 -1
  20. data/docs/_docs/intro/docker-image.md +66 -0
  21. data/docs/_docs/layering/merge.md +1 -1
  22. data/docs/_docs/learn/dsl/delete.md +10 -2
  23. data/docs/_docs/learn/dsl/review-project.md +2 -2
  24. data/docs/_docs/learn/yaml/delete.md +10 -2
  25. data/docs/_docs/learn/yaml/review-project.md +2 -2
  26. data/docs/_docs/patterns/clock-web-worker.md +3 -3
  27. data/docs/_docs/patterns/migrations.md +1 -1
  28. data/docs/_docs/patterns/secrets.md +0 -2
  29. data/docs/_docs/yaml.md +2 -2
  30. data/docs/_includes/commands.html +2 -2
  31. data/docs/_includes/helpers/base64.md +1 -0
  32. data/docs/_includes/layering/layers.md +1 -1
  33. data/docs/_includes/sidebar.html +23 -1
  34. data/docs/_reference/kubes-delete.md +1 -1
  35. data/docs/_reference/kubes-exec.md +17 -1
  36. data/docs/_reference/kubes-init.md +2 -2
  37. data/docs/_reference/kubes-logs.md +2 -1
  38. data/docs/_reference/kubes-new.md +58 -0
  39. data/docs/_reference/kubes-prune.md +22 -0
  40. data/docs/reference.md +2 -0
  41. data/kubes.gemspec +1 -0
  42. data/lib/kubes.rb +3 -1
  43. data/lib/kubes/autoloader.rb +9 -0
  44. data/lib/kubes/cli.rb +9 -1
  45. data/lib/kubes/cli/build.rb +6 -0
  46. data/lib/kubes/cli/compile.rb +7 -0
  47. data/lib/kubes/cli/deploy.rb +1 -6
  48. data/lib/kubes/cli/exec.rb +5 -1
  49. data/lib/kubes/cli/help/exec.md +15 -0
  50. data/lib/kubes/cli/help/new.md +30 -0
  51. data/lib/kubes/cli/init.rb +1 -1
  52. data/lib/kubes/cli/new.rb +97 -0
  53. data/lib/kubes/cli/sequence.rb +1 -0
  54. data/lib/kubes/command.rb +7 -0
  55. data/lib/kubes/compiler.rb +19 -21
  56. data/lib/kubes/compiler/dsl/syntax/job.rb +217 -0
  57. data/lib/kubes/compiler/shared/custom_helpers.rb +17 -0
  58. data/lib/kubes/compiler/shared/helpers.rb +12 -3
  59. data/lib/kubes/compiler/shared/helpers/deprecated.rb +37 -0
  60. data/lib/kubes/compiler/strategy/base.rb +3 -3
  61. data/lib/kubes/compiler/strategy/dsl.rb +3 -2
  62. data/lib/kubes/compiler/strategy/erb.rb +10 -1
  63. data/lib/kubes/compiler/strategy/erb/yaml_error.rb +60 -0
  64. data/lib/kubes/compiler/util/normalize.rb +1 -1
  65. data/lib/kubes/compiler/util/save_file.rb +8 -0
  66. data/lib/kubes/config.rb +1 -1
  67. data/lib/kubes/core.rb +6 -0
  68. data/lib/kubes/docker/strategy/image_name.rb +1 -1
  69. data/lib/kubes/kubectl.rb +5 -23
  70. data/lib/kubes/kubectl/batch.rb +27 -33
  71. data/lib/kubes/kubectl/ordering.rb +42 -0
  72. data/lib/kubes/version.rb +1 -1
  73. data/lib/templates/base/.kubes/config.rb.tt +1 -1
  74. data/lib/templates/base/.kubes/config/env/dev.rb +1 -1
  75. data/lib/templates/base/.kubes/config/env/prod.rb +1 -1
  76. data/lib/templates/dsl/.kubes/resources/web/deployment.rb +1 -1
  77. data/lib/templates/new/dsl/backend_config.rb +10 -0
  78. data/lib/templates/new/dsl/config_map.rb +5 -0
  79. data/lib/templates/new/dsl/daemon_set.rb +11 -0
  80. data/lib/templates/new/dsl/deployment.rb +4 -0
  81. data/lib/templates/new/dsl/ingress.rb +3 -0
  82. data/lib/templates/new/dsl/job.rb +2 -0
  83. data/lib/templates/new/dsl/managed_certificate.rb +2 -0
  84. data/lib/templates/new/dsl/namespace.rb +2 -0
  85. data/lib/templates/new/dsl/network_policy.rb +7 -0
  86. data/lib/templates/new/dsl/pod.rb +6 -0
  87. data/lib/templates/new/dsl/role.rb +4 -0
  88. data/lib/templates/new/dsl/role_binding.rb +7 -0
  89. data/lib/templates/new/dsl/secret.rb +5 -0
  90. data/lib/templates/new/dsl/service.rb +2 -0
  91. data/lib/templates/new/dsl/service_account.rb +1 -0
  92. data/lib/templates/new/yaml/backend_config.yaml +10 -0
  93. data/lib/templates/new/yaml/config_map.yaml +9 -0
  94. data/lib/templates/new/yaml/daemon_set.yaml +11 -0
  95. data/lib/templates/new/yaml/deployment.yaml +20 -0
  96. data/lib/templates/new/yaml/ingress.yaml +12 -0
  97. data/lib/templates/new/yaml/job.yaml +19 -0
  98. data/lib/templates/new/yaml/managed_certificate.yaml +7 -0
  99. data/lib/templates/new/yaml/namespace.yaml +6 -0
  100. data/lib/templates/new/yaml/network_policy.yaml +20 -0
  101. data/lib/templates/new/yaml/pod.yaml +11 -0
  102. data/lib/templates/new/yaml/role.yaml +13 -0
  103. data/lib/templates/new/yaml/role_binding.yaml +11 -0
  104. data/lib/templates/new/yaml/secret.yaml +9 -0
  105. data/lib/templates/new/yaml/service.yaml +14 -0
  106. data/lib/templates/new/yaml/service_account.yaml +4 -0
  107. data/lib/templates/yaml/.kubes/resources/base/all.yaml.tt +2 -0
  108. data/lib/templates/yaml/.kubes/resources/web/deployment.yaml.tt +1 -1
  109. data/spec/kubes/cli/prune_spec.rb +1 -0
  110. data/spec/kubes/compiler_spec.rb +5 -1
  111. metadata +67 -2
@@ -0,0 +1,37 @@
1
+ module Kubes::Compiler::Shared::Helpers
2
+ class Deprecated
3
+ def built_image
4
+ puts "DEPRECATED: built_image is deprecated, use docker_image helper instead.".color(:yellow)
5
+ print_source
6
+ end
7
+
8
+ def error_info
9
+ error_info = caller.find { |l| l.include?('.kubes/resources') }
10
+ path, line_number, _ = error_info.split(':')
11
+ {path: path, line_number: line_number}
12
+ end
13
+
14
+ def print_source
15
+ info = error_info
16
+ path = info[:path]
17
+ line_number = info[:line_number].to_i
18
+
19
+ pretty_path = path.sub("#{Kubes.root}/",'')
20
+ puts "Here's the line in #{pretty_path} that calls built_image:\n\n"
21
+
22
+ contents = IO.read(path)
23
+ content_lines = contents.split("\n")
24
+ context = 5 # lines of context
25
+ top, bottom = [line_number-context-1, 0].max, line_number+context-1
26
+ lpad = content_lines.size.to_s.size
27
+ content_lines[top..bottom].each_with_index do |line_content, index|
28
+ current_line = top+index+1
29
+ if current_line == line_number
30
+ printf("%#{lpad}d %s\n".color(:red), current_line, line_content)
31
+ else
32
+ printf("%#{lpad}d %s\n", current_line, line_content)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,13 +1,13 @@
1
1
  class Kubes::Compiler::Strategy
2
2
  class Base
3
3
  include Kubes::Logging
4
+ include Kubes::Compiler::Util::SaveFile
5
+ include Kubes::Compiler::Shared::CustomHelpers
4
6
 
5
7
  def initialize(options={})
6
8
  @options = options
7
9
  @path = options[:path]
8
-
9
- @filename = @path.sub(%r{.*\.kubes/resources/},'') # IE: web/deployment.rb or web/deployment.yaml
10
- @save_file = @filename.sub('.yml','.yaml').sub('.rb','.yaml')
10
+ @save_file = save_file(@path)
11
11
  end
12
12
  end
13
13
  end
@@ -3,6 +3,7 @@ class Kubes::Compiler::Strategy
3
3
  include Kubes::Compiler::Util::Normalize
4
4
 
5
5
  def run
6
+ load_custom_helpers
6
7
  dsl = dsl_class.new(@options) # Deployment, Service, etc
7
8
  data = dsl.run
8
9
  Result.new(@save_file, data)
@@ -17,7 +18,7 @@ class Kubes::Compiler::Strategy
17
18
  end
18
19
 
19
20
  def syntax_class
20
- klass_name = normalize_kind(@filename)
21
+ klass_name = normalize_kind(@save_file)
21
22
  "Kubes::Compiler::Dsl::Syntax::#{klass_name}".constantize
22
23
  rescue NameError
23
24
  logger.debug "Using default resource for: #{klass_name}"
@@ -25,7 +26,7 @@ class Kubes::Compiler::Strategy
25
26
  end
26
27
 
27
28
  def block_form?
28
- type = extract_type(@filename)
29
+ type = extract_type(@save_file)
29
30
  type.pluralize == type
30
31
  end
31
32
  end
@@ -4,10 +4,12 @@ class Kubes::Compiler::Strategy
4
4
  class Erb < Base
5
5
  extend Kubes::Compiler::Dsl::Core::Fields
6
6
  include Kubes::Compiler::Dsl::Core::Helpers
7
+ include Kubes::Compiler::Shared::CustomHelpers
7
8
  include Kubes::Compiler::Shared::Helpers
8
9
  include Kubes::Compiler::Layering
9
10
 
10
11
  def run
12
+ load_custom_helpers
11
13
  @data = {}
12
14
 
13
15
  render_files(pre_layers)
@@ -32,11 +34,18 @@ class Kubes::Compiler::Strategy
32
34
  def render_result(path)
33
35
  if File.exist?(path)
34
36
  yaml = RenderMePretty.result(path, context: self)
35
- result = YAML.load(yaml)
37
+ result = yaml_load(path, yaml)
36
38
  result.is_a?(Hash) ? result : {} # in case of blank yaml doc a Boolean false is returned
37
39
  else
38
40
  {}
39
41
  end
40
42
  end
43
+
44
+ def yaml_load(path, yaml)
45
+ YAML.load(yaml)
46
+ rescue Psych::SyntaxError
47
+ YamlError.new(path, yaml).show
48
+ exit 1
49
+ end
41
50
  end
42
51
  end
@@ -0,0 +1,60 @@
1
+ class Kubes::Compiler::Strategy::Erb
2
+ class YamlError
3
+ include Kubes::Compiler::Util::SaveFile
4
+
5
+ def initialize(path, rendered_yaml)
6
+ @path, @rendered_yaml = path, rendered_yaml
7
+ end
8
+
9
+ def show
10
+ FileUtils.mkdir_p(File.dirname(dest))
11
+ IO.write(dest, @rendered_yaml)
12
+ show_error(dest)
13
+ end
14
+
15
+ def dest
16
+ save_file = save_file(@path)
17
+ "#{Kubes.root}/.kubes/output/#{save_file}"
18
+ end
19
+
20
+ def show_error(path)
21
+ text = IO.read(path)
22
+ begin
23
+ YAML.load(text)
24
+ rescue Psych::SyntaxError => e
25
+ handle_yaml_syntax_error(e, path)
26
+ end
27
+ end
28
+
29
+ def handle_yaml_syntax_error(e, path)
30
+ io = StringIO.new
31
+ io.puts "Invalid yaml. Output written for debugging: #{path}".color(:red)
32
+ io.puts "ERROR: #{e.message}".color(:red)
33
+
34
+ # Grab line info. Example error:
35
+ # ERROR: (<unknown>): could not find expected ':' while scanning a simple key at line 2 column 1
36
+ md = e.message.match(/at line (\d+) column (\d+)/)
37
+ line = md[1].to_i
38
+
39
+ lines = IO.read(path).split("\n")
40
+ context = 5 # lines of context
41
+ top, bottom = [line-context-1, 0].max, line+context-1
42
+ spacing = lines.size.to_s.size
43
+ lines[top..bottom].each_with_index do |line_content, index|
44
+ line_number = top+index+1
45
+ if line_number == line
46
+ io.printf("%#{spacing}d %s\n".color(:red), line_number, line_content)
47
+ else
48
+ io.printf("%#{spacing}d %s\n", line_number, line_content)
49
+ end
50
+ end
51
+
52
+ if ENV['KUBES_TEST']
53
+ io.string
54
+ else
55
+ puts io.string
56
+ exit 1
57
+ end
58
+ end
59
+ end
60
+ end
@@ -5,7 +5,7 @@ module Kubes::Compiler::Util
5
5
  end
6
6
 
7
7
  def extract_type(path)
8
- File.basename(path).sub('.rb','').sub(/-.*/,'')
8
+ File.basename(path).sub('.yaml','').sub('.yml','').sub('.rb','').sub(/-.*/,'')
9
9
  end
10
10
  end
11
11
  end
@@ -0,0 +1,8 @@
1
+ module Kubes::Compiler::Util
2
+ module SaveFile
3
+ def save_file(path)
4
+ filename = path.sub(%r{.*\.kubes/resources/},'') # IE: web/deployment.rb or web/deployment.yaml
5
+ filename.sub('.yml','.yaml').sub('.rb','.yaml')
6
+ end
7
+ end
8
+ end
@@ -32,7 +32,7 @@ module Kubes
32
32
 
33
33
  config.repo = nil # expected to be set by .kubes/config.rb
34
34
 
35
- config.logger = Logger.new($stdout)
35
+ config.logger = Logger.new($stderr)
36
36
  config.logger.level = ENV['KUBES_LOG_LEVEL'] || :info
37
37
 
38
38
  config.skip = []
@@ -28,5 +28,11 @@ module Kubes
28
28
  def kustomize?
29
29
  Kubectl::Kustomize.detect?
30
30
  end
31
+
32
+ def check_project!
33
+ return if File.exist?("#{Kubes.root}/.kubes/config.rb")
34
+ logger.error "ERROR: It doesnt look like this is a kubes project. Are you sure you are in a kubes project?".color(:red)
35
+ ENV['TS_TEST'] ? raise : exit(1)
36
+ end
31
37
  end
32
38
  end
@@ -30,7 +30,7 @@ module Kubes::Docker::Strategy
30
30
  return "tongueroo/demo-kubes:kubes-12345678" if ENV['TEST']
31
31
 
32
32
  unless File.exist?(image_state_path)
33
- puts "Unable to find #{image_state_path} which contains the last docker image name built with kubes build. Please run `kubes docker build` first."
33
+ logger.error "ERROR: Unable to find #{image_state_path} which contains the last docker image name built with kubes build. Please run `kubes docker build` first."
34
34
  exit 1
35
35
  end
36
36
  IO.read(image_state_path).strip
@@ -17,13 +17,11 @@ module Kubes
17
17
  params = args.flatten.join(' ')
18
18
  args = "#{@name} #{params}" # @name: apply or delete
19
19
 
20
- switch_context do
21
- run_hooks("kubectl.rb", name: @name, file: @options[:file]) do
22
- if options[:capture]
23
- self.class.capture(args, options) # already includes kubectl
24
- else
25
- self.class.execute(args, options)
26
- end
20
+ run_hooks("kubectl.rb", name: @name, file: @options[:file]) do
21
+ if options[:capture]
22
+ self.class.capture(args, options) # already includes kubectl
23
+ else
24
+ self.class.execute(args, options)
27
25
  end
28
26
  end
29
27
  end
@@ -44,22 +42,6 @@ module Kubes
44
42
  Kubes.config.kubectl.exit_on_fail[@name]
45
43
  end
46
44
 
47
- def switch_context(&block)
48
- kubectl = Kubes.config.kubectl
49
- context = kubectl.context
50
- if context
51
- previous_context = capture("kubectl config current-context")
52
- sh("kubectl config use-context #{context}", mute: true)
53
- result = block.call
54
- if !previous_context.blank? && !kubectl.context_keep
55
- sh("kubectl config use-context #{previous_context}", mute: true)
56
- end
57
- result
58
- else
59
- block.call
60
- end
61
- end
62
-
63
45
  def args
64
46
  # base at end in case of redirection. IE: command > /path
65
47
  custom.args + default.args
@@ -3,6 +3,7 @@ class Kubes::Kubectl
3
3
  include Kubes::Hooks::Concern
4
4
  include Kubes::Logging
5
5
  include Kubes::Util::Consider
6
+ include Kubes::Util::Sh
6
7
  include Ordering
7
8
 
8
9
  def initialize(name, options={})
@@ -12,48 +13,41 @@ class Kubes::Kubectl
12
13
  def run
13
14
  # @options[:preview] is really only used for kubectl delete
14
15
  logger.info "Will run:" if @options[:preview]
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))
16
+ switch_context do
17
+ run_hooks("kubes.rb", name: @name) do
18
+ sorted_files.each do |file|
19
+ if @options[:preview]
20
+ logger.info " kubectl #{@name} -f #{file}"
21
+ else
22
+ Kubes::Kubectl.run(@name, @options.merge(file: file))
23
+ end
21
24
  end
22
25
  end
23
26
  end
24
27
  end
25
28
 
26
- # kubes apply # {role: nil, resource: nil}
27
- # kubes apply clock # {role: "clock", resource: nil}
28
- # kubes apply clock deployment # {role: "clock", resource: "deployment"}
29
- def search_expr
30
- role, resource = @options[:role], @options[:resource]
31
- if role && resource
32
- "#{Kubes.root}/.kubes/output/#{role}/#{resource}.yaml"
33
- elsif role
34
- "#{Kubes.root}/.kubes/output/#{role}/*.yaml"
35
- else
36
- "#{Kubes.root}/.kubes/output/**/*.yaml"
37
- end
38
- end
29
+ def switch_context(&block)
30
+ kubectl = Kubes.config.kubectl
31
+ context = kubectl.context
39
32
 
40
- def files
41
- files = []
42
- Dir.glob(search_expr).each do |path|
43
- next unless process?(path)
44
- file = path.sub("#{Kubes.root}/", '')
45
- files << file
33
+ unless context
34
+ block.call
35
+ return
46
36
  end
47
- files
48
- end
49
37
 
50
- def process?(path)
51
- consider?(path) && two_levels_deep?(path)
52
- end
38
+ previous_context = sh_capture("kubectl config current-context")
39
+ if previous_context == context
40
+ block.call
41
+ return
42
+ end
53
43
 
54
- def two_levels_deep?(path)
55
- rel_path = path.sub(%r{.*\.kubes/output/},'')
56
- rel_path.split('/').size == 2
44
+ logger.debug "Switching kubectl context to: #{context}"
45
+ sh("kubectl config use-context #{context}", mute: true)
46
+ result = block.call
47
+ if !previous_context.blank? && !kubectl.context_keep
48
+ sh("kubectl config use-context #{previous_context}", mute: true)
49
+ end
50
+ result
57
51
  end
58
52
  end
59
53
  end
@@ -35,5 +35,47 @@ class Kubes::Kubectl
35
35
  i = index.to_s.rjust(3, "0") # pad with 0
36
36
  "#{i}-#{value}" # append name so that terms with same index get order alphabetically
37
37
  end
38
+
39
+ # kubes apply # {role: nil, resource: nil}
40
+ # kubes apply clock # {role: "clock", resource: nil}
41
+ # kubes apply clock deployment # {role: "clock", resource: "deployment"}
42
+ def search_expr
43
+ role, resource = @options[:role], @options[:resource]
44
+ if role && resource
45
+ "#{Kubes.root}/.kubes/output/#{role}/#{resource}.yaml"
46
+ elsif role
47
+ "#{Kubes.root}/.kubes/output/#{role}/*.yaml"
48
+ else
49
+ "#{Kubes.root}/.kubes/output/**/*.yaml"
50
+ end
51
+ end
52
+
53
+ def files
54
+ files = []
55
+ Dir.glob(search_expr).each do |path|
56
+ next unless process?(path)
57
+ file = path.sub("#{Kubes.root}/", '')
58
+ files << file
59
+ end
60
+ files
61
+ end
62
+
63
+ # Only considering files 2 layers deep. So:
64
+ #
65
+ # Yes = web/deployment.yaml
66
+ # No = web/deployment/dev.yaml
67
+ #
68
+ def process?(path)
69
+ if Kubes.kustomize?
70
+ File.file?(path)
71
+ else
72
+ consider?(path) && two_levels_deep?(path)
73
+ end
74
+ end
75
+
76
+ def two_levels_deep?(path)
77
+ rel_path = path.sub(%r{.*\.kubes/(resources|output)/},'')
78
+ rel_path.split('/').size == 2
79
+ end
38
80
  end
39
81
  end
@@ -1,3 +1,3 @@
1
1
  module Kubes
2
- VERSION = "0.4.3"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -2,7 +2,7 @@ Kubes.configure do |config|
2
2
  config.repo = "<%= @options[:repo] %>"
3
3
  config.logger.level = "info"
4
4
  # auto-switching
5
- # config.kubectl.context = "dev-services"
5
+ # config.kubectl.context = "dev-cluster"
6
6
  # config.kubectl.context_keep = true # keep context after switching
7
7
  # config.kubectl.exit_on_fail_for_apply = true # whether or not continue if the kubectl command fails
8
8
  # config.kubectl.exit_on_fail_for_delete = false # whether or not continue if the kubectl command fails
@@ -2,5 +2,5 @@
2
2
  #
3
3
  # Example:
4
4
  # Kubes.configure do |config|
5
- # config.kubectl.context = "dev-services"
5
+ # config.kubectl.context = "dev-cluster"
6
6
  # end
@@ -2,5 +2,5 @@
2
2
  #
3
3
  # Example:
4
4
  # Kubes.configure do |config|
5
- # config.kubectl.context = "prod-services"
5
+ # config.kubectl.context = "prod-cluster"
6
6
  # end
@@ -2,7 +2,7 @@ name "web"
2
2
  labels(role: "web")
3
3
 
4
4
  replicas 1 # overridden on a env basis
5
- image built_image # IE: user/<%= app %>:kubes-2020-06-13T19-55-16-43afc6e
5
+ image docker_image # IE: user/<%= app %>:kubes-2020-06-13T19-55-16-43afc6e
6
6
 
7
7
  # revisionHistoryLimit 1 # uncomment to reduce old ReplicaSets, default is 10 https://bit.ly/3hqrzyP
8
8
  # maxUnavailable 25
@@ -0,0 +1,10 @@
1
+ name "<%= app %>"
2
+ spec(
3
+ timeoutSec: 40,
4
+ connectionDraining: {
5
+ drainingTimeoutSec: 60,
6
+ },
7
+ sessionAffinity: {
8
+ affinityType: "CLIENT_IP",
9
+ }
10
+ )