kubes 0.3.1 → 0.4.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -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/yaml/new-project.md +0 -1
  23. data/docs/_docs/{auto-context.md → misc/auto-context.md} +0 -0
  24. data/docs/_docs/{kustomize.md → misc/kustomize.md} +0 -0
  25. data/docs/_docs/misc/separate-steps.md +21 -0
  26. data/docs/_docs/patterns/migrations.md +121 -0
  27. data/docs/_includes/commands.html +2 -0
  28. data/docs/_includes/config/hooks/options.md +20 -0
  29. data/docs/_includes/sidebar.html +32 -10
  30. data/docs/_reference/kubes-exec.md +14 -6
  31. data/docs/_reference/kubes-init.md +1 -0
  32. data/docs/_reference/kubes-logs.md +1 -0
  33. data/docs/_sass/theme.scss +27 -2
  34. data/docs/img/logos/kubes-white.png +0 -0
  35. data/docs/index.html +2 -2
  36. data/lib/kubes/cli.rb +25 -6
  37. data/lib/kubes/cli/apply.rb +2 -1
  38. data/lib/kubes/cli/base.rb +11 -0
  39. data/lib/kubes/cli/delete.rb +1 -1
  40. data/lib/kubes/cli/exec.rb +37 -6
  41. data/lib/kubes/cli/get.rb +3 -2
  42. data/lib/kubes/cli/init.rb +34 -2
  43. data/lib/kubes/cli/logs.rb +52 -3
  44. data/lib/kubes/cli/prune.rb +95 -0
  45. data/lib/kubes/compiler.rb +18 -7
  46. data/lib/kubes/compiler/decorator/base.rb +7 -1
  47. data/lib/kubes/compiler/decorator/{resources/secret.rb → hashable.rb} +5 -4
  48. data/lib/kubes/compiler/decorator/hashable/field.rb +53 -0
  49. data/lib/kubes/compiler/decorator/hashable/storage.rb +19 -0
  50. data/lib/kubes/compiler/decorator/post.rb +77 -0
  51. data/lib/kubes/compiler/decorator/pre.rb +12 -0
  52. data/lib/kubes/compiler/strategy.rb +2 -2
  53. data/lib/kubes/compiler/strategy/base.rb +1 -1
  54. data/lib/kubes/compiler/strategy/result.rb +4 -6
  55. data/lib/kubes/config.rb +16 -11
  56. data/lib/kubes/docker/strategy/build/docker.rb +1 -1
  57. data/lib/kubes/docker/strategy/build/gcloud.rb +1 -1
  58. data/lib/kubes/docker/strategy/image_name.rb +1 -1
  59. data/lib/kubes/docker/strategy/push/docker.rb +1 -1
  60. data/lib/kubes/docker/strategy/push/gcloud.rb +1 -1
  61. data/lib/kubes/docker/strategy/utils.rb +1 -1
  62. data/lib/kubes/hooks/builder.rb +29 -15
  63. data/lib/kubes/hooks/concern.rb +10 -0
  64. data/lib/kubes/hooks/dsl.rb +2 -1
  65. data/lib/kubes/hooks/runner.rb +22 -0
  66. data/lib/kubes/kubectl.rb +21 -18
  67. data/lib/kubes/kubectl/batch.rb +8 -5
  68. data/lib/kubes/kubectl/{decider.rb → dispatcher.rb} +1 -1
  69. data/lib/kubes/kubectl/fetch/base.rb +13 -10
  70. data/lib/kubes/kubectl/fetch/deployment.rb +21 -11
  71. data/lib/kubes/kubectl/fetch/pods.rb +4 -15
  72. data/lib/kubes/kubectl/kustomize.rb +1 -1
  73. data/lib/kubes/kubectl/ordering.rb +12 -0
  74. data/lib/kubes/util/consider.rb +2 -1
  75. data/lib/kubes/util/sh.rb +1 -1
  76. data/lib/kubes/version.rb +1 -1
  77. data/lib/templates/dsl/.kubes/resources/base/all.rb.tt +6 -1
  78. data/lib/templates/dsl/.kubes/resources/shared/namespace.rb.tt +1 -1
  79. data/lib/templates/dsl/.kubes/resources/web/deployment.rb +1 -1
  80. data/lib/templates/yaml/.kubes/resources/base/all.yaml.tt +1 -1
  81. data/lib/templates/yaml/.kubes/resources/shared/namespace.yaml.tt +1 -1
  82. data/lib/templates/yaml/.kubes/resources/web/deployment.yaml.tt +1 -1
  83. data/spec/fixtures/decorators/deployment/both/envFrom.yaml +31 -0
  84. data/spec/fixtures/prune/capture.yaml +57 -0
  85. data/spec/fixtures/prune/fetch_items.yaml +268 -0
  86. data/spec/kubes/cli/prune_spec.rb +38 -0
  87. data/spec/kubes/compiler/decorator/{resources → post}/deployment_spec.rb +25 -6
  88. data/spec/kubes/compiler/decorator/{resources → post}/pod_spec.rb +2 -11
  89. metadata +39 -19
  90. data/lib/kubes/compiler/decorator.rb +0 -17
  91. data/lib/kubes/compiler/decorator/compile.rb +0 -12
  92. data/lib/kubes/compiler/decorator/resources/base.rb +0 -13
  93. data/lib/kubes/compiler/decorator/resources/container.rb +0 -76
  94. data/lib/kubes/compiler/decorator/resources/container/mapping.rb +0 -28
  95. data/lib/kubes/compiler/decorator/resources/deployment.rb +0 -10
  96. data/lib/kubes/compiler/decorator/resources/pod.rb +0 -10
  97. data/lib/kubes/compiler/decorator/write.rb +0 -14
  98. data/lib/kubes/docker/strategy/hooks.rb +0 -9
