Merge branch 'brodock/sentinel_daemon' into 'master'
Redis Sentinel daemon Related to #1565 !996 Updated documentation: * CE: gitlab-org/gitlab-ce!6471 * EE: ~~gitlab-org/gitlab-ee!786~~ (see: https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1000#note_17084207) Checklist ------------ - [x] Create a template for `sentinel.conf` - [x] `port` is configurable - [x] `working-directory` is configurable - [x] `auth-pass` is configurable - [x] `monitor` master instance is configurable - [x] `down-after-milliseconds` is configurable - [x] `failover-timeout` is configurable - [x] `myid` is configurable or will fallback to generated and stored in `.json` file after first `reconfigure` - [x] Create a service definition for sentinel daemon (definitions/sentinel_service.rb) - [x] Sentinel code is EE package only (`gitlab-ee` recipe) - [x] Add sentinel to runit init system - [x] Sentinel should be enabled only when specific `enabled` flag is present - [x] Sentinel-disable recipe - [x] Generate specific `redis://` URL pointing to `master-name` when sentinels are present Testplan ----------- - Clean install: - [x] Works with default / unmodified configuration parameters - [x] Can enable managed sentinel - [x] Can disable managed sentinel (stop service and remove from runit) - [x] Can point to external (managed/unmanaged) sentinel service - [x] Works with multiple sentinel instances. - Existing previous version - [x] Can upgrade with default / unmodified configuration parameters - [x] Can enable managed sentinel - [x] Can disable managed sentinel (stop service and remove from runit) See merge request !1000
This commit is contained in:
commit
457c47649e
|
@ -21,16 +21,32 @@ name "gitlab-cookbooks"
|
|||
license "Apache-2.0"
|
||||
license_file File.expand_path("LICENSE", Omnibus::Config.project_root)
|
||||
|
||||
source :path => File.expand_path("files/gitlab-cookbooks", Omnibus::Config.project_root)
|
||||
EE = system("#{Omnibus::Config.project_root}/support/is_gitlab_ee.sh")
|
||||
|
||||
source path: File.expand_path("files/gitlab-cookbooks", Omnibus::Config.project_root)
|
||||
|
||||
build do
|
||||
cookbook_name = 'gitlab'
|
||||
|
||||
command "mkdir -p #{install_dir}/embedded/cookbooks"
|
||||
sync "./", "#{install_dir}/embedded/cookbooks/"
|
||||
|
||||
# If EE package, use a different master cookbook
|
||||
if EE
|
||||
cookbook_name = 'gitlab-ee'
|
||||
else
|
||||
delete "#{install_dir}/embedded/cookbooks/gitlab-ee"
|
||||
end
|
||||
|
||||
# Create a package cookbook.
|
||||
command "mkdir -p #{install_dir}/embedded/cookbooks/package/attributes"
|
||||
erb :dest => "#{install_dir}/embedded/cookbooks/package/attributes/default.rb",
|
||||
:source => "cookbook_packages_default.erb",
|
||||
:mode => 0755,
|
||||
:vars => { :install_dir => project.install_dir }
|
||||
|
||||
erb :dest => "#{install_dir}/embedded/cookbooks/dna.json",
|
||||
:source => "dna.json.erb",
|
||||
:mode => 0644,
|
||||
:vars => { :master_cookbook => cookbook_name }
|
||||
end
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"run_list": [ "recipe[<%= master_cookbook %>]" ]
|
||||
}
|
|
@ -549,6 +549,14 @@ external_url 'GENERATED_EXTERNAL_URL'
|
|||
# redis['uid'] = nil
|
||||
# redis['gid'] = nil
|
||||
|
||||
## To enable only redis service in this machine, uncomment
|
||||
## one of the lines below (choose master or slave instance types).
|
||||
##
|
||||
## For more details please see:
|
||||
## https://docs.gitlab.com/ce/administration/high_availability/redis.html
|
||||
# redis_master_role['enable'] = true
|
||||
# redis_slave_role['enable'] = true
|
||||
|
||||
## Redis Sentinel support
|
||||
## You need a master slave redis replication to be able to do failover
|
||||
## Please read the documentation before enabling it to understand the
|
||||
|
@ -557,15 +565,79 @@ external_url 'GENERATED_EXTERNAL_URL'
|
|||
## Redis TCP support (will disable UNIX socket transport)
|
||||
# redis['bind'] = '0.0.0.0' # or specify an IP to bind to a single one
|
||||
# redis['port'] = 6379
|
||||
# redis['password'] = 'redis-password-goes-here'
|
||||
|
||||
## Master redis instance (if this will act as a master instance uncomment and fill below)
|
||||
# redis['password'] = '<huge password string here>' # Only define this in the master instance
|
||||
##
|
||||
## Replication support
|
||||
##
|
||||
|
||||
## Slave redis instance (if this will act as a slave / standby redis instance uncomment and fill below)
|
||||
## Slave redis instance
|
||||
# redis['master'] = false # by default this is true
|
||||
|
||||
## Slave and Sentinel shared configuration
|
||||
## Both need to point to the master redis instance to get replication and heartbeat monitoring
|
||||
|
||||
# redis['master_name'] = 'gitlab-redis'
|
||||
# redis['master_ip'] = nil
|
||||
# redis['master_port'] = nil
|
||||
# redis['master_password'] = nil
|
||||
# redis['master_port'] = 6379
|
||||
|
||||
## Master password should have the same value defined in redis['password']
|
||||
## to enable the instance to transition to/from master/slave in a failover event.
|
||||
# redis['master_password'] = 'redis-password-goes-here'
|
||||
|
||||
########################
|
||||
# GitLab Sentinel (EE) #
|
||||
########################
|
||||
|
||||
## Make sure you configured all redis['master_*'] keys above before continuing.
|
||||
|
||||
## To enable sentinel and disable all other services in this machine,
|
||||
## uncomment the line below (if you've enabled Redis role, it will keep it).
|
||||
##
|
||||
## For more details please see:
|
||||
## https://docs.gitlab.com/ce/administration/high_availability/redis.html
|
||||
# redis_sentinel_role['enable'] = true
|
||||
|
||||
# sentinel['enable'] = true
|
||||
# sentinel['bind'] = '0.0.0.0' # bind to all interfaces, uncomment to specify an IP and bind to a single one
|
||||
# sentinel['port'] = 26379 # uncomment to change default port
|
||||
|
||||
## Quorum must reflect the amount of voting sentinels it take to start a failover.
|
||||
## Value must NOT be greater then the ammount of sentinels.
|
||||
##
|
||||
## The quorum can be used to tune Sentinel in two ways:
|
||||
## 1. If a the quorum is set to a value smaller than the majority of Sentinels
|
||||
## we deploy, we are basically making Sentinel more sensible to master failures,
|
||||
## triggering a failover as soon as even just a minority of Sentinels is no longer
|
||||
## able to talk with the master.
|
||||
## 1. If a quorum is set to a value greater than the majority of Sentinels, we are
|
||||
## making Sentinel able to failover only when there are a very large number (larger
|
||||
## than majority) of well connected Sentinels which agree about the master being down.s
|
||||
# sentinel['quorum'] = 1
|
||||
|
||||
## Consider unresponsive server down after x amount of ms.
|
||||
# sentinel['down_after_milliseconds'] = 10000
|
||||
|
||||
## Specifies the failover timeout in milliseconds. It is used in many ways:
|
||||
##
|
||||
## - The time needed to re-start a failover after a previous failover was
|
||||
## already tried against the same master by a given Sentinel, is two
|
||||
## times the failover timeout.
|
||||
##
|
||||
## - The time needed for a slave replicating to a wrong master according
|
||||
## to a Sentinel current configuration, to be forced to replicate
|
||||
## with the right master, is exactly the failover timeout (counting since
|
||||
## the moment a Sentinel detected the misconfiguration).
|
||||
##
|
||||
## - The time needed to cancel a failover that is already in progress but
|
||||
## did not produced any configuration change (SLAVEOF NO ONE yet not
|
||||
## acknowledged by the promoted slave).
|
||||
##
|
||||
## - The maximum time a failover in progress waits for all the slaves to be
|
||||
## reconfigured as slaves of the new master. However even after this time
|
||||
## the slaves will be reconfigured by the Sentinels anyway, but not with
|
||||
## the exact parallel-syncs progression as specified.
|
||||
# sentinel['failover_timeout'] = 60000
|
||||
|
||||
#####################
|
||||
# GitLab Web server #
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"run_list": [ "recipe[gitlab]" ]
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
default['gitlab']['sentinel']['enable'] = false
|
||||
default['gitlab']['sentinel']['bind'] = '0.0.0.0'
|
||||
default['gitlab']['sentinel']['dir'] = '/var/opt/gitlab/sentinel'
|
||||
default['gitlab']['sentinel']['log_directory'] = '/var/log/gitlab/sentinel'
|
||||
default['gitlab']['sentinel']['ha'] = false
|
||||
default['gitlab']['sentinel']['port'] = 26379
|
||||
default['gitlab']['sentinel']['quorum'] = 1
|
||||
default['gitlab']['sentinel']['down_after_milliseconds'] = 10000
|
||||
default['gitlab']['sentinel']['failover_timeout'] = 60000
|
||||
default['gitlab']['sentinel']['myid'] = nil
|
|
@ -0,0 +1,95 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
define :sentinel_service, config_path: nil, redis_configuration: {}, sentinel_configuration: {}, logging_configuration: {}, action: :enable do
|
||||
redis = params[:redis_configuration]
|
||||
sentinel = params[:sentinel_configuration]
|
||||
logging = params[:logging_configuration]
|
||||
config_path = params[:config_path]
|
||||
|
||||
sentinel_service_name = 'sentinel'
|
||||
sentinel_dir = sentinel['dir']
|
||||
sentinel_log_dir = sentinel['log_directory']
|
||||
|
||||
redis_user = AccountHelper.new(node).redis_user
|
||||
|
||||
account 'user and group for sentinel' do
|
||||
username redis_user
|
||||
uid node['gitlab']['redis']['uid']
|
||||
ugid redis_user
|
||||
groupname redis_user
|
||||
gid node['gitlab']['redis']['gid']
|
||||
shell node['gitlab']['redis']['shell']
|
||||
home node['gitlab']['redis']['home']
|
||||
manage node['gitlab']['manage-accounts']['enable']
|
||||
end
|
||||
|
||||
case params[:action]
|
||||
when :enable
|
||||
|
||||
directory sentinel_dir do
|
||||
owner redis['username']
|
||||
group redis['group']
|
||||
mode '0750'
|
||||
end
|
||||
|
||||
directory sentinel_log_dir do
|
||||
owner redis['username']
|
||||
mode '0700'
|
||||
end
|
||||
|
||||
runit_service sentinel_service_name do
|
||||
down redis['ha']
|
||||
template_name sentinel_service_name
|
||||
options(
|
||||
{
|
||||
user: redis['username'],
|
||||
config_path: config_path,
|
||||
log_directory: sentinel_log_dir
|
||||
}.merge(params)
|
||||
)
|
||||
log_options redis.to_hash.merge(logging.to_hash)
|
||||
end
|
||||
|
||||
template config_path do
|
||||
source 'sentinel.conf.erb'
|
||||
owner redis['username']
|
||||
mode '0644'
|
||||
variables(
|
||||
{
|
||||
redis: redis.to_hash,
|
||||
sentinel: sentinel.to_hash
|
||||
}
|
||||
)
|
||||
notifies :restart, 'service[sentinel]', :immediately if OmnibusHelper.should_notify?('redis')
|
||||
only_if { config_path }
|
||||
end
|
||||
|
||||
when :disable
|
||||
runit_service sentinel_service_name do
|
||||
action :disable
|
||||
end
|
||||
|
||||
file config_path do
|
||||
action :delete
|
||||
end
|
||||
|
||||
directory sentinel['dir'] do
|
||||
action :delete
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,65 @@
|
|||
class SentinelHelper
|
||||
MYID_PATTERN = /^[0-9a-f]{40}$/
|
||||
JSON_FILE = '/etc/gitlab/gitlab-sentinel.json'.freeze
|
||||
|
||||
def initialize(node)
|
||||
@node = node
|
||||
end
|
||||
|
||||
def myid
|
||||
if sentinel['myid']
|
||||
restore_from_node
|
||||
else
|
||||
restore_or_generate_from_file
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Restore from node definition (gitlab.rb)
|
||||
def restore_from_node
|
||||
unless MYID_PATTERN =~ sentinel['myid']
|
||||
fail 'Sentinel myid must be exactly 40 hex-characters lowercase'
|
||||
end
|
||||
|
||||
sentinel['myid']
|
||||
end
|
||||
|
||||
# Restore from local JSON file or create a new myid
|
||||
def restore_or_generate_from_file
|
||||
existing_data = load_from_file
|
||||
if existing_data && existing_data['myid']
|
||||
existing_data['myid']
|
||||
else
|
||||
myid = generate_myid
|
||||
save_to_file({ 'myid' => myid })
|
||||
|
||||
myid
|
||||
end
|
||||
end
|
||||
|
||||
def sentinel
|
||||
@node['gitlab']['sentinel']
|
||||
end
|
||||
|
||||
# Load from local JSON file
|
||||
def load_from_file
|
||||
if File.exists?(JSON_FILE)
|
||||
Chef::JSONCompat.from_json(File.read(JSON_FILE))
|
||||
end
|
||||
end
|
||||
|
||||
# Save to local JSON file
|
||||
def save_to_file(data)
|
||||
if File.directory?('/etc/gitlab')
|
||||
File.open(JSON_FILE, 'w', 0600) do |f|
|
||||
f.puts(Chef::JSONCompat.to_json_pretty(data))
|
||||
f.chmod(0600) # update existing file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def generate_myid
|
||||
SecureRandom::hex(20) # size will be n*2 -> 40 characters
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
name "gitlab-ee"
|
||||
maintainer "GitLab Inc"
|
||||
maintainer_email "support@gitlab.com"
|
||||
license "Apache 2.0"
|
||||
description "Install and configure GitLab EE from Omnibus"
|
||||
long_description "Install and configure GitLab EE from Omnibus"
|
||||
version "0.0.1"
|
||||
recipe "gitlab", "Configures GitLab EE from Omnibus"
|
||||
|
||||
supports "ubuntu"
|
||||
supports "centos"
|
||||
|
||||
depends "runit"
|
||||
depends "gitlab"
|
||||
depends "package"
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
include_recipe 'gitlab::default'
|
||||
|
||||
if node['gitlab']['sentinel']['enable']
|
||||
include_recipe 'gitlab-ee::sentinel'
|
||||
else
|
||||
include_recipe 'gitlab-ee::sentinel_disable'
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
sentinel_helper = SentinelHelper.new(node)
|
||||
sentinel_cfg = node['gitlab']['sentinel'].to_hash.merge({ 'myid' => sentinel_helper.myid })
|
||||
|
||||
sentinel_service 'redis' do
|
||||
config_path File.join(node['gitlab']['sentinel']['dir'], 'sentinel.conf')
|
||||
redis_configuration node['gitlab']['redis']
|
||||
sentinel_configuration sentinel_cfg
|
||||
logging_configuration node['gitlab']['logging']
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
sentinel_service 'redis' do
|
||||
config_path File.join(node['gitlab']['sentinel']['dir'], 'sentinel.conf')
|
||||
redis_configuration node['gitlab']['redis']
|
||||
sentinel_configuration node['gitlab']['sentinel']
|
||||
logging_configuration node['gitlab']['logging']
|
||||
action :disable
|
||||
end
|
|
@ -0,0 +1,205 @@
|
|||
# This file is managed by gitlab-ctl. Manual changes will be
|
||||
# erased! To change the contents below, edit /etc/gitlab/gitlab.rb
|
||||
# and run `sudo gitlab-ctl reconfigure`.
|
||||
|
||||
# *** IMPORTANT ***
|
||||
#
|
||||
# By default Sentinel will not be reachable from interfaces different than
|
||||
# localhost, either use the 'bind' directive to bind to a list of network
|
||||
# interfaces, or disable protected mode with "protected-mode no" by
|
||||
# adding it to this configuration file.
|
||||
#
|
||||
# Before doing that MAKE SURE the instance is protected from the outside
|
||||
# world via firewalling or other means.
|
||||
#
|
||||
# For example you may use one of the following:
|
||||
#
|
||||
# bind 127.0.0.1 192.168.1.1
|
||||
#
|
||||
# protected-mode no
|
||||
bind <%= @sentinel['bind'] %>
|
||||
|
||||
# port <sentinel-port>
|
||||
# The port that this sentinel instance will run on
|
||||
port <%= @sentinel['port'] %>
|
||||
|
||||
# sentinel announce-ip <ip>
|
||||
# sentinel announce-port <port>
|
||||
#
|
||||
# The above two configuration directives are useful in environments where,
|
||||
# because of NAT, Sentinel is reachable from outside via a non-local address.
|
||||
#
|
||||
# When announce-ip is provided, the Sentinel will claim the specified IP address
|
||||
# in HELLO messages used to gossip its presence, instead of auto-detecting the
|
||||
# local address as it usually does.
|
||||
#
|
||||
# Similarly when announce-port is provided and is valid and non-zero, Sentinel
|
||||
# will announce the specified TCP port.
|
||||
#
|
||||
# The two options don't need to be used together, if only announce-ip is
|
||||
# provided, the Sentinel will announce the specified IP and the server port
|
||||
# as specified by the "port" option. If only announce-port is provided, the
|
||||
# Sentinel will announce the auto-detected local IP and the specified port.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# sentinel announce-ip 1.2.3.4
|
||||
|
||||
# dir <working-directory>
|
||||
# Every long running process should have a well-defined working directory.
|
||||
# For Redis Sentinel to chdir to /tmp at startup is the simplest thing
|
||||
# for the process to don't interfere with administrative tasks such as
|
||||
# unmounting filesystems.
|
||||
dir <%= %Q("#{@sentinel['dir']}") %>
|
||||
|
||||
# sentinel myid <id>
|
||||
#
|
||||
# Unique 40 hex-characters long identification of the instance in the cluster
|
||||
# This value is spread across all sentinels and each instance keep a list of
|
||||
# "known" instances to calculate majority in a failover consensus voting.
|
||||
<%= "sentinel myid #{@sentinel['myid']}" if @sentinel['myid'] %>
|
||||
|
||||
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
|
||||
#
|
||||
# Tells Sentinel to monitor this master, and to consider it in O_DOWN
|
||||
# (Objectively Down) state only if at least <quorum> sentinels agree.
|
||||
#
|
||||
# Note that whatever is the ODOWN quorum, a Sentinel will require to
|
||||
# be elected by the majority of the known Sentinels in order to
|
||||
# start a failover, so no failover can be performed in minority.
|
||||
#
|
||||
# Slaves are auto-discovered, so you don't need to specify slaves in
|
||||
# any way. Sentinel itself will rewrite this configuration file adding
|
||||
# the slaves using additional configuration options.
|
||||
# Also note that the configuration file is rewritten when a
|
||||
# slave is promoted to master.
|
||||
#
|
||||
# Note: master name should not include special characters or spaces.
|
||||
# The valid charset is A-z 0-9 and the three characters ".-_".
|
||||
sentinel monitor <%= @redis['master_name'] %> <%= @redis['master_ip'] %> <%= @redis['master_port'] %> <%= @sentinel['quorum'] %>
|
||||
|
||||
# sentinel down-after-milliseconds <master-name> <milliseconds>
|
||||
#
|
||||
# Number of milliseconds the master (or any attached slave or sentinel) should
|
||||
# be unreachable (as in, not acceptable reply to PING, continuously, for the
|
||||
# specified period) in order to consider it in S_DOWN state (Subjectively
|
||||
# Down).
|
||||
#
|
||||
# Default is 30 seconds.
|
||||
sentinel down-after-milliseconds <%= @redis['master_name'] %> <%= @sentinel['down_after_milliseconds'] %>
|
||||
|
||||
# sentinel parallel-syncs <master-name> <numslaves>
|
||||
#
|
||||
# How many slaves we can reconfigure to point to the new slave simultaneously
|
||||
# during the failover. Use a low number if you use the slaves to serve query
|
||||
# to avoid that all the slaves will be unreachable at about the same
|
||||
# time while performing the synchronization with the master.
|
||||
# sentinel parallel-syncs localhost 1
|
||||
|
||||
# sentinel failover-timeout <master-name> <milliseconds>
|
||||
#
|
||||
# Specifies the failover timeout in milliseconds. It is used in many ways:
|
||||
#
|
||||
# - The time needed to re-start a failover after a previous failover was
|
||||
# already tried against the same master by a given Sentinel, is two
|
||||
# times the failover timeout.
|
||||
#
|
||||
# - The time needed for a slave replicating to a wrong master according
|
||||
# to a Sentinel current configuration, to be forced to replicate
|
||||
# with the right master, is exactly the failover timeout (counting since
|
||||
# the moment a Sentinel detected the misconfiguration).
|
||||
#
|
||||
# - The time needed to cancel a failover that is already in progress but
|
||||
# did not produced any configuration change (SLAVEOF NO ONE yet not
|
||||
# acknowledged by the promoted slave).
|
||||
#
|
||||
# - The maximum time a failover in progress waits for all the slaves to be
|
||||
# reconfigured as slaves of the new master. However even after this time
|
||||
# the slaves will be reconfigured by the Sentinels anyway, but not with
|
||||
# the exact parallel-syncs progression as specified.
|
||||
#
|
||||
# Default is 3 minutes.
|
||||
sentinel failover-timeout <%= @redis['master_name'] %> <%= @sentinel['failover_timeout'] %>
|
||||
|
||||
# sentinel auth-pass <master-name> <password>
|
||||
#
|
||||
# Set the password to use to authenticate with the master and slaves.
|
||||
# Useful if there is a password set in the Redis instances to monitor.
|
||||
#
|
||||
# Note that the master password is also used for slaves, so it is not
|
||||
# possible to set a different password in masters and slaves instances
|
||||
# if you want to be able to monitor these instances with Sentinel.
|
||||
#
|
||||
# However you can have Redis instances without the authentication enabled
|
||||
# mixed with Redis instances requiring the authentication (as long as the
|
||||
# password set is the same for all the instances requiring the password) as
|
||||
# the AUTH command will have no effect in Redis instances with authentication
|
||||
# switched off.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
sentinel auth-pass <%= @redis['master_name'] %> <%= @redis['master_password'] %>
|
||||
|
||||
# SCRIPTS EXECUTION
|
||||
#
|
||||
# sentinel notification-script and sentinel reconfig-script are used in order
|
||||
# to configure scripts that are called to notify the system administrator
|
||||
# or to reconfigure clients after a failover. The scripts are executed
|
||||
# with the following rules for error handling:
|
||||
#
|
||||
# If script exits with "1" the execution is retried later (up to a maximum
|
||||
# number of times currently set to 10).
|
||||
#
|
||||
# If script exits with "2" (or an higher value) the script execution is
|
||||
# not retried.
|
||||
#
|
||||
# If script terminates because it receives a signal the behavior is the same
|
||||
# as exit code 1.
|
||||
#
|
||||
# A script has a maximum running time of 60 seconds. After this limit is
|
||||
# reached the script is terminated with a SIGKILL and the execution retried.
|
||||
|
||||
# NOTIFICATION SCRIPT
|
||||
#
|
||||
# sentinel notification-script <master-name> <script-path>
|
||||
#
|
||||
# Call the specified notification script for any sentinel event that is
|
||||
# generated in the WARNING level (for instance -sdown, -odown, and so forth).
|
||||
# This script should notify the system administrator via email, SMS, or any
|
||||
# other messaging system, that there is something wrong with the monitored
|
||||
# Redis systems.
|
||||
#
|
||||
# The script is called with just two arguments: the first is the event type
|
||||
# and the second the event description.
|
||||
#
|
||||
# The script must exist and be executable in order for sentinel to start if
|
||||
# this option is provided.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# sentinel notification-script mymaster /var/redis/notify.sh
|
||||
|
||||
# CLIENTS RECONFIGURATION SCRIPT
|
||||
#
|
||||
# sentinel client-reconfig-script <master-name> <script-path>
|
||||
#
|
||||
# When the master changed because of a failover a script can be called in
|
||||
# order to perform application-specific tasks to notify the clients that the
|
||||
# configuration has changed and the master is at a different address.
|
||||
#
|
||||
# The following arguments are passed to the script:
|
||||
#
|
||||
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
|
||||
#
|
||||
# <state> is currently always "failover"
|
||||
# <role> is either "leader" or "observer"
|
||||
#
|
||||
# The arguments from-ip, from-port, to-ip, to-port are used to communicate
|
||||
# the old address of the master and the new address of the elected slave
|
||||
# (now a master).
|
||||
#
|
||||
# This script should be resistant to multiple invocations.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
|
|
@ -0,0 +1,6 @@
|
|||
<%= "s#@svlogd_size" if @svlogd_size %>
|
||||
<%= "n#@svlogd_num" if @svlogd_num %>
|
||||
<%= "t#@svlogd_timeout" if @svlogd_timeout %>
|
||||
<%= "!#@svlogd_filter" if @svlogd_filter %>
|
||||
<%= "u#@svlogd_udp" if @svlogd_udp %>
|
||||
<%= "p#@svlogd_prefix" if @svlogd_prefix %>
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
exec svlogd -tt <%= @options[:log_directory] %>
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
exec 2>&1
|
||||
<%= render('mount_point_check.erb', cookbook: 'gitlab') %>
|
||||
umask 077
|
||||
|
||||
<% user = @options[:user] %>
|
||||
exec chpst -P -U <%= user %> -u <%= user %> /opt/gitlab/embedded/bin/redis-sentinel <%= @options[:config_path] %>
|
|
@ -425,8 +425,10 @@ default['gitlab']['redis']['tcp_keepalive'] = 300
|
|||
default['gitlab']['redis']['password'] = nil
|
||||
default['gitlab']['redis']['unixsocket'] = "/var/opt/gitlab/redis/redis.socket"
|
||||
default['gitlab']['redis']['unixsocketperm'] = "777"
|
||||
default['gitlab']['redis']['master'] = true
|
||||
default['gitlab']['redis']['master_name'] = 'gitlab-redis'
|
||||
default['gitlab']['redis']['master_ip'] = nil
|
||||
default['gitlab']['redis']['master_port'] = nil
|
||||
default['gitlab']['redis']['master_port'] = 6379
|
||||
default['gitlab']['redis']['master_password'] = nil
|
||||
|
||||
####
|
||||
|
|
|
@ -23,7 +23,8 @@ define :account, action: nil, username: nil, uid: nil, ugid: nil, groupname: nil
|
|||
username = params[:username]
|
||||
|
||||
if manage && groupname
|
||||
group groupname do
|
||||
group params[:name] do
|
||||
group_name groupname
|
||||
gid params[:gid]
|
||||
system params[:system]
|
||||
if params[:append_to_group]
|
||||
|
@ -35,7 +36,8 @@ define :account, action: nil, username: nil, uid: nil, ugid: nil, groupname: nil
|
|||
end
|
||||
|
||||
if manage && username
|
||||
user username do
|
||||
user params[:name] do
|
||||
username username
|
||||
shell params[:shell]
|
||||
home params[:home]
|
||||
uid params[:uid]
|
||||
|
|
|
@ -22,7 +22,7 @@ define :redis_service, :socket_group => nil do
|
|||
redis_log_dir = node['gitlab'][svc]['log_directory']
|
||||
redis_user = AccountHelper.new(node).redis_user
|
||||
|
||||
account "Redis user and group" do
|
||||
account 'user and group for redis' do
|
||||
username redis_user
|
||||
uid node['gitlab'][svc]['uid']
|
||||
ugid redis_user
|
||||
|
@ -33,6 +33,11 @@ define :redis_service, :socket_group => nil do
|
|||
manage node['gitlab']['manage-accounts']['enable']
|
||||
end
|
||||
|
||||
group 'Socket group' do
|
||||
append true # we need this so we don't remove members
|
||||
group_name params[:socket_group]
|
||||
end
|
||||
|
||||
directory redis_dir do
|
||||
owner redis_user
|
||||
group params[:socket_group]
|
||||
|
@ -45,12 +50,15 @@ define :redis_service, :socket_group => nil do
|
|||
end
|
||||
|
||||
redis_config = File.join(redis_dir, "redis.conf")
|
||||
is_slave = node['gitlab'][svc]['master_ip'] &&
|
||||
node['gitlab'][svc]['master_port'] &&
|
||||
!node['gitlab'][svc]['master']
|
||||
|
||||
template redis_config do
|
||||
source "redis.conf.erb"
|
||||
owner redis_user
|
||||
mode "0644"
|
||||
variables(node['gitlab'][svc].to_hash)
|
||||
variables(node['gitlab'][svc].to_hash.merge({is_slave: is_slave}))
|
||||
notifies :restart, "service[#{svc}]", :immediately if OmnibusHelper.should_notify?(svc)
|
||||
end
|
||||
|
||||
|
|
|
@ -1,40 +1,48 @@
|
|||
:mailboxes:
|
||||
<%
|
||||
require '/opt/gitlab/embedded/service/gitlab-rails/config/environment.rb'
|
||||
<%
|
||||
require "/opt/gitlab/embedded/service/gitlab-rails/lib/gitlab/mail_room" unless defined?(Gitlab::MailRoom)
|
||||
config = Gitlab::MailRoom.config
|
||||
|
||||
if Gitlab::IncomingEmail.enabled?
|
||||
config = Gitlab::IncomingEmail.config
|
||||
|
||||
redis_config_file = "/opt/gitlab/embedded/service/gitlab-rails/config/resque.yml"
|
||||
redis_url =
|
||||
if File.exists?(redis_config_file)
|
||||
YAML.load_file(redis_config_file)[Rails.env]['url']
|
||||
else
|
||||
"redis://localhost:6379"
|
||||
end
|
||||
if Gitlab::MailRoom.enabled?
|
||||
%>
|
||||
-
|
||||
:host: <%= config.host.to_json %>
|
||||
:port: <%= config.port.to_json %>
|
||||
:ssl: <%= config.ssl.to_json %>
|
||||
:start_tls: <%= config.start_tls.to_json %>
|
||||
:email: <%= config.user.to_json %>
|
||||
:password: <%= config.password.to_json %>
|
||||
:host: <%= config[:host].to_json %>
|
||||
:port: <%= config[:port].to_json %>
|
||||
:ssl: <%= config[:ssl].to_json %>
|
||||
:start_tls: <%= config[:start_tls].to_json %>
|
||||
:email: <%= config[:user].to_json %>
|
||||
:password: <%= config[:password].to_json %>
|
||||
:idle_timeout: 60
|
||||
|
||||
:name: <%= config.mailbox.to_json %>
|
||||
:name: <%= config[:mailbox].to_json %>
|
||||
|
||||
:delete_after_delivery: true
|
||||
|
||||
:delivery_method: sidekiq
|
||||
:delivery_options:
|
||||
:redis_url: <%= redis_url.to_json %>
|
||||
:namespace: resque:gitlab
|
||||
:redis_url: <%= config[:redis_url].to_json %>
|
||||
:namespace: <%= Gitlab::Redis::SIDEKIQ_NAMESPACE %>
|
||||
:queue: email_receiver
|
||||
:worker: EmailReceiverWorker
|
||||
<% if config[:sentinels] %>
|
||||
:sentinels:
|
||||
<% config[:sentinels].each do |sentinel| %>
|
||||
-
|
||||
:host: <%= sentinel[:host] %>
|
||||
:port: <%= sentinel[:port] %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
:arbitration_method: redis
|
||||
:arbitration_options:
|
||||
:redis_url: <%= redis_url.to_json %>
|
||||
:namespace: mail_room:gitlab
|
||||
<% end %>
|
||||
:redis_url: <%= config[:redis_url].to_json %>
|
||||
:namespace: <%= Gitlab::Redis::MAILROOM_NAMESPACE %>
|
||||
<% if config[:sentinels] %>
|
||||
:sentinels:
|
||||
<% config[:sentinels].each do |sentinel| %>
|
||||
-
|
||||
:host: <%= sentinel[:host] %>
|
||||
:port: <%= sentinel[:port] %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
@ -78,6 +78,7 @@ module Gitlab
|
|||
mattermost Mash.new
|
||||
gitlab_pages Mash.new
|
||||
registry Mash.new
|
||||
sentinel Mash.new
|
||||
node nil
|
||||
external_url nil
|
||||
pages_external_url nil
|
||||
|
@ -86,6 +87,17 @@ module Gitlab
|
|||
registry_external_url nil
|
||||
git_data_dirs Mash.new
|
||||
|
||||
# roles
|
||||
redis_sentinel_role Mash.new
|
||||
redis_master_role Mash.new
|
||||
redis_slave_role Mash.new
|
||||
|
||||
ROLES ||= [
|
||||
'redis_sentinel',
|
||||
'redis_master',
|
||||
'redis_slave'
|
||||
].freeze
|
||||
|
||||
class << self
|
||||
# guards against creating secrets on non-bootstrap node
|
||||
def generate_hex(chars)
|
||||
|
@ -178,12 +190,19 @@ module Gitlab
|
|||
"mattermost_external_url",
|
||||
"pages_external_url",
|
||||
"gitlab_pages",
|
||||
"registry"
|
||||
"registry",
|
||||
"sentinel"
|
||||
].each do |key|
|
||||
rkey = key.gsub('_', '-')
|
||||
results['gitlab'][rkey] = Gitlab[key]
|
||||
end
|
||||
|
||||
results['roles'] = {}
|
||||
ROLES.each do |key|
|
||||
rkey = key.gsub('_', '-')
|
||||
results['roles'][rkey] = Gitlab["#{key}_role"]
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
|
@ -204,7 +223,7 @@ module Gitlab
|
|||
# Parse nginx variables last because we want all external_url to be
|
||||
# parsed first
|
||||
Nginx.parse_variables
|
||||
GitlabRails.disable_gitlab_rails_services
|
||||
GitlabRails.disable_services
|
||||
# The last step is to convert underscores to hyphens in top-level keys
|
||||
generate_hash
|
||||
end
|
||||
|
|
|
@ -104,7 +104,7 @@ module GitlabRails
|
|||
# append urls to the list but without relative_url
|
||||
if Gitlab['gitlab_rails']['gitlab_relative_url']
|
||||
paths_without_relative_url = []
|
||||
Gitlab['gitlab_rails']['rack_attack_protected_paths'].each do |path|
|
||||
Gitlab['gitlab_rails']['rack_attack_protected_paths'].each do |path|
|
||||
if path.start_with?(Gitlab['gitlab_rails']['gitlab_relative_url'] + '/')
|
||||
stripped_path = path.sub(Gitlab['gitlab_rails']['gitlab_relative_url'], '')
|
||||
paths_without_relative_url.push(stripped_path)
|
||||
|
@ -115,17 +115,63 @@ module GitlabRails
|
|||
|
||||
end
|
||||
|
||||
def disable_gitlab_rails_services
|
||||
if Gitlab['gitlab_rails']["enable"] == false
|
||||
Gitlab['redis']["enable"] = false
|
||||
Gitlab['unicorn']["enable"] = false
|
||||
Gitlab['sidekiq']["enable"] = false
|
||||
Gitlab['gitlab_workhorse']["enable"] = false
|
||||
end
|
||||
def disable_services
|
||||
disable_services_roles if any_role_defined?
|
||||
|
||||
disable_gitlab_rails_services
|
||||
end
|
||||
|
||||
def public_path
|
||||
"#{Gitlab['node']['package']['install-dir']}/embedded/service/gitlab-rails/public"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def any_role_defined?
|
||||
Gitlab::ROLES.any? { |role| Gitlab["#{role}_role"]['enable'] }
|
||||
end
|
||||
|
||||
def disable_services_roles
|
||||
if Gitlab['redis_sentinel_role']['enable']
|
||||
disable_non_redis_services
|
||||
Gitlab['sentinel']['enable'] = true
|
||||
else
|
||||
Gitlab['sentinel']['enable'] = false
|
||||
end
|
||||
|
||||
if Gitlab['redis_master_role']['enable']
|
||||
disable_non_redis_services
|
||||
Gitlab['redis']['enable'] = true
|
||||
end
|
||||
|
||||
if Gitlab['redis_slave_role']['enable']
|
||||
disable_non_redis_services
|
||||
Gitlab['redis']['enable'] = true
|
||||
end
|
||||
|
||||
if Gitlab['redis_master_role']['enable'] && Gitlab['redis_slave_role']['enable']
|
||||
fail 'Cannot define both redis_master_role and redis_slave_role in the same machine.'
|
||||
elsif Gitlab['redis_master_role']['enable'] || Gitlab['redis_slave_role']['enable']
|
||||
disable_non_redis_services
|
||||
else
|
||||
Gitlab['redis']['enable'] = false
|
||||
end
|
||||
end
|
||||
|
||||
def disable_gitlab_rails_services
|
||||
if Gitlab['gitlab_rails']['enable'] == false
|
||||
Gitlab['unicorn']['enable'] = false
|
||||
Gitlab['sidekiq']['enable'] = false
|
||||
Gitlab['gitlab_workhorse']['enable'] = false
|
||||
end
|
||||
end
|
||||
|
||||
def disable_non_redis_services
|
||||
Gitlab['gitlab_rails']['enable'] = false
|
||||
Gitlab['bootstrap']['enable'] = false
|
||||
Gitlab['nginx']['enable'] = false
|
||||
Gitlab['postgresql']['enable'] = false
|
||||
Gitlab['mailroom']['enable'] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -289,12 +289,10 @@ class SecretsHelper
|
|||
secret_tokens['mattermost'].merge!(gitlab_oauth)
|
||||
end
|
||||
|
||||
if File.directory?("/etc/gitlab")
|
||||
File.open("/etc/gitlab/gitlab-secrets.json", "w") do |f|
|
||||
f.puts(
|
||||
Chef::JSONCompat.to_json_pretty(secret_tokens)
|
||||
)
|
||||
system("chmod 0600 /etc/gitlab/gitlab-secrets.json")
|
||||
if File.directory?('/etc/gitlab')
|
||||
File.open('/etc/gitlab/gitlab-secrets.json', 'w', 0600) do |f|
|
||||
f.puts(Chef::JSONCompat.to_json_pretty(secret_tokens))
|
||||
f.chmod(0600)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -28,17 +28,26 @@ module Redis
|
|||
Gitlab['redis']['unixsocket'] = false
|
||||
|
||||
# Try to discover gitlab_rails redis connection params
|
||||
# based on redis daemon definition if not defined
|
||||
Gitlab['gitlab_rails']['redis_host'] ||= Gitlab['redis']['bind']
|
||||
Gitlab['gitlab_rails']['redis_port'] ||= Gitlab['redis']['port']
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_host'] != Gitlab['redis']['bind']
|
||||
Chef::Log.warn "gitlab-rails 'redis_host' is different than 'bind' value defined for managed redis instance."
|
||||
# based on redis daemon
|
||||
unless has_sentinels?
|
||||
parse_redis_daemon!
|
||||
end
|
||||
end
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_port'] != Gitlab['redis']['port']
|
||||
Chef::Log.warn "gitlab-rails 'redis_port' is different than 'port' value defined for managed redis instance."
|
||||
end
|
||||
if Gitlab['redis_slave_role']['enable']
|
||||
Gitlab['redis']['master'] = false
|
||||
end
|
||||
|
||||
if sentinel_daemon_enabled? || is_redis_slave?
|
||||
fail "redis 'master_ip' is not defined" unless Gitlab['redis']['master_ip']
|
||||
fail "redis 'master_port' is not defined" unless Gitlab['redis']['master_port']
|
||||
fail "redis 'master_password' is not defined" unless Gitlab['redis']['master_password']
|
||||
end
|
||||
|
||||
# Try to discover gitlab_rails redis connection params
|
||||
# based on sentinels node data
|
||||
if has_sentinels?
|
||||
parse_sentinels!
|
||||
end
|
||||
|
||||
if is_gitlab_rails_redis_tcp?
|
||||
|
@ -53,12 +62,80 @@ module Redis
|
|||
|
||||
private
|
||||
|
||||
# Parses sentinel specific meta-data to fill gitlab_rails
|
||||
#
|
||||
# Redis sentinel requires the url to point to the 'master_name' instead of
|
||||
# an IP or a valid host. We also need to ignore port and make sure we use
|
||||
# correct password
|
||||
def parse_sentinels!
|
||||
master_name = Gitlab['redis']['master_name'] || node['gitlab']['redis']['master_name']
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_host'] != master_name
|
||||
Gitlab['gitlab_rails']['redis_host'] = master_name
|
||||
|
||||
Chef::Log.warn "gitlab-rails 'redis_host' will be ignored as sentinel is defined."
|
||||
end
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_port']
|
||||
Gitlab['gitlab_rails']['redis_port'] = nil
|
||||
|
||||
Chef::Log.warn "gitlab-rails 'redis_port' will be ignored as sentinel is defined."
|
||||
end
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_password'] != Gitlab['redis']['master_password']
|
||||
Gitlab['gitlab_rails']['redis_password'] = Gitlab['redis']['master_password']
|
||||
|
||||
Chef::Log.warn "gitlab-rails 'redis_password' will be ignored as sentinel is defined."
|
||||
end
|
||||
end
|
||||
|
||||
def parse_redis_daemon!
|
||||
return unless redis_managed?
|
||||
redis_bind = Gitlab['redis']['bind'] || node['gitlab']['redis']['bind']
|
||||
|
||||
Gitlab['gitlab_rails']['redis_host'] ||= redis_bind
|
||||
Gitlab['gitlab_rails']['redis_port'] ||= Gitlab['redis']['port']
|
||||
Gitlab['gitlab_rails']['redis_password'] ||= Gitlab['redis']['master_password']
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_host'] != redis_bind
|
||||
Chef::Log.warn "gitlab-rails 'redis_host' is different than 'bind' value defined for managed redis instance. Are you sure you are pointing to the same redis instance?"
|
||||
end
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_port'] != Gitlab['redis']['port']
|
||||
Chef::Log.warn "gitlab-rails 'redis_port' is different than 'port' value defined for managed redis instance. Are you sure you are pointing to the same redis instance?"
|
||||
end
|
||||
|
||||
if Gitlab['gitlab_rails']['redis_password'] != Gitlab['redis']['master_password']
|
||||
Chef::Log.warn "gitlab-rails 'redis_password' is different than 'master_password' value defined for managed redis instance. Are you sure you are pointing to the same redis instance?"
|
||||
end
|
||||
end
|
||||
|
||||
def node
|
||||
Gitlab[:node]
|
||||
end
|
||||
|
||||
def is_redis_tcp?
|
||||
Gitlab['redis']['bind'] && Gitlab['redis']['port'] != 0
|
||||
Gitlab['redis']['port'] && Gitlab['redis']['port'] > 0
|
||||
end
|
||||
|
||||
def is_redis_slave?
|
||||
Gitlab['redis']['master'] == false
|
||||
end
|
||||
|
||||
def sentinel_daemon_enabled?
|
||||
Gitlab['sentinel']['enable']
|
||||
end
|
||||
|
||||
def is_gitlab_rails_redis_tcp?
|
||||
Gitlab['gitlab_rails']['redis_host']
|
||||
end
|
||||
|
||||
def has_sentinels?
|
||||
Gitlab['gitlab_rails']['redis_sentinels'] && !Gitlab['gitlab_rails']['redis_sentinels'].empty?
|
||||
end
|
||||
|
||||
def redis_managed?
|
||||
Gitlab['redis']['enable'].nil? ? node['gitlab']['redis']['enable'] : Gitlab['redis']['enable']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Copyright:: Copyright (c) 2016 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.
|
||||
#
|
||||
|
||||
Gitlab[:node] = node
|
||||
|
||||
if File.exists?('/etc/gitlab/gitlab.rb')
|
||||
Gitlab.from_file('/etc/gitlab/gitlab.rb')
|
||||
end
|
||||
|
||||
node.consume_attributes(Gitlab.generate_config(node['fqdn']))
|
|
@ -23,11 +23,7 @@ require 'openssl'
|
|||
install_dir = node['package']['install-dir']
|
||||
ENV['PATH'] = "#{install_dir}/bin:#{install_dir}/embedded/bin:#{ENV['PATH']}"
|
||||
|
||||
Gitlab[:node] = node
|
||||
if File.exists?("/etc/gitlab/gitlab.rb")
|
||||
Gitlab.from_file("/etc/gitlab/gitlab.rb")
|
||||
end
|
||||
node.consume_attributes(Gitlab.generate_config(node['fqdn']))
|
||||
include_recipe 'gitlab::config'
|
||||
|
||||
directory "/etc/gitlab" do
|
||||
owner "root"
|
||||
|
|
|
@ -151,7 +151,7 @@ daemonize no
|
|||
#
|
||||
# Creating a pid file is best effort: if Redis is not able to create it
|
||||
# nothing bad happens, the server will start and run normally.
|
||||
pidfile /var/run/redis_<%= @port %>.pid
|
||||
pidfile "/var/run/redis_<%= @port %>.pid"
|
||||
|
||||
# Specify the server verbosity level.
|
||||
# This can be one of:
|
||||
|
@ -238,7 +238,7 @@ rdbcompression yes
|
|||
rdbchecksum yes
|
||||
|
||||
# The filename where to dump the DB
|
||||
dbfilename dump.rdb
|
||||
dbfilename "dump.rdb"
|
||||
|
||||
# The working directory.
|
||||
#
|
||||
|
@ -248,7 +248,7 @@ dbfilename dump.rdb
|
|||
# The Append Only File will also be created inside this directory.
|
||||
#
|
||||
# Note that you must specify a directory here, not a file name.
|
||||
dir <%= @dir %>
|
||||
dir <%= %Q("#{@dir}") %>
|
||||
|
||||
################################# REPLICATION #################################
|
||||
|
||||
|
@ -267,7 +267,7 @@ dir <%= @dir %>
|
|||
# and resynchronize with them.
|
||||
#
|
||||
# slaveof <masterip> <masterport>
|
||||
<%= "slaveof #{@master_ip} #{@master_port}" if @master_ip && @master_port %>
|
||||
<%= "slaveof #{@master_ip} #{@master_port}" if @is_slave %>
|
||||
|
||||
# If the master is password protected (using the "requirepass" configuration
|
||||
# directive below) it is possible to tell the slave to authenticate before
|
||||
|
@ -275,7 +275,7 @@ dir <%= @dir %>
|
|||
# refuse the slave request.
|
||||
#
|
||||
# masterauth <master-password>
|
||||
<%= %Q(masterauth "#{@master_password}") if @master_password %>
|
||||
<%= %Q(masterauth "#{@master_password}") if @master_password%>
|
||||
|
||||
# When a slave loses its connection with the master, or when the replication
|
||||
# is still in progress, the slave can act in two different ways:
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
require 'chef_helper'
|
||||
|
||||
describe 'gitlab::config' do
|
||||
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config') }
|
||||
let(:node) { chef_run.node }
|
||||
|
||||
before do
|
||||
allow(Gitlab).to receive(:[]).and_call_original
|
||||
end
|
||||
|
||||
shared_examples 'regular services are disabled' do
|
||||
it 'disables regular services' do
|
||||
expect(node['gitlab']['gitlab-rails']['enable']).to eq false
|
||||
expect(node['gitlab']['unicorn']['enable']).to eq false
|
||||
expect(node['gitlab']['sidekiq']['enable']).to eq false
|
||||
expect(node['gitlab']['gitlab-workhorse']['enable']).to eq false
|
||||
expect(node['gitlab']['bootstrap']['enable']).to eq false
|
||||
expect(node['gitlab']['nginx']['enable']).to eq false
|
||||
expect(node['gitlab']['postgresql']['enable']).to eq false
|
||||
expect(node['gitlab']['mailroom']['enable']).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'with roles' do
|
||||
context 'when redis_sentinel_role is enabled' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_sentinel_role: {
|
||||
enable: true
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'regular services are disabled'
|
||||
|
||||
it 'only sentinel is enabled' do
|
||||
expect(node['gitlab']['sentinel']['enable']).to eq true
|
||||
expect(node['gitlab']['redis']['enable']).to eq false
|
||||
end
|
||||
|
||||
context 'when redis_sentinel_role is enabled with redis_master_role' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_sentinel_role: {
|
||||
enable: true
|
||||
},
|
||||
redis_master_role: {
|
||||
enable: true
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'regular services are disabled'
|
||||
|
||||
it 'redis and sentinel are enabled' do
|
||||
expect(node['gitlab']['sentinel']['enable']).to eq true
|
||||
expect(node['gitlab']['redis']['enable']).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when redis_sentinel_role is enabled with redis_slave_role' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_sentinel_role: {
|
||||
enable: true
|
||||
},
|
||||
redis_slave_role: {
|
||||
enable: true
|
||||
},
|
||||
redis: {
|
||||
master_ip: '10.0.0.0',
|
||||
master_port: 6379,
|
||||
master_password: 'PASSWORD'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'regular services are disabled'
|
||||
|
||||
it 'only redis is enabled' do
|
||||
expect(node['gitlab']['sentinel']['enable']).to eq true
|
||||
expect(node['gitlab']['redis']['enable']).to eq true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when redis_master_role is enabled' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_master_role: {
|
||||
enable: true
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'regular services are disabled'
|
||||
|
||||
it 'only redis is enabled' do
|
||||
expect(node['gitlab']['redis']['enable']).to eq true
|
||||
expect(node['gitlab']['sentinel']['enable']).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when redis_slave_role is enabled' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_slave_role: {
|
||||
enable: true
|
||||
},
|
||||
redis: {
|
||||
master_ip: '10.0.0.0',
|
||||
master_port: 6379,
|
||||
master_password: 'PASSWORD'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it_behaves_like 'regular services are disabled'
|
||||
|
||||
it 'only redis is enabled' do
|
||||
expect(node['gitlab']['redis']['enable']).to eq true
|
||||
expect(node['gitlab']['sentinel']['enable']).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when redis_master_role and redis_slave_role are enabled' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_master_role: {
|
||||
enable: true
|
||||
},
|
||||
redis_slave_role: {
|
||||
enable: true
|
||||
},
|
||||
redis: {
|
||||
master_ip: '10.0.0.0',
|
||||
master_port: 6379,
|
||||
master_password: 'PASSWORD'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'fails with an error' do
|
||||
expect { chef_run }.to raise_error RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -118,11 +118,7 @@ describe 'gitlab::gitlab-shell' do
|
|||
redis_host: 'redis.example.com',
|
||||
redis_port: 8888,
|
||||
redis_database: 1,
|
||||
redis_password: "PASSWORD!",
|
||||
redis_sentinels: [
|
||||
{'host' => 'redis1.sentinel', 'port' => 26370},
|
||||
{'host' => 'redis2.sentinel', 'port' => 26371}
|
||||
]
|
||||
redis_password: 'PASSWORD!'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -140,6 +136,38 @@ describe 'gitlab::gitlab-shell' do
|
|||
.with_content(/namespace: resque:gitlab/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/pass: PASSWORD!/)
|
||||
expect(chef_run).to_not render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/socket: \/var\/opt\/gitlab\/redis\/redis.socket/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'with sentinels configured' do
|
||||
before {
|
||||
stub_gitlab_rb(
|
||||
gitlab_rails: {
|
||||
redis_sentinels: [
|
||||
{'host' => 'redis1.sentinel', 'port' => 26370},
|
||||
{'host' => 'redis2.sentinel', 'port' => 26371}
|
||||
]
|
||||
},
|
||||
redis: {
|
||||
master_name: 'sentinel-master',
|
||||
master_password: 'PASSWORD!'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
it 'creates the config file with the required redis settings' do
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/bin: \/opt\/gitlab\/embedded\/bin\/redis-cli/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/host: sentinel-master/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/port: 6379/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/namespace: resque:gitlab/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/pass: PASSWORD!/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
.with_content(/- {"host":"redis1.sentinel","port":26370}/)
|
||||
expect(chef_run).to render_file('/var/opt/gitlab/gitlab-shell/config.yml')
|
||||
|
|
|
@ -31,8 +31,9 @@ describe 'secrets' do
|
|||
before do
|
||||
allow(SecretsHelper).to receive(:system)
|
||||
allow(File).to receive(:directory?).with('/etc/gitlab').and_return(true)
|
||||
allow(File).to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w').and_yield(file).once
|
||||
allow(File).to receive(:open).with('/etc/gitlab/gitlab-secrets.json', 'w', 0600).and_yield(file).once
|
||||
allow(file).to receive(:puts) { |json| @new_secrets = JSON.parse(json) }
|
||||
allow(file).to receive(:chmod).and_return(true)
|
||||
end
|
||||
|
||||
context 'when there are no existing secrets' do
|
||||
|
|
|
@ -2,7 +2,7 @@ require_relative '../../files/gitlab-cookbooks/gitlab/libraries/redis_helper.rb'
|
|||
require 'chef_helper'
|
||||
|
||||
describe RedisHelper do
|
||||
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') }
|
||||
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config') }
|
||||
subject { described_class.new(chef_run.node) }
|
||||
|
||||
context '#redis_url' do
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'chef_helper'
|
||||
|
||||
describe 'Redis' do
|
||||
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::default') }
|
||||
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config') }
|
||||
let(:node) { chef_run.node }
|
||||
subject { ::Redis }
|
||||
before { allow(Gitlab).to receive(:[]).and_call_original }
|
||||
|
@ -17,47 +17,110 @@ describe 'Redis' do
|
|||
context '.parse_redis_settings' do
|
||||
context 'when no customization is made' do
|
||||
it 'keeps unixsocket' do
|
||||
expect(Gitlab['gitlab_rails']['unixsocket']).not_to eq false
|
||||
expect(node['gitlab']['gitlab-rails']['unixsocket']).not_to eq false
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
end
|
||||
|
||||
context 'within redis host and port synchronization with gitlab_rails' do
|
||||
let(:redis_host) { '0.0.0.0' }
|
||||
let(:redis_port) { 6379 }
|
||||
let(:redis_host) { '1.2.3.4' }
|
||||
let(:redis_port) { 6370 }
|
||||
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis: {
|
||||
bind: redis_host,
|
||||
port: redis_port
|
||||
}
|
||||
)
|
||||
node
|
||||
context 'when not using sentinels' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis: {
|
||||
bind: redis_host,
|
||||
port: redis_port
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'disables unix socket when redis tcp params are defined' do
|
||||
expect(node['gitlab']['redis']['unixsocket']).to eq false
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
|
||||
it 'expects redis_host to match bind value from redis' do
|
||||
expect(node['gitlab']['gitlab-rails']['redis_host']).to eq redis_host
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
|
||||
it 'expects redis_port to match port value from redis' do
|
||||
expect(node['gitlab']['gitlab-rails']['redis_port']).to eq redis_port
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
end
|
||||
|
||||
it 'disables unix socket when redis tcp params are defined' do
|
||||
expect(Gitlab['redis']['unixsocket']).to eq false
|
||||
context 'when using sentinels' do
|
||||
let(:master_name) { 'gitlabredis' }
|
||||
let(:master_pass) { 'PASSWORD' }
|
||||
|
||||
subject.parse_redis_settings
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis: {
|
||||
bind: redis_host,
|
||||
port: redis_port,
|
||||
master_name: master_name,
|
||||
master_password: master_pass
|
||||
},
|
||||
gitlab_rails: {
|
||||
redis_sentinels: [
|
||||
{ host: '1.2.3.4', port: '26379' }
|
||||
]
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
it 'disables unix socket when sentinel params are defined' do
|
||||
expect(node['gitlab']['redis']['unixsocket']).to eq false
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
|
||||
it 'expects redis_host to match bind value from redis' do
|
||||
expect(node['gitlab']['gitlab-rails']['redis_host']).to eq master_name
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
|
||||
it 'expects redis_port to match default port value from redis' do
|
||||
expect(node['gitlab']['gitlab-rails']['redis_port']).to eq 6379
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
|
||||
it 'expects redis_password to match master_password value from redis' do
|
||||
expect(node['gitlab']['gitlab-rails']['redis_password']).to eq master_pass
|
||||
end
|
||||
end
|
||||
|
||||
it 'expects redis_host to match bind value from redis' do
|
||||
expect(Gitlab['gitlab_rails']['redis_host']).to eq redis_host
|
||||
context 'when with redis_slave_role enabled' do
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
redis_slave_role: {
|
||||
enable: true
|
||||
},
|
||||
redis: {
|
||||
master_ip: '10.0.0.0',
|
||||
master_port: 6379,
|
||||
master_password: 'PASSWORD'
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
subject.parse_redis_settings
|
||||
end
|
||||
|
||||
it 'expects redis_port to match port value from redis' do
|
||||
expect(Gitlab['gitlab_rails']['redis_port']).to eq redis_port
|
||||
|
||||
subject.parse_redis_settings
|
||||
it 'defined redis master as false' do
|
||||
expect(node['gitlab']['redis']['master']).to eq false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'within gitlab-rails redis values' do
|
||||
let(:redis_host) { '0.0.0.0' }
|
||||
let(:redis_host) { '1.2.3.4' }
|
||||
|
||||
before do
|
||||
stub_gitlab_rb(
|
||||
|
@ -65,15 +128,14 @@ describe 'Redis' do
|
|||
redis_host: redis_host
|
||||
}
|
||||
)
|
||||
node
|
||||
end
|
||||
|
||||
it 'disables unix socket when gitlab-rails tcp params are defined' do
|
||||
expect(Gitlab['gitlab_rails']['redis_socket']).to eq false
|
||||
expect(node['gitlab']['gitlab-rails']['redis_socket']).to eq false
|
||||
end
|
||||
|
||||
it 'defaults port to 6379' do
|
||||
expect(Gitlab['gitlab_rails']['redis_port']).to eq 6379
|
||||
expect(node['gitlab']['gitlab-rails']['redis_port']).to eq 6379
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
require_relative '../../files/gitlab-cookbooks/gitlab-ee/libraries/sentinel_helper.rb'
|
||||
require 'chef_helper'
|
||||
|
||||
describe SentinelHelper do
|
||||
let(:chef_run) { ChefSpec::SoloRunner.converge('gitlab::config') }
|
||||
subject { described_class.new(chef_run.node) }
|
||||
before { allow(Gitlab).to receive(:[]).and_call_original }
|
||||
|
||||
context '#myid' do
|
||||
context 'when retrieving from config' do
|
||||
it 'fails when myid is not 40 hex-characters long' do
|
||||
stub_gitlab_rb(
|
||||
sentinel: {
|
||||
myid: 'wrongid'
|
||||
}
|
||||
)
|
||||
|
||||
expect { subject.myid }.to raise_error RuntimeError
|
||||
end
|
||||
|
||||
it 'works when myid is 40 hex-characters long' do
|
||||
stub_gitlab_rb(
|
||||
sentinel: {
|
||||
myid: '1234567890abcdef1234567890abcdef12345678'
|
||||
}
|
||||
)
|
||||
|
||||
expect { subject.myid }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no config is defined' do
|
||||
let(:myid) { 'abcdef1234567890abcdef1234567890abcdef1' }
|
||||
|
||||
it 'generates a random myid' do
|
||||
expect(subject.myid).not_to be_empty
|
||||
end
|
||||
|
||||
it 'persist generated value into JSON file' do
|
||||
allow(subject).to receive(:generate_myid).at_least(:once) { myid }
|
||||
|
||||
expect(subject).to receive(:save_to_file).with({ 'myid' => myid })
|
||||
subject.myid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue