environment.rb (115 lines of code) (raw):
# frozen_string_literal: true
require 'English'
require 'socket'
require 'resolv'
class Environment
attr_accessor :ctx
def initialize(role, state)
@ctx = {}
@ctx['__ROLE__'] = role
@ctx['__STATE__'] = state
domain_suffix = '.example.com'
@ctx['__DOMAIN_SUFFIX__'] = domain_suffix
@ctx['__EXTERNAL_HOSTNAME__'] = "gitlab#{domain_suffix}"
@ctx['__GEO_EXTERNAL_HOSTNAME__'] = "gitlab-geo#{domain_suffix}"
hostname = Socket.gethostname
site_a = 'site-a'
site_b = 'site-b'
# All hostnames are expected to have format "<site>sgl<host_type><host_id>".
site, host_type, host_id = hostname.match(/^(#{site_a}|#{site_a})sgl(web|inf|sk|sql|git|mon)(0\d\d)$/)[1, 3]
host_id = @ctx['__HOST_ID__'] = host_id.to_i
site_short = @ctx['__SITE_SHORT__'] = site
raise "ERROR: Invalid host format #{hostname}" if site.nil? || host_type.nil?
# Determine which nodes are consul or redis. inf001..3 are consul, inf004..6 are redis
if host_type == 'inf'
host_type = host_id <= 3 ? 'consul' : 'redis'
end
@ctx['__HOST_TYPE__'] = host_type
@ctx['__HOST_FQDN__'] = Addrinfo.getaddrinfo(hostname).first
expected = hostname + domain_suffix
if @ctx['__HOST_FQDN__'] != expected
err = "ERROR: #{hostname}: Invalid fqdn setup #{@ctx['__HOST_FQDN__']}. Expecting #{expected}"
warn err
end
# Get current nodes PUBLIC IP ADDRESS. Verify ip address matches with DNS.
ip1 = UDPSocket.open do |s|
s.connect('8.8.8.8', 1)
s.addr.last
end
ip2 = IPSocket.getaddress(hostname)
if ip1 != ip2
err = "ERROR: #{hostname}: IP of public interface #{ip1} != #{ip2} - DNSIP for #{Socket.gethostname}"
warn err
end
@ctx['__PUBLIC_IP_ADDRESS__'] = ip1
# Setup MON node IPs.
[1, 2].each do |i|
@ctx["__MON#{i}_NODE_IP__"] = IPSocket.getaddress("#{site}sglmon00#{i}#{domain_suffix}")
end
other_site = (site == site_a.nil? ? site_b : site_a)
@ctx['__GEO_NODE_NAME__'] = "gitlab#{other_site}"
# Generate list expected hosts for each type of host
@ctx['__SK_NODES__'] = (1..4).map { |i| "#{site}sglsk00#{i}#{domain_suffix}" }
@ctx['__CONSUL_NODES__'] = (1..3).map { |i| "#{site}sglinf00#{i}#{domain_suffix}" }
@ctx['__SQL_NODES__'] = (1..3).map { |i| "#{site}sglsql00#{i}#{domain_suffix}" }
@ctx['__OTHER_SQL_NODES__'] = (1..3).map { |i| "#{other_site}sglsql00#{i}#{domain_suffix}" }
@ctx['__REDIS_NODES__'] = [4, 5, 6].map { |i| "#{site}sglinf00#{i}#{domain_suffix}" }
@ctx['__WEB_NODES__'] = (1..8).map { |i| "#{site}sglweb00#{i}#{domain_suffix}" }
@ctx['__ALL_WEB_NODES__'] = [site_a, site_b].map do |s|
(1..8).map do |i|
"#{s}sglweb00#{i}#{domain_suffix}"
end
end.flatten
@ctx['__ALL_SQL_NODES__'] = [site_a, site_b].map do |s|
(1..3).map do |i|
"#{s}sglsql00#{i}#{domain_suffix}"
end
end.flatten
@ctx['__ALL_PGBOUNCER_NODES__'] = ["#{site_a}-", "#{site_b}-", ''].map do |s|
"gitlab-#{s}pgbouncer#{domain_suffix}"
end
# Generate Gitaly DIRs.
@ctx['__GITALY_DATA_DIRS_TLS__'] = {}
%w[default repos2 repos3 repos4].each_with_index do |r, i|
@ctx['__GITALY_DATA_DIRS_TLS__'][r] =
{ 'gitaly_address' => "tls://gitlab-#{site_short}-gitaly#{i + 1}#{domain_suffix}:9999" }
end
@ctx['__DB_TRACKING_HOST__'] = @ctx['__SQL_NODES__'][1]
# Load passwords.
encrypted_passwords = load_secrets # Load secrets that are needed in the gitlab.rb files (e.g. MD5 hashes, etc.)
encrypted_passwords.each_key { |k| @ctx[k] = encrypted_passwords[k] }
# Name of the gitaly repo.
if host_type == 'git'
@ctx['__GITALY_REPO_NAME__'] = host_id == 1 ? 'default' : "repos#{host_id}"
end
@ctx['__DMZ_PROXY__'] = "http://proxy#{domain_suffix}"
@ctx['__NO_PROXY__'] = "#{domain_suffix},127.0.0.1,localhost"
# Should include validation as part of the audit.
return unless role == 'GEO'
@ctx['__GEO_REPLICA_NODE__'] = @ctx['__SQL_NODES__'][0]
@ctx['__GEO_TRACKING_NODE__'] = @ctx['__SQL_NODES__'][1]
end
private
def vars_each
@ctx.each do |k, v|
yield k, v.inspect if k =~ /^__[A-Z0-9_]+__$/
end
end
def reference_rb_file
role = @ctx['__ROLE__'].downcase
base_ref_file = "reference/#{@ctx['__HOST_TYPE__']}_template.rb"
return base_ref_file if File.exist?(base_ref_file)
ref_file = "reference/#{role}_#{@ctx['__HOST_TYPE__']}_template.rb"
raise "Reference file #{ref_file} not found on host #{Socket.gethostname}" unless File.exist?(ref_file)
ref_file
end
def redis_master_node_port
@ctx['__WEB_NODES__'].each do |web_node|
cmd = "ssh -q #{web_node} /opt/gitlab/bin/gitlab-ctl get-redis-master"
cmd_out = `#{cmd}`
if $CHILD_STATUS.exitstatus != 0
warn "ERROR: getting redis-master from #{web_node} using cmd: #{cmd} - ExitStatus $?"
next
end
m = cmd_out.match(/^Redis master found at host (\S+) listening on port (\d+)/)
if m.nil?
warn "ERROR: invalid ouptut from '#{cmd}': '#{cmd_out}'"
next
end
ip, port = m[1, 2]
name = Resolv.getname(ip)
return name, port.to_i
end
raise 'ERROR: failed to find Redis master'
end
end