ConfigLMM 0.2.0 → 0.3.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 +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