minfra-cli 1.13.2 → 2.0.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +1 -2
  3. data/.rubocop.yml +31 -0
  4. data/CHANGELOG.md +18 -1
  5. data/Gemfile.lock +1 -1
  6. data/README.md +13 -1
  7. data/exe/minfra +1 -3
  8. data/lib/deep_merge.rb +35 -36
  9. data/lib/hash.rb +19 -18
  10. data/lib/minfra/cli/ask.rb +18 -16
  11. data/lib/minfra/cli/cli_starter.rb +173 -0
  12. data/lib/minfra/cli/command.rb +4 -1
  13. data/lib/minfra/cli/commands/dev.rb +26 -15
  14. data/lib/minfra/cli/commands/kube.rb +97 -88
  15. data/lib/minfra/cli/commands/plugin.rb +9 -8
  16. data/lib/minfra/cli/commands/project/branch.rb +7 -5
  17. data/lib/minfra/cli/commands/project/tag.rb +7 -6
  18. data/lib/minfra/cli/commands/project.rb +40 -40
  19. data/lib/minfra/cli/commands/setup.rb +18 -18
  20. data/lib/minfra/cli/commands/stack/app_template.rb +10 -13
  21. data/lib/minfra/cli/commands/stack/client_template.rb +10 -8
  22. data/lib/minfra/cli/commands/stack/kube_stack_template.rb +49 -51
  23. data/lib/minfra/cli/commands/stack.rb +55 -46
  24. data/lib/minfra/cli/commands/tag.rb +9 -8
  25. data/lib/minfra/cli/common.rb +7 -10
  26. data/lib/minfra/cli/config.rb +36 -63
  27. data/lib/minfra/cli/core_ext.rb +7 -0
  28. data/lib/minfra/cli/document.rb +5 -2
  29. data/lib/minfra/cli/env.rb +24 -0
  30. data/lib/minfra/cli/errors.rb +10 -0
  31. data/lib/minfra/cli/helm_runner.rb +3 -1
  32. data/lib/minfra/cli/hiera_looker.rb +54 -0
  33. data/lib/minfra/cli/hook.rb +36 -24
  34. data/lib/minfra/cli/kubectl_runner.rb +3 -1
  35. data/lib/minfra/cli/logging.rb +5 -1
  36. data/lib/minfra/cli/main_command.rb +2 -1
  37. data/lib/minfra/cli/plugin.rb +74 -0
  38. data/lib/minfra/cli/plugins.rb +18 -87
  39. data/lib/minfra/cli/runner.rb +23 -23
  40. data/lib/minfra/cli/templater.rb +17 -17
  41. data/lib/minfra/cli/version.rb +3 -1
  42. data/lib/minfra/cli.rb +20 -114
  43. data/lib/orchparty/ast.rb +13 -14
  44. data/lib/orchparty/cli.rb +35 -33
  45. data/lib/orchparty/context.rb +15 -15
  46. data/lib/orchparty/dsl_parser.rb +7 -11
  47. data/lib/orchparty/dsl_parser_kubernetes.rb +46 -56
  48. data/lib/orchparty/kubernetes_application.rb +2 -2
  49. data/lib/orchparty/plugin.rb +10 -9
  50. data/lib/orchparty/plugins/env.rb +14 -13
  51. data/lib/orchparty/transformations/all.rb +3 -1
  52. data/lib/orchparty/transformations/mixin.rb +24 -24
  53. data/lib/orchparty/transformations/remove_internal.rb +3 -2
  54. data/lib/orchparty/transformations/sort.rb +2 -1
  55. data/lib/orchparty/transformations/variable.rb +6 -5
  56. data/lib/orchparty/transformations.rb +2 -0
  57. data/lib/orchparty/version.rb +3 -1
  58. data/lib/orchparty.rb +14 -14
  59. metadata +9 -2
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minfra
4
+ module Cli
5
+ class HieraLooker
6
+ def initialize(root:, env_name:, env_path:)
7
+ @root = Pathname.new(root)
8
+ @env_name = env_name
9
+ @cache = {}
10
+
11
+ @hiera = Hiera.new(config: @root.join('hiera.yaml').to_s)
12
+ Hiera.logger = :noop
13
+
14
+ hiera_main_path = @root.join("hieradata/#{env_path}/#{env_name}.eyaml")
15
+
16
+ raise("unknown environment #{@env_name}, I expect a file at #{hiera_main_path}") unless hiera_main_path.exist?
17
+
18
+ scope = { 'hieraroot' => @root.to_s, 'env' => @env_name }
19
+
20
+ @special_lookups = @hiera.lookup('lookup_options', {}, scope, nil, :priority)
21
+
22
+ node_scope = @hiera.lookup('env', {}, scope, nil, :deeper)
23
+ @scope = scope.merge(node_scope)
24
+ end
25
+
26
+ def l(value, default = nil)
27
+ # debugger if @env_name == 'production-management' && value == 'env.tags'
28
+ return @cache[value] if @cache.key?(value)
29
+
30
+ values = value.split('.')
31
+ fst_value = values.shift
32
+
33
+ lookup_type = if @special_lookups[fst_value]
34
+ { merge_behavior: @special_lookups[fst_value]['merge'].to_sym }
35
+ else
36
+ :deep
37
+ end
38
+ result = @hiera.lookup(fst_value, default, @scope, nil, lookup_type)
39
+ result = result.dig(*values) if !values.empty? && result.is_a?(Hash) # we return nil or the scalar value and only drill down on hashes
40
+ result = default if result.nil?
41
+ result = Hashie::Mash.new(result) if result.is_a?(Hash)
42
+ @cache[value] = result
43
+ result
44
+ end
45
+
46
+ def l!(value, default = nil)
47
+ v = l(value, default)
48
+ raise("Value not found! #{value}") if v.nil?
49
+ v
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # no support for around hooks yet
2
4
 
