ConfigLMM 0.2.0 → 0.3.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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/Examples/Implemented.mm.yaml +75 -1
  4. data/Plugins/Apps/Authentik/Authentik-Server.container +18 -0
  5. data/Plugins/Apps/Authentik/Authentik-Worker.container +17 -0
  6. data/Plugins/Apps/Authentik/Authentik.conf.erb +35 -0
  7. data/Plugins/Apps/Authentik/Authentik.lmm.rb +73 -0
  8. data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +41 -0
  9. data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +148 -0
  10. data/Plugins/Apps/GitLab/GitLab.conf.erb +26 -0
  11. data/Plugins/Apps/GitLab/GitLab.container +17 -0
  12. data/Plugins/Apps/GitLab/GitLab.lmm.rb +75 -0
  13. data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +48 -10
  14. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +59 -2
  15. data/Plugins/Apps/Nextcloud/config.php +18 -0
  16. data/Plugins/Apps/Nginx/conf.d/configlmm.conf +62 -0
  17. data/Plugins/Apps/Nginx/config-lmm/errors.conf +1 -1
  18. data/Plugins/Apps/Nginx/main.conf.erb +31 -0
  19. data/Plugins/Apps/Nginx/nginx.conf +3 -68
  20. data/Plugins/Apps/Nginx/nginx.lmm.rb +71 -14
  21. data/Plugins/Apps/Odoo/Odoo.conf.erb +30 -13
  22. data/Plugins/Apps/Odoo/Odoo.container +17 -0
  23. data/Plugins/Apps/Odoo/Odoo.lmm.rb +62 -2
  24. data/Plugins/Apps/Odoo/odoo.conf +37 -0
  25. data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +95 -0
  26. data/Plugins/Apps/Peppermint/Peppermint.conf.erb +64 -0
  27. data/Plugins/Apps/Peppermint/Peppermint.container +14 -0
  28. data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +58 -0
  29. data/Plugins/Apps/Postfix/Postfix.lmm.rb +139 -31
  30. data/Plugins/Apps/Postfix/smtpd.conf +3 -0
  31. data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +172 -23
  32. data/Plugins/Apps/SSH/SSH.lmm.rb +51 -0
  33. data/Plugins/Apps/UVdesk/UVdesk.conf.erb +52 -0
  34. data/Plugins/Apps/UVdesk/UVdesk.lmm.rb +85 -0
  35. data/Plugins/Apps/Valkey/Valkey.lmm.rb +2 -1
  36. data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +35 -18
  37. data/Plugins/Apps/Vaultwarden/Vaultwarden.container +16 -0
  38. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +42 -3
  39. data/Plugins/Apps/gollum/gollum.conf.erb +45 -18
  40. data/Plugins/Apps/gollum/gollum.container +12 -0
  41. data/Plugins/Apps/gollum/gollum.lmm.rb +39 -10
  42. data/Plugins/OS/Linux/Distributions.yaml +10 -0
  43. data/Plugins/OS/Linux/Linux.lmm.rb +145 -12
  44. data/Plugins/OS/Linux/Packages.yaml +42 -4
  45. data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +108 -0
  46. data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +15 -0
  47. data/Plugins/OS/Linux/systemd/systemd.lmm.rb +28 -0
  48. data/Plugins/OS/Linux/systemd/user-0.slice +9 -0
  49. data/Plugins/OS/Linux/systemd/user@.service.d/delegate.conf +3 -0
  50. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +6 -2
  51. data/Plugins/Services/DNS/PowerDNS.lmm.rb +69 -6
  52. data/README.md +6 -0
  53. data/bootstrap.sh +54 -0
  54. data/lib/ConfigLMM/Framework/plugins/dns.rb +1 -2
  55. data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +157 -35
  56. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +24 -6
  57. data/lib/ConfigLMM/Framework/plugins/plugin.rb +52 -12
  58. data/lib/ConfigLMM/version.rb +1 -1
  59. metadata +31 -3
  60. data/Plugins/Apps/Nginx/main.conf +0 -30
@@ -13,6 +13,9 @@ module ConfigLMM
13
13
  DEFAULT_HOST = 'localhost'
14
14
  DEFAULT_PORT = 8081
