minfra-cli 0.1.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 +7 -0
- data/.dockerignore +12 -0
- data/.gitignore +16 -0
- data/.rspec +2 -0
- data/CHANGELOG.md +2 -0
- data/Dockerfile +12 -0
- data/bin/build +20 -0
- data/bin/console +16 -0
- data/bin/container_exec +9 -0
- data/bin/run_tests +74 -0
- data/bin/setup.sh +22 -0
- data/exe/minfra +6 -0
- data/lib/deep_merge.rb +149 -0
- data/lib/hash.rb +28 -0
- data/lib/minfra/cli/ask.rb +43 -0
- data/lib/minfra/cli/command.rb +35 -0
- data/lib/minfra/cli/commands/dev.rb +54 -0
- data/lib/minfra/cli/commands/kube.rb +279 -0
- data/lib/minfra/cli/commands/project/branch.rb +17 -0
- data/lib/minfra/cli/commands/project/tag.rb +40 -0
- data/lib/minfra/cli/commands/project.rb +113 -0
- data/lib/minfra/cli/commands/setup.rb +49 -0
- data/lib/minfra/cli/commands/stack/app_template.rb +65 -0
- data/lib/minfra/cli/commands/stack/client_template.rb +36 -0
- data/lib/minfra/cli/commands/stack/kube_stack_template.rb +94 -0
- data/lib/minfra/cli/commands/stack.rb +120 -0
- data/lib/minfra/cli/commands/tag.rb +86 -0
- data/lib/minfra/cli/common.rb +41 -0
- data/lib/minfra/cli/config.rb +111 -0
- data/lib/minfra/cli/document.rb +19 -0
- data/lib/minfra/cli/hook.rb +65 -0
- data/lib/minfra/cli/logging.rb +26 -0
- data/lib/minfra/cli/main_command.rb +32 -0
- data/lib/minfra/cli/plugins.rb +34 -0
- data/lib/minfra/cli/runner.rb +59 -0
- data/lib/minfra/cli/templater.rb +63 -0
- data/lib/minfra/cli/version.rb +5 -0
- data/lib/minfra/cli.rb +80 -0
- data/lib/orchparty/ast.rb +53 -0
- data/lib/orchparty/cli.rb +69 -0
- data/lib/orchparty/context.rb +22 -0
- data/lib/orchparty/dsl_parser.rb +229 -0
- data/lib/orchparty/dsl_parser_kubernetes.rb +361 -0
- data/lib/orchparty/kubernetes_application.rb +305 -0
- data/lib/orchparty/plugin.rb +24 -0
- data/lib/orchparty/plugins/env.rb +41 -0
- data/lib/orchparty/transformations/all.rb +18 -0
- data/lib/orchparty/transformations/mixin.rb +73 -0
- data/lib/orchparty/transformations/remove_internal.rb +16 -0
- data/lib/orchparty/transformations/sort.rb +10 -0
- data/lib/orchparty/transformations/variable.rb +56 -0
- data/lib/orchparty/transformations.rb +24 -0
- data/lib/orchparty/version.rb +3 -0
- data/lib/orchparty.rb +59 -0
- data/minfra-cli.gemspec +40 -0
- data/project.json +7 -0
- data/templates/kind.yaml.erb +33 -0
- data/templates/kube_config.yaml.erb +7 -0
- data/templates/minfra_config.json.erb +26 -0
- 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
|