4
votes

I am attempting to set up postgresql on a Vagrant box using Chef solo and am running into some problems. I need the default postgres encoding/locale to be UTF8. By default, the locale of the precise64 Ubuntu box is set to "C" so postgres is using LATIN1 for encoding. This is what I've done so far:

I have a chef recipe that sets the locale by doing the following:

template "/etc/profile.d/lang.sh" do
  source  "lang.sh.erb"
  mode "0644"
end

execute "locale-gen" do
  command "locale-gen en_US.UTF-8"
end

execute "dpkg-reconfigure-locales" do
  command "dpkg-reconfigure locales"
end

where lang.sh.erb looks like:

export LANGUAGE="en_US.UTF-8"
export LANG="en_US.UTF-8"
export LC_ALL="en_US.UTF-8"

This sets up the locale correctly, but unfortunately it doesn't modify the current environment. So I have another recipe that just sets the ENV before including postgresql

ENV["LANGUAGE"] = ENV["LANG"] = ENV["LC_ALL"] = "en_US.UTF-8"
include_recipe "postgresql::server"

This has no effect. The locale is set up correctly:

postgres@precise64:~$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=en_US.UTF-8

But postgres used the "C" locale when it was installed.

postgres@precise64:~$ psql -l
                             List of databases
   Name    |  Owner   | Encoding | Collate | Ctype |   Access privileges
-----------+----------+----------+---------+-------+-----------------------
 postgres  | postgres | LATIN1   | en_US   | en_US |
 template0 | postgres | LATIN1   | en_US   | en_US | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
 template1 | postgres | LATIN1   | en_US   | en_US | =c/postgres          +
           |          |          |         |       | postgres=CTc/postgres
(3 rows)

For attribution's sake, I got all this from http://www.softr.li/blog/2012/05/22/chef-recipe-to-install-a-postgresql-server-on-a-machine-configured-with-en_us-locales.

3
I found stackoverflow.com/questions/20815440/… was a bit of hack but quite helpful - lacostenycoder

3 Answers

3
votes

I found that the solution that worked for me was to either in the bootstrap shell script, or as inline shell, to copy the /etc/default/lang.sh to the box prior to any recipes being run. (So should be first thing done in the Vagrant file after box definitions) lang file:

export LANGUAGE=en_US.UTF-8
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8

From here the database should get setup with the UTF-8 encoding. Hope this helps as I have spent days searching for solutions to this, and came up with the bits and pieces from various discussions, but realized that the problem was timing of when the values are set...

1
votes

the environment variable doesn't work for chef resources.

According to the postgresql cookbook, you should set the attribute node['postgresql']['initdb_locale'] to setup the locale when initializing a database. For example, use this section under parent section name: serverin your .kitchen.yml:

attributes:
  postgresql:
    initdb_locale: "en_US.UTF_8"
1
votes

You can drop and recreate the postgres template database as UTF-8 after the fact. Not a perfect solution, but it does work in your Chef recipe. See: http://www.pebra.net/blog/2013/06/10/when-struggling-with-postgresql-and-utf8-slash-latin/

include_recipe "postgresql::server"
include_recipe "database::postgresql"

execute "Psql template1 to UTF8" do
user "postgres"
command <<-SQL
echo "
UPDATE pg_database SET datistemplate = FALSE WHERE datname = 'template1';
DROP DATABASE template1;
CREATE DATABASE template1 WITH TEMPLATE = template0 ENCODING = 'UNICODE' LC_CTYPE='en_US.utf8'      LC_COLLATE='en_US.utf8';
UPDATE pg_database SET datistemplate = TRUE WHERE datname = 'template1';
\\c template1
VACUUM FREEZE;" | psql postgres -t
SQL
# only_if '[ $(echo "select count(*) from pg_database where datname = \'template1\' and datcollate = \'en_US.utf8\'" |psql postgres -t) -eq 0 ]'
end