15
15
  SSH_TIMEOUT = 10
16
+ PACKAGE_NAME = 'PowerDNS'
17
+ SERVICE_NAME = 'pdns'
18
+ USER = 'pdns'
16
19
 
17
20
  # TODO
18
21
  # def actionPowerDNSValidate(id, target, activeState, context, options)
@@ -21,6 +24,7 @@ module ConfigLMM
21
24
 
22
25
  def actionPowerDNSBuild(id, target, activeState, context, options)
23
26
  if target['Settings']
27
+ prepareSettings(target)
24
28
  targetDir = options['output'] + CONFIG_DIR + '/'
25
29
  mkdir(targetDir, options['dry'])
26
30
  content = ''
@@ -44,7 +48,7 @@ module ConfigLMM
44
48
  def actionPowerDNSDeploy(id, target, activeState, context, options)
45
49
  #actionPowerDNSDiff(id, target, activeState, context, options)
46
50
 
47
- deploySettings(target)
51
+ deploySettings(target, options)
48
52
  if target['DNS']
49
53
  connect(id, target, activeState, context, options) do |host, port, key|
50
54
  updateDNS(host, port, key, target['DNS'])
@@ -103,8 +107,8 @@ module ConfigLMM
103
107
  rrsets = []
104
108
  remove = []
105
109
  info.each do |name, data|
106
- fullName = name + '.' + domain + '.'
107
- fullName = domain + '.' if name == '@'
110
+ fullName = Addressable::IDNA.to_ascii(name) + '.' + Addressable::IDNA.to_ascii(domain) + '.'
111
+ fullName = Addressable::IDNA.to_ascii(domain) + '.' if name == '@'
108
112
  self.processDNS(domain, data).each do |type, records|
109
113
  #remove += removeConflicting(zone, fullName, type)
110
114
  rrset = {
@@ -115,6 +119,12 @@ module ConfigLMM
115
119
  records: []
116
120
  }
117
121
  records.each do |record|
122
+ record[:content] = Addressable::IDNA.to_ascii(record[:content]) + '.' if type == 'CNAME' || type == 'ALIAS'
123
+ if type == 'MX'
124
+ priority, name = record[:content].split(' ')
125
+ name = Addressable::IDNA.to_ascii(name) + '.'
126
+ record[:content] = [priority, name].join(' ')
127
+ end
118
128
  rrset[:records] << { content: record[:content], disabled: false }
119
129
  end
120
130
  rrsets << rrset
@@ -129,12 +139,65 @@ module ConfigLMM
129
139
 
130
140
  end
131
141
 
132
- def deploySettings(target)
133
- if target['Settings']
134
- # TODO
142
+ def prepareSettings(target)
143
+ if !target['Settings'].key?('api')
144
+ target['Settings']['api'] = 'yes'
145
+ end
146
+ if !target['Settings'].key?('expand-alias')
147
+ target['Settings']['expand-alias'] = 'yes'
148
+ end
149
+ if !target['Settings'].key?('launch')
150
+ target['Settings']['launch'] = 'gpgsql'
151
+ target['Settings']['gpgsql-host'] = '/run/postgresql'
152
+ target['Settings']['gpgsql-user'] = USER
153
+ target['Settings']['gpgsql-dbname'] = USER
154
+ end
155
+ end
156
+
157
+ def deploySettings(target, options)
158
+ if target['Location']
159
+ uri = Addressable::URI.parse(target['Location'])
160
+ params = {}
161
+ params = CGI.parse(uri.query) if uri.query
162
+ if uri.scheme == 'ssh' && !params.key?('host')
163
+ self.class.sshStart(uri) do |ssh|
164
+ Framework::LinuxApp.ensurePackages([PACKAGE_NAME], ssh)
165
+ Framework::LinuxApp.ensureServiceAutoStartOverSSH(SERVICE_NAME, ssh)
166
+ if target['Settings']
167
+ prepareSettings(target)
168
+ self.class.sshExec!(ssh, "mkdir -p #{CONFIG_DIR}")
169
+ self.class.sshExec!(ssh, "sed -i 's|# include-dir=|include-dir=#{CONFIG_DIR}|' /etc/pdns/pdns.conf")
170
+ ssh.scp.upload!(options['output'] + CONFIG_DIR + '/configlmm.conf', CONFIG_DIR + '/configlmm.conf')
171
+ apiKeyFile = CONFIG_DIR + '/apiKey.conf'
172
+ if !self.class.remoteFilePresent?(apiKeyFile, ssh)
173
+ apiKey = ENV['POWERDNS_API_KEY']
174
+ apiKey = SecureRandom.urlsafe_base64(60) unless apiKey
175
+ self.class.sshExec!(ssh, " echo 'api-key=#{apiKey}' > #{apiKeyFile}")
176
+ self.class.sshExec!(ssh, " chown #{USER}:#{USER} #{apiKeyFile}")
177
+ self.class.sshExec!(ssh, " chmod 400 #{apiKeyFile}")
178
+ prompt.say("PowerDNS API Key: #{apiKey}", )
179
+ end
180
+ self.configurePostgreSQL(target['Settings'], ssh)
181
+ end
182
+ Framework::LinuxApp.startServiceOverSSH(SERVICE_NAME, ssh)
183
+ end
184
+ end
135
185
  end
