chef/cookbooks/postgresql/recipes/server.rb
#
# Cookbook Name:: postgresql
# Recipe:: server
#
# Author:: Joshua Timberman (<joshua@opscode.com>)
# Author:: Lamont Granquist (<lamont@opscode.com>)
# Author:: Ralf Haferkamp (<rhafer@suse.com>)
# Copyright 2009-2011, Opscode, Inc.
# Copyright 2012, SUSE
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
::Chef::Recipe.send(:include, Opscode::OpenSSL::Password)
include_recipe "postgresql::client"
dirty = false
# For Crowbar, we need to set the address to bind - default to admin node.
newaddr = CrowbarDatabaseHelper.get_listen_address(node)
if node["postgresql"]["config"]["listen_addresses"] != newaddr
node.set["postgresql"]["config"]["listen_addresses"] = newaddr
dirty = true
end
# We also need to add the network + mask to give access to other nodes
# in pg_hba.conf
# XXX this assumes that crowbar is the only one changing pg_hba attributes
if node["postgresql"]["pg_hba"][4]
netaddr, netmask = node["postgresql"]["pg_hba"][4][:addr].split
else
pg_hba = node["postgresql"]["pg_hba"].dup # we're modifying this, so dup it
pg_hba << {
type: "host",
db: "all",
user: "all",
method: "md5"
}
netaddr, netmask = "", ""
if node["postgresql"]["pg_hba"] != pg_hba
node.set["postgresql"]["pg_hba"] = pg_hba
dirty = true
end
end
newnetaddr = Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, "admin").subnet
newnetmask = Chef::Recipe::Barclamp::Inventory.get_network_by_type(node, "admin").netmask
if netaddr != newnetaddr or netmask != newnetmask
node.set["postgresql"]["pg_hba"][4][:addr] = [newnetaddr, newnetmask].join(" ")
dirty = true
end
# randomly generate postgres password, unless using solo - see README
if Chef::Config[:solo]
missing_attrs = %w{
postgres
}.select do |attr|
node["postgresql"]["password"][attr].nil?
end.map { |attr| "node['postgresql']['password']['#{attr}']" }
if !missing_attrs.empty?
Chef::Application.fatal!([
"You must set #{missing_attrs.join(', ')} in chef-solo mode.",
"For more information, see https://github.com/opscode-cookbooks/postgresql#chef-solo-note"
].join(" "))
end
elsif node["postgresql"]["password"]["postgres"].nil?
# TODO: The "secure_password" is randomly generated plain text, so it
# should be converted to a PostgreSQL specific "encrypted password" if
# it should actually install a password (as opposed to disable password
# login for user 'postgres'). However, a random password wouldn't be
# useful if it weren't saved as clear text in Chef Server for later
# retrieval.
node.set["postgresql"]["password"]["postgres"] = secure_password
dirty = true
end
node.save if dirty
# While we would like to include the "postgresql::ha_storage" recipe from here,
# it's not possible: we need to have the packages installed first, and we need
# to include it before we do templates. Which means we need to do it in the
# server_* recipe directly, since they do both.
# Include the right "family" recipe for installing the server
# since they do things slightly differently.
case node[:platform_family]
when "rhel", "fedora", "suse"
include_recipe "postgresql::server_redhat"
when "debian"
include_recipe "postgresql::server_debian"
end
change_notify = node["postgresql"]["server"]["config_change_notify"]
template "#{node['postgresql']['dir']}/postgresql.conf" do
source "postgresql.conf.erb"
owner "postgres"
group "postgres"
mode 0600
notifies change_notify, "service[postgresql]", :immediately
end
template "#{node['postgresql']['dir']}/pg_hba.conf" do
source "pg_hba.conf.erb"
owner "postgres"
group "postgres"
mode 00600
notifies change_notify, "service[postgresql]", :immediately
end
ha_enabled = node[:database][:ha][:enabled]
if ha_enabled
log "HA support for postgresql is enabled"
include_recipe "postgresql::ha"
# Only run the psql commands if the service is running on this node, so that
# we don't depend on the node running the service to be as fast as this one
service_name = "postgresql"
only_if_command = "crm resource show #{service_name} | grep -q \" #{node.hostname} *$\""
else
log "HA support for postgresql is disabled"
end
# NOTE: Consider two facts before modifying "assign-postgres-password":
# (1) Passing the "ALTER ROLE ..." through the psql command only works
# if passwordless authorization was configured for local connections.
# For example, if pg_hba.conf has a "local all postgres ident" rule.
# (2) It is probably fruitless to optimize this with a not_if to avoid
# setting the same password. This chef recipe doesn't have access to
# the plain text password, and testing the encrypted (md5 digest)
# version is not straight-forward.
bash "assign-postgres-password" do
user "postgres"
code <<-EOH
echo "ALTER ROLE postgres ENCRYPTED PASSWORD '#{node['postgresql']['password']['postgres']}';" | psql -p #{node['postgresql']['config']['port']}
EOH
# shouldn't try to update the password on the current 'passive' nodes
only_if only_if_command if ha_enabled
action :run
end
# For Crowbar we also need the "db_maker" user
bash "assign-db_maker-password" do
user "postgres"
code <<-EOH
echo "SELECT rolname FROM pg_roles WHERE rolname='db_maker';" | psql | grep -q db_maker
if [ $? -ne 0 ]; then
echo "CREATE ROLE db_maker WITH LOGIN CREATEDB CREATEROLE ENCRYPTED PASSWORD '#{node[:database][:db_maker_password]}';" | psql
else
echo "ALTER ROLE db_maker ENCRYPTED PASSWORD '#{node[:database][:db_maker_password]}';" | psql
fi
EOH
only_if only_if_command if ha_enabled
action :run
end
service = "postgresql"
if node[:database][:resource_limits] && node[:database][:resource_limits][service]
limits = node[:database][:resource_limits][service]
limit_action = limits.values.any? ? :create : :delete
# If using HA, we manage pam_limits for postres with limits.conf, otherwise
# we manage it through systemd
if ha_enabled
limits = Hash[limits.map { |k, v| [k.gsub(/^Limit/, "").downcase, v] }]
directory "/etc/security/limits.d" do
action :create
owner "root"
group "root"
mode "0755"
end
template "/etc/security/limits.d/postgres.conf" do
action limit_action
source "limits.erb"
owner "root"
group "root"
mode "0644"
variables(
limits: limits
)
notifies :restart, resources(service: service)
end
else
utils_systemd_override_limits "Resource limits for #{service}" do
service_name service
limits limits
action limit_action
end
end
end