minfra-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +12 -0
  3. data/.gitignore +16 -0
  4. data/.rspec +2 -0
  5. data/CHANGELOG.md +2 -0
  6. data/Dockerfile +12 -0
  7. data/bin/build +20 -0
  8. data/bin/console +16 -0
  9. data/bin/container_exec +9 -0
  10. data/bin/run_tests +74 -0
  11. data/bin/setup.sh +22 -0
  12. data/exe/minfra +6 -0
  13. data/lib/deep_merge.rb +149 -0
  14. data/lib/hash.rb +28 -0
  15. data/lib/minfra/cli/ask.rb +43 -0
  16. data/lib/minfra/cli/command.rb +35 -0
  17. data/lib/minfra/cli/commands/dev.rb +54 -0
  18. data/lib/minfra/cli/commands/kube.rb +279 -0
  19. data/lib/minfra/cli/commands/project/branch.rb +17 -0
  20. data/lib/minfra/cli/commands/project/tag.rb +40 -0
  21. data/lib/minfra/cli/commands/project.rb +113 -0
  22. data/lib/minfra/cli/commands/setup.rb +49 -0
  23. data/lib/minfra/cli/commands/stack/app_template.rb +65 -0
  24. data/lib/minfra/cli/commands/stack/client_template.rb +36 -0
  25. data/lib/minfra/cli/commands/stack/kube_stack_template.rb +94 -0
  26. data/lib/minfra/cli/commands/stack.rb +120 -0
  27. data/lib/minfra/cli/commands/tag.rb +86 -0
  28. data/lib/minfra/cli/common.rb +41 -0
  29. data/lib/minfra/cli/config.rb +111 -0
  30. data/lib/minfra/cli/document.rb +19 -0
  31. data/lib/minfra/cli/hook.rb +65 -0
  32. data/lib/minfra/cli/logging.rb +26 -0
  33. data/lib/minfra/cli/main_command.rb +32 -0
  34. data/lib/minfra/cli/plugins.rb +34 -0
  35. data/lib/minfra/cli/runner.rb +59 -0
  36. data/lib/minfra/cli/templater.rb +63 -0
  37. data/lib/minfra/cli/version.rb +5 -0
  38. data/lib/minfra/cli.rb +80 -0
  39. data/lib/orchparty/ast.rb +53 -0
  40. data/lib/orchparty/cli.rb +69 -0
  41. data/lib/orchparty/context.rb +22 -0
  42. data/lib/orchparty/dsl_parser.rb +229 -0
  43. data/lib/orchparty/dsl_parser_kubernetes.rb +361 -0
  44. data/lib/orchparty/kubernetes_application.rb +305 -0
  45. data/lib/orchparty/plugin.rb +24 -0
  46. data/lib/orchparty/plugins/env.rb +41 -0
  47. data/lib/orchparty/transformations/all.rb +18 -0
  48. data/lib/orchparty/transformations/mixin.rb +73 -0
  49. data/lib/orchparty/transformations/remove_internal.rb +16 -0
  50. data/lib/orchparty/transformations/sort.rb +10 -0
  51. data/lib/orchparty/transformations/variable.rb +56 -0
  52. data/lib/orchparty/transformations.rb +24 -0
  53. data/lib/orchparty/version.rb +3 -0
  54. data/lib/orchparty.rb +59 -0
  55. data/minfra-cli.gemspec +40 -0
  56. data/project.json +7 -0
  57. data/templates/kind.yaml.erb +33 -0
  58. data/templates/kube_config.yaml.erb +7 -0
  59. data/templates/minfra_config.json.erb +26 -0
  60. metadata +196 -0