136
186
  end
137
187
 
188
+ def configurePostgreSQL(settings, ssh)
189
+ password = SecureRandom.alphanumeric(20)
190
+ if settings['gpgsql-host'] == 'localhost' || settings['gpgsql-host'].start_with?('/')
191
+ PostgreSQL.createUserAndDBOverSSH(USER, password, ssh)
192
+ PostgreSQL.importSQL(USER, USER, '/usr/share/doc/packages/pdns/schema.pgsql.sql', ssh)
193
+ else
194
+ self.class.sshStart("ssh://#{settings['gpgsql-host']}/") do |ssh|
195
+ PostgreSQL.createUserAndDBOverSSH(USER, password, ssh)
196
+ PostgreSQL.importSQL(USER, USER, '/usr/share/doc/packages/pdns/schema.pgsql.sql', ssh)
197
+ end
198
+ end
199
+ password
200
+ end
138
201
 
139
202
  def connect(id, target, activeState, context, options)
140
203
  host = DEFAULT_HOST
data/README.md CHANGED
@@ -221,6 +221,12 @@ First you need to have Ruby and RubyGems. Then you can install it with:
221
221
 
222
222
  $ gem install ConfigLMM
223
223
 
224
+ If that doesn't work (eg. your Ruby is too old) then install with (it will install [RVM](https://rvm.io/))
225
+
226
+ ```
227
+ $ curl -sS https://raw.githubusercontent.com/ConfigLMM/ConfigLMM/master/bootstrap.sh | sh
228
+ ```
229
+
224
230
  ## Usage
225
231
 
226
232
  Create yaml file with desired config, eg.
data/bootstrap.sh ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/sh
2
+
3
+ distro=$(cat /etc/os-release | grep ^ID= | cut -d '=' -f 2 | cut -d '"' -f 2)
4
+
5
+ function admin {
6
+ if [ "$EUID" -eq "0" ]; then
7
+ "$@"
8
+ else
9
+ sudo "$@"
10
+ fi
11
+ }
12
+
13
+
14
+ case $distro in
15
+
16
+ opensuse-leap)
17
+ admin zypper install --no-confirm ruby libvirt-devel
18
+ ;;
19
+
20
+ arch)
21
+ admin pacman -S --noconfirm --needed ruby rubygems
22
+ ;;
23
+
24
+ *)
25
+ if ! command -v ruby &> /dev/null
26
+ then
27
+ echo "Ruby not found!" >&2
28
+ echo "Don't know how to install it for your $distro distribution!" >&2
29
+ echo "Submit a PR :)" >&2
30
+ exit 1
31
+ fi
32
+ ;;
33
+ esac
34
+
35
+ if ! command -v gem &> /dev/null; then
36
+ echo "RubyGems not found!" >&2
37
+ exit 2
38
+ fi
39
+
40
+ rubyTooOld=$(ruby -e 'puts RUBY_VERSION.to_f < 3.3 ? 1 : 0')
41
+
42
+ if [ "$rubyTooOld" -eq "1" ]; then
43
+ echo "Ruby is too old! Will install RVM" >&2
44
+ gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB >/dev/null
45
+ curl -sSL https://get.rvm.io | bash -s stable --ruby=3.3.4
46
+ source /etc/profile.d/rvm.sh
47
+
48
+ if [ "$SHELL" = "/usr/bin/fish" ]; then
49
+ curl -L --create-dirs -o ~/.config/fish/functions/rvm.fish https://raw.github.com/lunks/fish-nuggets/master/functions/rvm.fish
50
+ echo "rvm default" >> ~/.config/fish/config.fish
51
+ fi
52
+ fi
53
+
54
+ gem install ConfigLMM
@@ -23,8 +23,7 @@ module ConfigLMM
23
23
 