3
5
  class Thor
@@ -8,20 +10,25 @@ class Thor
8
10
  if private_method?(instance)
9
11
  instance.class.handle_no_command_error(name)
10
12
  elsif public_method?(instance)
11
- hooks=instance.instance_variable_get(:@_invocations).values.flatten
13
+ hooks = instance.instance_variable_get(:@_invocations).values.flatten
12
14
  arity = instance.method(name).arity
13
- Minfra::Cli.call_before_hooks(instance,hooks)
15
+ Minfra::Cli.call_before_hooks(instance, hooks)
14
16
  instance.__send__(name, *args)
15
- Minfra::Cli.call_after_hooks(instance,hooks)
17
+ Minfra::Cli.call_after_hooks(instance, hooks)
16
18
  elsif local_method?(instance, :method_missing)
17
- #Minfra::Cli.call_before_hooks(instance,hooks)
19
+ # Minfra::Cli.call_before_hooks(instance,hooks)
18
20
  instance.__send__(:method_missing, name.to_sym, *args)
19
- #Minfra::Cli.call_after_hooks(instance,hooks)
21
+ # Minfra::Cli.call_after_hooks(instance,hooks)
20
22
  else
21
23
  instance.class.handle_no_command_error(name)
22
24
  end
23
25
  rescue ArgumentError => e
24
- handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e)
26
+ if handle_argument_error?(instance, e,
27
+ caller)
28
+ instance.class.handle_argument_error(self, e, args, arity)
29
+ else
30
+ (raise e)
31
+ end
25
32
  rescue NoMethodError => e
26
33
  handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (raise e)
27
34
  end
@@ -33,16 +40,16 @@ module Minfra
33
40
  module Hook
34
41
  class Hook
35
42
  def initialize(type, names, block)
36
- @type=type
37
- @names=names
38
- @block=block
43
+ @type = type
44
+ @names = names
45
+ @block = block
39
46
  end
