minfra-cli 1.13.3 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) 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 -0
  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 +172 -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 +21 -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/plugin.rb +10 -9
  49. data/lib/orchparty/plugins/env.rb +14 -13
  50. data/lib/orchparty/transformations/all.rb +3 -1
  51. data/lib/orchparty/transformations/mixin.rb +24 -24
  52. data/lib/orchparty/transformations/remove_internal.rb +3 -2
  53. data/lib/orchparty/transformations/sort.rb +2 -1
  54. data/lib/orchparty/transformations/variable.rb +6 -5
  55. data/lib/orchparty/transformations.rb +2 -0
  56. data/lib/orchparty/version.rb +3 -1
  57. data/lib/orchparty.rb +14 -14
  58. 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.3'.freeze
5
+ VERSION = '2.0.1'
4
6
  end
5
7
  end