24
24
  items.each do |item|
25
25
  type, content = item.strip.split('=')
26
- content += '.' if type == 'CNAME' || type == 'ALIAS'
27
- content = domain + '.' if content == '@' || content == '@.'
26
+ content = domain if content == '@'
28
27
  content = self.class.externalIp if content == '@me'
29
28
  records[type] ||= []
30
29
  records[type] << { type: type, content: content, ttl: DEFAULT_TTL }
@@ -8,29 +8,80 @@ module ConfigLMM
8
8
  LINUX_FOLDER = __dir__ + '/../../../../Plugins/OS/Linux/'
9
9
  SUSE_NAME = 'openSUSE Leap'
10
10
  SUSE_ID = 'opensuse-leap'
11
+ PODMAN_PACKAGE = 'Podman'
12
+ SYSTEMD_CONTAINERS_PATH = '~/.config/containers/systemd/'
11
13
 
12
14
  def ensurePackage(name, location)
13
- self.class.ensurePackage(name, location)
15
+ self.class.ensurePackages([name], location)
14
16
  end
15
17
 
16
- def self.ensurePackage(name, location)
18
+ def ensurePackages(names, location)
19
+ self.class.ensurePackages(names, location)
20
+ end
21
+
22
+ def self.ensurePackages(names, locationOrSSH)
23
+ distroInfo = nil
24
+ closure = Proc.new do |ssh|
25
+ distroInfo = self.currentDistroInfo(ssh)
26
+ reposPackages = self.mapPackages(names, distroInfo['Name'])
27
+
28
+ repos = []
29
+ pkgs = []
30
+ reposPackages.each do |pkg|
31
+ if pkg.include?('|')
32
+ repoName, pkg = pkg.split('|')
33
+ repos << repoName
34
+ pkgs << pkg
35
+ else
36
+ pkgs << pkg
37
+ end
38
+ end
39
+ repos.each do |repoName|
40
+ self.addRepo(repoName, distroInfo, ssh)
41
+ end
42
+ command = distroInfo['InstallPackage'] + ' ' + pkgs.map { |pkg| pkg.shellescape }.join(' ')
43
+ if ssh
44
+ self.sshExec!(ssh, command)
45
+ else
46
+ if `echo $EUID`.strip == '0'
47
+ `#{command} >/dev/null`
48
+ else
49
+ `sudo #{command} >/dev/null`
50
+ end
51
+ end
52
+ distroInfo
53
+ end
54
+
55
+ if locationOrSSH.nil? || locationOrSSH == '@me'
56
+ distroInfo = closure.call(nil)
57
+ elsif locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
58
+ uri = Addressable::URI.parse(locationOrSSH)
59
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
60
+
61
+ self.sshStart(locationOrSSH) do |ssh|
62
+ distroInfo = closure.call(ssh)
63
+ end
64
+ else
65
+ distroInfo = closure.call(locationOrSSH)
66
+ end
67
+ distroInfo
68
+ end
69
+
70
+ def ensureServiceAutoStart(name, location)
17
71
  if location && location != '@me'
18
72
  uri = Addressable::URI.parse(location)
19
73
  raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
20
- self.ensurePackageOverSSH(name, uri)
74
+ self.class.ensureServiceAutoStartOverSSH(name, uri)
21
75
  else
22
76
  # TODO
23
77
  end
24
78
  end
25
79
 
26
- def self.ensurePackageOverSSH(name, locationOrSSH)
27
-
80
+ def self.ensureServiceAutoStartOverSSH(name, locationOrSSH)
28
81
  closure = Proc.new do |ssh|