40
47
 
41
48
  def match?(type, names)
42
- @type == type && @names == names.map(&:to_sym)
49
+ @type == type && @names == names.map(&:to_sym)
43
50
  end
44
51
 
45
- def exec (obj)
52
+ def exec(obj)
46
53
  obj.instance_eval(&@block)
47
54
  end
48
55
  end
@@ -50,8 +57,8 @@ module Minfra
50
57
  class Hooker
51
58
  include Logging
52
59
  def initialize(klass)
53
- @klass=klass
54
- @hooks=[]
60
+ @klass = klass
61
+ @hooks = []
55
62
  end
56
63
 
57
64
  def register_before(names, block)
@@ -63,16 +70,16 @@ module Minfra
63
70
  end
64
71
 
65
72
  def call_before_hooks(obj, names)
66
- @hooks.select do |h| h.match?(:before, names) end.each do |h|
73
+ @hooks.select { |h| h.match?(:before, names) }.each do |h|
67
74
  debug("Hook before: #{names.join(',')}")
68
- h.exec(obj)
75
+ h.exec(obj)
69
76
  end
70
77
  end
71
78
 
72
- def call_after_hooks(obj,names)
73
- @hooks.select do |h| h.match?(:after, names) end.each do |h|
79
+ def call_after_hooks(obj, names)
80
+ @hooks.select { |h| h.match?(:after, names) }.each do |h|
74
81
  debug("Hook after: #{names.join(',')}")
75
- h.exec(obj)
82
+ h.exec(obj)
76
83
  end
77
84
  end
78
85
  end
@@ -82,21 +89,26 @@ module Minfra
82
89
  end
83
90
 
84
91
  module ClassMethods
85
- def after_hook(*names,&block)
92
+ def after_hook(*names, &block)
86
93
  hooks.register_after(names, block)
87
94
  end
95
+
88
96
  def before_hook(*names, &block)
89
97
  hooks.register_before(names, block)
90
98
  end
91
- def call_before_hooks(obj,names)
92
- hooks.call_before_hooks(obj,names)
99
+
100
+ def call_before_hooks(obj, names)
101
+ hooks.call_before_hooks(obj, names)
93
102
  end
94
- def call_after_hooks(obj,names)
95
- hooks.call_after_hooks(obj,names)
103
+
104
+ def call_after_hooks(obj, names)
105
+ hooks.call_after_hooks(obj, names)
96
106
  end
107
+
97
108
  private
109
+
98
110
  def hooks
99
- @hooks||=Hooker.new(self)
111
+ @hooks ||= Hooker.new(self)
100
112
  end
101
113
  end
102
114
  end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfra
2
4
  module Cli
3
5
  class KubeCtlRunner < Runner
4
6
  def initialize(cmd, **args)
5
- insecure_flag = l("infra::allow_insecure_k8s_connections") ? "--insecure-skip-tls-verify" : ""
7
+ insecure_flag = l('infra::allow_insecure_k8s_connections') ? '--insecure-skip-tls-verify' : ''
6
8
  cmd = "kubectl #{insecure_flag} #{cmd}"
7
9
  super(cmd, **args)
8
10
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfra
2
4
  module Cli
3
5
  module Logging
@@ -7,7 +9,7 @@ module Minfra
7
9
 
8
10
  def exit_error(str)
9
11
  error str
10
- exit 1
12
+ raise Minfra::Cli::Errors::ExitError, str
11
13
  end
12
14
 
13
15
  def info(str)
@@ -25,7 +27,9 @@ module Minfra
25
27
  def deprecated(comment)
26
28
  logger.warn "DEPRECATED: #{comment}"
27
29
  end
30
+
28
31
  private
32
+
29
33
  def logger
30
34
  Minfra::Cli.logger
31
35
  end
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfra
2
4
  module Cli
