snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
class zulip::apache_sso {
include zulip::localhost_sso
case $facts['os']['family'] {
'Debian': {
$apache_packages = [ 'apache2', 'libapache2-mod-wsgi-py3', ]
$conf_dir = '/etc/apache2'
$apache2 = 'apache2'
}
'RedHat': {
$apache_packages = [ 'httpd', 'python36u-mod_wsgi', ]
$conf_dir = '/etc/httpd'
$apache2 = 'httpd'
}
default: {
fail('osfamily not supported')
}
}
package { $apache_packages: ensure => installed }
apache2mod { [ 'headers', 'proxy', 'proxy_http', 'rewrite', 'ssl', 'wsgi', ]:
ensure => present,
require => Package[$apache2],
}
file { "${conf_dir}/ports.conf":
ensure => file,
require => Package[$apache2],
owner => 'root',
group => 'root',
mode => '0640',
source => 'puppet:///modules/zulip/apache/ports.conf',
}
file { "${conf_dir}/sites-available/":
recurse => true,
require => Package[$apache2],
owner => 'root',
group => 'root',
mode => '0640',
source => 'puppet:///modules/zulip/apache/sites/',
}
}

View File

@@ -0,0 +1,318 @@
# Minimal configuration to run a Zulip application server.
# Default nginx configuration is included in extension app_frontend.pp.
class zulip::app_frontend_base {
include zulip::nginx
include zulip::tusd
include zulip::sasl_modules
include zulip::supervisor
include zulip::tornado_sharding
include zulip::hooks::base
if $facts['os']['family'] == 'Debian' {
# Upgrade and other tooling wants to be able to get a database
# shell. This is not necessary on CentOS because the PostgreSQL
# package already includes the client.
include zulip::postgresql_client
}
zulip::safepackage {
[
# For `manage.py compilemessages` when upgrading from Git.
'gettext',
# For Slack import.
'unzip',
# Ensures `/etc/ldap/ldap.conf` exists; the default
# `TLS_CACERTDIR` specified there is necessary for LDAP
# authentication to work. This package is "Recommended" by
# `libldap` where is required by postgres, so has been on most
# Zulip servers by default; adding it here explicitly ensures it
# is available on those that don't include the database server.
'libldap-common',
]:
ensure => installed,
}
file { '/etc/nginx/zulip-include/app':
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/app',
notify => Service['nginx'],
}
file { '/etc/nginx/zulip-include/uploads.types':
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/uploads.types',
notify => Service['nginx'],
}
file { '/etc/nginx/zulip-include/app.d/':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/etc/nginx/zulip-include/localhost.d/':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
$loadbalancers = split(zulipconf('loadbalancer', 'ips', ''), ',')
if $loadbalancers != [] {
file { '/etc/nginx/zulip-include/app.d/keepalive-loadbalancer.conf':
require => File['/etc/nginx/zulip-include/app.d'],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/nginx/zulip-include-app.d/keepalive-loadbalancer.conf',
notify => Service['nginx'],
}
} else {
file { '/etc/nginx/zulip-include/app.d/keepalive-loadbalancer.conf':
ensure => absent,
notify => Service['nginx'],
}
}
file { '/etc/nginx/zulip-include/app.d/accept-loadbalancer.conf':
# This moved to /etc/nginx/zulip-include/trusted-ip, via
# nginx.pp. This block can be removed once direct Zulip upgrades
# from Zulip 11 are no longer supported.
ensure => absent,
notify => Service['nginx'],
}
file { '/etc/nginx/zulip-include/app.d/healthcheck.conf':
require => File['/etc/nginx/zulip-include/app.d'],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/nginx/healthcheck.conf.template.erb'),
notify => Service['nginx'],
}
file { '/etc/nginx/zulip-include/upstreams':
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/upstreams',
notify => Service['nginx'],
}
$s3_memory_cache_size = zulipconf('application_server', 's3_memory_cache_size', '1M')
$s3_disk_cache_size = zulipconf('application_server', 's3_disk_cache_size', '200M')
$s3_cache_inactive_time = zulipconf('application_server', 's3_cache_inactive_time', '30d')
$configured_nginx_resolver = zulipconf('application_server', 'nameserver', '')
if $configured_nginx_resolver == '' {
# This may fail in the unlikely change that there is no configured
# resolver in /etc/resolv.conf, so only call it is unset in zulip.conf
$nginx_resolver_ip = resolver_ip()
} elsif (':' in $configured_nginx_resolver) and ! ('.' in $configured_nginx_resolver) and ! ('[' in $configured_nginx_resolver) {
# Assume this is IPv6, which needs square brackets.
$nginx_resolver_ip = "[${configured_nginx_resolver}]"
} else {
$nginx_resolver_ip = $configured_nginx_resolver
}
file { '/etc/nginx/zulip-include/s3-cache':
require => [
Package[$zulip::common::nginx],
File['/srv/zulip-uploaded-files-cache'],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/nginx/s3-cache.template.erb'),
notify => Service['nginx'],
}
file { '/etc/nginx/zulip-include/app.d/uploads-internal.conf':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
source => 'puppet:///modules/zulip/nginx/zulip-include-frontend/uploads-internal.conf',
}
# This determines whether we run queue processors multithreaded or
# multiprocess. Multiprocess scales much better, but requires more
# RAM; we just auto-detect based on available system RAM.
#
# Because Zulip can run in the multiprocess mode with 4GB of memory,
# and it's a common instance size, we aim for that to be the cutoff
# for this higher-performance mode.
#
# We use a cutoff less than 4000 here to detect systems advertised
# as "4GB"; some may have as little as 4 x 1000^3 / 1024^2 ≈ 3815 MiB
# of memory.
$queues_multiprocess_default = $zulip::common::total_memory_mb > 3800
$queues_multiprocess = zulipconf('application_server', 'queue_workers_multiprocess', $queues_multiprocess_default)
$base_queues = [
'deferred_work',
'digest_emails',
'email_mirror',
'embed_links',
'embedded_bots',
'email_senders',
'deferred_email_senders',
'missedmessage_emails',
'missedmessage_mobile_notifications',
'outgoing_webhooks',
'thumbnail',
'user_activity',
'user_activity_interval',
]
# Soft reactivations normally share the deferred_work queue; larger
# servers can opt into a dedicated queue (and worker process) for them.
$queues = zulipconf('application_server', 'dedicated_soft_reactivation_queue', false) ? {
true => $base_queues + ['soft_reactivation'],
default => $base_queues,
}
if $zulip::common::total_memory_mb > 24000 {
$uwsgi_default_processes = 16
} elsif $zulip::common::total_memory_mb > 12000 {
$uwsgi_default_processes = 8
} elsif $zulip::common::total_memory_mb > 6000 {
$uwsgi_default_processes = 6
} elsif $zulip::common::total_memory_mb > 3000 {
$uwsgi_default_processes = 4
} else {
$uwsgi_default_processes = 3
}
# Not the different naming scheme for sharded workers, where each gets its own queue,
# vs when multiple workers service the same queue.
$worker_counts = Hash(zulipconf_keys('application_server').filter |$key| {
$key =~ /_workers$/
}.map |$key| {
[regsubst($key, '_workers$', ''), Integer(zulipconf('application_server', $key, 1))]
})
$mobile_notification_shards = Integer(zulipconf('application_server', 'mobile_notification_shards', 1))
$user_activity_shards = Integer(zulipconf('application_server', 'user_activity_shards', 1))
$tornado_ports = $zulip::tornado_sharding::tornado_ports
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
$proxy_port = zulipconf('http_proxy', 'port', '4750')
if ($proxy_host in ['localhost', '127.0.0.1', '::1']) and ($proxy_port == '4750') {
include zulip::smokescreen
}
$katex_server = zulipconf('application_server', 'katex_server', true)
$katex_server_port = zulipconf('application_server', 'katex_server_port', '9700')
$tusd_server_listen = zulipconf('application_server', 'tusd_server_listen', '127.0.0.1')
# Supervisor sets this as HTTP_proxy/HTTPS_proxy in every process's
# environment, so that all outgoing requests go through Smokescreen;
# including those made directly by libraries rather than through
# OutgoingSession.
if $proxy_host != '' and $proxy_port != '' {
$proxy = "http://${proxy_host}:${proxy_port}"
} else {
$proxy = ''
}
$custom_ca_path = zulipconf('application_server','custom_ca_path', '')
if $custom_ca_path != '' {
file { '/usr/local/share/ca-certificates/custom-zulip-ca.crt':
ensure => file,
source => $custom_ca_path,
owner => 'root',
group => 'root',
mode => '0644',
notify => Exec['update-ca-certificates'],
}
exec { 'update-ca-certificates':
command => '/usr/sbin/update-ca-certificates',
require => Package['ca-certificates'],
before => File["${zulip::common::supervisor_conf_dir}/zulip.conf"],
refreshonly => true,
}
$ca_bundle=',REQUESTS_CA_BUNDLE="/etc/ssl/certs/ca-certificates.crt"'
} else {
$ca_bundle=''
}
file { "${zulip::common::supervisor_conf_dir}/zulip.conf":
ensure => file,
require => [Package[supervisor], Exec['stage_updated_sharding']],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/zulip.conf.template.erb'),
notify => Service[$zulip::common::supervisor_service],
}
$uwsgi_rolling_restart = zulipconf('application_server', 'rolling_restart', false)
$uwsgi_listen_backlog_limit = zulipconf('application_server', 'uwsgi_listen_backlog_limit', 128)
$uwsgi_processes = zulipconf('application_server', 'uwsgi_processes', $uwsgi_default_processes)
$somaxconn = 2 * Integer($uwsgi_listen_backlog_limit)
file { '/etc/zulip/uwsgi.ini':
ensure => file,
require => Package[supervisor],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/uwsgi.ini.template.erb'),
notify => Service[$zulip::common::supervisor_service],
}
zulip::sysctl { 'uwsgi':
comment => 'Allow larger listen backlog',
key => 'net.core.somaxconn',
value => $somaxconn,
}
file { [
'/home/zulip/tornado',
'/home/zulip/prod-static',
'/home/zulip/deployments',
'/srv/zulip-locks',
'/srv/zulip-emoji-cache',
'/srv/zulip-uploaded-files-cache',
]:
ensure => directory,
owner => 'zulip',
group => 'zulip',
mode => '0755',
}
# /home/zulip/uploads is documented as something administrators may
# replace with a symlink to a different storage location, so we use
# an exec with a `test -d` guard (which follows symlinks) rather
# than a file resource that would replace the symlink.
exec { 'create-uploads-dir':
command => 'install -d -o zulip -g zulip -m 0755 /home/zulip/uploads',
unless => 'test -d /home/zulip/uploads',
path => '/usr/bin:/bin',
}
file { [
'/var/log/zulip/queue_error',
'/var/log/zulip/queue_stats',
]:
ensure => directory,
owner => 'zulip',
group => 'zulip',
mode => '0750',
}
$access_log_retention_days = zulipconf('application_server','access_log_retention_days', 14)
file { '/etc/logrotate.d/zulip':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/logrotate/zulip.template.erb'),
}
zulip::nagios_plugins {'zulip_app_frontend': }
# This cron job does nothing unless RATE_LIMIT_TOR_TOGETHER is enabled.
zulip::cron { 'fetch-tor-exit-nodes':
minute => '17',
}
# This was originally added with a typo in the name.
file { '/etc/cron.d/fetch-for-exit-nodes':
ensure => absent,
}
}

View File

@@ -0,0 +1,88 @@
# Cron jobs and other tools that should run on only one Zulip server
# in a cluster.
class zulip::app_frontend_once {
include zulip::hooks::send_zulip_update_announcements
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
$proxy_port = zulipconf('http_proxy', 'port', '4750')
if $proxy_host != '' and $proxy_port != '' {
$proxy = "http://${proxy_host}:${proxy_port}"
} else {
$proxy = ''
}
file { "${zulip::common::supervisor_conf_dir}/zulip-once.conf":
ensure => file,
require => [Package[supervisor], Exec['stage_updated_sharding']],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/zulip-once.conf.template.erb'),
notify => Service[$zulip::common::supervisor_service],
}
# Every-hour
zulip::cron { 'update-analytics-counts':
minute => '5',
}
zulip::cron { 'check-analytics-state':
minute => '30',
}
zulip::cron { 'promote-new-full-members':
minute => '35',
}
zulip::cron { 'send_zulip_update_announcements':
minute => '47',
}
# Daily
zulip::cron { 'soft-deactivate-users':
hour => '5',
minute => '0',
manage => 'soft_deactivate_users -d',
}
zulip::cron { 'delete-old-unclaimed-attachments':
hour => '5',
minute => '0',
manage => 'delete_old_unclaimed_attachments -f',
}
# If changing the time of this job, then the global time string
# generated by demo_organizations.get_scheduled_deletion_global_time
# needs to be updated as well.
zulip::cron { 'archive-messages':
hour => '6',
minute => '0',
}
zulip::cron { 'send-digest-emails':
hour => '18',
minute => '0',
manage => 'enqueue_digest_emails',
}
# Most deploys will re-run this for all streams, daily; large
# deploys may set this to a number >= 25 to only update streams with
# recent potential changes.
$update_subscriber_count_incremental = zulipconf('application_server', 'update_subscriber_count_incremental', '')
if $update_subscriber_count_incremental != '' {
$update_subscriber_count_arg = " --since ${update_subscriber_count_incremental}"
} else {
$update_subscriber_count_arg = ''
}
zulip::cron { 'update_subscriber_counts':
hour => '6',
minute => '0',
manage => "update_subscriber_counts${update_subscriber_count_arg}"
}
zulip::cron { 'clearsessions':
hour => '22',
minute => '22',
}
# Weekly
zulip::cron { 'update-channel-recently-active-status':
hour => '5',
minute => '0',
dow => '0',
manage => 'update_channel_recently_active_status',
}
}

View File

@@ -0,0 +1,7 @@
class zulip::apt_repository {
$setup_apt_repo_file = "${facts['zulip_scripts_path']}/lib/setup-apt-repo"
exec{'setup_apt_repo':
command => "bash -c '${setup_apt_repo_file}'",
unless => "bash -c '${setup_apt_repo_file} --verify'",
}
}

View File

@@ -0,0 +1,53 @@
class zulip::camo (String $listen_address = '0.0.0.0') {
$version = $zulip::common::versions['go-camo']['version']
$goversion = $zulip::common::versions['go-camo']['goversion']
$dir = "/srv/zulip-go-camo-${version}"
$bin = "${dir}/bin/go-camo"
zulip::external_dep { 'go-camo':
version => $version,
url => "https://github.com/cactus/go-camo/releases/download/v${version}/go-camo-${version}.go${goversion}.linux-${zulip::common::goarch}.tar.gz",
tarball_prefix => "go-camo-${version}",
bin => [$bin],
cleanup_after => [Service[supervisor]],
}
# We would like to not waste resources by going through Smokescreen,
# as go-camo already prohibits private-IP access; but a
# non-Smokescreen exit proxy may be required to access the public
# Internet. The `enable_for_camo` flag, if it exists, can override
# our guess, in either direction.
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
$proxy_port = zulipconf('http_proxy', 'port', '4750')
$proxy_is_smokescreen = ($proxy_host in ['localhost', '127.0.0.1', '::1']) and ($proxy_port == '4750')
$camo_use_proxy = zulipconf('http_proxy', 'enable_for_camo', !$proxy_is_smokescreen)
if $camo_use_proxy {
if $proxy_is_smokescreen {
include zulip::smokescreen
}
if $proxy_host != '' and $proxy_port != '' {
$proxy = "http://${proxy_host}:${proxy_port}"
} else {
$proxy = ''
}
} else {
$proxy = ''
}
$zulip_version = $facts['zulip_version']
$external_uri = pick(get_django_setting_slow('ROOT_DOMAIN_URI'), 'https://zulip.com')
file { "${zulip::common::supervisor_conf_dir}/go-camo.conf":
ensure => file,
require => [
Package[supervisor],
File[$bin],
File['/usr/local/bin/secret-env-wrapper'],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/go-camo.conf.erb'),
notify => Service[supervisor],
}
}

View File

@@ -0,0 +1,12 @@
class zulip::certbot {
package { 'certbot':
ensure => installed,
}
file { ['/etc/letsencrypt/renewal-hooks', '/etc/letsencrypt/renewal-hooks/deploy']:
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
require => Package[certbot],
}
}

View File

@@ -0,0 +1,224 @@
class zulip::common {
# Common parameters
case $facts['os']['family'] {
'Debian': {
$nagios_plugins = 'monitoring-plugins-basic'
$nagios_plugins_dir = '/usr/lib/nagios/plugins'
$nginx = 'nginx-full'
$supervisor_system_conf_dir = '/etc/supervisor/conf.d'
$supervisor_conf_file = '/etc/supervisor/supervisord.conf'
$supervisor_service = 'supervisor'
$supervisor_start = '/usr/sbin/service supervisor start'
$supervisor_reload = @(EOT)
# The init script's timeout waiting for supervisor is shorter
# than supervisor's timeout waiting for its programs, so we need
# to ask supervisor to stop its programs first.
supervisorctl stop all &&
service supervisor restart &&
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=877086
# "restart" is actually "stop" under sysvinit
{ service supervisor start || true; } &&
service supervisor status
| EOT
$supervisor_status = '/usr/sbin/service supervisor status'
}
'RedHat': {
$nagios_plugins = 'nagios-plugins'
$nagios_plugins_dir = '/usr/lib64/nagios/plugins'
$nginx = 'nginx'
$supervisor_system_conf_dir = '/etc/supervisord.d/conf.d'
$supervisor_conf_file = '/etc/supervisord.conf'
$supervisor_service = 'supervisord'
$supervisor_start = 'systemctl start supervisord'
$supervisor_reload = 'systemctl reload supervisord'
$supervisor_status = 'systemctl status supervisord'
}
default: {
fail('osfamily not supported')
}
}
$supervisor_conf_dir = "${supervisor_system_conf_dir}/zulip"
if $facts['container_memory_limit_mb'] {
$total_memory_mb = Integer($facts['container_memory_limit_mb'])
} else {
$total_memory_mb = Integer($facts['memory']['system']['total_bytes'] / 1024 / 1024)
}
$goarch = $facts['os']['architecture'] ? {
'amd64' => 'amd64',
'aarch64' => 'arm64',
}
$versions = {
# https://github.com/cactus/go-camo/releases
'go-camo' => {
'version' => '2.7.3',
'goversion' => '1260',
'sha256' => {
'amd64' => 'd1071300b6692d194a439cb4e380ff95e1f4d894e2318bf47aaec077722370b7',
'aarch64' => '22afef25d43cb0cb44dac100ec5602846bd66de0d9d3a3c6fc2c98eae82e6639',
},
},
# https://go.dev/dl/
'golang' => {
'version' => '1.26.0',
'sha256' => {
'amd64' => 'aac1b08a0fb0c4e0a7c1555beb7b59180b05dfc5a3d62e40e9de90cd42f88235',
'aarch64' => 'bd03b743eb6eb4193ea3c3fd3956546bf0e3ca5b7076c8226334afe6b75704cd',
},
},
# https://github.com/stripe/smokescreen/tags
'smokescreen-src' => {
'version' => '464b1115f802cbd91ffee555a41e71546646d396',
# Source code, so arch-invariant sha256
'sha256' => '800d455bbdd23df0587b72e05e9bb51ce80d0f5816bd1b7070c2c26f4340cfca',
},
# https://github.com/tus/tusd/releases
# Keep in sync with tools/setup/install-tusd
'tusd' => {
'version' => '2.9.1',
'sha256' => {
'amd64' => '140066be543e40493abd1fda1d1e33ab7fa0e8b9a61d247130f6777e64bf28f6',
'aarch64' => 'c9c46eba6e46b8062f863af6a5423f6f72b5157939d967087cd9b6d7c6bd19cf',
},
},
# https://github.com/wal-g/wal-g/releases
'wal-g' => {
'version' => '3.0.8',
'sha256' => {
'amd64' => 'b0df1b484035eb5f131db7bbd303d1a460391848fdcce34ba1e0a564cca493e9', # wal-g-pg-22.04-amd64.tar.gz
'aarch64' => '048b5033c34c1487dc1fccc0572db030e323230984ccc093e80e4e4081c797d1', # wal-g-pg-22.04-aarch64.tar.gz
},
},
### kandra packages
# https://docs.aws.amazon.com/rolesanywhere/latest/userguide/credential-helper.html
'aws_signing_helper' => {
'version' => '1.7.3',
'sha256' => {
'amd64' => 'ef609ae021e86a2778b63dc80f4280033fcb1450bddb8b234b4ccd30f917ed21',
'aarch64' => '7aaf8b3a4ceac464931dec27bcfd58e4facc93fb48402381e3f96bb45de5a356',
},
},
# https://release-registry.services.sentry.io/apps/sentry-cli/latest
'sentry-cli' => {
'version' => '2.58.4',
'sha256' => {
'amd64' => 'a4932b4315b192b3d037678a16eb2a5a8731609f671fc4008e643b85c3c74cb6',
'aarch64' => '672cb986b0c5d84ef724f39b3aa189be802bceb8bc7dc8c5776a0ca90fcf41bd',
},
},
# https://grafana.com/grafana/download?edition=oss
'grafana' => {
'version' => '12.3.3',
'sha256' => {
'amd64' => 'd69b850d02903bcfe27289661c5b0b1b9a67d0bf0c42d344d55d0e63e62a7bda',
'aarch64' => 'a2a50f6a63c89c59914b604482500de2648d12ee266d8eac8886815710482614',
},
},
# https://github.com/fstab/grok_exporter/tags
'grok_exporter' => {
'version' => '1.0.0.RC5',
'sha256' => {
'amd64' => 'b8771a6d7ca8447c222548d6cb8b2f8ee058b55bfd1801c2f6eb739534df5ded',
# No aarch64 builds
},
},
# https://prometheus.io/download/#node_exporter
'node_exporter' => {
'version' => '1.10.2',
'sha256' => {
'amd64' => 'c46e5b6f53948477ff3a19d97c58307394a29fe64a01905646f026ddc32cb65b',
'aarch64' => 'de69ec8341c8068b7c8e4cfe3eb85065d24d984a3b33007f575d307d13eb89a6',
},
},
# https://github.com/prometheus-community/postgres_exporter/tags
'postgres_exporter' => {
'version' => '0.19.0',
'sha256' => {
'amd64' => '11033f9016d0c5a5b91742d17aa37490b170f055ec1bb0d7d69509d223952ed0',
'aarch64' => '8a2f55b1a00694758ea4bfd96a8bb814c1b279cf9d233a24fecd28a9e87c6280',
},
},
# https://github.com/prometheus-community/postgres_exporter/pull/843
'postgres_exporter-src' => {
'version' => '86a2b77aa522f57a136d04ffa33f0e46713d1925',
'sha256' => '03b3a4e794b6c01e911c3f7f4d77a2889d6946e2245f8d12d14844c29554a884',
},
# https://github.com/ncabatoff/process-exporter/releases
'process_exporter' => {
'version' => '0.8.7',
'sha256' => {
'amd64' => '6d274cca5e94c6a25e55ec05762a472561859ce0a05b984aaedb67dd857ceee2',
'aarch64' => '4a2502f290323e57eeeb070fc10e64047ad0cd838ae5a1b347868f75667b5ab0',
}
},
# https://prometheus.io/download/#prometheus
'prometheus' => {
'version' => '3.9.1',
'sha256' => {
'amd64' => '86a6999dd6aacbd994acde93c77cfa314d4be1c8e7b7c58f444355c77b32c584',
'aarch64' => '4e7f291e527d8aca912a995c183128388c9e048065aff84f74f5a55c9bef3793',
},
},
# https://github.com/prometheus/pushgateway/releases
'pushgateway' => {
'version' => '1.11.2',
'sha256' => {
'amd64' => '2ec72315e150dda071fdeef09360780a386a67e5207ebaa53bb18f2f1a3b89cf',
'aarch64' => 'b3fb835dbb0a29b1d6f9cd7ae3568a5615e59b96f8787965248cea67163d4db1',
},
},
# https://github.com/oliver006/redis_exporter/releases
'redis_exporter' => {
'version' => '1.81.0',
'sha256' => {
'amd64' => 'd2f4740fa81e1a274ca99677783f3fe1544f6022fed997d4cd632d01a8eff1bb',
'aarch64' => '038d0d2d8f044075cc97acd323c16a8c3628f08ef7d186601fedaa38b71aad8f',
},
},
# https://github.com/alexmv/teleport-sd/releases
'teleport-sd' => {
'version' => '0.1.0',
'sha256' => {
'amd64' => '2128d78dd602c6a24c13fbd759bf19d0c3cebaa732ad0824019ced53163cf455',
'aarch64' => 'df93ba9510cb8898ba0f15b293d7c6877173a8d06b1f284dcc42ffc7ff32978e',
},
},
# https://github.com/timonwong/uwsgi_exporter/releases
'uwsgi_exporter' => {
'version' => '1.3.0',
'sha256' => {
'amd64' => 'f83411b508676237bbd1b791c1bdc043a68bf914c7e48e005e2e295255f9245f',
'aarch64' => 'ab7c9298d2fe5c5f58e3fe7c905929e93979d2b3b11c75eb8ba6ccc7a547238c',
},
},
# https://vector.dev/download/
'vector' => {
'version' => '0.53.0',
'sha256' => {
'amd64' => '5460f93310eb59cc9e7ee7717a179f1bf2ff8fbc046b7e3eb625159a5e8714bb',
'aarch64' => '2722dec4f5c358793caeb86b4f4febd047b37a2d688af181e8264a764ecd28e5',
},
},
}
}

View File

@@ -0,0 +1,50 @@
# @summary Install a cron file into /etc/cron.d
#
define zulip::cron(
String $minute,
String $hour = '*',
String $dow = '*',
String $user = 'zulip',
Optional[String] $command = undef,
Optional[String] $manage = undef,
Boolean $use_proxy = true,
) {
if $use_proxy {
$proxy_host = zulipconf('http_proxy', 'host', 'localhost')
$proxy_port = zulipconf('http_proxy', 'port', '4750')
if $proxy_host != '' and $proxy_port != '' {
$proxy = "http://${proxy_host}:${proxy_port}"
} else {
$proxy = ''
}
} else {
$proxy = ''
}
$dsn = zulipconf('sentry', 'project_dsn', '')
if $dsn != '' {
include zulip::sentry_cli
$environment = zulipconf('machine', 'deploy_type', 'development')
$sentry = "sentry-cli monitors run -e ${environment} --schedule '${minute} ${hour} * * ${dow}' ${title} -- "
$cron_require = [File['/usr/local/bin/sentry-cli']]
} else {
$sentry = ''
$cron_require = []
}
if $command != undef {
$run = "${sentry}${command}"
} elsif $manage != undef {
$run = "cd /home/zulip/deployments/current/ && ${sentry}./manage.py ${manage} >/dev/null"
} else {
$underscores = regsubst($title, '-', '_', 'G')
$run = "cd /home/zulip/deployments/current/ && ${sentry}./manage.py ${underscores} >/dev/null"
}
file { "/etc/cron.d/${title}":
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/cron.template.erb'),
require => $cron_require,
}
}

View File

@@ -0,0 +1,4 @@
# @summary Temporary shim for docker all-in-one profile
class zulip::dockervoyager {
include zulip::profile::docker
}

View File

@@ -0,0 +1,69 @@
define zulip::external_dep(
String $version,
String $url,
String $tarball_prefix = '',
String $sha256 = '',
String $mode = '0755',
Array[String] $bin = [],
Array[Type[Resource]] $cleanup_after = [],
) {
$arch = $facts['os']['architecture']
if $sha256 == '' {
if $zulip::common::versions[$title]['sha256'] =~ Hash {
$sha256_filled = $zulip::common::versions[$title]['sha256'][$arch]
if $sha256_filled == undef {
err("No sha256 found for ${title} for architecture ${arch}")
fail()
}
} else {
# For things like source code which are arch-invariant
$sha256_filled = $zulip::common::versions[$title]['sha256']
}
} else {
$sha256_filled = $sha256
}
$path = "/srv/zulip-${title}-${version}"
if $tarball_prefix == '' {
zulip::sha256_file_to { $title:
url => $url,
sha256 => $sha256_filled,
install_to => $path,
notify => Exec["Cleanup ${title}"],
}
file { $path:
ensure => file,
require => Zulip::Sha256_File_To[$title],
before => Exec["Cleanup ${title}"],
mode => $mode,
}
} else {
zulip::sha256_tarball_to { $title:
url => $url,
sha256 => $sha256_filled,
install_from => $tarball_prefix,
install_to => $path,
notify => Exec["Cleanup ${title}"],
}
file { $path:
ensure => present,
require => Zulip::Sha256_Tarball_To[$title],
before => Exec["Cleanup ${title}"],
}
file { $bin:
ensure => file,
require => [File[$path], Zulip::Sha256_Tarball_To[$title]],
before => Exec["Cleanup ${title}"],
mode => $mode,
}
}
exec { "Cleanup ${title}":
refreshonly => true,
provider => shell,
onlyif => "ls -d /srv/zulip-${title}-* | grep -xv '${path}'",
command => "ls -d /srv/zulip-${title}-* | grep -xv '${path}' | xargs rm -r",
require => $cleanup_after,
}
}

View File

@@ -0,0 +1,13 @@
# @summary go compiler and tools
#
class zulip::golang {
$version = $zulip::common::versions['golang']['version']
$dir = "/srv/zulip-golang-${version}"
$bin = "${dir}/bin/go"
zulip::external_dep { 'golang':
version => $version,
url => "https://go.dev/dl/go${version}.linux-${zulip::common::goarch}.tar.gz",
tarball_prefix => 'go',
}
}

View File

@@ -0,0 +1,16 @@
# @summary Install sentry-cli binary and pre/post deploy hooks
#
class zulip::hooks::base {
file { [
'/etc/zulip/hooks',
'/etc/zulip/hooks/common',
'/etc/zulip/hooks/pre-deploy.d',
'/etc/zulip/hooks/post-deploy.d',
]:
ensure => directory,
owner => 'zulip',
group => 'zulip',
mode => '0755',
tag => ['hooks'],
}
}

View File

@@ -0,0 +1,14 @@
# @summary Install a static hook file
#
define zulip::hooks::file() {
include zulip::hooks::base
file { "/etc/zulip/hooks/${title}":
ensure => file,
mode => '0755',
owner => 'zulip',
group => 'zulip',
source => "puppet:///modules/zulip/hooks/${title}",
tag => ['hooks'],
}
}

View File

@@ -0,0 +1,9 @@
# @summary Push the merge_base to a git repo after deploy
#
class zulip::hooks::push_git_ref {
include zulip::hooks::base
zulip::hooks::file { [
'post-deploy.d/push_git_ref.hook',
]: }
}

View File

@@ -0,0 +1,9 @@
# @summary Send zuip update announcements after deploy
#
class zulip::hooks::send_zulip_update_announcements {
include zulip::hooks::base
zulip::hooks::file { [
'post-deploy.d/send_zulip_update_announcements.hook',
]: }
}

View File

@@ -0,0 +1,12 @@
# @summary Install Sentry pre/post deploy hooks
#
class zulip::hooks::sentry {
include zulip::hooks::base
include zulip::sentry_cli
zulip::hooks::file { [
'common/sentry.sh',
'pre-deploy.d/sentry.hook',
'post-deploy.d/sentry.hook',
]: }
}

View File

@@ -0,0 +1,7 @@
# @summary zulip_notify common file
#
class zulip::hooks::zulip_common {
include zulip::hooks::base
zulip::hooks::file { 'common/zulip_notify.sh': }
}

View File

@@ -0,0 +1,11 @@
# @summary Install hook that notifies when a deploy starts/stops
#
class zulip::hooks::zulip_notify {
include zulip::hooks::base
include zulip::hooks::zulip_common
zulip::hooks::file { [
'pre-deploy.d/zulip_notify.hook',
'post-deploy.d/zulip_notify.hook',
]: }
}

View File

@@ -0,0 +1,34 @@
class zulip::local_mailserver {
include zulip::snakeoil
include zulip::certbot
if zulipconf('postfix', 'uninstall', true) {
package { 'postfix':
# TODO/compatibility: We can remove this when upgrading directly
# from 10.x is no longer possible. We do not use "purged" here,
# since that would remove config files, which users may have had
# installed.
ensure => absent,
before => Service[$zulip::common::supervisor_service],
}
}
$listen = zulipconf('email_gateway', 'listen', '')
file { "${zulip::common::supervisor_conf_dir}/email-mirror.conf":
ensure => file,
require => [
Package[supervisor],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/email-mirror.conf.template.erb'),
notify => Service[$zulip::common::supervisor_service],
}
file { '/etc/letsencrypt/renewal-hooks/deploy/055-email-server.sh':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/letsencrypt/055-email-server.sh',
}
}

View File

@@ -0,0 +1,16 @@
class zulip::localhost_camo {
class { 'zulip::camo':
listen_address => '127.0.0.1',
}
# Install nginx configuration to run camo locally
file { '/etc/nginx/zulip-include/app.d/camo.conf':
ensure => file,
require => Package['nginx-full'],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
source => 'puppet:///modules/zulip/nginx/zulip-include-app.d/camo.conf',
}
}

View File

@@ -0,0 +1,11 @@
class zulip::localhost_sso {
file { '/etc/nginx/zulip-include/app.d/external-sso.conf':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
source => 'puppet:///modules/zulip/nginx/zulip-include-app.d/external-sso.conf',
}
}

View File

@@ -0,0 +1,14 @@
# @summary Installs a subdirectory from puppet/zulip/files/nagios_plugins/
define zulip::nagios_plugins () {
include zulip::common
file { "${zulip::common::nagios_plugins_dir}/${name}":
require => Package[$zulip::common::nagios_plugins],
recurse => true,
purge => true,
owner => 'root',
group => 'root',
mode => '0755',
source => "puppet:///modules/zulip/nagios_plugins/${name}",
}
}

View File

@@ -0,0 +1,120 @@
class zulip::nginx {
include zulip::certbot
package { $zulip::common::nginx: ensure => installed }
package { 'ca-certificates': ensure => latest }
if $facts['os']['family'] == 'RedHat' {
file { '/etc/nginx/sites-available':
ensure => directory,
owner => 'root',
group => 'root',
}
file { '/etc/nginx/sites-enabled':
ensure => directory,
owner => 'root',
group => 'root',
}
}
file { '/etc/nginx/zulip-include/':
require => Package[$zulip::common::nginx],
recurse => true,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/nginx/zulip-include-common/',
notify => Service['nginx'],
}
file { '/etc/nginx/dhparam.pem':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
source => 'puppet:///modules/zulip/nginx/dhparam.pem',
}
if $facts['os']['family'] == 'Debian' {
$ca_crt = '/etc/ssl/certs/ca-certificates.crt'
} else {
$ca_crt = '/etc/pki/tls/certs/ca-bundle.crt'
}
$worker_processes = zulipconf('application_server', 'nginx_worker_processes', 'auto')
$worker_connections = zulipconf('application_server', 'nginx_worker_connections', 10000)
file { '/etc/nginx/nginx.conf':
ensure => file,
require => Package[$zulip::common::nginx, 'ca-certificates'],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
content => template('zulip/nginx.conf.template.erb'),
}
$loadbalancers = split(zulipconf('loadbalancer', 'ips', ''), ',')
$raw_true_client_header = zulipconf('loadbalancer', 'true_client_header', '')
$true_client_header = regsubst(downcase($raw_true_client_header), '-', '_', 'G')
$true_client_header_from = split(zulipconf('loadbalancer', 'true_client_header_from', ''), ',')
$lb_rejects_http_requests = zulipconf('loadbalancer', 'rejects_http_requests', false)
file { '/etc/nginx/zulip-include/trusted-ip':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
content => template('zulip/nginx/trusted-ip.template.erb'),
}
file { '/etc/nginx/zulip-include/trusted-proto':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
content => template('zulip/nginx/trusted-proto.template.erb'),
}
file { '/etc/nginx/uwsgi_params':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
notify => Service['nginx'],
source => 'puppet:///modules/zulip/nginx/uwsgi_params',
}
file { '/etc/nginx/sites-enabled/default':
ensure => absent,
notify => Service['nginx'],
}
file { '/var/log/nginx':
ensure => directory,
owner => 'zulip',
group => 'adm',
mode => '0750',
}
$access_log_retention_days = zulipconf('application_server','access_log_retention_days', 14)
file { '/etc/logrotate.d/nginx':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/logrotate/nginx.template.erb'),
}
file { ['/var/lib/zulip', '/var/lib/zulip/certbot-webroot']:
ensure => directory,
owner => 'zulip',
group => 'adm',
mode => '0770',
}
service { 'nginx':
ensure => running,
}
}

View File

@@ -0,0 +1,47 @@
# @summary Use wal-g to take daily backups of PostgreSQL
#
class zulip::postgresql_backups {
include zulip::postgresql_common
include zulip::wal_g
file { '/var/log/pg_backup_and_purge.log':
ensure => file,
owner => 'postgres',
group => 'postgres',
mode => '0644',
}
file { '/usr/local/bin/pg_backup_and_purge':
ensure => file,
owner => 'root',
group => 'postgres',
mode => '0754',
source => 'puppet:///modules/zulip/postgresql/pg_backup_and_purge',
require => [
File['/usr/local/bin/env-wal-g'],
Package[
$zulip::postgresql_common::postgresql,
],
],
}
zulip::cron { 'pg_backup_and_purge':
hour => '2',
minute => '0',
command => '/usr/local/bin/pg_backup_and_purge >/var/log/pg_backup_and_purge.log 2>&1',
user => 'postgres',
require => [
File['/var/log/pg_backup_and_purge.log'],
File['/usr/local/bin/pg_backup_and_purge'],
],
}
$postgresql_backup_directory = zulipconf('postgresql', 'backups_directory', '')
if $postgresql_backup_directory != '' {
file { $postgresql_backup_directory:
ensure => directory,
owner => 'postgres',
group => 'postgres',
mode => '0600',
}
}
}

View File

@@ -0,0 +1,80 @@
# Minimal shared configuration needed to run a Zulip PostgreSQL database.
class zulip::postgresql_base {
include zulip::postgresql_common
include zulip::process_fts_updates
case $facts['os']['family'] {
'Debian': {
$postgresql = "postgresql-${zulip::postgresql_common::version}"
$postgresql_sharedir = "/usr/share/postgresql/${zulip::postgresql_common::version}"
$postgresql_confdirs = [
"/etc/postgresql/${zulip::postgresql_common::version}",
"/etc/postgresql/${zulip::postgresql_common::version}/main",
]
$postgresql_confdir = $postgresql_confdirs[-1]
$postgresql_datadir = "/var/lib/postgresql/${zulip::postgresql_common::version}/main"
$tsearch_datadir = "${postgresql_sharedir}/tsearch_data"
$pgroonga_setup_sql_path = "${postgresql_sharedir}/pgroonga_setup.sql"
$setup_system_deps = 'setup_apt_repo'
}
'RedHat': {
$postgresql = "postgresql${zulip::postgresql_common::version}"
$postgresql_sharedir = "/usr/pgsql-${zulip::postgresql_common::version}/share"
$postgresql_confdirs = [
"/var/lib/pgsql/${zulip::postgresql_common::version}",
"/var/lib/pgsql/${zulip::postgresql_common::version}/data",
]
$postgresql_confdir = $postgresql_confdirs[-1]
$postgresql_datadir = "/var/lib/pgsql/${zulip::postgresql_common::version}/data"
$tsearch_datadir = "${postgresql_sharedir}/tsearch_data/"
$pgroonga_setup_sql_path = "${postgresql_sharedir}/pgroonga_setup.sql"
$setup_system_deps = 'setup_yum_repo'
}
default: {
fail('osfamily not supported')
}
}
file { "${tsearch_datadir}/zulip_english.stop":
ensure => file,
require => Package[$postgresql],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/postgresql/zulip_english.stop',
tag => ['postgresql_upgrade'],
}
zulip::nagios_plugins { 'zulip_postgresql': }
$pgroonga = zulipconf('machine', 'pgroonga', false)
if $pgroonga {
# Needed for optional our full text search system
package{"${postgresql}-pgdg-pgroonga":
ensure => latest,
require => [
Package[$postgresql],
Exec[$setup_system_deps]
],
tag => ['postgresql_upgrade'],
}
exec { 'pgroonga-config':
require => Package["${postgresql}-pgdg-pgroonga"],
unless => @("EOT"/$),
test -f ${pgroonga_setup_sql_path}.applied &&
test "$(dpkg-query --show --showformat='\${Version}' "${postgresql}-pgdg-pgroonga")" \
= "$(cat ${pgroonga_setup_sql_path}.applied)"
| EOT
command => "${facts['zulip_scripts_path']}/setup/pgroonga-config ${postgresql_sharedir}",
}
}
$backups_s3_bucket = zulipsecret('secrets', 's3_backups_bucket', '')
$backups_directory = zulipconf('postgresql', 'backups_directory', '')
if $backups_s3_bucket != '' or $backups_directory != '' {
include zulip::postgresql_backups
} else {
file { '/etc/cron.d/pg_backup_and_purge':
ensure => absent,
}
}
}

View File

@@ -0,0 +1,12 @@
class zulip::postgresql_client {
$version = zulipconf('postgresql', 'version', undef)
if $version != undef {
package { "postgresql-client-${version}":
ensure => installed,
}
} else {
package { 'postgresql-client':
ensure => installed,
}
}
}

View File

@@ -0,0 +1,79 @@
class zulip::postgresql_common {
include zulip::snakeoil
$version = zulipconf('postgresql', 'version', undef)
case $facts['os']['family'] {
'Debian': {
$postgresql = "postgresql-${version}"
$postgresql_packages = [
# The database itself
$postgresql,
# tools for database monitoring; formerly ptop
'pgtop',
# our dictionary
'hunspell-en-us',
# PostgreSQL Nagios check plugin
'check-postgres',
]
$postgresql_user_reqs = [
Package[$postgresql],
Package['ssl-cert'],
]
}
'RedHat': {
$postgresql = "postgresql${version}"
$postgresql_packages = [
$postgresql,
"${postgresql}-server",
"${postgresql}-devel",
'pg_top',
'hunspell-en-US',
# exists on CentOS 6 and Fedora 29 but not CentOS 7
# see https://pkgs.org/download/check_postgres
# alternatively, download directly from:
# https://bucardo.org/check_postgres/
# 'check-postgres', # TODO
]
group { 'ssl-cert':
ensure => present,
}
# allows ssl-cert group to read /etc/pki/tls/private
file { '/etc/pki/tls/private':
ensure => directory,
mode => '0750',
owner => 'root',
group => 'ssl-cert',
}
$postgresql_user_reqs = [
Package[$postgresql],
Group['ssl-cert'],
]
}
default: {
fail('osfamily not supported')
}
}
zulip::safepackage { $postgresql_packages:
ensure => installed,
require => Exec['generate-default-snakeoil'],
tag => ['postgresql_upgrade'],
}
if $facts['os']['family'] == 'Debian' {
# The logrotate file only created in debian-based systems
exec { 'disable_logrotate':
# lint:ignore:140chars
command => '/usr/bin/dpkg-divert --rename --divert /etc/logrotate.d/postgresql-common.disabled --add /etc/logrotate.d/postgresql-common',
# lint:endignore
creates => '/etc/logrotate.d/postgresql-common.disabled',
}
}
# Use arcane puppet virtual resources to add postgres user to zulip group
@user { 'postgres':
groups => ['ssl-cert'],
membership => minimum,
require => $postgresql_user_reqs,
}
User <| title == postgres |> { groups +> 'zulip' }
}

View File

@@ -0,0 +1,38 @@
class zulip::process_fts_updates {
include zulip::supervisor
case $facts['os']['family'] {
'Debian': {
$fts_updates_packages = [
# Needed to run process_fts_updates
'python3-psycopg2', # TODO: use a virtualenv instead
]
zulip::safepackage { $fts_updates_packages: ensure => installed }
}
'RedHat': {
exec {'pip_process_fts_updates':
command => 'python3 -m pip install psycopg2',
}
}
default: {
fail('osfamily not supported')
}
}
file { '/usr/local/bin/process_fts_updates':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/postgresql/process_fts_updates',
}
file { "${zulip::common::supervisor_conf_dir}/zulip_db.conf":
ensure => file,
require => [Package[supervisor], Package['python3-psycopg2']],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/supervisor/conf.d/zulip_db.conf',
notify => Service[$zulip::common::supervisor_service],
}
}

View File

@@ -0,0 +1,65 @@
# Default configuration for a Zulip app frontend
class zulip::profile::app_frontend {
include zulip::profile::base
include zulip::app_frontend_base
include zulip::app_frontend_once
$nginx_http_only = zulipconf('application_server', 'http_only', false)
if $nginx_http_only {
$nginx_listen_port = zulipconf('application_server', 'nginx_listen_port', 80)
} else {
$nginx_listen_port = zulipconf('application_server', 'nginx_listen_port', 443)
}
$nginx_server_name = zulipconf('application_server', 'nginx_server_name', '')
$ssl_dir = $facts['os']['family'] ? {
'Debian' => '/etc/ssl',
'RedHat' => '/etc/pki/tls',
}
file { '/etc/nginx/sites-available/zulip-enterprise':
ensure => file,
require => Package[$zulip::common::nginx],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/nginx/zulip-enterprise.template.erb'),
notify => Service['nginx'],
}
file { '/etc/nginx/sites-enabled/zulip-enterprise':
ensure => link,
require => Package[$zulip::common::nginx],
target => '/etc/nginx/sites-available/zulip-enterprise',
notify => Service['nginx'],
}
# Reload nginx after deploying a new cert.
file { '/etc/letsencrypt/renewal-hooks/deploy/001-nginx.sh':
# This was renumbered
ensure => absent,
}
file { '/etc/letsencrypt/renewal-hooks/deploy/020-symlink.sh':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/letsencrypt/020-symlink.sh',
require => [
Package[certbot],
File['/etc/letsencrypt/renewal-hooks/deploy'],
]
}
file { '/etc/letsencrypt/renewal-hooks/deploy/050-nginx.sh':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/letsencrypt/050-nginx.sh',
}
# Restart the server regularly to avoid potential memory leak problems.
zulip::cron { 'restart-zulip':
hour => '6',
minute => '0',
dow => '0',
command => '/home/zulip/deployments/current/scripts/restart-server --fill-cache --skip-client-reloads',
}
}

View File

@@ -0,0 +1,142 @@
# @summary Included only by classes that can be deployed.
#
# This class should only be included by classes that are intended to
# be able to be deployed on their own host.
class zulip::profile::base {
include zulip::timesync
include zulip::common
case $facts['os']['family'] {
'Debian': {
include zulip::apt_repository
}
'RedHat': {
include zulip::yum_repository
}
default: {
fail('osfamily not supported')
}
}
case $facts['os']['family'] {
'Debian': {
$base_packages = [
# Basics
'python3',
'python3-yaml',
'puppet',
'git',
'curl',
'jq',
'procps',
# Used to read /etc/zulip/zulip.conf for `zulipconf` Puppet function
'crudini',
# Used for tools like sponge
'moreutils',
# Nagios monitoring plugins
$zulip::common::nagios_plugins,
# Required for using HTTPS in apt repositories.
'apt-transport-https',
# Needed for the cron jobs installed by Puppet
'cron',
# Applies log rotation config installed by Puppet
'logrotate',
]
}
'RedHat': {
$base_packages = [
'python3',
'python3-pyyaml',
'puppet',
'git',
'curl',
'jq',
'crudini',
'moreutils',
'nmap-ncat',
'nagios-plugins', # there is no dummy package on CentOS 7
'cronie',
]
}
default: {
fail('osfamily not supported')
}
}
package { $base_packages: ensure => installed }
group { 'zulip':
ensure => present,
}
user { 'zulip':
ensure => present,
require => Group['zulip'],
gid => 'zulip',
shell => '/bin/bash',
home => '/home/zulip',
managehome => true,
}
file { '/etc/zulip':
ensure => directory,
mode => '0755',
owner => 'zulip',
group => 'zulip',
links => follow,
}
file { ['/etc/zulip/zulip.conf', '/etc/zulip/settings.py']:
ensure => present,
require => File['/etc/zulip'],
mode => '0644',
owner => 'zulip',
group => 'zulip',
}
file { '/etc/zulip/zulip-secrets.conf':
ensure => present,
require => File['/etc/zulip'],
mode => '0640',
owner => 'zulip',
group => 'zulip',
}
file { '/etc/security/limits.d/zulip.conf':
ensure => file,
mode => '0640',
owner => 'root',
group => 'root',
source => 'puppet:///modules/zulip/limits.d/zulip.conf',
}
file { '/etc/systemd/system.conf.d/':
ensure => directory,
mode => '0755',
owner => 'root',
group => 'root',
}
file { '/etc/systemd/system.conf.d/limits.conf':
ensure => file,
mode => '0644',
owner => 'root',
group => 'root',
source => 'puppet:///modules/zulip/systemd/system.conf.d/limits.conf',
}
service { 'puppet':
ensure => stopped,
enable => false,
require => Package['puppet'],
}
# This directory is written to by cron jobs for reading by Nagios
file { '/var/lib/nagios_state/':
ensure => directory,
group => 'zulip',
mode => '0775',
}
file { '/var/log/zulip':
ensure => directory,
owner => 'zulip',
group => 'zulip',
mode => '0750',
}
zulip::nagios_plugins { 'zulip_base': }
}

View File

@@ -0,0 +1,29 @@
# This class includes all the modules you need to install/run a Zulip installation
# in a single container (without the database, memcached, Redis services).
# The database, memcached, Redis services need to be run in separate containers.
# Through this split of services, it is easier to scale the services to the needs.
class zulip::profile::docker {
include zulip::profile::base
include zulip::profile::app_frontend
include zulip::localhost_camo
include zulip::local_mailserver
include zulip::supervisor
include zulip::process_fts_updates
file { "${zulip::common::supervisor_conf_dir}/cron.conf":
ensure => file,
require => Package[supervisor],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/supervisor/conf.d/cron.conf',
}
file { "${zulip::common::supervisor_conf_dir}/nginx.conf":
ensure => file,
require => Package[supervisor],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/supervisor/conf.d/nginx.conf',
}
}

View File

@@ -0,0 +1,97 @@
class zulip::profile::memcached {
include zulip::profile::base
include zulip::sasl_modules
case $facts['os']['family'] {
'Debian': {
$memcached_packages = [ 'memcached', 'sasl2-bin' ]
$memcached_user = 'memcache'
}
'RedHat': {
$memcached_packages = [ 'memcached', 'cyrus-sasl' ]
$memcached_user = 'memcached'
}
default: {
fail('osfamily not supported')
}
}
package { $memcached_packages: ensure => installed }
$memcached_max_item_size = zulipconf('memcached', 'max_item_size', '1m')
$memcached_size_reporting = zulipconf('memcached', 'size_reporting', false)
$memcached_memory = zulipconf('memcached', 'memory', $zulip::common::total_memory_mb / 8)
file { '/etc/sasl2':
ensure => directory,
}
file { '/etc/sasl2/memcached-zulip-password':
# We cache the password in this file so we can check whether it
# changed and avoid running saslpasswd2 if it didn't.
require => File['/etc/sasl2'],
owner => 'root',
group => 'root',
mode => '0600',
content => zulipsecret('secrets', 'memcached_password', ''),
notify => Exec[generate_memcached_sasldb2],
}
file { '/var/lib/zulip/memcached-sasldb2.stamp':
owner => 'root',
group => 'root',
mode => '0644',
content => '1',
notify => Exec[generate_memcached_sasldb2],
}
exec { 'generate_memcached_sasldb2':
require => [
Package[$memcached_packages],
Package[$zulip::sasl_modules::sasl_module_packages],
],
refreshonly => true,
# Use localhost for the currently recommended MEMCACHED_USERNAME =
# "zulip@localhost" and the hostname for compatibility with
# MEMCACHED_USERNAME = "zulip".
command => "bash -euc '
rm -f /etc/sasl2/memcached-sasldb2
saslpasswd2 -p -f /etc/sasl2/memcached-sasldb2 \
-a memcached -u localhost zulip < /etc/sasl2/memcached-zulip-password
saslpasswd2 -p -f /etc/sasl2/memcached-sasldb2 \
-a memcached -u \"\$HOSTNAME\" zulip < /etc/sasl2/memcached-zulip-password
'",
}
file { '/etc/sasl2/memcached-sasldb2':
require => Exec[generate_memcached_sasldb2],
owner => $memcached_user,
group => $memcached_user,
mode => '0600',
}
file { '/etc/sasl2/memcached.conf':
require => File['/etc/sasl2'],
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/sasl2/memcached.conf',
notify => Service[memcached],
}
file { '/etc/memcached.conf':
ensure => file,
require => [
Package[$memcached_packages],
Package[$zulip::sasl_modules::sasl_module_packages]
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/memcached.conf.template.erb'),
}
file { '/run/memcached':
ensure => directory,
owner => 'memcache',
group => 'memcache',
mode => '0755',
require => Package[$memcached_packages],
}
service { 'memcached':
ensure => running,
subscribe => File['/etc/memcached.conf'],
require => File['/run/memcached'],
}
}

View File

@@ -0,0 +1,116 @@
# @summary Extends postgresql_base by tuning the configuration.
class zulip::profile::postgresql(Boolean $start = true) {
include zulip::profile::base
include zulip::postgresql_base
include zulip::systemd_daemon_reload
$version = $zulip::postgresql_common::version
if defined(Class['zulip::app_frontend_base']) {
$total_postgres_memory_mb = zulipconf('postgresql', 'memory', $zulip::common::total_memory_mb / 2)
} else {
$total_postgres_memory_mb = zulipconf('postgresql', 'memory', $zulip::common::total_memory_mb)
}
$work_mem = zulipconf('postgresql', 'work_mem', sprintf('%dMB', $total_postgres_memory_mb / 256))
$shared_buffers = zulipconf('postgresql', 'shared_buffers', sprintf('%dMB', $total_postgres_memory_mb / 4))
$effective_cache_size = zulipconf('postgresql', 'effective_cache_size', sprintf('%dMB', $total_postgres_memory_mb * 3 / 4))
$maintenance_work_mem = zulipconf('postgresql', 'maintenance_work_mem', sprintf('%dMB', min(2048, $total_postgres_memory_mb / 8)))
$max_worker_processes = zulipconf('postgresql', 'max_worker_processes', undef)
$max_parallel_workers_per_gather = zulipconf('postgresql', 'max_parallel_workers_per_gather', undef)
$max_parallel_workers = zulipconf('postgresql', 'max_parallel_workers', undef)
$max_parallel_maintenance_workers = zulipconf('postgresql', 'max_parallel_maintenance_workers', undef)
$wal_buffers = zulipconf('postgresql', 'wal_buffers', undef)
$min_wal_size = zulipconf('postgresql', 'min_wal_size', undef)
$max_wal_size = zulipconf('postgresql', 'max_wal_size', undef)
$random_page_cost = zulipconf('postgresql', 'random_page_cost', '1.1')
$effective_io_concurrency = zulipconf('postgresql', 'effective_io_concurrency', undef)
$listen_addresses = zulipconf('postgresql', 'listen_addresses', undef)
$s3_backups_bucket = zulipsecret('secrets', 's3_backups_bucket', '')
$replication_primary = zulipconf('postgresql', 'replication_primary', undef)
$replication_user = zulipconf('postgresql', 'replication_user', undef)
$replication_password = zulipsecret('secrets', 'postgresql_replication_password', '')
$ssl_cert_file = zulipconf('postgresql', 'ssl_cert_file', undef)
$ssl_key_file = zulipconf('postgresql', 'ssl_key_file', undef)
$ssl_ca_file = zulipconf('postgresql', 'ssl_ca_file', undef)
$ssl_mode = zulipconf('postgresql', 'ssl_mode', undef)
file { $zulip::postgresql_base::postgresql_confdirs:
ensure => directory,
owner => 'postgres',
group => 'postgres',
}
if $version in ['14'] {
$postgresql_conf_file = "${zulip::postgresql_base::postgresql_confdir}/postgresql.conf"
file { $postgresql_conf_file:
ensure => file,
require => Package[$zulip::postgresql_base::postgresql],
owner => 'postgres',
group => 'postgres',
mode => '0644',
content => template("zulip/postgresql/${version}/postgresql.conf.template.erb"),
}
} elsif $version in ['15', '16', '17', '18'] {
$postgresql_conf_file = "${zulip::postgresql_base::postgresql_confdir}/conf.d/zulip.conf"
file { $postgresql_conf_file:
ensure => file,
require => Package[$zulip::postgresql_base::postgresql],
owner => 'postgres',
group => 'postgres',
mode => '0644',
content => template('zulip/postgresql/zulip.conf.template.erb'),
}
} else {
fail("PostgreSQL ${version} not supported")
}
# PostgreSQL 18's io_method = io_uring requires a larger
# RLIMIT_MEMLOCK than systemd's default on Linux 6.14 and newer
if $facts['os']['family'] == 'Debian' {
file { '/etc/systemd/system/postgresql@.service.d':
ensure => directory,
owner => 'root',
group => 'root',
mode => '0755',
}
file { '/etc/systemd/system/postgresql@.service.d/zulip.conf':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/postgresql/postgresql@.service.d/zulip.conf',
notify => [Exec['reload systemd'], Service['postgresql']],
}
}
if $replication_primary != undef and $replication_user != undef {
# The presence of a standby.signal file triggers replication
file { "${zulip::postgresql_base::postgresql_datadir}/standby.signal":
ensure => file,
require => Package[$zulip::postgresql_base::postgresql],
before => Service['postgresql'],
owner => 'postgres',
group => 'postgres',
mode => '0644',
content => '',
}
}
$backups_s3_bucket = zulipsecret('secrets', 's3_backups_bucket', '')
$backups_directory = zulipconf('postgresql', 'backups_directory', '')
if $backups_s3_bucket != '' or $backups_directory != '' {
$require = [File['/usr/local/bin/env-wal-g'], Package[$zulip::postgresql_base::postgresql]]
} else {
$require = [Package[$zulip::postgresql_base::postgresql]]
}
service { 'postgresql':
ensure => $start,
require => $require + [Exec['reload systemd']],
subscribe => [ File[$postgresql_conf_file] ],
}
}

View File

@@ -0,0 +1,95 @@
class zulip::profile::rabbitmq {
include zulip::profile::base
$erlang = $facts['os']['family'] ? {
'Debian' => 'erlang-base',
'RedHat' => 'erlang',
}
$rabbitmq_packages = [
$erlang,
'rabbitmq-server',
]
group { 'rabbitmq':
ensure => present,
system => true,
}
user { 'rabbitmq':
ensure => present,
comment => 'RabbitMQ messaging server',
gid => 'rabbitmq',
home => '/var/lib/rabbitmq',
shell => '/usr/sbin/nologin',
system => true,
require => Group['rabbitmq'],
}
file { '/etc/rabbitmq':
ensure => directory,
owner => 'rabbitmq',
group => 'rabbitmq',
mode => '0755',
require => User['rabbitmq'],
before => Package['rabbitmq-server'],
}
file { '/etc/rabbitmq/rabbitmq.config':
ensure => file,
owner => 'rabbitmq',
group => 'rabbitmq',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq.config',
# This config file must be installed before the package, so that
# port 25672 is not even briefly open to the Internet world, which
# would be a security risk, due to insecure defaults in the
# RabbitMQ package.
before => Package['rabbitmq-server'],
notify => Service['rabbitmq-server'],
}
exec { 'warn-rabbitmq-nodename-change':
command => "${facts['zulip_scripts_path']}/lib/warn-rabbitmq-nodename-change",
onlyif => '[ -f /etc/rabbitmq/rabbitmq-env.conf ] && ! grep -xq NODENAME=zulip@localhost /etc/rabbitmq/rabbitmq-env.conf',
before => [
File['/etc/rabbitmq/rabbitmq-env.conf'],
Service['rabbitmq-server'],
],
logoutput => true,
loglevel => warning,
}
file { '/etc/rabbitmq/rabbitmq-env.conf':
ensure => file,
owner => 'rabbitmq',
group => 'rabbitmq',
mode => '0644',
source => 'puppet:///modules/zulip/rabbitmq/rabbitmq-env.conf',
before => Package['rabbitmq-server'],
notify => [
Service['rabbitmq-server'],
Exec['configure-rabbitmq'],
],
}
package { $rabbitmq_packages:
ensure => installed,
}
# epmd doesn't have an init script, so we just check if it is
# running, and if it isn't, start it. Even in case of a race, this
# won't leak epmd processes, because epmd checks if one is already
# running and exits if so.
exec { 'epmd':
command => 'epmd -daemon',
unless => 'which pgrep && pgrep -x epmd >/dev/null',
require => Package[$erlang],
path => '/usr/bin/:/bin/',
}
service { 'rabbitmq-server':
ensure => running,
require => [
Exec['epmd'],
Package['rabbitmq-server'],
],
}
exec { 'configure-rabbitmq':
command => "${facts['zulip_scripts_path']}/setup/configure-rabbitmq",
refreshonly => true,
require => Service['rabbitmq-server'],
}
}

View File

@@ -0,0 +1,71 @@
class zulip::profile::redis {
include zulip::profile::base
case $facts['os']['family'] {
'Debian': {
$redis = 'redis-server'
$redis_dir = '/etc/redis'
}
'RedHat': {
$redis = 'redis'
$redis_dir = '/etc'
}
default: {
fail('osfamily not supported')
}
}
$redis_packages = [ # The server itself
$redis,
]
package { $redis_packages: ensure => installed }
$file = "${redis_dir}/redis.conf"
$zulip_redisconf = "${redis_dir}/zulip-redis.conf"
$line = "include ${zulip_redisconf}"
exec { 'update-redis-conf':
unless => "/bin/grep -Fxqe '${line}' '${file}'",
path => '/bin',
command => "bash -c \"(/bin/echo; /bin/echo '# Include Zulip-specific configuration'; /bin/echo '${line}') >> '${file}'\"",
require => [
Package[$redis],
File[$zulip_redisconf],
],
}
$redis_password = zulipsecret('secrets', 'redis_password', '')
file { $zulip_redisconf:
ensure => file,
require => Package[$redis],
owner => 'redis',
group => 'redis',
mode => '0640',
content => template('zulip/zulip-redis.template.erb'),
}
# https://redis.io/docs/management/admin/#linux
zulip::sysctl { 'redis-server':
key => 'vm.overcommit_memory',
value => '1',
}
package { 'sysfsutils': }
file { '/etc/sysfs.d/40-disable-transpatent-hugepages.conf':
require => Package['sysfsutils'],
notify => Service['sysfsutils'],
content => 'kernel/mm/transparent_hugepage/enabled = never',
}
service { 'sysfsutils':
ensure => running,
require => Package['sysfsutils'],
}
service { $redis:
ensure => running,
require => [
Service['sysfsutils'],
Package['redis-server'],
],
subscribe => [
File[$zulip_redisconf],
Exec['update-redis-conf'],
],
}
}

View File

@@ -0,0 +1,6 @@
# @summary Outgoing HTTP CONNECT proxy for HTTP/HTTPS on port 4750.
#
class zulip::profile::smokescreen {
include zulip::profile::base
include zulip::smokescreen
}

View File

@@ -0,0 +1,21 @@
# @summary Complete Zulip server on one server
#
# This class includes all the modules you need to run an entire Zulip
# installation on a single server. If desired, you can split up the
# different `zulip::profile::*` components of a Zulip installation
# onto different servers:
#
# - zulip::profile::app_frontend
# - zulip::profile::memcached
# - zulip::profile::postgresql
# - zulip::profile::rabbitmq
# - zulip::profile::redis
# - zulip::profile::smokescreen
#
# See the corresponding configuration in /etc/zulip/settings.py for
# how to find the various services is also required to make this work.
class zulip::profile::standalone {
include zulip::profile::standalone_nodb
include zulip::profile::postgresql
}

View File

@@ -0,0 +1,16 @@
# @summary Complete Zulip server, except the database server.
#
# This includes all of the parts necessary to run an entire Zulip
# installation on a single server, except the database. It is assumed
# that the PostgreSQL database is either hosted on another server with
# the `zulip::profile::postgresql` class applied, or a cloud-managed
# database is used (e.g. AWS RDS).
#
# @see https://zulip.readthedocs.io/en/latest/production/postgresql.html
class zulip::profile::standalone_nodb {
include zulip::profile::app_frontend
include zulip::profile::memcached
include zulip::profile::rabbitmq
include zulip::profile::redis
include zulip::localhost_camo
}

View File

@@ -0,0 +1,5 @@
define zulip::safepackage ( $ensure = present ) {
if !defined(Package[$title]) {
package { $title: ensure => $ensure }
}
}

View File

@@ -0,0 +1,7 @@
class zulip::sasl_modules {
$sasl_module_packages = $facts['os']['family'] ? {
'Debian' => [ 'libsasl2-modules' ],
'RedHat' => [ 'cyrus-sasl-plain' ],
}
package { $sasl_module_packages: ensure => installed }
}

View File

@@ -0,0 +1,23 @@
# @summary Install sentry-cli binary
#
class zulip::sentry_cli {
$version = $zulip::common::versions['sentry-cli']['version']
$bin = "/srv/zulip-sentry-cli-${version}"
$arch = $facts['os']['architecture'] ? {
'amd64' => 'x86_64',
'aarch64' => 'aarch64',
}
zulip::external_dep { 'sentry-cli':
version => $version,
url => "https://downloads.sentry-cdn.com/sentry-cli/${version}/sentry-cli-Linux-${arch}",
}
file { '/usr/local/bin/sentry-cli':
ensure => link,
target => $bin,
require => File[$bin],
before => Exec['Cleanup sentry-cli'],
}
}

View File

@@ -0,0 +1,13 @@
# @summary Downloads, verifies hash, and copies the one file out.
#
define zulip::sha256_file_to(
String $sha256,
String $url,
String $install_to,
) {
exec { $url:
command => "${facts['zulip_scripts_path']}/setup/sha256-file-to ${sha256} ${url} ${install_to}",
creates => $install_to,
timeout => 600,
}
}

View File

@@ -0,0 +1,14 @@
# @summary Downloads, verifies hash, and copies files out.
#
define zulip::sha256_tarball_to(
String $sha256,
String $url,
String $install_from,
String $install_to,
) {
exec { $url:
command => "${facts['zulip_scripts_path']}/setup/sha256-tarball-to ${sha256} ${url} ${install_from} ${install_to}",
creates => $install_to,
timeout => 600,
}
}

View File

@@ -0,0 +1,65 @@
class zulip::smokescreen {
include zulip::supervisor
include zulip::golang
$version = $zulip::common::versions['smokescreen-src']['version']
$dir = "/srv/zulip-smokescreen-src-${version}"
$bin = "/usr/local/bin/smokescreen-${version}-go-${zulip::golang::version}"
zulip::external_dep { 'smokescreen-src':
version => $version,
url => "https://github.com/stripe/smokescreen/archive/${version}.tar.gz",
tarball_prefix => "smokescreen-${version}",
}
exec { 'compile smokescreen':
command => "${zulip::golang::bin} build -o ${bin}",
cwd => $dir,
# GOCACHE is required; nothing is written to GOPATH, but it is required to be set
environment => ['GOCACHE=/tmp/gocache', 'GOPATH=/root/go'],
creates => $bin,
require => [
Zulip::External_Dep['golang'],
Zulip::External_Dep['smokescreen-src'],
],
}
# This resource is created by the 'compile smokescreen' step.
file { $bin:
ensure => file,
require => Exec['compile smokescreen'],
}
exec { 'Cleanup smokescreen':
refreshonly => true,
provider => shell,
onlyif => "ls /usr/local/bin/smokescreen-* | grep -xv '${bin}'",
command => "ls /usr/local/bin/smokescreen-* | grep -xv '${bin}' | xargs rm -r",
require => [File[$bin], Service[supervisor]],
}
$listen_address = zulipconf('http_proxy', 'listen_address', '127.0.0.1')
$allow_addresses = split(zulipconf('http_proxy', 'allow_addresses', ''), ',')
$allow_ranges = split(zulipconf('http_proxy', 'allow_ranges', ''), ',')
$deny_addresses = split(zulipconf('http_proxy', 'deny_addresses', ''), ',')
$deny_ranges = split(zulipconf('http_proxy', 'deny_ranges', ''), ',')
file { "${zulip::common::supervisor_conf_dir}/smokescreen.conf":
ensure => file,
require => [
Package[supervisor],
Exec['compile smokescreen'],
],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/smokescreen.conf.erb'),
notify => Service[supervisor],
}
file { '/etc/logrotate.d/smokescreen':
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/zulip/logrotate/smokescreen',
}
}

View File

@@ -0,0 +1,12 @@
class zulip::snakeoil {
zulip::safepackage { 'ssl-cert': ensure => installed }
# We use the snakeoil certificate for PostgreSQL and SMTP; some VMs
# install the `ssl-cert` package but (reasonably) don't build the
# snakeoil certs into the image; build them as needed.
exec { 'generate-default-snakeoil':
require => Package['ssl-cert'],
creates => '/etc/ssl/certs/ssl-cert-snakeoil.pem',
command => '/usr/sbin/make-ssl-cert generate-default-snakeoil',
}
}

View File

@@ -0,0 +1,101 @@
class zulip::supervisor {
$supervisor_service = $zulip::common::supervisor_service
package { 'supervisor': ensure => installed }
$system_conf_dir = $zulip::common::supervisor_system_conf_dir
file { $system_conf_dir:
ensure => directory,
require => Package['supervisor'],
owner => 'root',
group => 'root',
}
$conf_dir = $zulip::common::supervisor_conf_dir
file { $conf_dir:
ensure => directory,
require => Package['supervisor'],
owner => 'root',
group => 'root',
purge => true,
recurse => true,
notify => Service[$supervisor_service],
}
# In the docker environment, we don't want/need supervisor to be
# started/stopped /bin/true is used as a decoy command, to maintain
# compatibility with other code using the supervisor service.
$puppet_classes = zulipconf('machine', 'puppet_classes', undef)
if 'docker' in $puppet_classes {
service { $supervisor_service:
ensure => running,
require => [
File['/var/log/zulip'],
Package['supervisor'],
],
hasstatus => true,
status => '/bin/true',
hasrestart => true,
restart => '/bin/true',
}
exec { 'supervisor-restart':
refreshonly => true,
command => '/bin/true',
}
} else {
service { $supervisor_service:
ensure => running,
require => [
File['/var/log/zulip'],
Package['supervisor'],
],
hasstatus => true,
status => $zulip::common::supervisor_status,
# Restarting the whole supervisorctl on every update to its
# configuration files has the unfortunate side-effect of
# restarting all of the services it controls; this results in an
# unduly large disruption. The better option is to tell
# supervisord to reread its config via supervisorctl and then to
# "update". You need to do both -- after a "reread", supervisor
# won't actually take action based on the changed configuration
# until you do an "update" (I assume this is so you can check if
# your config file parses without doing anything, but it's
# really confusing).
#
# If restarting supervisor itself is necessary, see
# Exec['supervisor-restart']
#
# Also, to handle the case that supervisord wasn't running at
# all, we check if it is not running and if so, start it.
hasrestart => true,
# lint:ignore:140chars
restart => "bash -c 'if pgrep -x supervisord >/dev/null; then supervisorctl reread && supervisorctl update; else ${zulip::common::supervisor_start}; fi'",
# lint:endignore
}
exec { 'supervisor-restart':
refreshonly => true,
provider => shell,
command => $zulip::common::supervisor_reload,
require => Service[$supervisor_service],
}
}
$file_descriptor_limit = zulipconf('application_server', 'service_file_descriptor_limit', 40000)
file { $zulip::common::supervisor_conf_file:
ensure => file,
require => Package[supervisor],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/supervisor/supervisord.conf.erb'),
notify => Exec['supervisor-restart'],
}
file { '/usr/local/bin/secret-env-wrapper':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/secret-env-wrapper',
}
}

View File

@@ -0,0 +1,31 @@
# @summary Adds a sysctl file, and immediately runs it.
define zulip::sysctl (
$key,
$value,
$order = 40,
$comment = '',
) {
if $comment == '' {
$content = "${key} = ${value}\n"
} else {
$content = "# ${comment}\n${key} = ${value}\n"
}
file { "/etc/sysctl.d/${order}-${name}.conf":
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
content => $content,
}
# Try to touch the procfile before trying to adjust it -- if we're
# in a containerized environment, failure to set this is not a fatal
# exception.
$procpath = regsubst($key, '\.', '/')
exec { "sysctl_p_${name}":
command => "/sbin/sysctl -p /etc/sysctl.d/${order}-${name}.conf",
subscribe => File["/etc/sysctl.d/${order}-${name}.conf"],
refreshonly => true,
onlyif => "touch /proc/sys/${procpath}",
}
}

View File

@@ -0,0 +1,9 @@
class zulip::systemd_daemon_reload {
exec { 'reload systemd':
# /run/systemd/system only exists if systemd is the running init
# system (see sd_booted(3)), as opposed to merely installed, as may
# be the case in containers.
command => 'sh -c "! test -d /run/systemd/system || systemctl daemon-reload"',
refreshonly => true,
}
}

View File

@@ -0,0 +1,23 @@
# @summary Allows configuration of timesync tool.
class zulip::timesync {
$timesync = zulipconf('machine', 'timesync', 'chrony')
case $timesync {
'chrony': {
package { 'ntp': ensure => purged, before => Package['chrony'] }
package { 'chrony': ensure => installed }
service { 'chrony': require => Package['chrony'] }
}
'ntpd': {
package { 'chrony': ensure => purged, before => Package['ntp'] }
package { 'ntp': ensure => installed }
service { 'ntp': require => Package['ntp'] }
}
'none': {
package { ['ntp', 'chrony']: ensure => purged }
}
default: {
fail('Unknown timesync tool: $timesync')
}
}
}

View File

@@ -0,0 +1,57 @@
class zulip::tornado_sharding {
include zulip::nginx
# The file entries below serve only to initialize the sharding config files
# with the correct default content for the "only one shard" setup. For this
# reason they use "replace => false", because the files are managed by
# the sharding script afterwards and Puppet shouldn't overwrite them.
file { '/etc/zulip/nginx_sharding_map.conf':
ensure => file,
owner => 'zulip',
group => 'zulip',
mode => '0644',
notify => Service['nginx'],
content => @(EOT),
map "" $tornado_server {
default http://tornado;
}
| EOT
replace => false,
}
file { '/etc/zulip/nginx_sharding.conf':
ensure => absent,
}
file { '/etc/zulip/sharding.json':
ensure => file,
require => User['zulip'],
owner => 'zulip',
group => 'zulip',
mode => '0644',
content => "{}\n",
replace => false,
}
# This creates .tmp files which scripts/refresh-sharding-and-restart
# moves into place
exec { 'stage_updated_sharding':
command => "${facts['zulip_scripts_path']}/lib/sharding.py",
onlyif => "${facts['zulip_scripts_path']}/lib/sharding.py --errors-ok",
require => [File['/etc/zulip/nginx_sharding_map.conf'], File['/etc/zulip/sharding.json']],
logoutput => true,
loglevel => warning,
}
# The ports of Tornado processes to run on the server, computed from
# the zulip.conf configuration. Default is just port 9800.
$tornado_groups = zulipconf_keys('tornado_sharding').map |$key| { $key.regsubst(/_regex$/, '').split('_') }.unique
$tornado_ports = $tornado_groups.flatten.unique
file { '/etc/nginx/zulip-include/tornado-upstreams':
require => [Package[$zulip::common::nginx], Exec['stage_updated_sharding']],
owner => 'root',
group => 'root',
mode => '0644',
content => template('zulip/nginx/tornado-upstreams.conf.template.erb'),
notify => Service['nginx'],
}
}

View File

@@ -0,0 +1,21 @@
# @summary Provide the tusd service binary
#
class zulip::tusd {
$version = $zulip::common::versions['tusd']['version']
$bin = "/srv/zulip-tusd-${version}/tusd"
# This tarball contains only a single file, which is extracted as $bin
zulip::external_dep { 'tusd':
version => $version,
url => "https://github.com/tus/tusd/releases/download/v${version}/tusd_linux_${zulip::common::goarch}.tar.gz",
tarball_prefix => "tusd_linux_${zulip::common::goarch}",
bin => [$bin],
cleanup_after => [Service[supervisor]],
}
file { '/usr/local/bin/tusd':
ensure => link,
target => $bin,
require => File[$bin],
before => Exec['Cleanup tusd'],
}
}

View File

@@ -0,0 +1,40 @@
# @summary Provide the wal-g and env-wal-g binaries.
#
class zulip::wal_g {
$wal_g_version = $zulip::common::versions['wal-g']['version']
$bin = "/srv/zulip-wal-g-${wal_g_version}"
$package = "wal-g-pg-22.04-${zulip::common::goarch}"
# This tarball contains only a single file, which is extracted as $bin
zulip::external_dep { 'wal-g':
version => $wal_g_version,
url => "https://github.com/wal-g/wal-g/releases/download/v${wal_g_version}/${package}.tar.gz",
tarball_prefix => $package,
}
file { '/usr/local/bin/wal-g':
ensure => link,
target => $bin,
require => File[$bin],
before => Exec['Cleanup wal-g'],
}
# We used to install versions into /usr/local/bin/wal-g-VERSION,
# until we moved to using Zulip::External_Dep which places them in
# /srv/zulip-wal-g-VERSION. Tidy old versions.
tidy { '/usr/local/bin/wal-g-*':
recurse => 1,
path => '/usr/local/bin/',
matches => 'wal-g-*',
}
file { '/usr/local/bin/env-wal-g':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/zulip/postgresql/env-wal-g',
require => [
File['/usr/local/bin/wal-g'],
],
}
}

View File

@@ -0,0 +1,6 @@
class zulip::yum_repository {
$setup_yum_repo_file = "${facts['zulip_scripts_path']}/lib/setup-yum-repo"
exec{'setup_yum_repo':
command => "bash -c '${setup_yum_repo_file} --prod'",
}
}