#!/usr/bin/env ruby

require 'yaml'
require 'fileutils'
require 'mongo'
require 'colorize'

configfile = "/opt/plugandwork/config/paw_init.yml"


###
#   Explanations and checks
###

puts "
    This script will initialize many Linux system configurations, as well as initialize databases clusters, and finally PAW configurations.

    Is is expected to be run on all machines running this PAW application.

    It will read a config file #{'/opt/plugandwork/config/paw_init.yml'.underline} , which is expected to contain the complete list of all machines running this PAW application.
    An example is provided at #{'/opt/plugandwork/config/paw_init.yml.example'.underline} .

"

case (Process.uid)
    when 0
        # We are root !
    else
        STDERR.puts("ERROR: You must run this with script with #{'root'.underline} privileges")
        exit 1
end


###
#   Read config file and get local attributes
###

config = {}
begin
    config = YAML.load_file(configfile, aliases: true)
rescue ArgumentError
    begin
        config = YAML.load_file(configfile)
    rescue Errno::ENOENT => e
        # STDERR.puts "WARNING: File #{configfile} does not exist. -> Interactive questions will be asked."
        STDERR.puts "ERROR: File #{configfile.underline} does not exist."
        exit 2
    rescue Errno::EACCES => e
        # STDERR.puts "WARNING: No permission to read #{configfile} . -> Interactive questions will be asked."
        STDERR.puts "ERROR: No permission to read #{configfile.underline} ."
        exit 3
    rescue Psych::BadAlias => e
        STDERR.puts "ERROR: Yaml aliases not supported by this Ruby version."
        exit 4
    # rescue => e  # or use $! which is the last exception
    #     puts "Exception #{e.class}"
    #     puts e.message
    #     # puts e.trace
    #     raise e
    end
rescue Errno::ENOENT => e
    # STDERR.puts "WARNING: File #{configfile} does not exist. -> Interactive questions will be asked."
    STDERR.puts "ERROR: File #{configfile.underline} does not exist."
    exit 2
rescue Errno::EACCES => e
    # STDERR.puts "WARNING: No permission to read #{configfile} . -> Interactive questions will be asked."
    STDERR.puts "ERROR: No permission to read #{configfile.underline} ."
    exit 3
rescue Psych::BadAlias => e
    STDERR.puts "ERROR: Yaml aliases not supported by this Ruby version."
    exit 4
# rescue => e  # or use $! which is the last exception
#     puts "Exception #{e.class}"
#     puts e.message
#     # puts e.trace
#     raise e
end

config['hosts_attributes_from_ip'] ||= {}

# Get list of local and non-loopback IP addresses
# https://stackoverflow.com/a/27454154/92471
my_ip_addresses = Socket.ip_address_list.reject( &:ipv4_loopback? ).reject( &:ipv6_loopback? ).map{ |addr_info| addr_info.ip_address }

my_ip = (config['hosts_attributes_from_ip'].keys & my_ip_addresses).first

my_attributes = config['hosts_attributes_from_ip'][(config['hosts_attributes_from_ip'].keys & my_ip_addresses).first]
my_attributes['roles'] ||= []

frontends_ip = config['hosts_attributes_from_ip'].map { |k,v| (v['roles'] || []).include?('frontend') ? k : nil }.compact

workers_ip = config['hosts_attributes_from_ip'].map { |k,v| (v['roles'] || []).include?('worker') ? k : nil }.compact

backends_ip = config['hosts_attributes_from_ip'].map { |k,v| (v['roles'] || []).include?('backend') ? k : nil }.compact

# only one suported at this time
vectordb_ip = config['hosts_attributes_from_ip'].map { |k,v| (v['roles'] || []).include?('vectordb') ? k : nil }.compact.first

puts ''


###
#   Set hostname
###

puts "INFO: Set hostname"

