Add a build phase, make deployment revision-driven
Split the deployment into kosmos-mastodon::build (checkout + bundle/yarn/assets, no database) and kosmos-mastodon::deploy (migrations + services), dispatched by the new 'build' attribute. Both phases are idempotent per checked-out revision via stamps, and the migration/restart/search-index chain is now triggered by the deployed revision instead of the git resource, so it still runs when the build was pre-staged.
This commit is contained in:
5 files changed
+392
-322
No files matched your search
+2
-1
@@ -1,7 +1,8 @@
|
||||
name "mastodon"
|
||||
|
||||
default_attributes 'kosmos-mastodon' => {
|
||||
'deploy' => false
|
||||
'deploy' => false,
|
||||
'build' => false
|
||||
}
|
||||
|
||||
run_list %w(
|
||||
|
||||
@@ -12,6 +12,11 @@ node.default["kosmos-mastodon"]["sidekiq_threads"] = 25
|
||||
# is true. Set to false to only install the runtime dependencies.
|
||||
node.default["kosmos-mastodon"]["deploy"] = true
|
||||
|
||||
# Only check out and build the application (no migrations, no services) when
|
||||
# this is true. Implied by `deploy`. Useful to pre-stage a deployment without
|
||||
# touching the database.
|
||||
node.default["kosmos-mastodon"]["build"] = true
|
||||
|
||||
# Runtime versions
|
||||
node.default["kosmos-mastodon"]["nodejs_version"] = "24.21.0"
|
||||
node.default["kosmos-mastodon"]["ruby_version"] = "4.0.7"
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
#
|
||||
# Cookbook Name:: kosmos-mastodon
|
||||
# Recipe:: build
|
||||
#
|
||||
# Checks out and builds the application without running migrations or starting
|
||||
# any services, so a deployment can be pre-staged before the maintenance
|
||||
# window. The build is skipped while the checked-out revision is unchanged.
|
||||
#
|
||||
|
||||
require 'uri'
|
||||
|
||||
postgresql_credentials = data_bag_item('credentials', 'postgresql')
|
||||
|
||||
mastodon_path = node["kosmos-mastodon"]["directory"]
|
||||
mastodon_user = "mastodon"
|
||||
|
||||
ruby_version = node["kosmos-mastodon"]["ruby_version"]
|
||||
ruby_path = "/opt/ruby_build/builds/#{ruby_version}"
|
||||
|
||||
# External Redis cluster (see the kosmos_redis cookbook)
|
||||
redis_host = search(:node, "role:#{node['kosmos-mastodon']['redis_server_role']}").first&.dig("knife_zero", "host")
|
||||
|
||||
if redis_host.nil?
|
||||
Chef::Log.fatal("No node found with '#{node['kosmos-mastodon']['redis_server_role']}' role. Stopping here.")
|
||||
return
|
||||
end
|
||||
|
||||
redis_password = URI.encode_www_form_component(data_bag_item('credentials', 'redis')['password'])
|
||||
redis_url = "redis://:#{redis_password}@#{redis_host}:#{node['kosmos-mastodon']['redis_port']}/#{node['kosmos-mastodon']['redis_db']}"
|
||||
|
||||
rails_env = node.chef_environment == "development" ? "development" : "production"
|
||||
deploy_env = {
|
||||
# FIXME: /usr/bin was missing from PATH when running `yarn install`
|
||||
"PATH" => "#{ruby_path}/bin:/usr/bin:$PATH",
|
||||
"HOME" => mastodon_path,
|
||||
"RAILS_ENV" => rails_env,
|
||||
"NODE_ENV" => rails_env,
|
||||
"COREPACK_ENABLE_DOWNLOAD_PROMPT" => "0"
|
||||
}
|
||||
|
||||
# Skip the build while the checked-out revision is unchanged
|
||||
build_uptodate = "test -f #{mastodon_path}/tmp/built-revision && " \
|
||||
"test \"$(cat #{mastodon_path}/tmp/built-revision)\" = \"$(git -C #{mastodon_path} rev-parse HEAD)\""
|
||||
|
||||
git mastodon_path do
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
|
||||
repository node["kosmos-mastodon"]["repo"]
|
||||
revision node["kosmos-mastodon"]["revision"]
|
||||
end
|
||||
|
||||
credentials = data_bag_item('credentials', 'mastodon')
|
||||
|
||||
ldap_config = {
|
||||
host: "ldap.kosmos.local",
|
||||
port: 389,
|
||||
method: "plain",
|
||||
base: "ou=kosmos.org,cn=users,dc=kosmos,dc=org",
|
||||
bind_dn: credentials["ldap_bind_dn"],
|
||||
password: credentials["ldap_password"],
|
||||
uid: "cn",
|
||||
mail: "mail",
|
||||
search_filter: "(&(|(cn=%{email})(mail=%{email}))(serviceEnabled=mastodon))",
|
||||
uid_conversion_enabled: "true",
|
||||
uid_conversion_search: "-",
|
||||
uid_conversion_replace: "_"
|
||||
}
|
||||
|
||||
template "#{mastodon_path}/.env.#{rails_env}" do
|
||||
source "env.erb"
|
||||
mode "0640"
|
||||
owner mastodon_user
|
||||
group mastodon_user
|
||||
sensitive true
|
||||
variables redis_url: redis_url,
|
||||
domain: node["kosmos-mastodon"]["domain"],
|
||||
alternate_domains: node["kosmos-mastodon"]["alternate_domains"],
|
||||
active_record_encryption_deterministic_key: credentials["active_record_encryption_deterministic_key"],
|
||||
active_record_encryption_key_derivation_salt: credentials["active_record_encryption_key_derivation_salt"],
|
||||
active_record_encryption_primary_key: credentials["active_record_encryption_primary_key"],
|
||||
paperclip_secret: credentials['paperclip_secret'],
|
||||
secret_key_base: credentials['secret_key_base'],
|
||||
ldap: ldap_config,
|
||||
smtp_login: credentials['smtp_user_name'],
|
||||
smtp_password: credentials['smtp_password'],
|
||||
smtp_from_address: "mail@#{node['kosmos-mastodon']['domain']}",
|
||||
s3_endpoint: node["kosmos-mastodon"]["s3_endpoint"],
|
||||
s3_region: node["kosmos-mastodon"]["s3_region"],
|
||||
s3_bucket: node["kosmos-mastodon"]["s3_bucket"],
|
||||
s3_alias_host: node["kosmos-mastodon"]["s3_alias_host"],
|
||||
aws_access_key_id: credentials['s3_key_id'],
|
||||
aws_secret_access_key: credentials['s3_secret_key'],
|
||||
vapid_private_key: credentials['vapid_private_key'],
|
||||
vapid_public_key: credentials['vapid_public_key'],
|
||||
db_pass: postgresql_credentials['mastodon_user_password'],
|
||||
db_host: "pg.kosmos.local",
|
||||
sso_account_sign_up_url: node["kosmos-mastodon"]["sso_account_sign_up_url"],
|
||||
sso_account_reset_password_url: node["kosmos-mastodon"]["sso_account_reset_password_url"],
|
||||
sso_account_resend_confirmation_url: node["kosmos-mastodon"]["sso_account_resend_confirmation_url"],
|
||||
default_locale: node["kosmos-mastodon"]["default_locale"],
|
||||
force_default_locale: node["kosmos-mastodon"]["force_default_locale"],
|
||||
disable_email_subscriptions: node["kosmos-mastodon"]["disable_email_subscriptions"],
|
||||
allowed_private_addresses: node["kosmos-mastodon"]["allowed_private_addresses"],
|
||||
libre_translate_endpoint: node["kosmos-mastodon"]["libre_translate_endpoint"]
|
||||
notifies :run, "execute[restart mastodon services]", :delayed if node["kosmos-mastodon"]["deploy"]
|
||||
end
|
||||
|
||||
execute "bundle install" do
|
||||
environment deploy_env.merge("BUNDLE_BUILD__CHARLOCK_HOLMES" => "--with-cxxflags=-std=c++17")
|
||||
user mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle install --without development,test --deployment"
|
||||
not_if build_uptodate
|
||||
end
|
||||
|
||||
execute "yarn install" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
cwd mastodon_path
|
||||
command "yarn install --immutable"
|
||||
not_if build_uptodate
|
||||
end
|
||||
|
||||
execute "rake assets:precompile" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle exec rake assets:precompile"
|
||||
not_if build_uptodate
|
||||
notifies :create, "file[#{mastodon_path}/tmp/built-revision]", :immediately
|
||||
end
|
||||
|
||||
file "#{mastodon_path}/tmp/built-revision" do
|
||||
content lazy { `git -C #{mastodon_path} rev-parse HEAD`.strip }
|
||||
owner mastodon_user
|
||||
group mastodon_user
|
||||
mode "0644"
|
||||
action :nothing
|
||||
end
|
||||
@@ -5,325 +5,8 @@
|
||||
|
||||
include_recipe "kosmos-mastodon::dependencies"
|
||||
|
||||
# The rest is the actual Mastodon deployment. Skip it when only the runtime
|
||||
# dependencies should be installed (e.g. to pre-stage a new VM).
|
||||
return unless node["kosmos-mastodon"]["deploy"]
|
||||
# Check out and build the application without touching the database.
|
||||
include_recipe "kosmos-mastodon::build" if node["kosmos-mastodon"]["build"] || node["kosmos-mastodon"]["deploy"]
|
||||
|
||||
require 'uri'
|
||||
|
||||
postgresql_credentials = data_bag_item('credentials', 'postgresql')
|
||||
|
||||
mastodon_path = node["kosmos-mastodon"]["directory"]
|
||||
mastodon_user = "mastodon"
|
||||
|
||||
bind_ip = if node.chef_environment == "production"
|
||||
node["knife_zero"]["host"]
|
||||
else
|
||||
node["kosmos-mastodon"]["bind_ip"]
|
||||
end
|
||||
|
||||
ruby_version = node["kosmos-mastodon"]["ruby_version"]
|
||||
ruby_path = "/opt/ruby_build/builds/#{ruby_version}"
|
||||
bundle_path = "#{ruby_path}/bin/bundle"
|
||||
|
||||
# External Redis cluster (see the kosmos_redis cookbook)
|
||||
redis_host = search(:node, "role:#{node['kosmos-mastodon']['redis_server_role']}").first&.dig("knife_zero", "host")
|
||||
|
||||
if redis_host.nil?
|
||||
Chef::Log.fatal("No node found with '#{node['kosmos-mastodon']['redis_server_role']}' role. Stopping here.")
|
||||
return
|
||||
end
|
||||
|
||||
redis_password = URI.encode_www_form_component(data_bag_item('credentials', 'redis')['password'])
|
||||
redis_url = "redis://:#{redis_password}@#{redis_host}:#{node['kosmos-mastodon']['redis_port']}/#{node['kosmos-mastodon']['redis_db']}"
|
||||
|
||||
execute "systemctl daemon-reload" do
|
||||
command "systemctl daemon-reload"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
# mastodon-web service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-web.service" do
|
||||
source "mastodon-web.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bind: bind_ip,
|
||||
port: node["kosmos-mastodon"]["app_port"],
|
||||
bundle_path: bundle_path
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-web]", :delayed
|
||||
end
|
||||
|
||||
# mastodon-sidekiq service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-sidekiq.service" do
|
||||
source "mastodon-sidekiq.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bundle_path: bundle_path,
|
||||
sidekiq_threads: node["kosmos-mastodon"]["sidekiq_threads"]
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-sidekiq]", :delayed
|
||||
end
|
||||
|
||||
# mastodon-sidekiq-scheduler service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-sidekiq-scheduler.service" do
|
||||
source "mastodon-sidekiq-scheduler.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bundle_path: bundle_path,
|
||||
sidekiq_threads: 1
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-sidekiq-scheduler]", :delayed
|
||||
end
|
||||
|
||||
# mastodon-streaming service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-streaming.service" do
|
||||
source "mastodon-streaming.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bind: bind_ip,
|
||||
port: node["kosmos-mastodon"]["streaming_port"]
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-streaming]", :delayed
|
||||
end
|
||||
|
||||
rails_env = node.chef_environment == "development" ? "development" : "production"
|
||||
deploy_env = {
|
||||
# FIXME: /usr/bin was missing from PATH when running `yarn install`
|
||||
"PATH" => "#{ruby_path}/bin:/usr/bin:$PATH",
|
||||
"HOME" => mastodon_path,
|
||||
"RAILS_ENV" => rails_env,
|
||||
"NODE_ENV" => rails_env,
|
||||
"COREPACK_ENABLE_DOWNLOAD_PROMPT" => "0"
|
||||
}
|
||||
|
||||
git mastodon_path do
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
|
||||
repository node["kosmos-mastodon"]["repo"]
|
||||
revision node["kosmos-mastodon"]["revision"]
|
||||
# Restart services (and run post-deployment migrations) on deployments
|
||||
notifies :run, "execute[restart mastodon services]", :delayed
|
||||
end
|
||||
|
||||
execute "restart mastodon services" do
|
||||
command "systemctl restart mastodon-web mastodon-sidekiq mastodon-sidekiq-scheduler mastodon-streaming"
|
||||
action :nothing
|
||||
notifies :run, "execute[rake db:migrate (post-deployment)]", :immediately
|
||||
end
|
||||
|
||||
credentials = data_bag_item('credentials', 'mastodon')
|
||||
|
||||
ldap_config = {
|
||||
host: "ldap.kosmos.local",
|
||||
port: 389,
|
||||
method: "plain",
|
||||
base: "ou=kosmos.org,cn=users,dc=kosmos,dc=org",
|
||||
bind_dn: credentials["ldap_bind_dn"],
|
||||
password: credentials["ldap_password"],
|
||||
uid: "cn",
|
||||
mail: "mail",
|
||||
search_filter: "(&(|(cn=%{email})(mail=%{email}))(serviceEnabled=mastodon))",
|
||||
uid_conversion_enabled: "true",
|
||||
uid_conversion_search: "-",
|
||||
uid_conversion_replace: "_"
|
||||
}
|
||||
|
||||
template "#{mastodon_path}/.env.#{rails_env}" do
|
||||
source "env.erb"
|
||||
mode "0640"
|
||||
owner mastodon_user
|
||||
group mastodon_user
|
||||
sensitive true
|
||||
variables redis_url: redis_url,
|
||||
domain: node["kosmos-mastodon"]["domain"],
|
||||
alternate_domains: node["kosmos-mastodon"]["alternate_domains"],
|
||||
active_record_encryption_deterministic_key: credentials["active_record_encryption_deterministic_key"],
|
||||
active_record_encryption_key_derivation_salt: credentials["active_record_encryption_key_derivation_salt"],
|
||||
active_record_encryption_primary_key: credentials["active_record_encryption_primary_key"],
|
||||
paperclip_secret: credentials['paperclip_secret'],
|
||||
secret_key_base: credentials['secret_key_base'],
|
||||
ldap: ldap_config,
|
||||
smtp_login: credentials['smtp_user_name'],
|
||||
smtp_password: credentials['smtp_password'],
|
||||
smtp_from_address: "mail@#{node['kosmos-mastodon']['domain']}",
|
||||
s3_endpoint: node["kosmos-mastodon"]["s3_endpoint"],
|
||||
s3_region: node["kosmos-mastodon"]["s3_region"],
|
||||
s3_bucket: node["kosmos-mastodon"]["s3_bucket"],
|
||||
s3_alias_host: node["kosmos-mastodon"]["s3_alias_host"],
|
||||
aws_access_key_id: credentials['s3_key_id'],
|
||||
aws_secret_access_key: credentials['s3_secret_key'],
|
||||
vapid_private_key: credentials['vapid_private_key'],
|
||||
vapid_public_key: credentials['vapid_public_key'],
|
||||
db_pass: postgresql_credentials['mastodon_user_password'],
|
||||
db_host: "pg.kosmos.local",
|
||||
sso_account_sign_up_url: node["kosmos-mastodon"]["sso_account_sign_up_url"],
|
||||
sso_account_reset_password_url: node["kosmos-mastodon"]["sso_account_reset_password_url"],
|
||||
sso_account_resend_confirmation_url: node["kosmos-mastodon"]["sso_account_resend_confirmation_url"],
|
||||
default_locale: node["kosmos-mastodon"]["default_locale"],
|
||||
force_default_locale: node["kosmos-mastodon"]["force_default_locale"],
|
||||
disable_email_subscriptions: node["kosmos-mastodon"]["disable_email_subscriptions"],
|
||||
allowed_private_addresses: node["kosmos-mastodon"]["allowed_private_addresses"],
|
||||
libre_translate_endpoint: node["kosmos-mastodon"]["libre_translate_endpoint"]
|
||||
notifies :run, "execute[restart mastodon services]", :delayed
|
||||
end
|
||||
|
||||
execute "bundle install" do
|
||||
environment deploy_env.merge("BUNDLE_BUILD__CHARLOCK_HOLMES" => "--with-cxxflags=-std=c++17")
|
||||
user mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle install --without development,test --deployment"
|
||||
end
|
||||
|
||||
execute "yarn install" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
cwd mastodon_path
|
||||
command "yarn install --immutable"
|
||||
end
|
||||
|
||||
execute "rake assets:precompile" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle exec rake assets:precompile"
|
||||
end
|
||||
|
||||
# Mastodon 4.4+ splits migrations into pre- and post-deployment phases.
|
||||
# Pre-deployment migrations must run before the services are (re)started.
|
||||
execute "rake db:migrate (pre-deployment)" do
|
||||
environment deploy_env.merge("SKIP_POST_DEPLOYMENT_MIGRATIONS" => "true")
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle exec rake db:migrate"
|
||||
timeout 21_600
|
||||
end
|
||||
|
||||
execute "rake db:migrate (post-deployment)" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle exec rake db:migrate"
|
||||
timeout 21_600
|
||||
action :nothing
|
||||
notifies :run, "execute[tootctl search deploy (create indices)]", :immediately
|
||||
end
|
||||
|
||||
# Create or upgrade the Elasticsearch indices and mappings without importing
|
||||
# data, so that search does not fail on a missing index. The (potentially very
|
||||
# long) import is deferred to a systemd unit started in the background below.
|
||||
execute "tootctl search deploy (create indices)" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "#{bundle_path} exec bin/tootctl search deploy --no-import"
|
||||
timeout 3_600
|
||||
action :nothing
|
||||
notifies :run, "execute[start mastodon search deploy]", :immediately
|
||||
end
|
||||
|
||||
execute "start mastodon search deploy" do
|
||||
command "systemctl start --no-block mastodon-search-deploy.service"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
service "mastodon-web" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
service "mastodon-sidekiq" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
service "mastodon-sidekiq-scheduler" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
service "mastodon-streaming" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
#
|
||||
# Delete cached remote media older than 30 days
|
||||
# Will be re-fetched if necessary
|
||||
#
|
||||
|
||||
systemd_unit 'mastodon-delete-old-media-cache.service' do
|
||||
content({
|
||||
Unit: {
|
||||
Description: 'Delete old Mastodon media cache'
|
||||
},
|
||||
Service: {
|
||||
Type: "oneshot",
|
||||
WorkingDirectory: mastodon_path,
|
||||
Environment: "RAILS_ENV=#{rails_env}",
|
||||
ExecStart: "#{bundle_path} exec bin/tootctl media remove --days 30",
|
||||
}
|
||||
})
|
||||
triggers_reload true
|
||||
action [:create]
|
||||
end
|
||||
|
||||
systemd_unit 'mastodon-delete-old-media-cache.timer' do
|
||||
content({
|
||||
Unit: {
|
||||
Description: 'Delete old Mastodon media cache'
|
||||
},
|
||||
Timer: {
|
||||
OnCalendar: '*-*-* 00:00:00',
|
||||
Persistent: 'true'
|
||||
},
|
||||
Install: {
|
||||
WantedBy: 'timer.target'
|
||||
}
|
||||
})
|
||||
triggers_reload true
|
||||
action [:create, :enable, :start]
|
||||
end
|
||||
|
||||
#
|
||||
# Populate the Elasticsearch indices in the background. The indices and
|
||||
# mappings are created synchronously by the recipe above; this unit only does
|
||||
# the (potentially very long) import, so it is started without blocking.
|
||||
#
|
||||
|
||||
systemd_unit 'mastodon-search-deploy.service' do
|
||||
content({
|
||||
Unit: {
|
||||
Description: 'Populate the Mastodon search index'
|
||||
},
|
||||
Service: {
|
||||
Type: "oneshot",
|
||||
User: mastodon_user,
|
||||
WorkingDirectory: mastodon_path,
|
||||
Environment: "RAILS_ENV=#{rails_env}",
|
||||
ExecStart: "#{bundle_path} exec bin/tootctl search deploy",
|
||||
TimeoutStartSec: "21600",
|
||||
}
|
||||
})
|
||||
triggers_reload true
|
||||
action [:create]
|
||||
end
|
||||
|
||||
firewall_rule "mastodon_app" do
|
||||
port node['kosmos-mastodon']['app_port']
|
||||
source "10.1.1.0/24"
|
||||
protocol :tcp
|
||||
command :allow
|
||||
end
|
||||
|
||||
firewall_rule 'mastodon_streaming' do
|
||||
port node['kosmos-mastodon']['streaming_port']
|
||||
source "10.1.1.0/24"
|
||||
protocol :tcp
|
||||
command :allow
|
||||
end
|
||||
# Run migrations and manage the services.
|
||||
include_recipe "kosmos-mastodon::deploy" if node["kosmos-mastodon"]["deploy"]
|
||||
@@ -0,0 +1,240 @@
|
||||
#
|
||||
# Cookbook Name:: kosmos-mastodon
|
||||
# Recipe:: deploy
|
||||
#
|
||||
# Runs database migrations and manages the services. Requires the application
|
||||
# to have been checked out and built by kosmos-mastodon::build. Migrations and
|
||||
# the service restart run once per checked-out revision.
|
||||
#
|
||||
|
||||
mastodon_path = node["kosmos-mastodon"]["directory"]
|
||||
mastodon_user = "mastodon"
|
||||
|
||||
bind_ip = if node.chef_environment == "production"
|
||||
node["knife_zero"]["host"]
|
||||
else
|
||||
node["kosmos-mastodon"]["bind_ip"]
|
||||
end
|
||||
|
||||
ruby_version = node["kosmos-mastodon"]["ruby_version"]
|
||||
ruby_path = "/opt/ruby_build/builds/#{ruby_version}"
|
||||
bundle_path = "#{ruby_path}/bin/bundle"
|
||||
|
||||
rails_env = node.chef_environment == "development" ? "development" : "production"
|
||||
deploy_env = {
|
||||
# FIXME: /usr/bin was missing from PATH when running `yarn install`
|
||||
"PATH" => "#{ruby_path}/bin:/usr/bin:$PATH",
|
||||
"HOME" => mastodon_path,
|
||||
"RAILS_ENV" => rails_env,
|
||||
"NODE_ENV" => rails_env,
|
||||
"COREPACK_ENABLE_DOWNLOAD_PROMPT" => "0"
|
||||
}
|
||||
|
||||
# Run migrations, restart services and (re)build the search index once per
|
||||
# checked-out revision, regardless of whether the code was pre-built.
|
||||
deploy_uptodate = "test -f #{mastodon_path}/tmp/deployed-revision && " \
|
||||
"test \"$(cat #{mastodon_path}/tmp/deployed-revision)\" = \"$(git -C #{mastodon_path} rev-parse HEAD)\""
|
||||
|
||||
execute "systemctl daemon-reload" do
|
||||
command "systemctl daemon-reload"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
# mastodon-web service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-web.service" do
|
||||
source "mastodon-web.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bind: bind_ip,
|
||||
port: node["kosmos-mastodon"]["app_port"],
|
||||
bundle_path: bundle_path
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-web]", :delayed
|
||||
end
|
||||
|
||||
# mastodon-sidekiq service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-sidekiq.service" do
|
||||
source "mastodon-sidekiq.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bundle_path: bundle_path,
|
||||
sidekiq_threads: node["kosmos-mastodon"]["sidekiq_threads"]
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-sidekiq]", :delayed
|
||||
end
|
||||
|
||||
# mastodon-sidekiq-scheduler service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-sidekiq-scheduler.service" do
|
||||
source "mastodon-sidekiq-scheduler.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bundle_path: bundle_path,
|
||||
sidekiq_threads: 1
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-sidekiq-scheduler]", :delayed
|
||||
end
|
||||
|
||||
# mastodon-streaming service
|
||||
#
|
||||
template "/lib/systemd/system/mastodon-streaming.service" do
|
||||
source "mastodon-streaming.systemd.service.erb"
|
||||
variables user: mastodon_user,
|
||||
app_dir: mastodon_path,
|
||||
bind: bind_ip,
|
||||
port: node["kosmos-mastodon"]["streaming_port"]
|
||||
notifies :run, "execute[systemctl daemon-reload]", :immediately
|
||||
notifies :restart, "service[mastodon-streaming]", :delayed
|
||||
end
|
||||
|
||||
execute "restart mastodon services" do
|
||||
command "systemctl restart mastodon-web mastodon-sidekiq mastodon-sidekiq-scheduler mastodon-streaming"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
# Mastodon 4.4+ splits migrations into pre- and post-deployment phases.
|
||||
# Pre-deployment migrations must run before the services are (re)started.
|
||||
execute "rake db:migrate (pre-deployment)" do
|
||||
environment deploy_env.merge("SKIP_POST_DEPLOYMENT_MIGRATIONS" => "true")
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle exec rake db:migrate"
|
||||
timeout 21_600
|
||||
not_if deploy_uptodate
|
||||
notifies :run, "execute[restart mastodon services]", :immediately
|
||||
notifies :run, "execute[rake db:migrate (post-deployment)]", :immediately
|
||||
end
|
||||
|
||||
execute "rake db:migrate (post-deployment)" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "bundle exec rake db:migrate"
|
||||
timeout 21_600
|
||||
action :nothing
|
||||
notifies :run, "execute[tootctl search deploy (create indices)]", :immediately
|
||||
end
|
||||
|
||||
# Create or upgrade the Elasticsearch indices and mappings without importing
|
||||
# data, so that search does not fail on a missing index. The (potentially very
|
||||
# long) import is deferred to a systemd unit started in the background below.
|
||||
execute "tootctl search deploy (create indices)" do
|
||||
environment deploy_env
|
||||
user mastodon_user
|
||||
group mastodon_user
|
||||
cwd mastodon_path
|
||||
command "#{bundle_path} exec bin/tootctl search deploy --no-import"
|
||||
timeout 3_600
|
||||
action :nothing
|
||||
notifies :run, "execute[start mastodon search deploy]", :immediately
|
||||
notifies :create, "file[#{mastodon_path}/tmp/deployed-revision]", :immediately
|
||||
end
|
||||
|
||||
execute "start mastodon search deploy" do
|
||||
command "systemctl start --no-block mastodon-search-deploy.service"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
file "#{mastodon_path}/tmp/deployed-revision" do
|
||||
content lazy { `git -C #{mastodon_path} rev-parse HEAD`.strip }
|
||||
owner mastodon_user
|
||||
group mastodon_user
|
||||
mode "0644"
|
||||
action :nothing
|
||||
end
|
||||
|
||||
service "mastodon-web" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
service "mastodon-sidekiq" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
service "mastodon-sidekiq-scheduler" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
service "mastodon-streaming" do
|
||||
action [:enable, :start]
|
||||
end
|
||||
|
||||
#
|
||||
# Delete cached remote media older than 30 days
|
||||
# Will be re-fetched if necessary
|
||||
#
|
||||
|
||||
systemd_unit 'mastodon-delete-old-media-cache.service' do
|
||||
content({
|
||||
Unit: {
|
||||
Description: 'Delete old Mastodon media cache'
|
||||
},
|
||||
Service: {
|
||||
Type: "oneshot",
|
||||
WorkingDirectory: mastodon_path,
|
||||
Environment: "RAILS_ENV=#{rails_env}",
|
||||
ExecStart: "#{bundle_path} exec bin/tootctl media remove --days 30",
|
||||
}
|
||||
})
|
||||
triggers_reload true
|
||||
action [:create]
|
||||
end
|
||||
|
||||
systemd_unit 'mastodon-delete-old-media-cache.timer' do
|
||||
content({
|
||||
Unit: {
|
||||
Description: 'Delete old Mastodon media cache'
|
||||
},
|
||||
Timer: {
|
||||
OnCalendar: '*-*-* 00:00:00',
|
||||
Persistent: 'true'
|
||||
},
|
||||
Install: {
|
||||
WantedBy: 'timer.target'
|
||||
}
|
||||
})
|
||||
triggers_reload true
|
||||
action [:create, :enable, :start]
|
||||
end
|
||||
|
||||
#
|
||||
# Populate the Elasticsearch indices in the background. The indices and
|
||||
# mappings are created synchronously by the recipe above; this unit only does
|
||||
# the (potentially very long) import, so it is started without blocking.
|
||||
#
|
||||
|
||||
systemd_unit 'mastodon-search-deploy.service' do
|
||||
content({
|
||||
Unit: {
|
||||
Description: 'Populate the Mastodon search index'
|
||||
},
|
||||
Service: {
|
||||
Type: "oneshot",
|
||||
User: mastodon_user,
|
||||
WorkingDirectory: mastodon_path,
|
||||
Environment: "RAILS_ENV=#{rails_env}",
|
||||
ExecStart: "#{bundle_path} exec bin/tootctl search deploy",
|
||||
TimeoutStartSec: "21600",
|
||||
}
|
||||
})
|
||||
triggers_reload true
|
||||
action [:create]
|
||||
end
|
||||
|
||||
firewall_rule "mastodon_app" do
|
||||
port node['kosmos-mastodon']['app_port']
|
||||
source "10.1.1.0/24"
|
||||
protocol :tcp
|
||||
command :allow
|
||||
end
|
||||
|
||||
firewall_rule 'mastodon_streaming' do
|
||||
port node['kosmos-mastodon']['streaming_port']
|
||||
source "10.1.1.0/24"
|
||||
protocol :tcp
|
||||
command :allow
|
||||
end
|
||||
Reference in new issue
Block a user