ConfigLMM 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +5 -0
- data/Examples/Android.mm.yaml +8 -0
- data/Examples/Apps/Blog.mm.yaml +7 -0
- data/Examples/Apps/Jellyfin.mm.yaml +3 -0
- data/Examples/Implemented.mm.yaml +155 -0
- data/Examples/Keys.ini +7 -0
- data/Examples/Linux.mm.yaml +16 -0
- data/Examples/Windows.mm.yaml +11 -0
- data/Examples/configlmmAuth.sh +26 -0
- data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.conf.erb +38 -0
- data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.lmm.rb +19 -0
- data/Plugins/Apps/IPFS/IPFS.conf.erb +44 -0
- data/Plugins/Apps/IPFS/IPFS.lmm.rb +23 -0
- data/Plugins/Apps/InfluxDB/InfluxDB.conf.erb +34 -0
- data/Plugins/Apps/InfluxDB/InfluxDB.lmm.rb +19 -0
- data/Plugins/Apps/Jackett/Jackett.conf.erb +38 -0
- data/Plugins/Apps/Jackett/Jackett.lmm.rb +19 -0
- data/Plugins/Apps/Jellyfin/Jellyfin.conf.erb +59 -0
- data/Plugins/Apps/Jellyfin/Jellyfin.lmm.rb +23 -0
- data/Plugins/Apps/Mastodon/Mastodon.conf.erb +81 -0
- data/Plugins/Apps/Mastodon/Mastodon.lmm.rb +23 -0
- data/Plugins/Apps/Matrix/Matrix.conf.erb +36 -0
- data/Plugins/Apps/Matrix/Matrix.lmm.rb +23 -0
- data/Plugins/Apps/Netdata/Netdata.conf.erb +37 -0
- data/Plugins/Apps/Netdata/Netdata.lmm.rb +23 -0
- data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +165 -0
- data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +23 -0
- data/Plugins/Apps/Nginx/config-lmm/errors.conf +31 -0
- data/Plugins/Apps/Nginx/config-lmm/private.conf +6 -0
- data/Plugins/Apps/Nginx/config-lmm/proxy.conf +15 -0
- data/Plugins/Apps/Nginx/config-lmm/public.conf +3 -0
- data/Plugins/Apps/Nginx/config-lmm/ssl.conf +18 -0
- data/Plugins/Apps/Nginx/main.conf +30 -0
- data/Plugins/Apps/Nginx/nginx.conf +90 -0
- data/Plugins/Apps/Nginx/nginx.lmm.rb +62 -0
- data/Plugins/Apps/Nginx/proxy.conf.erb +31 -0
- data/Plugins/Apps/Odoo/Odoo.conf.erb +44 -0
- data/Plugins/Apps/Odoo/Odoo.lmm.rb +23 -0
- data/Plugins/Apps/Pterodactyl/Pterodactyl.conf.erb +50 -0
- data/Plugins/Apps/Pterodactyl/Pterodactyl.lmm.rb +30 -0
- data/Plugins/Apps/Pterodactyl/Wings.conf.erb +38 -0
- data/Plugins/Apps/Sunshine/Sunshine.conf.erb +31 -0
- data/Plugins/Apps/Sunshine/Sunshine.lmm.rb +21 -0
- data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +48 -0
- data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +25 -0
- data/Plugins/Apps/bitmagnet/bitmagnet.conf.erb +35 -0
- data/Plugins/Apps/bitmagnet/bitmagnet.lmm.rb +19 -0
- data/Plugins/Apps/gollum/config.ru +11 -0
- data/Plugins/Apps/gollum/gollum.conf.erb +41 -0
- data/Plugins/Apps/gollum/gollum.lmm.rb +52 -0
- data/Plugins/OS/Linux.lmm.rb +64 -0
- data/Plugins/OS/Routers/Aruba/ArubaInstant.lmm.rb +144 -0
- data/Plugins/Platforms/GitHub.lmm.rb +57 -0
- data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +83 -0
- data/Plugins/Platforms/GoDaddy/zone.txt.erb +13 -0
- data/Plugins/Platforms/porkbun.lmm.rb +129 -0
- data/Plugins/Platforms/porkbun_spec.rb +110 -0
- data/Plugins/Services/DNS/AmberBit.lmm.rb +14 -0
- data/Plugins/Services/DNS/ArubaItDNS.lmm.rb +14 -0
- data/Plugins/Services/DNS/NICLV.lmm.rb +18 -0
- data/Plugins/Services/DNS/PowerDNS.lmm.rb +261 -0
- data/Plugins/Services/DNS/tonic.lmm.rb +126 -0
- data/README.md +337 -0
- data/Rakefile +15 -0
- data/UNLICENSE +24 -0
- data/bin/configlmm +7 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/lib/ConfigLMM/Framework/plugins/dns.rb +63 -0
- data/lib/ConfigLMM/Framework/plugins/errors.rb +23 -0
- data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +55 -0
- data/lib/ConfigLMM/Framework/plugins/plugin.rb +167 -0
- data/lib/ConfigLMM/Framework/plugins/ssh.rb +37 -0
- data/lib/ConfigLMM/Framework/plugins/store.rb +57 -0
- data/lib/ConfigLMM/Framework/plugins.rb +5 -0
- data/lib/ConfigLMM/Framework/registrator.rb +32 -0
- data/lib/ConfigLMM/Framework.rb +9 -0
- data/lib/ConfigLMM/LMM/plugins.rb +5 -0
- data/lib/ConfigLMM/LMM.rb +8 -0
- data/lib/ConfigLMM/cli.rb +161 -0
- data/lib/ConfigLMM/command.rb +53 -0
- data/lib/ConfigLMM/commands/build.rb +41 -0
- data/lib/ConfigLMM/commands/cleanup.rb +30 -0
- data/lib/ConfigLMM/commands/configsCommand.rb +167 -0
- data/lib/ConfigLMM/commands/deploy.rb +39 -0
- data/lib/ConfigLMM/commands/diff.rb +45 -0
- data/lib/ConfigLMM/commands/list.rb +15 -0
- data/lib/ConfigLMM/commands/refresh.rb +46 -0
- data/lib/ConfigLMM/commands/types.rb +35 -0
- data/lib/ConfigLMM/commands/validate.rb +49 -0
- data/lib/ConfigLMM/context.rb +52 -0
- data/lib/ConfigLMM/io/configList.rb +98 -0
- data/lib/ConfigLMM/io/path.rb +48 -0
- data/lib/ConfigLMM/io/source.rb +47 -0
- data/lib/ConfigLMM/io.rb +2 -0
- data/lib/ConfigLMM/state.rb +78 -0
- data/lib/ConfigLMM/utils/filters.rb +126 -0
- data/lib/ConfigLMM/version.rb +5 -0
- data/lib/ConfigLMM.rb +6 -0
- data/sig/ConfigLMM.rbs +4 -0
- metadata +485 -0
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'errors'
|
4
|
+
require_relative 'store'
|
5
|
+
require 'http'
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
module ConfigLMM
|
9
|
+
module Framework
|
10
|
+
|
11
|
+
class Plugin
|
12
|
+
|
13
|
+
|
14
|
+
def self.inherited(plugin)
|
15
|
+
Store.registerPlugin(plugin)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.id
|
19
|
+
@ID ||= self.normalizeId(self.className.to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.addMeta(*fields)
|
23
|
+
fields.each do |field|
|
24
|
+
self.define_singleton_method(field) do |value|
|
25
|
+
@Meta ||= {}
|
26
|
+
@Meta[field] = value
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.persistBuildDir
|
32
|
+
@PersistBuildDir = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.persistBuildDir?
|
36
|
+
@PersistBuildDir == true
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
alias :className :name
|
41
|
+
end
|
42
|
+
|
43
|
+
addMeta :name, :description
|
44
|
+
|
45
|
+
def initialize(logger, prompt, plugins)
|
46
|
+
@Logger = logger
|
47
|
+
@Prompt = prompt
|
48
|
+
@Plugins = plugins
|
49
|
+
@Diff = {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def id
|
53
|
+
self.class.id
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.actionMethod(type, action)
|
57
|
+
name = type.to_s
|
58
|
+
name[0] = name[0].upcase
|
59
|
+
('action' + name + action.to_s.capitalize).to_sym
|
60
|
+
end
|
61
|
+
|
62
|
+
def hasAction?(type, action)
|
63
|
+
self.methods.include?(self.class.actionMethod(type, action))
|
64
|
+
end
|
65
|
+
|
66
|
+
def diff
|
67
|
+
@Diff
|
68
|
+
end
|
69
|
+
|
70
|
+
def cleanup(configs, state, context, options)
|
71
|
+
# Do nothing
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def logger
|
77
|
+
@Logger
|
78
|
+
end
|
79
|
+
|
80
|
+
def prompt
|
81
|
+
@Prompt
|
82
|
+
end
|
83
|
+
|
84
|
+
def plugins
|
85
|
+
@Plugins
|
86
|
+
end
|
87
|
+
|
88
|
+
def shouldMatch(id, targetKey, stateKey, target, activeState)
|
89
|
+
if target[targetKey] != activeState[stateKey]
|
90
|
+
@Diff.update({targetKey => [target[targetKey], activeState[stateKey]]})
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def fileWrite(target, data, dry)
|
95
|
+
if dry
|
96
|
+
prompt.say('Would write file ' + target)
|
97
|
+
else
|
98
|
+
File.write(target, data)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def copy(source, target, dry)
|
103
|
+
if dry
|
104
|
+
prompt.say('Would copy ' + source + ' to ' + target)
|
105
|
+
else
|
106
|
+
FileUtils.cp_r(source, target, noop: dry)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def copyNotPresent(source, target, dry)
|
111
|
+
if !File.exist?(target + File.basename(source))
|
112
|
+
if dry
|
113
|
+
prompt.say('Would copy ' + source + ' to ' + target)
|
114
|
+
else
|
115
|
+
FileUtils.cp_r(source, target, noop: dry)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def rm(path, dry)
|
121
|
+
if dry
|
122
|
+
prompt.say('Would remove ' + path)
|
123
|
+
else
|
124
|
+
FileUtils.rm_r(path, noop: dry)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def mkdir(target, dry)
|
129
|
+
if dry
|
130
|
+
prompt.say('Would create ' + target)
|
131
|
+
else
|
132
|
+
FileUtils.mkdir_p(target)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def chown(user, group, target, dry)
|
137
|
+
if dry
|
138
|
+
prompt.say("Would chown #{target} as #{user}:#{group}")
|
139
|
+
else
|
140
|
+
FileUtils.chown_R(user, group, target)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def renderTemplate(template, target, outputPath, options)
|
145
|
+
variables = {
|
146
|
+
config: target,
|
147
|
+
}
|
148
|
+
result = template.result_with_hash(variables)
|
149
|
+
mkdir(File.dirname(outputPath), options['dry'])
|
150
|
+
if options['dry']
|
151
|
+
prompt.say('Would write to ' + outputPath)
|
152
|
+
else
|
153
|
+
File.write(outputPath, result)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def self.normalizeId(id)
|
158
|
+
id = id.split('::').last
|
159
|
+
if id.downcase.end_with?('plugin')
|
160
|
+
id = id[0...-6]
|
161
|
+
end
|
162
|
+
id.to_sym
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative 'plugin'
|
5
|
+
require_relative 'errors'
|
6
|
+
require 'net/ssh'
|
7
|
+
|
8
|
+
module ConfigLMM
|
9
|
+
module Framework
|
10
|
+
|
11
|
+
class SSH < Framework::Plugin
|
12
|
+
|
13
|
+
def parseLocation(location)
|
14
|
+
user, hostname = location.split('@')
|
15
|
+
if hostname.nil?
|
16
|
+
hostname = user
|
17
|
+
user = nil
|
18
|
+
end
|
19
|
+
hostname, port = hostname.split(':')
|
20
|
+
port = 22 unless port
|
21
|
+
{
|
22
|
+
hostname: hostname,
|
23
|
+
user: user,
|
24
|
+
port: port
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def checkSSHAuth!(location, password)
|
29
|
+
creds = parseLocation(location)
|
30
|
+
Net::SSH.start(creds[:hostname], creds[:user], password: password, port: creds[:port]) do |ssh|
|
31
|
+
# All good if we got here
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'errors'
|
4
|
+
|
5
|
+
module ConfigLMM
|
6
|
+
module Framework
|
7
|
+
class Store
|
8
|
+
|
9
|
+
@@AvailablePlugins = {}
|
10
|
+
|
11
|
+
def self.registerPlugin(plugin)
|
12
|
+
@@AvailablePlugins[plugin.id] = plugin
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.plugins
|
16
|
+
@@AvailablePlugins.values
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.countPlugins
|
20
|
+
@@AvailablePlugins.length
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.plugin(pluginId)
|
24
|
+
raise PluginMissingError.new("Couldn't find plugin '#{pluginId}'") unless @@AvailablePlugins.key?(pluginId)
|
25
|
+
@@AvailablePlugins[pluginId]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.leafPlugins
|
29
|
+
nonLeaves = Set.new
|
30
|
+
@@AvailablePlugins.each do |id, plugin|
|
31
|
+
plugin.ancestors.each do |ancestor|
|
32
|
+
nonLeaves << ancestor if ancestor != plugin
|
33
|
+
end
|
34
|
+
end
|
35
|
+
@@AvailablePlugins.reject do |id, plugin|
|
36
|
+
nonLeaves.include?(plugin)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.boot(logger, prompt, plugins)
|
41
|
+
leafPlugins.each do |id, plugin|
|
42
|
+
self.initPlugin(id, logger, prompt, plugins)
|
43
|
+
rescue PluginLoadError => error
|
44
|
+
logger.warn("Plugin '#{id}' failed to load!\n#{error.message}" + (error.cause ? ' - ' : ''), error.cause)
|
45
|
+
end
|
46
|
+
true
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.initPlugin(pluginId, logger, prompt, plugins)
|
50
|
+
pluginId = pluginId.to_sym
|
51
|
+
raise 'Recursive/cyclic plugin' if plugins.key?(pluginId)
|
52
|
+
plugins[pluginId] = @@AvailablePlugins[pluginId].new(logger, prompt, plugins)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConfigLMM
|
4
|
+
module Framework
|
5
|
+
module Registrator
|
6
|
+
|
7
|
+
@BasePaths = []
|
8
|
+
|
9
|
+
def self.addPath(basePath)
|
10
|
+
@BasePaths << basePath
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.registerAll(logger)
|
14
|
+
@BasePaths.each do |basePath|
|
15
|
+
self.register(basePath, logger)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def self.register(basePath, logger)
|
22
|
+
Dir.glob(basePath + 'Plugins/**/**.lmm.rb').each do |file|
|
23
|
+
require file
|
24
|
+
rescue ScriptError => error
|
25
|
+
logger.error("Failed to load #{File.realdirpath(file)}\n", error)
|
26
|
+
raise error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'thor'
|
5
|
+
require_relative 'LMM'
|
6
|
+
|
7
|
+
module ConfigLMM
|
8
|
+
# Handle the application command line parsing
|
9
|
+
# and the dispatch to various command objects
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
class CLI < Thor
|
13
|
+
ArgumentError = Class.new(RuntimeError)
|
14
|
+
MissingArgument = Class.new(ArgumentError)
|
15
|
+
InvalidOption = Class.new(ArgumentError)
|
16
|
+
|
17
|
+
#class_option :locations, type: :string, default: '', group: :configs, desc: 'Filter by config file locations'
|
18
|
+
#class_option :things, type: :string, default: '', group: :configs, desc: 'Filter things to use'
|
19
|
+
class_option :level, type: :string, enum: ['debug', 'info', 'warn', 'error'], default: 'info', desc: 'Logging level to use'
|
20
|
+
class_option :dry, aliases: '-n', type: :boolean, desc: 'Only show actions without performing'
|
21
|
+
|
22
|
+
desc 'version', 'Show program\'s version'
|
23
|
+
def version
|
24
|
+
require_relative 'version'
|
25
|
+
puts "v" + ConfigLMM::VERSION
|
26
|
+
end
|
27
|
+
map %w[--version -v] => :version
|
28
|
+
|
29
|
+
|
30
|
+
desc 'list [CONFIGS...]', 'List things'
|
31
|
+
method_option :help, aliases: '-h', type: :boolean,
|
32
|
+
desc: 'Display usage information'
|
33
|
+
def list(*configPaths)
|
34
|
+
handleCommand(:list, configPaths, options)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
desc 'validate [CONFIGS...]', 'Check whether the configuration is valid'
|
39
|
+
method_option :help, aliases: '-h', type: :boolean,
|
40
|
+
desc: 'Display usage information'
|
41
|
+
def validate(*configPaths)
|
42
|
+
handleCommand(:validate, configPaths, options)
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'refresh [CONFIGS...]', 'Update local state to match deployed things'
|
46
|
+
method_option :help, aliases: '-h', type: :boolean,
|
47
|
+
desc: 'Display usage information'
|
48
|
+
method_option :state, aliases: '-s', type: :string,
|
49
|
+
desc: 'Path to the state file'
|
50
|
+
method_option :context, aliases: '-c', type: :string,
|
51
|
+
desc: 'Path to context file'
|
52
|
+
def refresh(*configPaths)
|
53
|
+
handleCommand(:refresh, configPaths, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
desc 'diff [CONFIGS...]', 'Show changes that will be applied with next deploy'
|
58
|
+
method_option :help, aliases: '-h', type: :boolean,
|
59
|
+
desc: 'Display usage information'
|
60
|
+
method_option :state, aliases: '-s', type: :string,
|
61
|
+
desc: 'Path to the state file'
|
62
|
+
method_option :context, aliases: '-c', type: :string,
|
63
|
+
desc: 'Path to context file'
|
64
|
+
def diff(*configPaths)
|
65
|
+
handleCommand(:diff, configPaths, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
desc 'build [CONFIGS...]', 'Build configuration in deployable form'
|
70
|
+
method_option :help, aliases: '-h', type: :boolean,
|
71
|
+
desc: 'Display usage information'
|
72
|
+
method_option :context, aliases: '-c', type: :string,
|
73
|
+
desc: 'Path to context file'
|
74
|
+
method_option :output, aliases: '-o', type: :string,
|
75
|
+
default: './build',
|
76
|
+
desc: 'Output folder'
|
77
|
+
def build(*configPaths)
|
78
|
+
handleCommand(:build, configPaths, options)
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
desc 'deploy [CONFIGS...]', 'Deploy configuration'
|
83
|
+
method_option :help, aliases: '-h', type: :boolean,
|
84
|
+
desc: 'Display usage information'
|
85
|
+
method_option :state, aliases: '-s', type: :string,
|
86
|
+
desc: 'Path to the state file'
|
87
|
+
method_option :context, aliases: '-c', type: :string,
|
88
|
+
desc: 'Path to context file'
|
89
|
+
method_option :output, aliases: '-o', type: :string,
|
90
|
+
default: '/tmp or ./build',
|
91
|
+
desc: 'Output folder'
|
92
|
+
def deploy(*configPaths)
|
93
|
+
handleCommand(:deploy, configPaths, options)
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
desc 'cleanup [CONFIGS...]', 'In deployed infrastructure cleanup/delete unused things (eg. deployment leftover junk) (note this can be risky due to mistakes)'
|
98
|
+
method_option :help, aliases: '-h', type: :boolean,
|
99
|
+
desc: 'Display usage information'
|
100
|
+
method_option :state, aliases: '-s', type: :string,
|
101
|
+
desc: 'Path to the state file'
|
102
|
+
def cleanup(*configPaths)
|
103
|
+
handleCommand(:cleanup, configPaths, options)
|
104
|
+
end
|
105
|
+
|
106
|
+
desc 'types', 'List available types/plugins'
|
107
|
+
method_option :help, aliases: '-h', type: :boolean,
|
108
|
+
desc: 'Display usage information'
|
109
|
+
def types
|
110
|
+
handleCommand(:types, options)
|
111
|
+
end
|
112
|
+
|
113
|
+
=begin
|
114
|
+
# TODO
|
115
|
+
desc 'test [CONFIGS...]', 'Test whether deployed things work as expected'
|
116
|
+
method_option :help, aliases: '-h', type: :boolean,
|
117
|
+
desc: 'Display usage information'
|
118
|
+
method_option :load, aliases: '-l', type: :boolean,
|
119
|
+
desc: 'Run performance/load tests (might be dangerous as it can affect live users)'
|
120
|
+
method_option :chaos, aliases: '-c', type: :boolean,
|
121
|
+
desc: 'Test whether systems keep working while random things die (might be dangerous as it can affect live users due to injecting real faults)'
|
122
|
+
method_option :alerts, aliases: '-a', type: :boolean,
|
123
|
+
desc: 'Test failure conditions and whether alerts work (might be dangerous as it can affect live users due to injecting real faults)'
|
124
|
+
method_option :tools, aliases: '-t', type: :string, desc: 'Filter tools to use for testing'
|
125
|
+
|
126
|
+
def test(*configPaths)
|
127
|
+
handleCommand(:test, configPaths, options)
|
128
|
+
end
|
129
|
+
|
130
|
+
desc 'compare [CONFIGS...]', 'Show changes between local state and deployed things'
|
131
|
+
method_option :help, aliases: '-h', type: :boolean,
|
132
|
+
desc: 'Display usage information'
|
133
|
+
method_option :state, aliases: '-s', type: :string,
|
134
|
+
desc: 'Path to state file'
|
135
|
+
def compare(*configPaths)
|
136
|
+
handleCommand(:compare, configPaths, options)
|
137
|
+
end
|
138
|
+
|
139
|
+
=end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def handleCommand(name, *params)
|
144
|
+
if options[:help]
|
145
|
+
invoke :help, [name.to_s]
|
146
|
+
else
|
147
|
+
require_relative('commands/' + name.to_s)
|
148
|
+
Object.const_get('ConfigLMM::Commands::' + name.to_s.capitalize).new(*params).execute
|
149
|
+
end
|
150
|
+
rescue ArgumentError => e
|
151
|
+
$stderr.puts(e)
|
152
|
+
invoke :help, [name.to_s]
|
153
|
+
exit 1
|
154
|
+
end
|
155
|
+
|
156
|
+
|
157
|
+
def self.exit_on_failure?
|
158
|
+
true
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ConfigLMM
|
4
|
+
class Command
|
5
|
+
# Execute this command
|
6
|
+
#
|
7
|
+
# @api public
|
8
|
+
def execute(*)
|
9
|
+
raise(NotImplementedError, "#{self.class}##{__method__} must be implemented")
|
10
|
+
end
|
11
|
+
|
12
|
+
# A readable, structured and beautiful logging for the terminal
|
13
|
+
#
|
14
|
+
# @see http://www.rubydoc.info/gems/tty-logger
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
def logger
|
18
|
+
if @Logger.nil?
|
19
|
+
require 'tty-logger'
|
20
|
+
@Logger = TTY::Logger.new do |config|
|
21
|
+
yield(config)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
@Logger
|
25
|
+
end
|
26
|
+
|
27
|
+
# The external commands runner
|
28
|
+
#
|
29
|
+
# @see http://www.rubydoc.info/gems/tty-command
|
30
|
+
#
|
31
|
+
# @api public
|
32
|
+
def command(**options)
|
33
|
+
if @Command.nil?
|
34
|
+
require 'tty-command'
|
35
|
+
@Command = TTY::Command.new(options)
|
36
|
+
end
|
37
|
+
@Command
|
38
|
+
end
|
39
|
+
|
40
|
+
# The interactive prompt
|
41
|
+
#
|
42
|
+
# @see http://www.rubydoc.info/gems/tty-prompt
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def prompt
|
46
|
+
if @Prompt.nil?
|
47
|
+
require 'tty-prompt'
|
48
|
+
@Prompt = TTY::Prompt.new
|
49
|
+
end
|
50
|
+
@Prompt
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'configsCommand'
|
4
|
+
|
5
|
+
module ConfigLMM
|
6
|
+
module Commands
|
7
|
+
class Build < ConfigsCommand
|
8
|
+
def processConfig(config, options)
|
9
|
+
|
10
|
+
config.each do |id, target|
|
11
|
+
|
12
|
+
target['Resources'].to_h.each do |id, target|
|
13
|
+
IO::ConfigList.processConfig(id, target, target[:Parent])
|
14
|
+
processBuild(IO::ConfigList.normalizeId(id), target, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
processBuild(id, target, options)
|
18
|
+
end
|
19
|
+
prompt.ok('Build successful, artifacts are in ' + options['output'])
|
20
|
+
end
|
21
|
+
|
22
|
+
def processBuild(id, target, options)
|
23
|
+
providers = []
|
24
|
+
self.plugins.each do |pluginId, plugin|
|
25
|
+
if plugin.hasAction?(target['Type'], :build)
|
26
|
+
providers << plugin
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if providers.empty?
|
31
|
+
logger.debug("Skipping ID=#{id} - Type=#{target['Type']}")
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
bestProvider = self.findBestProvider(providers)
|
36
|
+
invokeBuildAction(id, bestProvider, target, options)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'configsCommand'
|
4
|
+
|
5
|
+
module ConfigLMM
|
6
|
+
module Commands
|
7
|
+
class Cleanup < ConfigsCommand
|
8
|
+
|
9
|
+
def processConfig(config, options)
|
10
|
+
plugins.each do |pluginId, plugin|
|
11
|
+
configs = {}
|
12
|
+
loadConfigs(plugin, config, configs)
|
13
|
+
plugin.cleanup(configs, state, context, options)
|
14
|
+
end
|
15
|
+
prompt.ok('Cleanup successful!')
|
16
|
+
end
|
17
|
+
|
18
|
+
def loadConfigs(plugin, config, configs)
|
19
|
+
config.each do |id, target|
|
20
|
+
loadConfigs(plugin, target['Resources'], configs) if target['Resources']
|
21
|
+
if plugin.hasAction?(target['Type'], :deploy)
|
22
|
+
configs[id] = target
|
23
|
+
end
|
24
|
+
end
|
25
|
+
configs
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|