if my_attributes['set_hostname']
    filename = '/etc/hostname'
    FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
    value = my_attributes['set_hostname']
    File.open(filename, "w") { |file| file.puts value }

    `hostname #{value}`  # using backticks to raise exceptions
end


###
#   Set DNS resolvers
###

puts "INFO: Set DNS resolver"

# Get domain after having set hostname
domain = `hostname --domain`.strip

filename = '/etc/resolv.conf'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
value = domain
content = "search local #{value}\nnameserver 127.0.0.1\n"
File.open(filename, "w") { |file| file << content }  # file.puts content

filename = '/etc/named.conf'
content = File.read(filename)
value = '        ' + config['dns_resolvers_ip'].join(";\n        ") + ";\n"
content = content.gsub(/forwarders\s+{.*?}/m, "forwarders {\n#{value}    }")
File.open(filename, "w") { |file| file << content }  # file.puts content

filename = '/etc/named.conf'
content = File.read(filename)
File.open(filename, "w") { |file| file << content }  # file.puts content

`systemctl restart named`  # using backticks to raise exceptions


###
#   Set Facter facts
###

puts "INFO: Set Facter facts"

filename = '/etc/puppetlabs/facter/facts.d/vrac.yaml'
content = YAML.load_file(filename)
content['platform'] = config['platform']
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end


###
#   Set email aliases
###

puts "INFO: Set root email alias"