29
- distroInfo = self.distroInfoFromSSH(ssh)
30
- pkgs = self.mapPackages([name], distroInfo['Name']).first
82
+ distroInfo = self.currentDistroInfo(ssh)
31
83
 
32
- pkgs = [pkgs] unless pkgs.is_a?(Array)
33
- command = distroInfo['InstallPackage'] + ' ' + pkgs.map { |pkg| pkg.shellescape }.join(' ')
84
+ command = distroInfo['AutoStartService'] + ' ' + name.shellescape
34
85
  self.sshExec!(ssh, command)
35
86
  end
36
87
 
@@ -39,38 +90,69 @@ module ConfigLMM
39
90
  closure.call(ssh)
40
91
  end
41
92
  else
42
- closure.call(locationOrSSH)
93
+ closure.call(locationOrSSH)
43
94
  end
44
-
45
95
  end
46
96
 
47
- def ensureServiceAutoStart(name, location)
97
+ def startService(name, location)
48
98
  if location && location != '@me'
49
99
  uri = Addressable::URI.parse(location)
50
100
  raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
51
- self.class.sshStart(uri) do |ssh|
52
- distroInfo = self.class.distroInfoFromSSH(ssh)
101
+ self.class.startServiceOverSSH(name, location)
102
+ else
103
+ # TODO
104
+ end
105
+ end
106
+
107
+ def self.startServiceOverSSH(name, locationOrSSH)
108
+ closure = Proc.new do |ssh|
109
+ distroInfo = self.currentDistroInfo(ssh)
110
+
111
+ command = distroInfo['StartService'] + ' ' + name.shellescape
112
+ self.sshExec!(ssh, command)
113
+ end
53
114
 
54
- command = distroInfo['AutoStartService'] + ' ' + name.shellescape
55
- self.class.sshExec!(ssh, command)
115
+ if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
116
+ self.sshStart(locationOrSSH) do |ssh|
117
+ closure.call(ssh)
56
118
  end
57
119
  else
58
- # TODO
120
+ closure.call(locationOrSSH)
59
121
  end
60
122
  end
61
123
 
62
- def startService(name, location)
63
- if location && location != '@me'
64
- uri = Addressable::URI.parse(location)
65
- raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
66
- self.class.sshStart(uri) do |ssh|
67
- distroInfo = self.class.distroInfoFromSSH(ssh)
68
124
 
69
- command = distroInfo['StartService'] + ' ' + name.shellescape
70
- self.class.sshExec!(ssh, command)
125
+ def self.firewallAddServiceOverSSH(serviceName, locationOrSSH)
126
+ closure = Proc.new do |ssh|
127
+ command = 'firewall-cmd --permanent --add-service ' + serviceName.shellescape
128
+ self.sshExec!(ssh, command, true)
129
+ command = 'firewall-cmd --add-service ' + serviceName.shellescape
130
+ self.sshExec!(ssh, command, true)
131
+ end
132
+
133
+ if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
134
+ self.sshStart(locationOrSSH) do |ssh|
135
+ closure.call(ssh)
71
136
  end
72
137
  else
73
- # TODO
138
+ closure.call(locationOrSSH)
139
+ end
140
+ end
141
+
142
+ def self.firewallAddPortOverSSH(portName, locationOrSSH)
143
+ closure = Proc.new do |ssh|
144
+ command = 'firewall-cmd --permanent --add-port ' + portName.shellescape
145
+ self.sshExec!(ssh, command, true)
146
+ command = 'firewall-cmd --add-port ' + portName.shellescape
147
+ self.sshExec!(ssh, command, true)
148
+ end
149
+
150
+ if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
151
+ self.sshStart(locationOrSSH) do |ssh|
152
+ closure.call(ssh)
153
+ end
154
+ else
155
+ closure.call(locationOrSSH)
74
156
  end
75
157
  end
76
158
 
@@ -80,7 +162,11 @@ module ConfigLMM
80
162
  packages.to_a.each do |pkg|
81
163
  packageName = distroPackages[distroName][pkg]
82
164
  if packageName
83
- names << packageName
165
+ if packageName.is_a?(Array)
166
+ names += packageName
167
+ else
168
+ names << packageName
169
+ end
84
170
  else
