Commit edb06ab2 authored by 徐豪's avatar 徐豪
Browse files

init

parents

Too many changes to show.

To preserve performance only 532 of 532+ files are displayed.
# This is a helper to establish the listening status of PostgreSQL
require_relative 'base_helper'
require_relative '../../../package/libraries/helpers/shell_out_helper'
class PgStatusHelper
include ShellOutHelper
attr_reader :maximum_service_checks
attr_reader :total_service_checks
attr_reader :service_check_interval
attr_reader :service_checks
def initialize(connection_info, node)
@conn = connection_info
@total_service_checks = 0
@maximum_service_checks = node['postgresql']['max_service_checks']
@service_check_interval = node['postgresql']['service_check_interval']
@service_checks = {}
@status_executable = "#{node['package']['install-dir']}/embedded/bin/pg_isready"
end
# Mutator Methods
def remaining_service_checks
@maximum_service_checks - @total_service_checks
end
def service_checks_exhausted?
remaining_service_checks < 1
end
# Check Methods
[
'accepting_connections?',
'rejecting_connections?',
'not_responding?',
'invalid_connection_parameters?'
].each.with_index do |method, index|
define_method method do
service_state == index
end
end
def ready?
@total_service_checks = 1
until accepting_connections?
warning = if invalid_connection_parameters?
"PostgreSQL is not receiving the correct connection parameters"
elsif service_checks_exhausted?
if not_responding?
"PostgreSQL did not respond before service checks were exhausted"
else
"Exhausted service checks and database is still not available"
end
end
raise warning unless warning.nil?
sleep(service_check_interval)
@total_service_checks += 1
end
true
end
def service_state
return @service_checks[@total_service_checks] if @service_checks.include?(@total_service_checks)
cmd = %W(#{@status_executable} -d #{@conn.dbname} -h #{@conn.dbhost} -p #{@conn.port} -U #{@conn.pguser})
@service_checks[@total_service_checks] = do_shell_out(cmd).exitstatus
end
end
class WebServerHelper
class << self
def enabled?
Services.enabled?('puma')
end
def service_name
'puma'
end
def internal_api_url(node)
workhorse_helper = GitlabWorkhorseHelper.new(node)
gitlab_url = node['gitlab']['gitlab_rails']['internal_api_url']
# If no internal_api_url is specified, default to Workhorse settings
workhorse_url = node['gitlab']['gitlab_workhorse']['listen_addr']
relative_path = Gitlab['gitlab_workhorse']['relative_url']
gitlab_url ||= workhorse_helper.unix_socket? ? "http+unix://#{ERB::Util.url_encode(workhorse_url)}" : "http://#{workhorse_url}#{relative_path}"
gitlab_relative_path = relative_path || '' if workhorse_helper.unix_socket?
[gitlab_url, gitlab_relative_path]
end
end
end
#
# Copyright:: Copyright (c) 2016 GitLab Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module IncomingEmail
class << self
def parse_variables
parse_incoming_email || parse_service_desk_email
end
def parse_secrets
# mailroom expects exactly 32 bytes, encoded with base64
# rubocop:disable Style/IfUnlessModifier,Style/GuardClause
if rails_user_config_or_default('incoming_email_enabled') && rails_user_config_or_default('incoming_email_delivery_method') == "webhook"
Gitlab['mailroom']['incoming_email_auth_token'] ||= Gitlab['gitlab_rails']['incoming_email_auth_token'] || SecretsHelper.generate_base64(32)
end
if rails_user_config_or_default('service_desk_email_enabled') && rails_user_config_or_default('service_desk_email_delivery_method') == "webhook"
Gitlab['mailroom']['service_desk_email_auth_token'] ||= Gitlab['gitlab_rails']['service_desk_email_auth_token'] || SecretsHelper.generate_base64(32)
end
# rubocop:enable Style/IfUnlessModifier,Style/GuardClause
end
def parse_incoming_email
return unless Gitlab['gitlab_rails']['incoming_email_enabled']
Gitlab['mailroom']['enable'] = true if Gitlab['mailroom']['enable'].nil?
end
def parse_service_desk_email
return unless Gitlab['gitlab_rails']['service_desk_email_enabled']
Gitlab['mailroom']['enable'] = true if Gitlab['mailroom']['enable'].nil?
end
private
def default_rails_config
Gitlab['node']['gitlab']['gitlab_rails']
end
def user_rails_config
Gitlab['gitlab_rails']
end
def rails_user_config_or_default(key)
# Note that we must not use an `a || b`` truthiness check here since that would mean a `false`
# user setting would fail over to the default, which is not what we want.
user_rails_config[key].nil? ? default_rails_config[key] : user_rails_config[key]
end
end
end
#
# Copyright:: Copyright (c) 2015 GitLab B.V.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require_relative 'account_helper'
class LogfilesHelper < AccountHelper
def default_logdir_ownership # rubocop:disable Metrics/AbcSize
# TODO: Make log directory creation in all service recipes use this method
# instead of directly using `node` values. This will ensure we don't miss
# to add a service here.
# https://gitlab.com/gitlab-org/omnibus-gitlab/issues/4606
{
'alertmanager' => { username: prometheus_user, group: prometheus_user },
'consul' => { username: consul_user, group: consul_group, mode: '0755' },
'crond' => { username: 'root', group: 'root' },
'geo-logcursor' => { username: gitlab_user, group: gitlab_group },
'geo-postgresql' => { username: postgresql_user, group: postgresql_group },
'gitaly' => { username: gitlab_user, group: gitlab_group },
'gitlab-exporter' => { username: gitlab_user, group: gitlab_group },
'gitlab-pages' => { username: gitlab_user, group: gitlab_group },
'gitlab-kas' => { username: gitlab_user, group: gitlab_group },
'gitlab-rails' => { username: gitlab_user, group: gitlab_group },
'gitlab-shell' => { username: gitlab_user, group: gitlab_group },
'gitlab-sshd' => { username: gitlab_user, group: gitlab_group },
'gitlab-workhorse' => { username: gitlab_user, group: gitlab_group },
'logrotate' => { username: 'root', group: 'root' },
'mailroom' => { username: gitlab_user, group: gitlab_group },
'mattermost' => { username: mattermost_user, group: mattermost_group, mode: '0755' },
'nginx' => { username: 'root', group: 'root' },
'node-exporter' => { username: prometheus_user, group: prometheus_group },
'patroni' => { username: postgresql_user, group: postgresql_group },
'pgbouncer' => { username: postgresql_user, group: postgresql_group },
'pgbouncer-exporter' => { username: postgresql_user, group: postgresql_group },
'postgres-exporter' => { username: postgresql_user, group: postgresql_group },
'postgresql' => { username: postgresql_user, group: postgresql_group },
'praefect' => { username: gitlab_user, group: gitlab_group },
'prometheus' => { username: prometheus_user, group: prometheus_group },
'puma' => { username: gitlab_user, group: gitlab_group },
'redis' => { username: redis_user, group: redis_group },
'redis-exporter' => { username: redis_user, group: redis_group },
'registry' => { username: registry_user, group: registry_group },
'remote-syslog' => { username: 'root', group: 'root' },
'sidekiq' => { username: gitlab_user, group: gitlab_group },
'storage-check' => { username: gitlab_user, group: gitlab_group },
'sentinel' => { username: redis_user, group: redis_group },
'spamcheck' => { username: gitlab_user, group: gitlab_group },
'spam-classifier' => { username: gitlab_user, group: gitlab_group }
}
end
def service_parent(service)
available_settings = Gitlab.settings
setting_name = SettingsDSL::Utils.node_attribute_key(service)
raise "Service #{service} is not a valid service." unless available_settings.include?(setting_name)
available_settings[setting_name][:parent]
end
def service_settings(service)
case service
when 'spam-classifier'
# special case for `spam-classifier`
if parent = service_parent('spamcheck')
node[parent]['spamcheck']['classifier']
else
node['spamcheck']['classifier']
end
else
node_attribute_key = SettingsDSL::Utils.node_attribute_key(service)
if parent = service_parent(service)
node[parent][node_attribute_key]
else
node[node_attribute_key]
end
end
end
def logdir(service)
case service
when 'gitaly'
service_settings('gitaly')['configuration']['logging']['dir']
when 'mattermost'
# mattermost uses 'log_file_directory' instead of 'log_directory'
service_settings('mattermost')['log_file_directory']
else
service_settings(service)['log_directory']
end
end
def logging_options(service)
node['gitlab']['logging'].to_hash.merge(service_settings(service).to_hash)
end
def log_group(service)
if log_group = service_settings(service)['log_group']
log_group
else
node['gitlab']['logging']['log_group']
end
end
def logdir_owner(service)
unless default_logdir_ownership.dig(service, :username)
Chef::Log.warn("#{service} does not have a default set for the log directory user. Setting to root.")
return 'root'
end
default_logdir_ownership[service][:username]
end
# Does not change the group on the log_directory unless the service
# is nginx or a log_group is explicitly configured for the service
def logdir_group(service)
if log_directory_group = log_group(service)
log_directory_group
elsif service == 'nginx'
# special case to use web_server_group
web_server_group
end
# implicitly returns nil
end
def runit_owner(service)
# currently hardcoded as 'root'
'root'
end
def logrotate_group(service)
if configured_log_group = log_group(service)
configured_log_group
elsif default_logdir_ownership.key?(service)
default_logdir_ownership[service][:group] || 'root'
else
Chef::Log.warn("#{service} does not have a default group set for logrotate. Setting to root.")
'root'
end
end
def logdir_mode(service)
if logdir_group(service)
# log_group is set - make mode 0750
'0750'
elsif default_logdir_ownership.key?(service) && default_logdir_ownership[service][:mode]
default_logdir_ownership[service][:mode]
else
Chef::Log.warn("#{service} does not have a log_group or default logdir mode defined. Setting to 0700.")
'0700'
end
end
def logging_settings(service)
# Ensure we are using the hyphenated form of the service name as that is
# expected by various methods being called here
service = SettingsDSL::Utils.service_name(service)
{
log_directory: logdir(service),
log_directory_owner: logdir_owner(service),
log_directory_group: logdir_group(service),
log_directory_mode: logdir_mode(service),
runit_owner: runit_owner(service),
runit_group: log_group(service),
logrotate_group: logrotate_group(service),
options: logging_options(service)
}
end
def logrotate_services_list
services = {}
logrotate_services = node['logrotate']['services']
available_settings = Gitlab.settings
logrotate_services.each do |svc|
# In `Gitlab.settings`, settings aren't hyphenated, but use underscores.
setting_name = svc.tr('-', '_')
raise "Service #{svc} was specified in logrotate['services'], but is not a valid service." unless available_settings.include?(setting_name)
services[svc] = logging_settings(svc)
end
services
end
end
#
# Copyright:: Copyright (c) 2016 GitLab Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module Logging
class << self
def parse_variables
parse_udp_log_shipping
end
def parse_udp_log_shipping
logging = Gitlab['logging']
return unless logging['udp_log_shipping_host']
Gitlab['remote_syslog']['enable'] ||= true
Gitlab['remote_syslog']['destination_host'] ||= logging['udp_log_shipping_host']
if logging['udp_log_shipping_port']
Gitlab['remote_syslog']['destination_port'] ||= logging['udp_log_shipping_port']
Gitlab['logging']['svlogd_udp'] ||= "#{logging['udp_log_shipping_host']}:#{logging['udp_log_shipping_port']}"
else
Gitlab['logging']['svlogd_udp'] ||= logging['udp_log_shipping_host']
end
%w(
alertmanager
geo-logcursor
geo-postgresql
gitaly
praefect
gitlab-pages
gitlab-shell
gitlab-workhorse
gitlab-exporter
logrotate
mailroom
mattermost
nginx
node-exporter
pgbouncer
postgres-exporter
postgresql
prometheus
redis
redis-exporter
registry
remote-syslog
sentinel
sidekiq
puma
storage-check
).each do |runit_sv|
Gitlab[SettingsDSL::Utils.node_attribute_key(runit_sv)]['svlogd_prefix'] ||= "#{Gitlab['node']['hostname']} #{runit_sv}: "
end
end
end
end
# frozen_string_literal: true
class MailroomHelper
attr_reader :node
def initialize(node)
@node = node
end
def internal_api_url
# The second returned value is for UNIX socket only.
api_url, _ = WebServerHelper.internal_api_url(node)
# In general, Ruby and most gems don't support HTTP request over Unix
# socket. For internal API requests, we want to point the internal API
# endpoint to workhorse to avoid round-trip through LB. Unfortunately, if
# workhorse uses Unix socket, we have no choice except to point it at
# the external URL.
api_url = @node['gitlab']['gitlab_rails']['gitlab_url'] if api_url.start_with?("http+unix")
api_url
end
end
#
# Copyright:: Copyright (c) 2016 GitLab Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require_relative '../../package/libraries/settings_dsl.rb'
module Nginx
class << self
def parse_variables
parse_nginx_listen_ports
parse_nginx_proxy_protocol
end
def generate_host_header(fqdn, port, is_https)
header = fqdn.dup
if is_https
header << ":#{port}" unless port == 443
else
header << ":#{port}" unless port == 80
end
header
end
def parse_nginx_listen_ports
[
[%w(nginx listen_port), %w(gitlab_rails gitlab_port)],
[%w(mattermost_nginx listen_port), %w(mattermost port)],
[%w(pages_nginx listen_port), %w(gitlab_rails pages_port)],
].each do |left, right|
next unless Gitlab[left.first][left.last].nil?
# This conditional is required until all services are extracted to
# their own cookbook. Mattermost exists directly on node while
# others exists on node['gitlab']
node_attribute_key = SettingsDSL::Utils.node_attribute_key(right.first)
service_attribute_key = right.last
default_set_gitlab_port = if Gitlab['node']['gitlab'].key?(node_attribute_key)
Gitlab['node']['gitlab'][node_attribute_key][service_attribute_key]
else
Gitlab['node'][node_attribute_key][service_attribute_key]
end
user_set_gitlab_port = Gitlab[right.first][right.last]
Gitlab[left.first][left.last] = user_set_gitlab_port || default_set_gitlab_port
end
end
def parse_nginx_proxy_protocol
[
'nginx',
'mattermost_nginx',
'pages_nginx',
'registry_nginx',
'gitlab_kas_nginx'
].each do |app|
Gitlab[app]['real_ip_header'] ||= 'proxy_protocol' if Gitlab[app]['proxy_protocol']
end
end
def parse_proxy_headers(app, ssl, allow_other_schemes = false)
values_from_gitlab_rb = Gitlab[app]['proxy_set_headers']
dashed_app = SettingsDSL::Utils.node_attribute_key(app)
default_from_attributes = Gitlab['node']['gitlab'][dashed_app]['proxy_set_headers'].to_hash
default_from_attributes['X-Forwarded-Ssl'] = 'on' if ssl
unless allow_other_schemes
scheme = ssl ? 'https' : 'http'
default_from_attributes['X-Forwarded-Proto'] = scheme
end
if Gitlab[app]['proxy_protocol']
default_from_attributes = default_from_attributes.merge({
'X-Real-IP' => '$proxy_protocol_addr',
'X-Forwarded-For' => '$proxy_protocol_addr'
})
end
if values_from_gitlab_rb
values_from_gitlab_rb.each do |key, value|
if value.nil?
default_attrs = Gitlab['node'].default['gitlab'][dashed_app]['proxy_set_headers']
default_attrs.delete(key)
end
end
default_from_attributes = default_from_attributes.merge(values_from_gitlab_rb.to_hash)
end
Gitlab[app]['proxy_set_headers'] = default_from_attributes
end
def parse_error_pages
# At the least, provide error pages for 404, 402, 500, 502 errors
errors = Hash[%w(404 500 502).map { |x| [x, "#{x}.html"] }]
if Gitlab['nginx'].key?('custom_error_pages')
Gitlab['nginx']['custom_error_pages'].each_key do |err|
errors[err] = "#{err}-custom.html"
end
end
errors
end
end
end
require 'socket'
module Patroni
class << self
def parse_variables
return unless Services.enabled?('patroni')
Gitlab['patroni']['connect_address'] ||= private_ipv4 || Gitlab['node']['ipaddress']
Gitlab['patroni']['connect_port'] ||= Gitlab['patroni']['port'] || Gitlab['node']['patroni']['port']
check_consul_is_enabled
parse_postgresql_overrides
auto_detect_wal_log_hint
end
def private_ipv4
Socket.getifaddrs.select { |ifaddr| ifaddr.addr&.ipv4_private? }.first&.addr&.ip_address
end
private
POSTGRESQL_DCS_PARAMETERS ||= %w(
max_connections
max_locks_per_transaction
max_worker_processes
max_prepared_transactions
track_commit_timestamp
max_wal_senders
max_replication_slots
wal_keep_segments
wal_keep_size
checkpoint_timeout
).freeze
def check_consul_is_enabled
return if Services.enabled?('consul')
LoggingHelper.warning('Patroni is enabled but Consul seems to be disabled. Patroni requires Consul to be enabled.')
end
def postgresql_setting(key)
Gitlab['postgresql'][key] || Gitlab['node']['patroni']['postgresql']&.[](key) || Gitlab['node']['postgresql'][key]
end
# These attributes are the postgres settings that patroni manages through its DCS,
# but that we also have existing settings for in our postgresql defaults.
# DCS only config settings are documented here: https://patroni.readthedocs.io/en/latest/dynamic_configuration.html
# We will use the existing `postgresql[]` setting for patroni DCS, if a patroni specific
# one hasn't been specified in gitlab.rb
def parse_postgresql_overrides
Gitlab['patroni']['postgresql'] ||= {}
POSTGRESQL_DCS_PARAMETERS.each do |key|
Gitlab['patroni']['postgresql'][key] ||= postgresql_setting(key)
end
end
# `wal_log_hints` must be `on` for `pg_rewind`
def auto_detect_wal_log_hint
return if Gitlab['patroni']['postgresql']['wal_log_hints']
Gitlab['patroni']['postgresql']['wal_log_hints'] = Gitlab['patroni']['use_pg_rewind'] ? 'on' : postgresql_setting('wal_log_hints')
end
end
end
#
# Copyright:: Copyright (c) 2018 GitLab Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
class PGVersion < String
attr_reader :major, :minor
VERSION_PATTERN ||= %r{
\A(?<part1>\d+)
(\.(?<part2>\d+))?
(\.(?<part3>\d+))?\z
}x.freeze
def initialize(version_string)
super(version_string)
parse_version_parts
end
def self.parse(version_string)
new(version_string) if version_string
end
def valid?
self.class::VERSION_PATTERN.match?(self) && !!major
end
private
def parse_version_parts
match_data = self.class::VERSION_PATTERN.match(self)
return unless match_data
part1 = match_data[:part1].to_i
part2 = match_data[:part2].to_i if match_data.names.include?('part2') && match_data[:part2]
part3 = match_data[:part3].to_i if match_data.names.include?('part3') && match_data[:part3]
# Prior the Postgres 10, the major version was considered the first two parts
# eg. 9.6, with 10, just 10 is considered the major version
# https://www.postgresql.org/support/versioning/
if part1 >= 10
@major = part1.to_s
@minor = part2.to_s if part2
else
@major = "#{part1}.#{part2}" if part2
@minor = part3.to_s if part3
end
end
end
#
# Copyright:: Copyright (c) 2016 GitLab Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module Postgresql
class << self
def parse_variables
parse_connect_port
parse_mattermost_postgresql_settings
parse_wal_keep_size
end
def parse_secrets
gitlab_postgresql_crt, gitlab_postgresql_key = generate_postgresql_keypair
Gitlab['postgresql']['internal_certificate'] ||= gitlab_postgresql_crt
Gitlab['postgresql']['internal_key'] ||= gitlab_postgresql_key
end
def parse_mattermost_postgresql_settings
value_from_gitlab_rb = Gitlab['mattermost']['sql_data_source']
user = Gitlab['postgresql']['sql_mattermost_user'] || Gitlab['node']['postgresql']['sql_mattermost_user']
unix_socket_directory = Gitlab['postgresql']['unix_socket_directory'] || Gitlab['node']['postgresql']['unix_socket_directory']
postgres_directory = Gitlab['postgresql']['dir'] || Gitlab['node']['postgresql']['dir']
port = Gitlab['postgresql']['port'] || Gitlab['node']['postgresql']['port']
database_name = Gitlab['mattermost']['database_name'] || Gitlab['node']['mattermost']['database_name']
host = unix_socket_directory || postgres_directory
value_from_attributes = "user=#{user} host=#{host} port=#{port} dbname=#{database_name}"
Gitlab['mattermost']['sql_data_source'] = value_from_gitlab_rb || value_from_attributes
end
def parse_wal_keep_size
wal_segment_size = 16
wal_keep_segments = Gitlab['postgresql']['wal_keep_segments'] || Gitlab['node']['postgresql']['wal_keep_segments']
wal_keep_size = Gitlab['postgresql']['wal_keep_size'] || Gitlab['node']['postgresql']['wal_keep_size']
Gitlab['postgresql']['wal_keep_size'] = if wal_keep_size.nil?
wal_keep_segments.to_i * wal_segment_size
else
wal_keep_size
end
end
def parse_connect_port
Gitlab['postgresql']['connect_port'] ||= Gitlab['postgresql']['port'] || Gitlab['node']['postgresql']['port']
end
def postgresql_managed?
Services.enabled?('postgresql')
end
def generate_postgresql_keypair
key, cert = SecretsHelper.generate_keypair(
bits: 4096,
subject: "/C=USA/O=GitLab/OU=Database/CN=PostgreSQL",
validity: 365 * 10 # ten years from now
)
[cert.to_pem, key.to_pem]
end
end
end
#
# Copyright:: Copyright (c) 2016 GitLab Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
module Puma
class << self
include MetricsExporterHelper
def parse_variables
return unless Services.enabled?('puma')
parse_listen_address
check_consistent_exporter_tls_settings('puma')
end
def parse_listen_address
https_url = puma_https_url
# As described in https://gitlab.com/gitlab-org/gitlab/-/blob/master/workhorse/doc/operations/configuration.md#interaction-of-authbackend-and-authsocket,
# Workhorse will give precedence to a UNIX socket. In order to ensure
# traffic is sent over an encrypted channel, set auth_backend if SSL
# has been enabled on Puma.
if https_url
Gitlab['gitlab_workhorse']['auth_backend'] = https_url if Gitlab['gitlab_workhorse']['auth_backend'].nil?
Gitlab['puma']['prometheus_scrape_scheme'] ||= 'https'
else
Gitlab['puma']['listen'] ||= '127.0.0.1'
Gitlab['gitlab_workhorse']['auth_socket'] = puma_socket if Gitlab['gitlab_workhorse']['auth_backend'].nil?
end
end
def workers(total_memory = Gitlab['node']['memory']['total'].to_i)
[
2, # Two is the minimum or web editor will no longer work.
[
cpu_threads,
worker_memory(total_memory).to_i,
].min # min because we want to exceed neither CPU nor RAM
].max # max because we need at least 2 workers
end
def cpu_threads
# Ohai may not parse lscpu properly: https://github.com/chef/ohai/issues/1760
return 1 if Gitlab['node']['cpu'].nil?
# lscpu may return 0 for total number of CPUs: https://github.com/chef/ohai/issues/1755
[Gitlab['node']['cpu']['total'].to_i, Gitlab['node']['cpu']['real'].to_i].max
end
# See how many worker processes fit in the system.
# Reserve 1.5G of memory for other processes.
# Currently, Puma workers can use 1GB per process.
def worker_memory(total_memory, reserved_memory = 1572864, per_worker_ram = 1048576)
(total_memory - reserved_memory) / per_worker_ram
end
private
def puma_socket
user_config_or_default('socket')
end
def puma_https_url
url(host: user_config['ssl_listen'], port: user_config['ssl_port'], scheme: 'https') if user_config['ssl_listen'] && user_config['ssl_port']
end
def default_config
Gitlab['node']['gitlab']['puma']
end
def user_config
Gitlab['puma']
end
def metrics_enabled?
user_config_or_default('exporter_enabled')
end
def url(host:, port:, scheme:)
Addressable::URI.new(host: host, port: port, scheme: scheme).to_s
end
end
end
# frozen_string_literal: true
class RailsMigrationHelper
REVISION_FILE ||= '/opt/gitlab/embedded/service/gitlab-rails/REVISION'
attr_reader :node, :status_file_prefix, :attributes_node, :migration_task_name
def initialize(node)
@node = node
@status_file_prefix = 'db-migrate'
@attributes_node = node['gitlab']['gitlab_rails']
end
def migrated?
check_status_file(db_migrate_status_file)
end
def db_migrate_status_file
@db_migrate_status_file ||= begin
upgrade_status_dir = ::File.join(node['gitlab']['gitlab_rails']['dir'], 'upgrade-status')
::File.join(upgrade_status_dir, "#{status_file_prefix}-#{connection_digest}-#{revision}")
end
end
private
def check_status_file(file)
::File.exist?(file) && IO.read(file).chomp == '0'
end
def revision
@revision ||= IO.read(REVISION_FILE).chomp if ::File.exist?(REVISION_FILE)
end
def connection_digest
connection_attributes = %w(
db_adapter
db_database
db_host
db_port
db_socket
).collect { |attribute| attributes_node[attribute] }
# To trigger a db:migrate run when new databases are introduced, so that
# those schema is populated in them.
database_names = attributes_node['databases']&.select { |_, details| details['enable'] }&.keys
connection_attributes << database_names if database_names
Digest::MD5.hexdigest(Marshal.dump(connection_attributes))
end
end
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment