stax-helm 0.0.3 → 0.0.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 915820e964fa23b1d20b876f7e89d22ee55a97e4aa60fec75e508e47f36daf54
4
- data.tar.gz: 6da2869fac93946398ec4c0c83df102f4cb2a883b0b5dd72ce6486d5a804ea63
3
+ metadata.gz: 90efb4bc5fd77b16200e4693747b303df7644ebfe21149d715327a56397f1d25
4
+ data.tar.gz: 19d2b39ef1cbd165dbd32091f317f8c11b12624d381920bdffd5295b8b8c5b68
5
5
  SHA512:
6
- metadata.gz: 80d9424da2501a5b209af37868fd4b615d53acb125517effb7c3a759019cf3de9ef8c025f0e10b3473b48823b986ec11c4e9cf7a010b36e2f829ca2b878c939e
7
- data.tar.gz: 4bef3ab94b44a3fec3c20d3df75b353bd89a823f2c5ad0959985130a8797cbe694388f4649bd1152cf53b3265e4d5e49ce897a0e1a6da8b90847632e34e25020
6
+ metadata.gz: 5566799f8341ace21b46a2ec7c3d90a3c5dc11d26f54c7efa1dc593df439e6dd59732286153823995573a53337252f8249000d54f26dd4813e72ebad23e943ff
7
+ data.tar.gz: 8d1b6ca7b1e7465cac0f77754438ccff9ad880e12bf7d1eb9b2c58a6f48921bbad3471696ab4cd2e609aeee1cd749c0ce6304c23ad248ece555a2291140c5785
@@ -1,4 +1,9 @@
1
+ require 'stax/helm/base'
1
2
  require 'stax/helm/cmd'
2
3
  require 'stax/helm/kubectl'
4
+ require 'stax/helm/ingress'
5
+ require 'stax/helm/pod'
6
+ require 'stax/helm/deployment'
3
7
  require 'stax/helm/stern'
8
+ require 'stax/helm/runcmd'
4
9
  Stax.add_command(:helm, Stax::Helm::Cmd)
@@ -0,0 +1,16 @@
1
+ module Stax
2
+ class Base < Thor
3
+
4
+ no_commands do
5
+ def helm_release_name
6
+ @_helm_release_name ||= helm_safe("#{app_name}-#{branch_name}")
7
+ end
8
+
9
+ ## make string safe to use in naming helm stuff
10
+ def helm_safe(string)
11
+ string.slice(0, 53).gsub(/[\W_]/, '-').downcase
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -1,13 +1,10 @@
1
1
  module Stax
2
2
  module Helm
3
+
3
4
  class Cmd < Base
4
5
  class_option :recon, aliases: '--just-print', type: :boolean, default: false, desc: 'print command that would be run'
5
6
 
6
7
  no_commands do
7
- def helm_release_name
8
- @_helm_release_name ||= "#{app_name}-#{branch_name}"
9
- end
10
-
11
8
  ## location of helm chart
12
9
  def helm_dir
13
10
  File.join(Stax.root_path, 'helm')