data/lib/minfra/cli.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'thor'
2
+ require 'open3'
3
+ require 'json'
4
+ require 'ostruct'
5
+ require 'orchparty'
6
+
7
+ require_relative 'cli/logging'
8
+ require_relative 'cli/config'
9
+ require_relative 'cli/version'
10
+ require_relative 'cli/hook'
11
+ require_relative 'cli/common'
12
+ require_relative 'cli/command'
13
+ require_relative 'cli/templater'
14
+ require_relative 'cli/ask'
15
+ require_relative 'cli/document'
16
+ require_relative 'cli/runner'
17
+ require_relative 'cli/plugins'
18
+
19
+ require 'active_support'
20
+ require 'active_support/core_ext'
21
+
22
+ require "ENV['MINFRA_PATH']/config/preload.rb" if File.exist?("ENV['MINFRA_PATH']/config/preload.rb")
23
+
24
+ module Minfra
25
+ module Cli
26
+ extend Minfra::Cli::Logging
27
+
28
+ def self.root_path
29
+ Pathname.new(File.expand_path(File.join(__FILE__, '../../../')))
30
+ end
31
+
32
+ def self.config
33
+ @config ||= Config.new
34
+ end
35
+ def self.scan
36
+ root_path.join("lib/minfra/cli/commands").each_child do |command_path|
37
+ require command_path if command_path.to_s.match(/\.rb$/) && !command_path.to_s.match(/\#/)
38
+ end
39
+ # this is like railties but their called minfracs
40
+ $LOAD_PATH.each do |path|
41
+ minfra_path = Pathname.new(path).join("..","minfracs","init.rb")
42
+ if minfra_path.exist?
43
+ require minfra_path # this should register the command
44
+ end
45
+ end
46
+ end
47
+
48
+ def self.register(subcommand,info,command)
49
+ #debug("Registered command #{subcommand}")
50
+ @subcommands ||= {}
51
+ @subcommands[subcommand.to_sym]= OpenStruct.new(name: subcommand, info: info, command: command)
52
+ end
53
+
54
+ def self.resolve
55
+ @subcommands.values.each do |sub|
56
+ Minfra::Cli::Main.desc(sub.name,sub.info)
57
+ Minfra::Cli::Main.subcommand(sub.name,sub.command)
58
+ end
59
+ end
60
+
61
+ def self.subcommand(name)
62
+ @subcommands[name.to_sym]&.command
63
+ end
64
+
65
+ def self.before_hook(subcommand, command, &block)
66
+ subcommand(subcommand).before_hook(command, &block)
67
+ end
68
+
69
+ def self.after_hook(subcommand, command, &block)
70
+ subcommand(subcommand).after_hook(command, &block)
71
+ end
72
+ end
73
+ end
74
+
75
+ Minfra::Cli::Plugins.load
76
+ Minfra::Cli.scan
77
+ require_relative 'cli/main_command'
78
+ Minfra::Cli.resolve
79
+
80
+
@@ -0,0 +1,53 @@
1
+ require 'hashie'
2
+
3
+ module Orchparty
4
+ class AST
5
+ class Node < ::Hashie::Mash
6
+ include Hashie::Extensions::DeepMerge
7
+ include Hashie::Extensions::DeepMergeConcat
8
+ include Hashie::Extensions::MethodAccess
9
+ include Hashie::Extensions::Mash::KeepOriginalKeys
10
+ disable_warnings
11
+
12
+ def get_binding
13
+ binding
14
+ end
15
+ end
16
+
17
+ def self.hash(args = {})
18
+ Node.new.merge(args)
19
+ end
20
+
21
+ def self.array(args = [])
22
+ args
23
+ end
24
+
25
+ def self.root(args = {})
26
+ Node.new(applications: {}, _mixins: {}).merge(args)
27
+ end
28
+
29
+ def self.mixin(args = {})
30
+ Node.new({services: {}, _mixins: {}, volumes: {}, _variables: {}, networks: {}, _service_order: []}).merge(args)
31
+ end
32
+
33
+ def self.application(args = {})
34
+ Node.new({services: {}, _mixins: {}, _mix:[], volumes: {}, _variables: {}, networks: {}, _service_order: []}).merge(args)
35
+ end
36
+
37
+ def self.all(args = {})
38
+ Node.new(_mix:[], _variables: {}).merge(args)
39
+ end
40
+
41
+ def self.application_mixin(args = {})
42
+ Node.new(_mix:[], _variables: {}).merge(args)
43
+ end
44
+
45
+ def self.service(args = {})
46
+ Node.new(_mix:[], _variables: {}).merge(args)
47
+ end
48
+
49
+ def self.chart(args = {})
50
+ Node.new(_mix:[], _variables: {}, _services: []).merge(args)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,69 @@
1
+ require 'orchparty'
2
+ require 'gli'
3
+
4
+ Orchparty.load_all_available_plugins
5
+
6
+ class OrchPartyApp
7
+ extend GLI::App
8
+
9
+ program_desc 'Write your orchestration configuration with a Ruby DSL that allows you to have mixins, imports and variables.'
10
+ version Orchparty::VERSION
11
+
12
+ subcommand_option_handling :normal
13
+
14
+ desc "install kubernetes application"
15
+ command :print do |com|
16
+ com.flag [:cluster_name,:c,'cluster-name'], required: true, :desc => 'The cluster to install the app'
17
+ com.flag [:filename,:f,'file-name'], required: true, :desc => 'The Orchparty input file'
18
+ com.flag [:application,:a], required: true, :desc => 'The application that should be installed'
19
+ com.switch :"force-variable-definition", :default_value => false, :desc => "Raises an Error if the input contains a not defined variable"
20
+ com.flag [:method,:m], required: true, :desc => 'method to print upgrade/install'
21
+ com.action do |_, args|
22
+ Orchparty.print(cluster_name: args[:c], application_name: args[:a], force_variable_definition: args["force-variable-definition"], file_name: args[:f], method: args[:m])
23
+ end
24
+ end
25
+
26
+ desc "install kubernetes application"
27
+ command :install do |com|
28
+ com.flag [:cluster_name,:c,'cluster-name'], required: true, :desc => 'The cluster to install the app'
29
+ com.flag [:filename,:f,'file-name'], required: true, :desc => 'The Orchparty input file'
30
+ com.flag [:application,:a], required: true, :desc => 'The application that should be installed'
31
+ com.switch :"force-variable-definition", :default_value => false, :desc => "Raises an Error if the input contains a not defined variable"
32
+ com.action do |_, args|
33
+ Orchparty.install(cluster_name: args[:c], application_name: args[:a], force_variable_definition: args["force-variable-definition"], file_name: args[:f])
34
+ end
35
+ end
36
+
37
+ desc "upgrade kubernetes application"
38
+ command :upgrade do |com|
39
+ com.flag [:cluster_name,:c,'cluster-name'], required: true, :desc => 'The cluster to install the app'
40
+ com.flag [:filename,:f,'file-name'], required: true, :desc => 'The Orchparty input file'
41
+ com.flag [:application,:a], required: true, :desc => 'The application that should be installed'
42
+ com.switch :"force-variable-definition", :default_value => false, :desc => "Raises an Error if the input contains a not defined variable"
43
+ com.action do |_, args|
44
+ Orchparty.upgrade(cluster_name: args[:c], application_name: args[:a], force_variable_definition: args["force-variable-definition"], file_name: args[:f])
45
+ end
46
+ end
47
+
48
+ desc "Compiles a Orchparty input file to a orchestration framework configuration"
49
+ command :generate do |com|
50
+ Orchparty.plugins.each do |name, plugin|
51
+ com.desc plugin.desc
52
+ com.command(name) do |plugin_command|
53
+ plugin_command.flag [:filename,:f,'file-name'], required: true, :desc => 'The Orchparty input file'
54
+ plugin_command.flag [:application,:a], required: true, :desc => 'The application that should be compiled'
55
+ plugin_command.switch :"force-variable-definition", :default_value => false, :desc => "Raises an Error if the input contains a not defined variable"
56
+ plugin.define_flags(plugin_command)
57
+ plugin_command.action do |global_options,plugin_options,args|
58
+ options = plugin_options.delete(GLI::Command::PARENT)
59
+ options[:application] = plugin_options[:application]
60
+ options[:filename] = plugin_options[:filename]
61
+ options[:force_variable_definition] = plugin_options[:"force-variable-definition"]
62
+ Orchparty.generate(name, options, plugin_options)
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ exit OrchPartyApp.run(ARGV)
@@ -0,0 +1,22 @@
1
+ module Orchparty
2
+ class Context < ::Hashie::Mash
3
+ include Hashie::Extensions::DeepMerge
4
+ include Hashie::Extensions::DeepMergeConcat
5
+ include Hashie::Extensions::MethodAccess
6
+ include Hashie::Extensions::Mash::KeepOriginalKeys
7
+
8
+
9
+ def method_missing(name, *args)
10
+ raise "#{name} not declared for #{application.name}.#{service.name}" if @_force_variable_definition && !key?(name) && !key?(name.to_s)
11
+ super
12
+ end
13
+
14
+ def _force_variable_definition=(v)
15
+ @_force_variable_definition = v
16
+ end
17
+
18
+ def context
19
+ self
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,229 @@
1
+ require 'pathname'
2
+ module Orchparty
3
+ class DSLParser
4
+ attr_reader :filename
5
+
6
+ def initialize(filename)
7
+ @filename = filename
8
+ end
9
+
10
+ def parse
11
+ file_content = File.read(filename)
12
+ builder = RootBuilder.new
13
+ builder.instance_eval(file_content, filename)
14
+ builder._build
15
+ end
16
+ end
17
+
18
+ class Builder
19
+ def self.build(*args, block)
20
+ builder = self.new(*args)
21
+ builder.instance_eval(&block)
22
+ builder._build
23
+ end
24
+
25
+ def assign_or_merge(node, key, value)
26
+ if node[key]
27
+ merged = node[key].deep_merge_concat(value)
28
+ puts "'#{key}' updated to '#{merged}'"
29
+ node[key] = merged
30
+ else
31
+ puts "'#{key}' set to '#{value}'"
32
+ node[key] = value
33
+ end
34
+ end
35
+ end
36
+
37
+ class RootBuilder < Builder
38
+
39
+ def initialize
40
+ @root = AST.root
41
+ end
42
+
43
+ def import(rel_file)
44
+ old_file_path = Pathname.new(caller[0][/[^:]+/]).parent
45
+ rel_file_path = Pathname.new rel_file
46
+ new_file_path = old_file_path + rel_file_path
47
+ file_content = File.read(new_file_path)
48
+ instance_eval(file_content, new_file_path.expand_path.to_s)
49
+ end
50
+
51
+ def application(name, &block)
52
+ @root.applications[name] = ApplicationBuilder.build(name, block)
53
+ self
54
+ end
55
+
56
+ def mixin(name, &block)
57
+ @root._mixins[name] = MixinBuilder.build(name, block)
58
+ self
59
+ end
60
+
61
+ def _build
62
+ @root
63
+ end
64
+ end
65
+
66
+ class MixinBuilder < Builder
67
+
68
+ def initialize(name)
69
+ @mixin = AST.mixin(name: name)
70
+ end
71
+
72
+ def service(name, &block)
73
+ result = ServiceBuilder.build(name, block)
74
+ @mixin.services[name] = result
75
+ @mixin._mixins[name] = result
76
+ self
77
+ end
78
+
79
+ def variables(&block)
80
+ @mixin._variables = VariableBuilder.build(block)
81
+ self
82
+ end
83
+
84
+ def mixin(name, &block)
85
+ @mixin._mixins[name] = ServiceBuilder.build(name, block)
86
+ end
87
+
88
+ def volumes(&block)
89
+ @mixin.volumes = HashBuilder.build(block)
90
+ end
91
+
92
+ def networks(&block)
93
+ @mixin.networks = HashBuilder.build(block)
94
+ end
95
+
96
+ def _build
97
+ @mixin
98
+ end
99
+ end
100
+
101
+ class ApplicationBuilder < Builder
102
+
103
+ def initialize(name)
104
+ @application = AST.application(name: name)
105
+ end
106
+
107
+ def mix(name)
108
+ @application._mix << name
109
+ end
110
+
111
+ def mixin(name, &block)
112
+ @application._mixins[name] = ApplicationMixinBuilder.build(block)
113
+ self
114
+ end
115
+
116
+ def all(&block)
117
+ @application.all = AllBuilder.build(block)
118
+ self
119
+ end
120
+
121
+ def variables(&block)
122
+ @application._variables = VariableBuilder.build(block)
123
+ self
124
+ end
125
+
126
+ def volumes(&block)
127
+ @application.volumes = HashBuilder.build(block)
128
+ self
129
+ end
130
+
131
+ def networks(&block)
132
+ @application.networks = HashBuilder.build(block)
133
+ end
134
+
135
+ def service(name, &block)
136
+ @application.services[name] = ServiceBuilder.build(name, block)
137
+ self
138
+ end
139
+
140
+ def _build
141
+ @application
142
+ end
143
+ end
144
+
145
+ class HashBuilder < Builder
146
+
147
+ def method_missing(_, *values, &block)
148
+ if block_given?
149
+ value = HashBuilder.build(block)
150
+ if values.count == 1
151
+ @hash ||= AST.hash
152
+ @hash[values.first.to_sym] = value
153
+ else
154
+ @hash ||= AST.array
155
+ @hash << value
156
+ end
157
+ else
158
+ value = values.first
159
+ if value.is_a? Hash
160
+ @hash ||= AST.hash
161
+ key, value = value.first
162
+ @hash[key.to_sym] = value
163
+ else
164
+ @hash ||= AST.array
165
+ @hash << value
166
+ end
167
+ end
168
+ self
169
+ end
170
+
171
+ def _build
172
+ @hash
173
+ end
174
+ end
175
+
176
+ class VariableBuilder < HashBuilder
177
+ def _build
178
+ super || {}
179
+ end
180
+ end
181
+
182
+ class CommonBuilder < Builder
183
+
184
+ def initialize(node)
185
+ @node = node
186
+ end
187
+
188
+ def mix(name)
189
+ @node._mix << name
190
+ end
191
+
192
+ def method_missing(name, *values, &block)
193
+ if block_given?
194
+ assign_or_merge(@node, name, HashBuilder.build(block))
195
+ else
196
+ assign_or_merge(@node, name, values.first)
197
+ end
198
+ end
199
+
200
+ def _build
201
+ @node
202
+ end
203
+
204
+ def variables(&block)
205
+ @node._variables ||= {}
206
+ @node._variables = @node._variables.merge(VariableBuilder.build(block))
207
+ self
208
+ end
209
+ end
210
+
211
+ class AllBuilder < CommonBuilder
212
+ def initialize
213
+ super AST.all
214
+ end
215
+ end
216
+
217
+ class ApplicationMixinBuilder < CommonBuilder
218
+ def initialize
219
+ super AST.application_mixin
220
+ end
221
+ end
222
+
223
+ class ServiceBuilder < CommonBuilder
224
+
225
+ def initialize(name)
226
+ super AST.service(name: name)
227
+ end
228
+ end
229
+ end