85
171
  names << pkg.downcase
86
172
  end
@@ -88,20 +174,56 @@ module ConfigLMM
88
174
  names
89
175
  end
90
176
 
91
- def self.createSubuidsOverSSH(user, distroInfo, ssh)
92
- self.sshExec!(ssh, "#{distroInfo['ModifyUser']} --add-subuids 100000-165535 --add-subgids 100000-165535 #{user}")
177
+ def self.createCertificateOverSSH(ssh)
178
+ dir = "/etc/letsencrypt/live/Wildcard/"
179
+ self.sshExec!(ssh, "mkdir -p #{dir}")
180
+ # Need this temporarily before real certs are created
181
+ if !self.remoteFilePresent?(dir + 'fullchain.pem', ssh)
182
+ self.sshExec!(ssh, "openssl req -x509 -noenc -days 90 -newkey rsa:2048 -keyout #{dir}privkey.pem -out #{dir}fullchain.pem -subj '/C=US/O=ConfigLMM/CN=Wildcard'")
183
+ self.sshExec!(ssh, "cp #{dir}fullchain.pem #{dir}chain.pem")
184
+ end
185
+ dir
93
186
  end
94
187
 
95
- def self.distroID
96
- `cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2`.strip.gsub('"', '')
188
+ def self.configurePodmanServiceOverSSH(user, homedir, userComment, distroInfo, ssh)
189
+ Framework::LinuxApp.ensurePackages([PODMAN_PACKAGE], ssh)
190
+ addUserCmd = "#{distroInfo['CreateServiceUser']} --home-dir '#{homedir}' --create-home --comment '#{userComment}' #{user}"
191
+ self.sshExec!(ssh, addUserCmd, true)
192
+ self.createSubuidsOverSSH(user, distroInfo, ssh)
193
+ self.sshExec!(ssh, "loginctl enable-linger #{user}")
194
+ self.sshExec!(ssh, "su --login #{user} --shell /bin/sh --command 'mkdir -p #{SYSTEMD_CONTAINERS_PATH}'")
97
195
  end
98
196
 
99
- def self.distroIDfromSSH(ssh)
100
- ssh.exec!('cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2').strip.gsub('"', '')
197
+ def self.addRepo(name, distroInfo, ssh = nil)
198
+ if distroInfo['Name'] == 'openSUSE Leap'
199
+ if ssh
200
+ versionId = ssh.exec!('cat /etc/os-release | grep "^VERSION_ID=" | cut -d "=" -f 2').strip.gsub('"', '')
201
+ self.sshExec!(ssh, "zypper addrepo https://download.opensuse.org/repositories/#{name}/#{versionId}/#{name}.repo", true)
202
+ self.sshExec!(ssh, "zypper --gpg-auto-import-keys refresh")
203
+ else
204
+ versionId = `cat /etc/os-release | grep "^VERSION_ID=" | cut -d "=" -f 2`.strip.gsub('"', '')
205
+ `zypper addrepo https://download.opensuse.org/repositories/#{name}/#{versionId}/#{name}.repo`
206
+ `zypper --gpg-auto-import-keys refresh`
207
+ end
208
+ else
209
+ # TODO
210
+ end
211
+ end
212
+
213
+ def self.createSubuidsOverSSH(user, distroInfo, ssh)
214
+ self.sshExec!(ssh, "#{distroInfo['ModifyUser']} --add-subuids 100000-165535 --add-subgids 100000-165535 #{user}")
215
+ end
216
+
217
+ def self.distroID(ssh = nil)
218
+ if ssh
219
+ ssh.exec!('cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2').strip.gsub('"', '')
220
+ else
221
+ `cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2`.strip.gsub('"', '')
222
+ end
101
223
  end
102
224
 
103
- def self.distroInfoFromSSH(ssh)
104
- distroInfo = self.distroInfo(self.distroIDfromSSH(ssh))
225
+ def self.currentDistroInfo(ssh)
226
+ self.distroInfo(self.distroID(ssh))
105
227
  end
106
228
 
107
229
  def self.distroInfo(distroID)
@@ -13,30 +13,48 @@ module ConfigLMM
13
13
 
14
14
  class NginxApp < Framework::Plugin