@@ -0,0 +1,56 @@
1
+ ## tasks to work on deployments
2
+ module Stax
3
+ module Helm
4
+ class Cmd < Base
5
+
6
+ no_commands do
7
+ def helm_deployments
8
+ jsonpath = '{.items[*].metadata.name}'
9
+ %x[kubectl get deployments -o=jsonpath='#{jsonpath}' -l #{helm_selector}].split
10
+ end
11
+
12
+ ## prompt user with a list of deployments to choose
13
+ def helm_ask_deployments(msg)
14
+ deployments = helm_deployments
15
+ if deployments.count > 1
16
+ puts deployments.each_with_index.map { |d, i| "#{i}: #{d}" }
17
+ resp = ask(msg, default: 'all')
18
+ if resp != 'all'
19
+ indices = resp.split.map(&:to_i)
20
+ deployments = Array(deployments.slice(*indices))
21
+ end
22
+ end
23
+ deployments
24
+ end
25
+ end
26
+
27
+ desc 'deployments', 'list deployments'
28
+ def deployments
29
+ kubectl_run(:get, :deployments, '-l', helm_selector)
30
+ end
31
+
32
+ desc 'restart', 'restart deployments'
33
+ def restart
34
+ helm_ask_deployments('choose deployments').each do |deployment|
35
+ kubectl_run(:rollout, :restart, :deployment, deployment)
36
+ end
37
+ end
38
+
39
+ desc 'scale', 'show/set scale for deployments'
40
+ method_option :replicas, aliases: '-r', type: :numeric, default: nil, desc: 'replicas'
41
+ def scale
42
+ if options[:replicas]
43
+ deployments = helm_ask_deployments('choose deployments').join(' ')
44
+ kubectl_run(:scale, :deployment, deployments, '--replicas', options[:replicas])
45
+ else
46
+ debug("Deployment replicas for #{helm_release_name}")
47
+ deployments = kubectl_json(:get, :deployments, '-l', helm_selector)
48
+ print_table deployments['items'].map { |i|
49
+ [ i['metadata']['name'], i['status']['replicas'] || 0 ]
50
+ }
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ ## tasks to get ingress details
2
+ module Stax
3
+ module Helm
4
+ class Cmd < Base
5
+
6
+ desc 'ingresses', 'list ingresses'
7
+ def ingresses
8
+ kubectl_run(:get, :ingresses, '-l', helm_selector)
9
+ end
10
+
11
+ desc 'dns', 'list external-dns hostnames'
12
+ def dns
13
+ jsonpath = '{.items[].metadata.annotations.external-dns\.alpha\.kubernetes\.io/hostname}' + "\n"
14
+ kubectl_run(:get, :ingresses, "-o=jsonpath='#{jsonpath}'", '-l', helm_selector)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -9,54 +9,24 @@ module Stax
9
9
 
10
10
  def kubectl_run(*args)
11
11
  cmd = [kubectl_bin, *args].join(' ')
12
- options[:dry_run] ? puts(cmd) : system(cmd)
12
+ options[:recon] ? puts(cmd) : system(cmd)
13
13
  end
14
14
 
15
- ## build a selector argument from a hash of label and value pairs
16
- def selector(hash)
17
- '-l ' + hash.compact.map { |k,v| "#{k}=#{v}" }.join(',')
15
+ def kubectl_json(*args)
16
+ args.push('-o=json')
17
+ cmd = [kubectl_bin, *args].join(' ')
18
+ options[:recon] ? puts(cmd) : JSON.parse(%x(#{cmd}))
19
+ end
20
+
21
+ ## override this to match all objects in your helm release
22
+ def helm_selector
23
+ "app.kubernetes.io/instance=#{helm_release_name}"
18
24
  end
19
25
  end
20
26
 
21
27
  desc 'services', 'list services'
22
28
  def services
23
- kubectl_run(:get, :services, selector('app.kubernetes.io/instance': helm_release_name))
24
- end
25
-
26
- desc 'ingresses', 'list ingresses'
27
- def ingresses
28
- kubectl_run(:get, :ingresses, selector('app.kubernetes.io/instance': helm_release_name))
29
- end
30
-
31
- desc 'deployments', 'list deployments'
32
- def deployments
33
- kubectl_run(:get, :deployments, selector('app.kubernetes.io/instance': helm_release_name))
34
- end
35
-
36
- desc 'pods', 'list pods'
37
- def pods
38
- kubectl_run(:get, :pods, selector('app.kubernetes.io/instance': helm_release_name))
39
- end
40
-
41
- desc 'containers', 'list containers'
42
- def containers
43
- columns = 'NAME:.metadata.name,CONTAINERS:.spec.containers[*].name'
44
- kubectl_run(:get, :pods, '-o', "custom-columns=#{columns}", selector('app.kubernetes.io/instance': helm_release_name))
45
- end
46
-
47
- ## FIXME this is terrible, will be replaced with something better later
48
- desc 'logs COMPONENT [CONTAINER]', 'show container logs'
49
- method_option :container, aliases: '-c', type: :string, default: nil, desc: 'container from pod'
50
- def logs(component)
51
- container = options[:container] ? "-c #{options[:container]}" : ''
52
- kubectl_run(
53
- :logs,
54
- container,
55
- selector(
56
- 'app.kubernetes.io/instance': helm_release_name,
57
- 'app.kubernetes.io/component': component,
58
- )
59
- )
29
+ kubectl_run(:get, :services, '-l', helm_selector)
60
30
  end
61
31
 
62
32
  end
@@ -0,0 +1,49 @@
1
+ ## tasks to work on pods
2
+ module Stax
3
+ module Helm
4
+ class Cmd < Base
5
+
6
+ no_commands do
7
+ def helm_pods
8
+ jsonpath = '{.items[*].metadata.name}'
9
+ %x[kubectl get pods -o=jsonpath='#{jsonpath}' -l #{helm_selector}].split
10
+ end
11
+
12
+ def helm_ask_pod(msg)
13
+ pods = helm_pods
14
+ index = 0
15
+ if pods.count > 1
16
+ puts pods.each_with_index.map { |p, i| "#{i}: #{p}" }
17
+ index = ask(msg, default: index)
18
+ end
19
+ pods[index.to_i]
20
+ end
21
+ end
22
+
23
+ desc 'pods', 'list pods'
24
+ def pods
25
+ kubectl_run(:get, :pods, '-l', helm_selector)
26
+ end
27
+
28
+ desc 'containers', 'list containers'
29
+ def containers
30
+ columns = 'NAME:.metadata.name,CONTAINERS:.spec.containers[*].name'
31
+ kubectl_run(:get, :pods, '-o', "custom-columns=#{columns}", '-l', helm_selector)
32
+ end
33
+
34
+ desc 'logs [OPTIONS]', 'run kubectl logs with same options'
35
+ def logs(*args)
36
+ trap('SIGINT', 'EXIT') # clean exit with ctrl-c
37
+ args = [ '--all-containers', '--prefix', '--follow' ] if args.empty? # helpful default args
38
+ kubectl_run(:logs, '-l', helm_selector, *args)
39
+ end
40
+
41
+ desc 'exec [CMD]', 'exec command in a web pod'
42
+ def exec(cmd = 'sh')
43
+ pod = helm_ask_pod('choose a pod')
44
+ kubectl_run(:exec, '-it', pod, '--', cmd)
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,101 @@
1
+ require 'securerandom'
2
+
3
+ module Stax
4
+ module Helm
5
+ class Cmd
6
+
7
+ no_commands do
8
+ ## construct a Job template from passed container spec
9
+ def helm_run_template(name, container_spec)
10
+ {
11
+ apiVersion: 'batch/v1',
12
+ kind: :Job,
13
+ metadata: {
14
+ name: name,
15
+ labels: {
16
+ 'app.kubernetes.io/managed-by' => :stax
17
+ }
18
+ },
19
+ spec: {
20
+ template: {
21
+ spec: {
22
+ restartPolicy: :Never,
23
+ containers: [ container_spec ],
24
+ }
25
+ }
26
+ }
27
+ }.to_json
28
+ end
29
+
30
+ ## Deployment to clone for container
31
+ def helm_run_deployment
32
+ "#{helm_release_name}-web"
33
+ end
34
+
35
+ ## name of container to clone from Deployment
36
+ def helm_run_container
37
+ 'web'
38
+ end
39
+
40
+ ## name for Job to create based on container
41
+ def helm_run_job
42
+ "#{helm_release_name}-run-#{SecureRandom.hex(4)}"
43
+ end
44
+
45
+ ## default command to run
46
+ def helm_run_cmd
47
+ 'bash'
48
+ end
49
+ end
50
+
51
+ ## task creates a dedicated unique kubernetes Job, with
52
+ ## container spec based on the helm release deployment
53
+ ## requested, then does an interactive exec to the pod and
54
+ ## container created, and deletes the Job on exit
55
+ desc 'runcmd [CMD]', 'run dedicated interactive container'
56
+ method_option :sleep, type: :string, default: '1h', description: 'kill container after time'
57
+ method_option :keep, type: :boolean, default: false, description: 'do not delete job'
58
+ def runcmd(*cmd)
59
+ ## use default if not set
60
+ cmd = Array(helm_run_cmd) if cmd.empty?
61
+
62
+ ## name of k8s Job to create
63
+ job = helm_run_job
64
+
65
+ ## get deployment and extract container spec
66
+ deployment = kubectl_json(:get, :deployment, helm_run_deployment)
67
+ spec = deployment['spec']['template']['spec']['containers'].find do |c|
68
+ c['name'] == helm_run_container
69
+ end
70
+
71
+ ## cleanup the container spec so we can use it in a Job
72
+ spec.delete('livenessProbe')
73
+ spec.delete('readinessProbe')
74
+ spec.delete('volumeMounts')
75
+ spec['name'] = 'run'
76
+ spec['args'] = ['sleep', options[:sleep]]
77
+
78
+ ## create new unique Job based on the container spec
79
+ debug("Creating job #{job}")
80
+ Open3.popen2('kubectl create -f -') { |stdin, stdout, _|
81
+ stdin.print(helm_run_template(job, spec))
82
+ stdin.close
83
+ puts stdout.gets
84
+ }
85
+
86
+ ## get name of the Pod created by the Job
87
+ pod = kubectl_json(:get, :pod, '-l', "job-name=#{job}")['items'].first['metadata']['name']
88
+
89
+ ## exec into the pod and run interactive command
90
+ debug("Connecting to pod #{pod}")
91
+ kubectl_run(:wait, '--for=condition=Ready', '--timeout=5m', :pod, pod)
92
+ kubectl_run(:exec, '-it', pod, '--', *cmd)
93
+ rescue JSON::ParserError
94
+ fail_task('cannot get kubernetes resource')
95
+ ensure
96
+ ## delete Job
97
+ kubectl_run(:delete, :job, job) unless options[:keep]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -16,7 +16,8 @@ module Stax
16
16
  ## pass through args to stern
17
17
  desc 'stern [STERN_ARGS]', 'use stern to show logs'
18
18
  def stern(*args)
19
- stern_run(selector('app.kubernetes.io/instance': helm_release_name), *args)
19
+ trap('SIGINT', 'EXIT') # clean exit with ctrl-c
20
+ stern_run('-l', helm_selector, *args)
20
21
  end
21
22
 
22
23
  end
@@ -1,5 +1,5 @@
1
1
  module Stax
2
2
  module Helm
3
- VERSION = '0.0.3'
3
+ VERSION = '0.0.8'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stax-helm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Lister
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-30 00:00:00.000000000 Z
11
+ date: 2020-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -67,8 +67,13 @@ files:
67
67
  - README.md
68
68
  - Rakefile
69
69
  - lib/stax/helm.rb
70
+ - lib/stax/helm/base.rb
70
71
  - lib/stax/helm/cmd.rb
72
+ - lib/stax/helm/deployment.rb
73
+ - lib/stax/helm/ingress.rb
71
74
  - lib/stax/helm/kubectl.rb
75
+ - lib/stax/helm/pod.rb
76
+ - lib/stax/helm/runcmd.rb
72
77
  - lib/stax/helm/stern.rb
73
78
  - lib/stax/helm/version.rb
74
79
  - stax-helm.gemspec
@@ -76,7 +81,7 @@ homepage: https://github.com/rlister/stax-helm
76
81
  licenses:
77
82
  - MIT
78
83
  metadata: {}
79
- post_install_message:
84
+ post_install_message:
80
85
  rdoc_options: []
81
86
  require_paths:
82
87
  - lib
@@ -91,8 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
96
  - !ruby/object:Gem::Version
92
97
  version: '0'
93
98
  requirements: []
94
- rubygems_version: 3.1.2
95
- signing_key:
99
+ rubygems_version: 3.0.3
100
+ signing_key:
96
101
  specification_version: 4
97
102
  summary: Control helm charts with stax.
98
103
  test_files: []