3
5
  class Main < Command
4
-
5
6
  desc 'kube', 'kubectl wrapper and other features'
6
7
  option :cluster
7
8
  option :stack, required: true
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Minfra
4
+ module Cli
5
+ class Plugins
6
+ class Plugin
7
+ include Logging
8
+ attr_reader :name, :version, :opts, :path
9
+
10
+ def initialize(name:, version:, opts:, disabled:)
11
+ @name = name
12
+ @version = version
13
+ @opts = opts.merge(require: false)
14
+ @disabled = disabled
15
+ return unless opts['path']
16
+
17
+ @path = Minfra::Cli.config.base_path.join(opts['path'])
18
+ end
19
+
20
+ def disabled?
21
+ @disabled
22
+ end
23
+
24
+ def install
25
+ if path
26
+ system("cd #{path}; bundle install")
27
+ else
28
+ system("gem install #{name} --version #{version}")
29
+ end
30
+ end
31
+
32
+ # adds the plugin to the
33
+ def prepare
34
+ debug("plugin prepare: #{name}, #{version}, disabled: #{disabled?}")
35
+ return if disabled?
36
+
37
+ if path
38
+ begin
39
+ lib_path = path.join('lib')
40
+ $LOAD_PATH.unshift lib_path
41
+ require name
42
+ rescue Gem::Requirement::BadRequirementError, LoadError
43
+ warn("plugin prepare path: #{name} (#{$ERROR_INFO})")
44
+ end
45
+ else
46
+ begin
47
+ @gem_spec = Gem::Specification.find_by_name(name)
48
+ gem name, version
49
+ rescue Gem::MissingSpecError
50
+ warn("plugin prepare gem: #{name}, #{version} (#{$ERROR_INFO})")
51
+ end
52
+ end
53
+ end
54
+
55
+ def setup
56
+ return if disabled?
57
+
58
+ if path
59
+ minfra_path = Pathname.new(path).join('minfracs', 'init.rb')
60
+ if minfra_path.exist?
61
+ begin
62
+ require minfra_path # this should register the command
63
+ rescue LoadError
64
+ logger.warn("Minfra plugin detected but dependencies not installed: #{minfra_path} (#{$ERROR_INFO}). TRY: minfra plugin install")
65
+ end
66
+ end
67
+ else
68
+ error('Gem based plugins not supported yet')
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,108 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
1
4
  module Minfra
2
5
  module Cli
3
6
  class Plugins
4
- class Plugin
5
- include Logging
6
- attr_reader :name, :version, :opts, :path
7
-
8
-
9
- def initialize(name:, version:, opts:, disabled:)
10
- @name= name
11
- @version = version
12
- @opts = opts.merge(require: false)
13
- @disabled= disabled
14
- if opts["path"]
15
- @path= Minfra::Cli.config.base_path.join(opts["path"])
16
- end
17
- end
18
- def disabled?
19
- @disabled
20
- end
21
-
22
- #adds the plugin to the
23
- def prepare
24
- debug("plugin prepare: #{name}, #{version}, disabled: #{disabled?}")
25
- return if disabled?
26
- if path
27
- begin
28
- lib_path=path.join('lib')
29
- $LOAD_PATH.unshift lib_path
30
- require name
31
- rescue Gem::Requirement::BadRequirementError, LoadError
32
- warn("plugin prepare path: #{name} (#{$!})")
33
- end
34
- else
35
- begin
36
- @gem_spec=Gem::Specification.find_by_name(name)
37
- gem name, version
38
- rescue Gem::MissingSpecError
39
- warn("plugin prepare gem: #{name}, #{version} (#{$!})")
40
- end
41
- end
42
- end
43
-
44
- def setup
45
- return if disabled?
46
- if path
47
- minfra_path = Pathname.new(path).join("minfracs","init.rb")
48
- if minfra_path.exist?
49
- begin
50
- require minfra_path # this should register the command
51
- rescue LoadError
52
- logger.warn("Minfra plugin detected but dependencies not installed: #{minfra_path} (#{$!}). TRY: minfra plugin install")
53
- end
54
- end
55
- else
56
- error("Gem based plugins not supported yet")
57
- end
58
- end
59
-
60
- def install
61
- return if disabled?
62
- if path
63
- system("cd #{path}; bundle install")
64
- else
65
- system("gem install #{name} --version #{version}")
66
- end
67
- end
68
- end
69
-
70
7
  def initialize(plugins)