filename = '/etc/aliases'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
value = config['root_email']
content = File.read(filename)
content = content.gsub(/^#? ?root\s*:.*/, "root: #{value}")
File.open(filename, "w") { |file| file << content }  # file.puts content

`newaliases`  # using backticks to raise exceptions


###
#   Set Tuned profile
###

puts "INFO: Set Tuned profile"

my_attributes['roles'].include?('backend') ? value = 'throughput-performance-no_thp-allow_overcommit' : value = 'throughput-performance-no_thp'

`tuned-adm profile #{value}`  # using backticks to raise exceptions


###
#   Set Telegraf config
###

puts "INFO: Configure Telegraf"

filename = '/etc/telegraf/telegraf.conf'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
value = domain
content = content.gsub(/ paw_group\s*=.*/, " paw_group = \"#{value}\"")
value = `hostname -f`.strip
content = content.gsub(/ hostname\s*=.*/, " hostname = \"#{value}\"")
value = config['platform']
content = content.gsub(/ platform\s*=.*/, " platform = \"#{value}\"")
my_attributes['roles'].include?('backend') ? value = 'true' : value = 'false'
content = content.gsub(/ role_backend\s*=.*/, " role_backend = \"#{value}\"")
my_attributes['roles'].include?('frontend') ? value = 'true' : value = 'false'
content = content.gsub(/ role_frontend\s*=.*/, " role_frontend = \"#{value}\"")
my_attributes['roles'].include?('worker') ? value = 'true' : value = 'false'
content = content.gsub(/ role_worker\s*=.*/, " role_worker = \"#{value}\"")
my_attributes['roles'].include?('storage') ? value = 'true' : value = 'false'
content = content.gsub(/ role_storage\s*=.*/, " role_storage = \"#{value}\"")
my_attributes['roles'].include?('vectordb') ? value = 'true' : value = 'false'
content = content.gsub(/ role_vectordb\s*=.*/, " role_vectordb = \"#{value}\"")
File.open(filename, "w") { |file| file << content }  # file.puts content

`systemctl is-active --quiet telegraf && systemctl restart telegraf`  # using backticks to raise exceptions


###
#   Set Iptables
###

puts "INFO: Set Iptables rules"

rules = "\n"
if config['platform_use_security_groups']
    rules += ' 80 443 ' if my_attributes['roles'].include?('frontend')
    rules += ' 8880 9292 ' if my_attributes['roles'].include?('frontend')
    rules += ' 27017 6379 26379 ' if my_attributes['roles'].include?('backend')
    rules += ' 9000 ' if my_attributes['roles'].include?('storage')
    rules += ' 9091 ' if my_attributes['roles'].include?('vectordb')
    rules += ' 9100 ' if config['firewall_allow_prometheus']
else
    rules += "80 443\n" if my_attributes['roles'].include?('frontend')

    rules += ( frontends_ip.map { |ip| ip + '--80' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('frontend')
    rules += ( frontends_ip.map { |ip| ip + '--443' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('frontend')
    rules += ( frontends_ip.map { |ip| ip + '--8880' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('frontend')
    rules += ( frontends_ip.map { |ip| ip + '--9292' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('frontend')

    rules += ( frontends_ip.map { |ip| ip + '--27017' }.sort + workers_ip.map { |ip| ip + '--27017' }.sort + backends_ip.map { |ip| ip + '--27017' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('backend')
    rules += ( frontends_ip.map { |ip| ip + '--6379' }.sort + workers_ip.map { |ip| ip + '--6379' }.sort + backends_ip.map { |ip| ip + '--6379' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('backend')
    rules += ( frontends_ip.map { |ip| ip + '--26379' }.sort + workers_ip.map { |ip| ip + '--26379' }.sort + backends_ip.map { |ip| ip + '--26379' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('backend')

    rules += ( frontends_ip.map { |ip| ip + '--9000' }.sort + workers_ip.map { |ip| ip + '--9000' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('storage')

    rules += ( frontends_ip.map { |ip| ip + '--9091' }.sort + workers_ip.map { |ip| ip + '--9091' }.sort ).join("\n") + "\n" if my_attributes['roles'].include?('vectordb')
end

filename = '/etc/puppetlabs/iptables/block_countries_iptables'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
content = content.gsub(/^\s*reconf_parameters\s*=\s*["'].*?["']/m, "reconf_parameters=\"#{rules}\"")
File.open(filename, "w") { |file| file << content }  # file.puts content

`/etc/puppetlabs/iptables/reconfigure_iptables stateful #{rules.gsub(/\n/m, ' ')}`  # using backticks to raise exceptions

`systemctl is-active --quiet fail2ban.service && systemctl restart fail2ban.service`  # using backticks to raise exceptions


###
#   Stop auto-remediation
###

puts "INFO: Stop auto-remediation"

`/usr/local/bin/prevent_autoremediation`  # using backticks to raise exceptions


###
#   Reset roles
###

puts "INFO: Reset Plugandwork roles"

FileUtils.rm_rf Dir.glob('/opt/plugandwork/roles/*')
my_attributes['roles'].each do |role|
    FileUtils.touch "/opt/plugandwork/roles/#{role}"
    FileUtils.chmod 0660, "/opt/plugandwork/roles/#{role}"
end
FileUtils.chown_R 'root', 'webapp', "/opt/plugandwork/roles"
FileUtils.chmod 0770, "/opt/plugandwork/roles"


###
#   Stop conflicting services
###

puts "INFO: Stop conflicting services"

`systemctl stop haproxy || true`  # using backticks to raise exceptions


###
#   Configure Mongo
###

puts "INFO: Configure Mongo"

filename = '/etc/mongod.conf'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
value = '0.0.0.0'
content = content.gsub(/^(\s*)bindIp\s*:.*/, "\\1bindIp: #{value}")
File.open(filename, "w") { |file| file << content }  # file.puts content

# format and mount Mongo volume if not already done
if !my_attributes['mongo_volume'].nil? && File.read('/proc/mounts').lines.grep(/^#{my_attributes['mongo_volume']}\s/).length <= 0
    `systemctl stop mongod || true`  # using backticks to raise exceptions

    # Dir.glob('/opt/mongo_data/*').each { |file| File.delete(file) }
    FileUtils.rm_rf Dir.glob('/opt/mongo_data/*')

    `mkfs.xfs #{my_attributes['mongo_volume']}`  # using backticks to raise exceptions

    filename = '/etc/fstab'
    content = File.read(filename)
    File.open(filename, "w") do |file|
        file.puts content
        file.puts "#{my_attributes['mongo_volume']}  /opt/mongo_data  xfs  defaults,noatime,nodiratime  0  0"
    end

    `mount /opt/mongo_data`  # using backticks to raise exceptions

    FileUtils.mkdir_p '/opt/mongo_data/mongo_data'
    FileUtils.chown_R 'mongod', 'mongod', '/opt/mongo_data/mongo_data'
    FileUtils.chmod_R 'g-w,a-rwx', '/opt/mongo_data/mongo_data'
    FileUtils.chmod 0750, '/opt/mongo_data/mongo_data'

    filename = '/etc/mongod.conf'
    content = File.read(filename)
    value = '/opt/mongo_data/mongo_data'
    content = content.gsub(/^(\s*)dbPath\s*:.*/, "\\1dbPath: #{value}")
    File.open(filename, "w") { |file| file << content }  # file.puts content

    # Seems no more present in image
    # filename = '/home/mongobackup/bin/mongo_resume'
    # FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
    # content = File.read(filename)
    # value = '/opt/mongo_data/mongo_data'
    # content = content.gsub(/^\s*mongo_mountpoint\s*=.*/, "mongo_mountpoint='#{value}'")
    # File.open(filename, "w") { |file| file << content }  # file.puts content

    # Seems no more present in image
    # filename = '/home/mongobackup/bin/mongo_freeze'
    # FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
    # content = File.read(filename)
    # value = '/opt/mongo_data/mongo_data'
    # content = content.gsub(/^\s*mongo_mountpoint\s*=.*/, "mongo_mountpoint='#{value}'")
    # File.open(filename, "w") { |file| file << content }  # file.puts content
end

`systemctl enable mongod`  # using backticks to raise exceptions
`systemctl restart mongod`  # using backticks to raise exceptions

if my_attributes['roles'].include?('backend')
    sleep 3

    Mongo::Client.new([ '127.0.0.1:27017' ], database: 'admin', direct_connection: true, server_selection_timeout: 5, connect_timeout: 5, socket_timeout: 6000) do |client|

        rs_conf = {}
        begin
            rs_conf = client.database.command(replSetGetConfig: 1).documents.first
        rescue Mongo::Error::OperationFailure => e
            # Very probably that the replica-set is not yet initialized
        end
        rs_conf ||= {}
        rs_conf['config'] ||= {}
        # There is an issue with Mongoid on remote frontends or workers when using 127.0.0.1 → always use the eth0 IP
        # rs_conf['config']['members'] = (backends_ip.length > 1) ? backends_ip.map.with_index { |ip,i| { '_id' => i, 'host' => "#{ip}:27017", 'priority' => config['hosts_attributes_from_ip'][ip]['mongo_priority'] || 1 } } : [{ '_id' => 1, 'host' => "127.0.0.1:27017" }]
        rs_conf['config']['members'] = backends_ip.map.with_index { |ip,i| { '_id' => i, 'host' => "#{ip}:27017", 'priority' => config['hosts_attributes_from_ip'][ip]['mongo_priority'] || 1 } }

        begin
            client.database.command(replSetInitiate: { _id:'rs1', members: rs_conf['config']['members'] }, server_selection_timeout: 5, connect_timeout: 5, socket_timeout: 6000)
        rescue Mongo::Error::OperationFailure => e
            # Very probably that the replica-set is already initialized

            begin
                client.database.command(replSetReconfig: rs_conf['config'], force: true, server_selection_timeout: 5, connect_timeout: 5, socket_timeout: 6000)
            rescue Mongo::Error::OperationFailure => e
                # Very probably that the other nodes are not ready
            end
        end
    end
end


###
#   Configure Redis and Sentinel
###

puts "INFO: Configure Redis and Sentinel"

`systemctl stop redis redis-sentinel || true`  # using backticks to raise exceptions

filename = '/etc/redis/redis.conf'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
value = 'no'
content = content.gsub(/^\s*protected-mode\s.*/, "protected-mode #{value}")
value = '0.0.0.0'
content = content.gsub(/^[#\s]*bind\s*.*/, "bind #{value}")
if my_attributes.has_key?('redis_maxmemory')
    value = my_attributes['redis_maxmemory']
    content = content.gsub(/^[#\s]*maxmemory\s.*/, "maxmemory #{value}")
else
    content = content.gsub(/^[#\s]*maxmemory\s.*/, '# maxmemory <bytes>')
end
value = my_attributes['redis_maxmemory_policy'] || 'volatile-ttl'
content = content.gsub(/^[#\s]*maxmemory-policy\s.*/, "maxmemory-policy #{value}")
value = my_attributes['redis_appendonly'] ? 'yes' : 'no'
content = content.gsub(/^[#\s]*appendonly\s.*/, "appendonly #{value}")
if my_attributes.has_key?('redis_rdb_save')
    value = my_attributes['redis_rdb_save']
    content = content.gsub(/^[#]? ?save\s.*/, "save #{value}")
else
    content = content.gsub(/^[#]? ?save\s.*/, '# save <seconds> <changes>')
end
value = (my_ip_addresses.include? backends_ip.first) ? "# slaveof <masterip> <masterport>" : "slaveof #{backends_ip.first} 6379"
content = content.gsub(/^[#\s]*slaveof\s.*/, value)
File.open(filename, "w") { |file| file << content }  # file.puts content

backends_ip.length > 1 ? quorum = 2 : quorum = 1

filename = '/etc/redis/sentinel.conf'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
# As for Mongo (because of Mongoid), also use main IP instead of 127.0.0.1
# _ip = (my_attributes['roles'].include?('backend') && backends_ip.length < 2) ? '127.0.0.1' : backends_ip.first
# _my_ip = (my_attributes['roles'].include?('backend') && backends_ip.length < 2) ? '127.0.0.1' : my_ip
_ip = backends_ip.first
_my_ip = my_ip

content = <<~EOF
    ### MANAGED BY PUPPET

    bind 0.0.0.0
    port 26379
    sentinel announce-ip #{_my_ip}
    dir /tmp
    daemonize no
    pidfile /var/run/redis/redis-sentinel.pid
    protected-mode no

    sentinel monitor paw #{_ip} 6379 #{quorum}
    sentinel down-after-milliseconds paw 30000
    sentinel parallel-syncs paw 1
    sentinel failover-timeout paw 180000

    loglevel notice
    logfile /var/log/redis/sentinel.log
    EOF
File.open(filename, "w") { |file| file << content }  # file.puts content

`systemctl enable redis redis-sentinel`  # using backticks to raise exceptions
`systemctl restart redis redis-sentinel`  # using backticks to raise exceptions


###
#   Configure Minio
###

puts "INFO: Configure Minio"

filename = '/etc/minio/config'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
value = config['minio_secret']
content = content.gsub(/"secretKey"\s*=>\s*:undef/, "\"secretKey\"=>\"#{value}\"")
content = content.gsub(/"secretKey"\s*=>\s*".*?"/, "\"secretKey\"=>\"#{value}\"")
File.open(filename, "w") { |file| file << content }  # file.puts content

`systemctl enable minio`  # using backticks to raise exceptions
`systemctl restart minio`  # using backticks to raise exceptions


###
#   Configure Milvus
###

puts "INFO: Configure Milvus"

filename = '/opt/milvus/etc/milvus.yaml'
# FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
# content = File.read(filename)
# (...)
# File.open(filename, "w") { |file| file << content }  # file.puts content


# format and mount Milvus volume if not already done
if !my_attributes['milvus_volume'].nil? && File.read('/proc/mounts').lines.grep(/^#{my_attributes['milvus_volume']}\s/).length <= 0
    `systemctl stop milvus || true`  # using backticks to raise exceptions

    # Dir.glob('/opt/milvus_data/*').each { |file| File.delete(file) }
    FileUtils.rm_rf Dir.glob('/opt/milvus/data/*')

    `mkfs.xfs #{my_attributes['milvus_volume']}`  # using backticks to raise exceptions

    filename = '/etc/fstab'
    content = File.read(filename)
    File.open(filename, "w") do |file|
        file.puts content
        file.puts "#{my_attributes['milvus_volume']}  /opt/milvus/data  xfs  defaults,noatime,nodiratime  0  0"
    end

    `mount /opt/milvus/data`  # using backticks to raise exceptions

    FileUtils.mkdir_p '/opt/milvus/data'
    # FileUtils.chown_R 'milvus', 'milvus', '/opt/milvus/data'
    FileUtils.chmod_R 'g-w,a-rwx', '/opt/milvus/data'
    FileUtils.chmod 0750, '/opt/milvus/data'

    # filename = '/opt/milvus/etc/milvus.yaml'
    # content = File.read(filename)
    # (...)
    # File.open(filename, "w") { |file| file << content }  # file.puts content
end

`systemctl enable milvus`  # using backticks to raise exceptions
`systemctl restart milvus`  # using backticks to raise exceptions


###
#   Configure Haproxy
###

puts "INFO: Configure Haproxy"

`systemctl stop haproxy || true`  # using backticks to raise exceptions

filename = '/etc/haproxy/haproxy.cfg'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
value = (backends_ip.map { |ip| '  server ' + config['hosts_attributes_from_ip'][ip]['set_hostname'].split(/\./).first + ' ' + ip + ':6379 check inter 5s fastinter 2s' }).join("\n")
content = File.read(filename)
content = content.sub(/^\s+server\s.*/, value)
File.open(filename, "w") { |file| file << content }  # file.puts content

`systemctl enable haproxy`  # using backticks to raise exceptions
`systemctl restart haproxy`  # using backticks to raise exceptions


###
#   Configure Coturn
###

puts "INFO: Disable Coturn"

`systemctl disable --now coturn`  # using backticks to raise exceptions


###
#   Configure Nginx
###

puts "INFO: Configure Nginx"

`openssl req -new -newkey rsa:4096 -days 3650 -nodes -x509 -subj "/C=FR/O=Plugandwork/CN=#{config['website_name']}" -keyout /etc/nginx/ssl/#{config['website_name']}.key.pem -out /etc/nginx/ssl/#{config['website_name']}.crt.pem`  # using backticks to raise exceptions

filename = '/etc/nginx/conf.d/plugandwork.conf'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
value = config['website_name']
content = File.read(filename)
content = content.gsub(/server_name\s.*;/, "server_name #{value};")
content = content.gsub(/\/etc\/nginx\/ssl\/.*?\.crt\.pem;/, "/etc/nginx/ssl/#{value}.crt.pem;")
content = content.gsub(/\/etc\/nginx\/ssl\/.*?\.key\.pem;/, "/etc/nginx/ssl/#{value}.key.pem;")
File.open(filename, "w") { |file| file << content }  # file.puts content

filename = '/etc/nginx/conf.d/plugandwork.part1'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
value = (frontends_ip.map { |ip| '    server ' + ((ip == my_ip) ? '127.0.0.1' : ip) + ':8880 max_fails=6 fail_timeout=15' + ((ip == my_ip) ? '' : ' backup') + ';  # ' + config['hosts_attributes_from_ip'][ip]['set_hostname'] + ((ip == my_ip) ? " #{ip}" : '') }).join("\n")
content = content.sub(/^\s*server\s.*8880.*/, value)
value = (frontends_ip.map { |ip| '    server ' + ((ip == my_ip) ? '127.0.0.1' : ip) + ':9292 max_fails=6 fail_timeout=15' + ((ip == my_ip) ? '' : ' backup') + ';  # ' + config['hosts_attributes_from_ip'][ip]['set_hostname'] + ((ip == my_ip) ? " #{ip}" : '') }).join("\n")
content = content.sub(/^\s*server\s.*9292.*/, value)
File.open(filename, "w") { |file| file << content }  # file.puts content

filename = '/etc/nginx/conf.d/plugandwork.part2'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
value = config['website_name']
content = File.read(filename)
content = content.gsub(/rewrite \^ https:\/\/.*?\$/, "rewrite ^ https://#{value}$")
File.open(filename, "w") { |file| file << content }  # file.puts content

`systemctl enable nginx`  # using backticks to raise exceptions
`systemctl restart nginx`  # using backticks to raise exceptions


###
#   Configure PAW application
###

puts "INFO: Configure PAW application"

filename = '/opt/plugandwork/config/config.yml'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = YAML.load_file(filename)
content['production']['app_domain'] = domain
content['production']['app_host'] = my_attributes['set_hostname']
content['production']['smtp_api_domain'] = domain
content['production']['smtp_no_reply'] = "ne-pas-repondre@#{domain}"
content['production']['team'] = "UNKNOWN"
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end

filename = '/opt/plugandwork/config/wopi.yml'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = YAML.load_file(filename)
content['production']['wopi_allowed_domain_host'] = domain
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end

filename = '/opt/plugandwork/config/mongoid.yml'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = YAML.load_file(filename)
content['production']['clients']['default']['hosts'] = (my_attributes['roles'].include?('backend') && backends_ip.length < 2) ? ['127.0.0.1:27017'] : backends_ip.map { |ip| ip + ':27017' }
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end

# Redis is always 127.0.0.1 to local Haproxy

filename = '/opt/plugandwork/config/milvus.yml'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = YAML.load_file(filename)
# content['production']['url'] = "http://#{vectordb_ip}:9091"
content['production']['url'] = (my_attributes['roles'].include?('vectordb')) ? "http://127.0.0.1:9091" : "http://#{vectordb_ip}:9091"
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end

filename = '/opt/plugandwork/config/puma.rb'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = File.read(filename)
value = "#{my_attributes['puma_threads_count_min']}, #{my_attributes['puma_threads_count_max']}"
content = content.gsub(/^\s*threads .*/, "threads #{value}")
value = my_attributes['puma_workers_count']
content = content.gsub(/^\s*workers .*/, "workers #{value}")
File.open(filename, "w") { |file| file << content }  # file.puts content

filename = '/opt/plugandwork/config/sidekiq-main.yml'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = YAML.load_file(filename)
content[:concurrency] = my_attributes['sidekiq_concurrency']
content['production'][:concurrency] = my_attributes['sidekiq_concurrency']
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end
filename = '/opt/plugandwork/config/sidekiq-add.yml'
FileUtils.cp filename, filename + Time.now.strftime('-%Y%m%d_%H%M'), preserve: true, verbose: false
content = YAML.load_file(filename)
content[:concurrency] = my_attributes['sidekiq_concurrency']
content['production'][:concurrency] = my_attributes['sidekiq_concurrency']
File.open(filename, "w") do |file|
    file.puts "### MANAGED BY PUPPET\n\n"
    file.puts content.to_yaml
end


###
#   Override services
###

puts "INFO: Override PAW services"

if my_attributes['override_services']
    FileUtils.mkdir_p '/opt/plugandwork/config/services'
    my_attributes['override_services'].each do |filename,value|
        filename = '/opt/plugandwork/config/services/' + filename
        content = value['content']
        File.open(filename, "w") { |file| file.puts content }
    end
end


###
#   Restart app
###

puts "INFO: Restart PAW application"

`/opt/plugandwork/bin/paw_restart`  # using backticks to raise exceptions


###
#   Resume auto-remediation
###

puts "INFO: Resume auto-remediation"

`/usr/local/bin/resume_autoremediation`  # using backticks to raise exceptions
