ConfigLMM 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/.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
|