71
- @plugins=plugins
8
+ @plugins = plugins
72
9
  end
73
10
 
74
11
  def prepare
75
12
  @plugins.each(&:prepare)
76
13
  end
14
+
77
15
  def setup
78
16
  @plugins.each(&:setup)
79
17
  end
80
-
81
- def install
82
- if path
83
- system("cd #{path}; bundle install")
84
- else
85
- system("gem install #{name} --version #{version}")
86
- end
87
- end
88
-
89
- def each(&block)
90
- @plugins.each(&block)
18
+
19
+ def each(&)
20
+ @plugins.each(&)
91
21
  end
92
-
93
- def self.load
94
- found=[]
95
- [Pathname.new(ENV["MINFRA_PATH"]).join("config","minfra_plugins.json"),
96
- Pathname.new(ENV["MINFRA_PATH"]).join("me","minfra_plugins.json")].each do |file|
22
+
23
+ def self.load(base_path)
24
+ found = []
25
+ [base_path.join('config', 'minfra_plugins.json'),
26
+ base_path.join('me', 'minfra_plugins.json')].each do |file|
97
27
  next unless File.exist?(file)
98
- plugins=JSON.parse(File.read(file))
99
- plugins["plugins"].each do |spec|
100
- found << Plugin.new(name: spec['name'], opts: spec['opts'] || {}, version: spec['version'], disabled: spec['disabled'])
28
+
29
+ plugins = JSON.parse(File.read(file))
30
+ plugins['plugins'].each do |spec|
31
+ found << Plugin.new(name: spec['name'], opts: spec['opts'] || {}, version: spec['version'],
32
+ disabled: spec['disabled'])
101
33
  end
102
34
  end
103
35
  new(found)
104
36
  end
105
-
106
37
  end
107
38
  end
108
39
  end
@@ -12,7 +12,7 @@ module Minfra
12
12
  attr_writer :status
13
13
 
14
14
  attr_reader :stdout_lines
15
-
15
+
16
16
  def initialize
17
17
  @stderr_lines = []
18
18
  @stdout_lines = []
@@ -33,7 +33,7 @@ module Minfra
33
33
  def exitstatus
34
34
  @status.exitstatus
35
35
  end
36
-
36
+
37
37
  def success?
38
38
  @status.success?
39
39
  end
@@ -70,21 +70,21 @@ module Minfra
70
70
 
71
71
  def run
72
72
  debug("running (#{@runner}): #{@cmd}")
73
- res=case @runner
74
- when :system
75
- run_system(Result.new)
76
- when :popen
77
- run_threaded(Result.new)
78
- when :exec
79
- run_exec(Result.new)
80
- else
81
- raise "unknown runner #{@runner}"
82
- end
83
-
73
+ res = case @runner
74
+ when :system
75
+ run_system(Result.new)
76
+ when :popen
77
+ run_threaded(Result.new)
78
+ when :exec
79
+ run_exec(Result.new)
80
+ else
81
+ raise "unknown runner #{@runner}"
82
+ end
83
+
84
84
  if res.error?
85
85
  error "command failed: #{@cmd}"
86
- debug res.stdout
87
- info res.stderr
86
+ debug res.stdout
87
+ info res.stderr
88
88
  end
89
89
  if exit_on_error && res.error?
90
90
  info "command exiting on error (#{res.exitstatus})"