15
15
 
16
+ NGINX_PACKAGE = 'nginx'
17
+ CONFIG_DIR = '/etc/nginx/'
18
+ WWW_DIR = '/srv/www/'
19
+
16
20
  def writeNginxConfig(dir, name, id, target, activeState, context, options)
17
21
  outputFolder = options['output']
18
22
 
19
23
  updateTargetConfig(target)
20
24
 
25
+ target = target.dup
26
+ target['NginxVersion'] = 0 unless target['NginxVersion']
21
27
  template = ERB.new(File.read(dir + '/' + name + '.conf.erb'))
22
28
  renderTemplate(template, target, outputFolder + '/nginx/servers-lmm/' + name + '.conf', options)
23
- plugins[:Nginx].actionNginxBuild(id, target, activeState, context, options)
24
29
  end
25
30
 
26
31
  def deployNginxConfig(id, target, activeState, context, options)
27
- outputFolder = options['output']
32
+ outputFolder = options['output'] + '/nginx/servers-lmm'
28
33
 
29
- if !target['Location'] || target['Location'] == '@me'
30
- copy(outputFolder + '/nginx/servers-lmm', '/etc/nginx/', options['dry'])
34
+ if target['Location'] && target['Location'] != '@me'
35
+ uri = Addressable::URI.parse(target['Location'])
36
+ raise Framework::PluginProcessError.new("Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
37
+ self.class.sshStart(uri) do |ssh|
38
+ self.class.uploadFolder(outputFolder, CONFIG_DIR, ssh)
39
+ Framework::LinuxApp.firewallAddServiceOverSSH('https', ssh)
40
+ end
41
+ else
42
+ copy(outputFolder, CONFIG_DIR, options['dry'])
31
43
  end
32
-
33
- plugins[:Nginx].actionNginxDeploy(id, target, activeState, context, options)
34
44
  end
35
45
 
36
46
  def cleanupNginxConfig(name, id, state, context, options)
37
47
  rm('/etc/nginx/servers-lmm/' + name + '.conf', options['dry'])
38
48
  end
39
49
 
50
+ def self.prepareNginxConfig(target, ssh = nil)
51
+ if ssh
52
+ target['NginxVersion'] = self.sshExec!(ssh, 'nginx -v').strip.split('/')[1].to_f
53
+ else
54
+ target['NginxVersion'] = `nginx -v`.strip.split('/')[1].to_f
55
+ end
56
+ end
57
+
40
58
  private
41
59
 
42
60
  def updateTargetConfig(target)
@@ -7,6 +7,7 @@ require 'http'
7
7
  require 'fileutils'
8
8
  require 'net/ssh'
9
9
  require 'net/scp'
10
+ require 'open3'
10
11
 
11
12
  module ConfigLMM
12
13
  module Framework
@@ -146,25 +147,41 @@ module ConfigLMM
146
147
  end
147
148
  end
148
149
 
150
+ def self.loadVariable(value, target)
151
+ variableStart = value.index('${')
152
+ return value unless variableStart
153
+ variableEnd = value.index('}', variableStart + 2)
154
+ variableName = value[variableStart + 2...variableEnd]
155
+ if variableName.start_with?('ENV:')
156
+ value = value[0...variableStart].to_s + ENV[variableName[4..variableEnd]] + value[(variableEnd + 1)..-1].to_s
157
+ else
158
+ raise 'Not implemented!'
159
+ end
160
+ value
161
+ end
162
+
149
163
  CONFIGLMM_SECTION_BEGIN = "# -----BEGIN CONFIGLMM-----\n"
150
164
  CONFIGLMM_SECTION_END = "# -----END CONFIGLMM-----\n"
151
165
 
152
- def updateLocalFile(file, options, atTop = false)
166
+ def updateLocalFile(file, options, atTop = false, comment = '#')
167
+ File.write(file, '') unless File.exist?(file)
168
+ sectionBegin = CONFIGLMM_SECTION_BEGIN.gsub('#', comment)
169
+ sectionEnd = CONFIGLMM_SECTION_END.gsub('#', comment)
153
170
  fileLines = File.read(file).lines
154
- sectionBeginIndex = fileLines.index(CONFIGLMM_SECTION_BEGIN)
155
- sectionEndIndex = fileLines.index(CONFIGLMM_SECTION_END)
171
+ sectionBeginIndex = fileLines.index(sectionBegin)
172
+ sectionEndIndex = fileLines.index(sectionEnd)
156
173
  if sectionBeginIndex.nil?
157
174
  linesBefore = []
158
175
  linesBefore = fileLines unless atTop
159
176
  linesBefore << "\n"
160
- linesBefore << CONFIGLMM_SECTION_BEGIN
161
- linesAfter = [CONFIGLMM_SECTION_END]
177
+ linesBefore << sectionBegin
178
+ linesAfter = [sectionEnd]
162
179
  linesAfter << "\n"
163
180
  linesAfter += fileLines if atTop
164
181
  else
165
182
  linesBefore = fileLines[0..sectionBeginIndex]
166
183
  if sectionEndIndex.nil?
167
- linesAfter = [CONFIGLMM_SECTION_END]
184
+ linesAfter = [sectionEnd]
168
185
  linesAfter << "\n"
169
186
  else
170
187
  linesAfter = fileLines[sectionEndIndex..fileLines.length]
@@ -178,14 +195,14 @@ module ConfigLMM
178
195
  fileWrite(file, fileLines.join(), options[:dry])
179
196
  end
180
197
 
181
- def updateRemoteFile(locationOrSSH, file, options, atTop = false, &block)
198
+ def updateRemoteFile(locationOrSSH, file, options, atTop = false, comment = '#', &block)
182
199
 
183
200
  closure = Proc.new do |ssh|
184
201
  localFile = options['output'] + '/' + SecureRandom.alphanumeric(10)
185
202
  File.write(localFile, '')
186
203
  self.class.sshExec!(ssh, "touch #{file}")
187
204
  ssh.scp.download!(file, localFile)
188
- updateLocalFile(localFile, options, atTop, &block)
205
+ updateLocalFile(localFile, options, atTop, comment, &block)
189
206
  ssh.scp.upload!(localFile, file)
190
207
  end
191
208
 
@@ -201,14 +218,23 @@ module ConfigLMM
201
218
  end
202
219
  end
203
220
 
204
- def self.remoteFilePresent?(file, ssh)
205
- result = self.sshExec!(ssh, "stat #{file}", true)
221
+ def self.filePresent?(file, ssh = nil)
222
+ result = self.exec("stat #{file}", ssh, true)
206
223
  !result.start_with?('stat: cannot')
207
224
  end
208
225
 
226
+ # DEPRECATED - use filePresent()
227
+ def self.remoteFilePresent?(file, ssh)
228
+ self.filePresent?(file, ssh)
229
+ end
230
+
231
+ def self.remoteFileContains?(file, content, ssh)
232
+ !self.sshExec!(ssh, "grep '#{content}' #{file}", true).strip.empty?
233
+ end
234
+
209
235
  def self.uploadNotPresent(file, target, ssh)
210
236
  target += '/' + File.basename(file)
211
- if !self.remoteFilePresent?(target)
237
+ if !self.remoteFilePresent?(target, ssh)
212
238
  ssh.scp.upload!(file, target)
213
239
  end
214
240
  end
@@ -217,7 +243,21 @@ module ConfigLMM
217
243
  target += '/' + File.basename(folder) + '/'
218
244
  self.sshExec!(ssh, "mkdir -p #{target}")
219
245
  Dir[folder + '/*'].each do |file|
220
- ssh.scp.upload!(file, target + File.basename(file))
246
+ ssh.scp.upload!(file, target + File.basename(file), recursive: true)
247
+ end
248
+ end
249
+
250
+ def self.exec(command, ssh = nil, allowFailure = false)
251
+ if ssh.nil?
252
+ stdout, stdeerr, status = Open3.capture3(command)
253
+ if !allowFailure && !status.success?
254
+ $stderr.puts(stdout)
255
+ $stderr.puts(stdeerr)
256
+ raise Framework::PluginProcessError.new("Failed '#{command}'")
257
+ end
258
+ stdout + stdeerr
259
+ else
260
+ self.sshExec!(ssh, command, allowFailure)
221
261
  end
222
262
  end
223
263
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConfigLMM
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end