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