@@ -92,24 +92,25 @@ module Minfra
92
92
  end
93
93
  res
94
94
  end
95
-
95
+
96
96
  private
97
97
 
98
98
  def run_exec(_res)
99
99
  exec(@cmd)
100
100
  end
101
+
101
102
  # you don't get stderr .... yet
102
103
  def run_system(res)
103
- #https://stackoverflow.com/questions/6338908/ruby-difference-between-exec-system-and-x-or-backticks
104
+ # https://stackoverflow.com/questions/6338908/ruby-difference-between-exec-system-and-x-or-backticks
104
105
  begin
105
- out=`#{@cmd}`
106
- out.each_line do |line| res.add(line, :stdout) end
106
+ out = `#{@cmd}`
107
+ out.each_line { |line| res.add(line, :stdout) }
107
108
  rescue StandardError
108
109
  end
109
- res.status = $?
110
+ res.status = $CHILD_STATUS
110
111
  res
111
112
  end
112
-
113
+
113
114
  def run_threaded(res)
114
115
  begin
115
116
  # see: http://stackoverflow.com/a/1162850/83386
@@ -117,7 +118,7 @@ module Minfra
117
118
  # Open4 might be a solution. Using Select might be a solution. Using Process.fork might be a solution....
118
119
  Open3.popen3(@cmd) do |_stdin, stdout, stderr, thread|
119
120
  # read each stream from a new thread
120
- { stdout: stdout, stderr: stderr }.each do |key, stream|
121
+ { stdout:, stderr: }.each do |key, stream|
121
122
  Thread.new do
122
123
  until (raw_line = stream.gets).nil?
123
124
  # stream.each do |raw_line|
@@ -135,7 +136,6 @@ module Minfra
135
136
  end
136
137
  res
137
138
  end
138
-
139
139
  end
140
140
  end
141
141
  end
@@ -1,22 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'erb'
2
4
 
3
5
  module Minfra
4
6
  module Cli
5
- class Templater # not threadsafe!
7
+ # not threadsafe!
8
+ class Templater
6
9
  def self.read(path, params: {}, fallback: nil)
7
- p=Pathname.new(path)
10
+ p = Pathname.new(path)
8
11
  if p.exist?
9
- content=File.read(path)
12
+ content = File.read(path)
10
13
  else
11
- if fallback
12
- content=fallback
13
- else
14
- raise "file #{path} not found"
15
- end
16
- end
14
+ raise "file #{path} not found" unless fallback
15
+
16
+ content = fallback
17
+
18
+ end
17
19
  render(content, params)
18
20
  end
19
-
21
+
20
22
  def self.render(template, params)
21
23
  new(template).render(params)
22
24
  end
@@ -25,7 +27,7 @@ module Minfra
25
27
  destination = Pathname.new(dst)
26
28
  destination.mkpath
27
29
  source = Pathname.new(src)
28
-
30
+
29
31
  source.glob('**/*') do |filename|
30
32
  rel_path = filename.relative_path_from(source)
31
33
 
@@ -40,11 +42,11 @@ module Minfra
40
42
  end
41
43
  end
42
44
  end
43
-
45
+
44
46
  def initialize(template)
45
47
  @erb = ERB.new(template)
46
- @check_mode=false
47
- @check_missing=[]
48
+ @check_mode = false
49
+ @check_missing = []
48
50
  end
49
51
 
50
52
  def missing?
@@ -70,9 +72,7 @@ module Minfra
70
72
 
71
73
  def method_missing(name)
72
74
  if @check_mode
73
- if @check_block
74
- @check_block.call(name)
75
- end
75
+ @check_block&.call(name)
76
76
  @check_missing << name
77
77
  else
78
78
  super
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Minfra
2
4
  module Cli
3
- VERSION = '1.13.2'.freeze
5
+ VERSION = '2.0.0'
4
6
  end
5
7
  end