kubes 0.3.5 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +6 -5
- data/docs/_docs/config/args.md +10 -0
- data/docs/_docs/config/args/docker.md +19 -0
- data/docs/_docs/config/{kubectl/args.md → args/kubectl.md} +2 -0
- data/docs/_docs/config/docker.md +4 -40
- data/docs/_docs/config/hooks.md +10 -0
- data/docs/_docs/config/hooks/docker.md +70 -0
- data/docs/_docs/config/hooks/kubectl.md +83 -0
- data/docs/_docs/config/hooks/kubes.md +67 -0
- data/docs/_docs/config/hooks/ruby.md +74 -0
- data/docs/_docs/config/kubectl.md +2 -2
- data/docs/_docs/config/reference.md +20 -0
- data/docs/_docs/config/skip.md +58 -0
- data/docs/_docs/dsl/resources.md +1 -1
- data/docs/_docs/intro.md +3 -1
- data/docs/_docs/patterns/migrations.md +121 -0
- data/docs/_includes/config/hooks/options.md +20 -0
- data/docs/_includes/sidebar.html +25 -12
- data/docs/_sass/theme.scss +25 -1
- data/lib/kubes/cli.rb +20 -5
- data/lib/kubes/cli/apply.rb +2 -1
- data/lib/kubes/cli/base.rb +11 -0
- data/lib/kubes/cli/delete.rb +1 -1
- data/lib/kubes/cli/exec.rb +37 -6
- data/lib/kubes/cli/get.rb +1 -1
- data/lib/kubes/cli/logs.rb +27 -3
- data/lib/kubes/cli/prune.rb +95 -0
- data/lib/kubes/compiler.rb +18 -7
- data/lib/kubes/compiler/decorator/base.rb +7 -1
- data/lib/kubes/compiler/decorator/{resources/secret.rb → hashable.rb} +5 -4
- data/lib/kubes/compiler/decorator/hashable/field.rb +53 -0
- data/lib/kubes/compiler/decorator/hashable/storage.rb +19 -0
- data/lib/kubes/compiler/decorator/post.rb +77 -0
- data/lib/kubes/compiler/decorator/pre.rb +12 -0
- data/lib/kubes/compiler/strategy.rb +2 -2
- data/lib/kubes/compiler/strategy/base.rb +1 -1
- data/lib/kubes/compiler/strategy/result.rb +4 -6
- data/lib/kubes/config.rb +16 -11
- data/lib/kubes/docker/strategy/build/docker.rb +1 -1
- data/lib/kubes/docker/strategy/build/gcloud.rb +1 -1
- data/lib/kubes/docker/strategy/image_name.rb +1 -1
- data/lib/kubes/docker/strategy/push/docker.rb +1 -1
- data/lib/kubes/docker/strategy/push/gcloud.rb +1 -1
- data/lib/kubes/docker/strategy/utils.rb +1 -1
- data/lib/kubes/hooks/builder.rb +29 -15
- data/lib/kubes/hooks/concern.rb +10 -0
- data/lib/kubes/hooks/dsl.rb +2 -1
- data/lib/kubes/hooks/runner.rb +22 -0
- data/lib/kubes/kubectl.rb +21 -18
- data/lib/kubes/kubectl/batch.rb +8 -5
- data/lib/kubes/kubectl/{decider.rb → dispatcher.rb} +1 -1
- data/lib/kubes/kubectl/fetch/base.rb +12 -9
- data/lib/kubes/kubectl/fetch/deployment.rb +12 -13
- data/lib/kubes/kubectl/fetch/pods.rb +4 -15
- data/lib/kubes/kubectl/kustomize.rb +1 -1
- data/lib/kubes/kubectl/ordering.rb +12 -0
- data/lib/kubes/util/consider.rb +2 -1
- data/lib/kubes/util/sh.rb +1 -1
- data/lib/kubes/version.rb +1 -1
- data/spec/fixtures/decorators/deployment/both/envFrom.yaml +31 -0
- data/spec/fixtures/prune/capture.yaml +57 -0
- data/spec/fixtures/prune/fetch_items.yaml +268 -0
- data/spec/kubes/cli/prune_spec.rb +38 -0
- data/spec/kubes/compiler/decorator/{resources → post}/deployment_spec.rb +25 -6
- data/spec/kubes/compiler/decorator/{resources → post}/pod_spec.rb +2 -11
- metadata +35 -19
- data/docs/_docs/config/kubectl/hooks.md +0 -39
- data/lib/kubes/compiler/decorator.rb +0 -17
- data/lib/kubes/compiler/decorator/compile.rb +0 -12
- data/lib/kubes/compiler/decorator/resources/base.rb +0 -13
- data/lib/kubes/compiler/decorator/resources/container.rb +0 -76
- data/lib/kubes/compiler/decorator/resources/container/mapping.rb +0 -28
- data/lib/kubes/compiler/decorator/resources/deployment.rb +0 -10
- data/lib/kubes/compiler/decorator/resources/pod.rb +0 -10
- data/lib/kubes/compiler/decorator/write.rb +0 -14
- data/lib/kubes/docker/strategy/hooks.rb +0 -9
data/lib/kubes/cli/exec.rb
CHANGED
@@ -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
|
-
|
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 =
|
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
|
-
|
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
|
data/lib/kubes/cli/get.rb
CHANGED
data/lib/kubes/cli/logs.rb
CHANGED
@@ -1,21 +1,45 @@
|
|
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
|
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
|
+
|
26
|
+
follow = " -f" if @options[:follow]
|
27
|
+
"#{args}#{follow}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def deployment_args
|
7
31
|
deployment = Kubes::Kubectl::Fetch::Deployment.new(@options)
|
8
32
|
metadata = deployment.metadata
|
33
|
+
return unless metadata
|
34
|
+
|
9
35
|
name = metadata['name']
|
10
36
|
ns = metadata['namespace']
|
11
37
|
|
12
|
-
follow = " -f" if @options[:follow]
|
13
38
|
container = container(deployment)
|
14
39
|
c = " -c #{container}" if container
|
15
|
-
|
40
|
+
"deployment/#{name} -n #{ns}#{c}"
|
16
41
|
end
|
17
42
|
|
18
|
-
private
|
19
43
|
def container(deployment)
|
20
44
|
container = @options[:container]
|
21
45
|
return container if container
|
@@ -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
|
data/lib/kubes/compiler.rb
CHANGED
@@ -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
|
|
@@ -7,17 +8,27 @@ module Kubes
|
|
7
8
|
@options = options
|
8
9
|
end
|
9
10
|
|
11
|
+
# Separate command like prune can call compile. Apply also calls Prune.
|
12
|
+
# Instead of moving Compile out of Prune, will use this class variable.
|
13
|
+
# In case we have other cases where compile is called in another area.
|
14
|
+
# We only want compiled to be called once so hooks only fire once.
|
15
|
+
@@compiled = false
|
10
16
|
def run
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
return if @@compiled
|
18
|
+
Kubes.config # trigger config load. So can set ENV['VAR'] in config/envs/dev.rb etc
|
19
|
+
run_hooks("kubes.rb", name: "compile") do
|
20
|
+
results = resources.map do |path|
|
21
|
+
strategy = Strategy.new(@options.merge(path: path))
|
22
|
+
strategy.compile
|
23
|
+
end.compact
|
15
24
|
|
16
|
-
|
17
|
-
|
25
|
+
results.each do |result|
|
26
|
+
write(result)
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
30
|
puts "Compiled .kubes/resources files to .kubes/output" if show_compiled_message?
|
31
|
+
@@compiled = true
|
21
32
|
end
|
22
33
|
|
23
34
|
def resources
|
@@ -49,7 +60,7 @@ module Kubes
|
|
49
60
|
end
|
50
61
|
|
51
62
|
def write(result)
|
52
|
-
result.
|
63
|
+
result.decorate!(:post)
|
53
64
|
filename, content = result.filename, result.content
|
54
65
|
dest = "#{Kubes.root}/.kubes/output/#{filename}"
|
55
66
|
|
@@ -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
|
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
|
4
|
-
class
|
3
|
+
module Kubes::Compiler::Decorator
|
4
|
+
class Hashable < Base
|
5
5
|
include Kubes::Compiler::Util::YamlDump
|
6
6
|
|
7
|
-
def
|
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
|
-
|
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
|