@@ -20,6 +20,7 @@ a, --app=APP # Docker repo name. Example: web. Generates .ku
20
20
  t, [--type=TYPE] # Type: dsl or yaml
21
21
  # Default: yaml
22
22
  --repo=REPO # Docker repo name. Example: user/repo. Configures .kubes/config.rb
23
+ n, [--namespace=NAMESPACE] # Namespace to use, defaults to the app option
23
24
  [--verbose], [--no-verbose]
24
25
  [--noop], [--no-noop]
25
26
  ```
@@ -18,6 +18,7 @@ logs from all deployment pods
18
18
  [--compile], [--no-compile] # whether or not to compile the .kube/resources
19
19
  # Default: true
20
20
  n, [--name=NAME] # deployment name to use. IE: demo-web
21
+ c, [--container=CONTAINER] # Container name. If omitted, the first container in the pod will be chosen
21
22
  f, [--follow], [--no-follow] # Follow logs
22
23
  # Default: true
23
24
  [--verbose], [--no-verbose]
@@ -13,12 +13,13 @@
13
13
  margin: auto;
14
14
  text-align: center;
15
15
  display: block;
16
+ width: 80%;
16
17
  padding-bottom: 30px;
17
18
 
18
19
  @media (min-width: 575px) {
19
20
  position: absolute;
20
21
  top: 50%;
21
- max-width: none;
22
+ width: 100%;
22
23
  margin: 0;
23
24
  transform: translateY(-50%);
24
25
  }
@@ -289,4 +290,28 @@ ul.toc {
289
290
  text-align: center;
290
291
  padding: 20px;
291
292
  }
292
- }
293
+ }
294
+
295
+ // https://coolestguidesontheplanet.com/videodrome/youtube/
296
+ // I added another box wrapper to control the width
297
+ .video-box {
298
+ max-width: 600px;
299
+ text-align: left;
300
+ margin: 0 auto 0 0;
301
+ padding-bottom: 20px;
302
+ .video-container {
303
+ position:relative;
304
+ padding-bottom:56.25%;
305
+ padding-top:30px;
306
+ height:0;
307
+ overflow:hidden;
308
+ }
309
+
310
+ .video-container iframe, .video-container object, .video-container embed {
311
+ position:absolute;
312
+ top:0;
313
+ left:0;
314
+ width:100%;
315
+ height:100%;
316
+ }
317
+ }
@@ -9,13 +9,13 @@ sidebar: false
9
9
  <div class="col-sm-8 col-sm-offset-2">
10
10
  <div class="header-content">
11
11
  <div class="header-content-inner">
12
- <h1>Kubes</h1>
12
+ <h1>Kubes: Kubernetes Deployment Tool</h1>
13
13
  <h2>Kubes is a Kubernetes Deployment Tool. It builds the docker image, creates the Kubernetes YAML, and runs kubectl apply. It automates the deployment process and saves you precious finger-typing energy.</h2>
14
14
  </div>
15
15
  </div>
16
16
  </div>
17
17
  <div class="col-sm-4">
18
- <img src="/img/logos/kubes-sign.png" class="homepage-logo" />
18
+ <img src="/img/logos/kubes-white.png" class="homepage-logo" />
19
19
  </div>
20
20
  </div>
21
21
  </div>
@@ -9,8 +9,17 @@ module Kubes
9
9
  compile_option = Proc.new {
10
10
  option :compile, type: :boolean, default: true, desc: "whether or not to compile the .kube/resources"
11
11
  }
12
- name_option = Proc.new {
13
- option :name, aliases: %w[n], desc: "deployment name to use. IE: demo-web"
12
+ pod_option = Proc.new {
13
+ option :pod, aliases: %w[p], desc: "pod to use. IE: web"
14
+ }
15
+ deployment_option = Proc.new {
16
+ option :deployment, aliases: %w[d], desc: "deployment name to use. IE: demo-web"
17
+ }
18
+ container_option = Proc.new {
19
+ option :container, aliases: %w[c], desc: "Container name. If omitted, the first container in the pod will be chosen"
20
+ }
21
+ yes_option = Proc.new {
22
+ option :yes, aliases: %w[y], type: :boolean, desc: "Skip are you sure prompt"
14
23
  }
15
24
 
16
25
  desc "docker SUBCOMMAND", "Docker subcommands"
@@ -42,7 +51,7 @@ module Kubes
42
51
  desc "delete [ROLE] [RESOURCE]", "Delete Kubernetes resources within the app folder"
43
52
  long_desc Help.text(:delete)
44
53
  image_option.call
45
- option :yes, aliases: %w[y], type: :boolean, desc: "Skip are you sure prompt"
54
+ yes_option.call
46
55
  def delete(role=nil, resource=nil)
47
56
  Delete.new(options.merge(role: role, resource: resource)).run
48
57
  end
@@ -66,8 +75,9 @@ module Kubes
66
75
  desc "exec", "Exec into the latest container from the deployment"
67
76
  long_desc Help.text(:exec)
68
77
  compile_option.call
69
- name_option.call
70
- option :container, aliases: %w[c], desc: "Container name. If omitted, the first container in the pod will be chosen"
78
+ pod_option.call
79
+ deployment_option.call
80
+ container_option.call
71
81
  def exec(*cmd)
72
82
  Exec.new(options.merge(cmd: cmd)).run
73
83
  end
@@ -85,12 +95,21 @@ module Kubes
85
95
  desc "logs", "logs from all deployment pods"
86
96
  long_desc Help.text(:logs)
87
97
  compile_option.call
88
- name_option.call
98
+ pod_option.call
99
+ deployment_option.call
100
+ container_option.call
89
101
  option :follow, aliases: %w[f], type: :boolean, default: true, desc: "Follow logs"
90
102
  def logs(*cmd)
91
103
  Logs.new(options.merge(cmd: cmd)).run
92
104
  end
93
105
 
106
+ desc "prune", "Prune old resources like secret and config maps"
107
+ long_desc Help.text(:prune)
108
+ yes_option.call
109
+ def prune
110
+ Prune.new(options).run
111
+ end
112
+
94
113
  long_desc Help.text(:init)
95
114
  Init.options.each { |args| option(*args) }
96
115
  register(Init, "init", "init", "Init project")
@@ -3,7 +3,8 @@ class Kubes::CLI
3
3
  def run
4
4
  compile
5
5
  logger.info "Deploying kubes resources"
6
- Kubes::Kubectl::Decider.new(:apply, @options).run
6
+ Kubes::Kubectl::Dispatcher.new(:apply, @options).run
7
+ Prune.new(@options.merge(yes: true, quiet: true)).run if Kubes.config.auto_prune # prune old secrets and config maps
7
8
  end
8
9
  end
9
10
  end
@@ -9,5 +9,16 @@ class Kubes::CLI
9
9
  def compile
10
10
  Compile.new(@options).run unless @options[:compile] == false
11
11
  end
12
+
13
+ def pod_name
14
+ return unless @options[:pod]
15
+
16
+ pods = Kubes::Kubectl::Fetch::Pods.new(@options)
17
+ items = pods.fetch(:pod)
18
+ metas = items.map { |i| i['metadata'] }
19
+ metas.select! { |i| i['name'].include?(@options[:pod]) }
20
+ meta = metas.sort { i['creationTimestamp'] }.last
21
+ meta['name'] if meta
22
+ end
12
23
  end
13
24
  end
@@ -10,7 +10,7 @@ class Kubes::CLI
10
10
  end
11
11
 
12
12
  def perform(preview: false)
13
- Kubes::Kubectl::Decider.new(:delete, @options.merge(preview: preview)).run
13
+ Kubes::Kubectl::Dispatcher.new(:delete, @options.merge(preview: preview)).run
14
14
  end
15
15
  end
16
16
  end
@@ -1,15 +1,49 @@
1
1
  class Kubes::CLI
2
2
  class Exec < Base
3
+ extend Memoist
4
+ include Kubes::Logging
3
5
  include Kubes::Util::Sh
4
6
 
5
7
  def run
6
8
  compile
7
- metadata = Kubes::Kubectl::Fetch::Deployment.new(@options).metadata
9
+ pod = find_pod
8
10
 
11
+ unless pod
12
+ logger.info <<~EOL
13
+ Unable to find a pod to exec into. This means there was no deployment found.
14
+ You can also try using the -p option and specifying enough of the pod name. Example:
15
+
16
+ kubes exec -p web
17
+
18
+ EOL
19
+ exit 1
20
+ end
21
+
22
+ container = " -c #{@options[:container]}" unless @options[:container].nil?
23
+ cmd = @options[:cmd].empty? ? "bash" : @options[:cmd].join(' ')
24
+ sh("kubectl exec #{ns} -ti #{pod}#{container} -- #{cmd}")
25
+ end
26
+
27
+ def find_pod
28
+ pod_name || deployment_pod
29
+ end
30
+
31
+ def ns
32
+ "-n #{metadata['namespace']}" if metadata
33
+ end
34
+
35
+ def metadata
36
+ deployment = Kubes::Kubectl::Fetch::Deployment.new(@options)
37
+ deployment.metadata if deployment.found
38
+ end
39
+ memoize :metadata
40
+
41
+ def deployment_pod
42
+ return unless metadata
9
43
  labels = metadata['labels'].map { |k,v| "#{k}=#{v}" }.join(',')
10
44
  ns = metadata['namespace']
11
45
 
12
- resp = capture("kubectl get pod -l #{labels} -n #{ns} -o json")
46
+ resp = sh_capture("kubectl get pod -l #{labels} -n #{ns} -o json")
13
47
  data = JSON.load(resp)
14
48
  pod = latest_pod(data['items'])
15
49
 
@@ -18,10 +52,7 @@ class Kubes::CLI
18
52
  exit 1
19
53
  end
20
54
 
21
- name = pod['metadata']['name']
22
- container = " -c #{@options[:container]}" unless @options[:container].nil?
23
- cmd = @options[:cmd].empty? ? "bash" : @options[:cmd].join(' ')
24
- sh("kubectl exec -n #{ns} -ti #{name}#{container} -- #{cmd}")
55
+ pod['metadata']['name']
25
56
  end
26
57
 
27
58
  # get latest running pod
@@ -2,9 +2,10 @@ class Kubes::CLI
2
2
  class Get < Base
3
3
  def run
4
4
  compile
5
- Kubes::Kubectl.run(:get, @options)
5
+ Kubes::Kubectl.run(:get, @options.merge(exit_on_fail: false))
6
+ return unless @options[:show_pods]
6
7
  pods = Kubes::Kubectl::Fetch::Pods.new(@options)
7
- pods.show if @options[:show_pods]
8
+ pods.show
8
9
  end
9
10
  end
10
11
  end
@@ -6,6 +6,7 @@ class Kubes::CLI
6
6
  [:force, type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
7
7
  [:type, aliases: ["t"], default: "yaml", desc: "Type: dsl or yaml"],
8
8
  [:repo, required: true, desc: "Docker repo name. Example: user/repo. Configures .kubes/config.rb"],
9
+ [:namespace, aliases: ["n"], desc: "Namespace to use, defaults to the app option"],
9
10
  ]
10
11
  end
11
12
 
@@ -17,6 +18,37 @@ class Kubes::CLI
17
18
  @options[:app]
18
19
  end
19
20
 
21
+ def namespace
22
+ @options[:namespace] || @options[:app]
23
+ end
24
+
25
+ def excludes
26
+ if namespace == "default"
27
+ case options[:type]
28
+ when "dsl"
29
+ %w[
30
+ namespace.rb.tt
31
+ ]
32
+ else
33
+ %w[
34
+ all.yaml.tt
35
+ namespace.yaml.tt
36
+ ]
37
+ end
38
+ else
39
+ []
40
+ end
41
+ end
42
+
43
+ def directory_options
44
+ if excludes.empty?
45
+ {}
46
+ else
47
+ pattern = Regexp.new(excludes.join('|'))
48
+ {exclude_pattern: pattern }
49
+ end
50
+ end
51
+
20
52
  public
21
53
  def create_dockefile
22
54
  return if File.exist?("Dockerfile")
@@ -32,13 +64,13 @@ class Kubes::CLI
32
64
  def create_dsl_files
33
65
  return unless @options[:type] == "dsl"
34
66
  set_source("dsl")
35
- directory ".", "."
67
+ directory ".", ".", directory_options
36
68
  end
37
69
 
38
70
  def create_yaml_files
39
71
  return if @options[:type] == "dsl"
40
72
  set_source("yaml")
41
- directory ".", "."
73
+ directory ".", ".", directory_options
42
74
  end
43
75
 
44
76
  def message
@@ -1,13 +1,62 @@
1
1
  class Kubes::CLI
2
2
  class Logs < Base
3
+ include Kubes::Logging
3
4
  include Kubes::Util::Sh
4
5
 
5
6
  def run
6
7
  compile
7
- metadata = Kubes::Kubectl::Fetch::Deployment.new(@options).metadata
8
- name = metadata['name']
8
+ sh("kubectl logs #{args}")
9
+ end
10
+
11
+ private
12
+ def args
13
+ args = pod_name || deployment_args
14
+
15
+ unless args
16
+ logger.info <<~EOL
17
+ Unable to find a pod to show logs for. This means there was no deployment found.
18
+ You can also try using the -p option and specifying enough of the pod name. Example:
19
+
20
+ kubes logs -p web
21
+
22
+ EOL
23
+ exit 1
24
+ end
25
+
9
26
  follow = " -f" if @options[:follow]
10
- sh("kubectl logs deployment/#{name}#{follow}")
27
+ "#{args}#{follow}"
28
+ end
29
+
30
+ def deployment_args
31
+ deployment = Kubes::Kubectl::Fetch::Deployment.new(@options)
32
+ metadata = deployment.metadata
33
+ return unless metadata
34
+
35
+ name = metadata['name']
36
+ ns = metadata['namespace']
37
+
38
+ container = container(deployment)
39
+ c = " -c #{container}" if container
40
+ "deployment/#{name} -n #{ns}#{c}"
41
+ end
42
+
43
+ def container(deployment)
44
+ container = @options[:container]
45
+ return container if container
46
+
47
+ spec = deployment.spec
48
+ containers = spec['template']['spec']['containers']
49
+ names = containers.map { |c| c['name'] }
50
+ if containers.size > 1
51
+ logger.info <<~EOL
52
+ INFO: More than one container found.
53
+ Container names: #{names.join(', ')}
54
+ Using #{names.first}
55
+ Note: You can specify the container to use with --container or -c
56
+ EOL
57
+
58
+ names.first
59
+ end
11
60
  end
12
61
  end
13
62
  end
@@ -0,0 +1,95 @@
1
+ class Kubes::CLI
2
+ class Prune < Base
3
+ KINDS = %w[ConfigMap Secret]
4
+ extend Memoist
5
+ include Kubes::Util::Sure
6
+
7
+ def run
8
+ return unless anything_to_prune?
9
+ logger.info "Pruning old resources: #{KINDS.join(', ')}"
10
+
11
+ perform(preview: true) unless @options[:yes]
12
+ sure?("This will prune/delete resources. Are you sure?")
13
+ perform(preview: false)
14
+ end
15
+
16
+ def fetcher
17
+ Kubes::Kubectl::Fetch::Base.new(@options)
18
+ end
19
+ memoize :fetcher
20
+
21
+ def namespace
22
+ fetcher.namespace
23
+ end
24
+
25
+ def anything_to_prune?
26
+ items = []
27
+ return unless namespace
28
+
29
+ with_old_items { |i| items << i }
30
+ if items.empty?
31
+ logger.info("There are no old resources that need pruning.") unless @options[:quiet]
32
+ end
33
+ !items.empty?
34
+ end
35
+
36
+ def perform(preview:)
37
+ with_old_items do |item|
38
+ prune(item, preview)
39
+ end
40
+ end
41
+
42
+ def with_old_items
43
+ items = get_all_items
44
+ items.each do |i|
45
+ next unless old?(i)
46
+ yield(i)
47
+ end
48
+ end
49
+
50
+ def old?(item)
51
+ name = item['metadata']['name']
52
+ kind = item['kind']
53
+ built = built_kinds[kind] || [] # IE: {"demo-secret"=>"ebd93b58dd"}
54
+ built.each do |original_name,hash|
55
+ return false unless name.include?(original_name)
56
+ current = "#{original_name}-#{hash}"
57
+ return false if name == current
58
+ # Spec cover the tricky regexp logic
59
+ regexp = Regexp.new("#{original_name}-\\w{10}$") # IE: # demo-secret-\w{10}$
60
+ return true if name.match(regexp)
61
+ end
62
+
63
+ false
64
+ end
65
+
66
+ def get_all_items
67
+ secrets = capture_items('secret')
68
+ config_maps = capture_items('configmap')
69
+ secrets + config_maps
70
+ end
71
+
72
+ def capture_items(kind)
73
+ data = Kubes::Kubectl.capture("get #{kind} -o json -n #{namespace}")
74
+ data['items'] || []
75
+ end
76
+
77
+ # IE: {"Secret"=>{"demo-secret"=>"ebd93b58dd"}}
78
+ def built_kinds
79
+ compile # compile so built kinds are in memory
80
+ Kubes::Compiler::Decorator::Hashable::Storage.md5s
81
+ end
82
+ memoize :built_kinds
83
+
84
+ def prune(item, preview=false)
85
+ kind = item['kind']
86
+ name = item['metadata']['name']
87
+ args = "delete #{kind} #{name} -n #{namespace}"
88
+ if preview
89
+ logger.info " kubectl #{args}"
90
+ else
91
+ Kubes::Kubectl.execute(args)
92
+ end
93
+ end
94
+ end
95
+ end