Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactored workhorse methods into libraries/default.rb

  • Loading branch information...
commit 160410c827ce0b5619a8b4271870a25075ebc0f8 1 parent 98f4a51
@davidc-donorschoose davidc-donorschoose authored
View
2  README.md
@@ -257,7 +257,7 @@ sizes, you would experiment with this node JSON attribute:
"postgresql": {
"config": {
- "shared_buffers": "2.5GB"
+ "shared_buffers": "3GB"
}
}
View
294 libraries/default.rb
@@ -0,0 +1,294 @@
+#
+# Cookbook Name:: postgresql
+# Library:: default
+# Author:: David Crane (<davidc@donorschoose.org>)
+#
+# 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.
+#
+
+module Opscode
+ module PostgresqlHelpers
+
+#######
+# Function to truncate value to 4 significant bits, render human readable.
+# Used in recipes/config_initdb.rb to set this attribute:
+#
+# The memory settings (shared_buffers, effective_cache_size, work_mem,
+# maintenance_work_mem and wal_buffers) will be rounded down to keep
+# the 4 most significant bits, so that SHOW will be likely to use a
+# larger divisor. The output is actually a human readable string that
+# ends with "GB", "MB" or "kB" if over 1023, exactly what Postgresql
+# will expect in a postgresql.conf setting. The output may be up to
+# 6.25% less than the original value because of the rounding.
+def binaryround(value)
+
+ # Keep a multiplier which grows through powers of 1
+ multiplier = 1
+
+ # Truncate value to 4 most significant bits
+ while value >= 16
+ value = (value / 2).floor
+ multiplier = multiplier * 2
+ end
+
+ # Factor any remaining powers of 2 into the multiplier
+ while value == 2*((value / 2).floor)
+ value = (value / 2).floor
+ multiplier = multiplier * 2
+ end
+
+ # Factor enough powers of 2 back into the value to
+ # leave the multiplier as a power of 1024 that can
+ # be represented as units of "GB", "MB" or "kB".
+ if multiplier >= 1024*1024*1024
+ while multiplier > 1024*1024*1024
+ value = 2*value
+ multiplier = (multiplier/2).floor
+ end
+ multiplier = 1
+ units = "GB"
+
+ elsif multiplier >= 1024*1024
+ while multiplier > 1024*1024
+ value = 2*value
+ multiplier = (multiplier/2).floor
+ end
+ multiplier = 1
+ units = "MB"
+
+ elsif multiplier >= 1024
+ while multiplier > 1024
+ value = 2*value
+ multiplier = (multiplier/2).floor
+ end
+ multiplier = 1
+ units = "kB"
+
+ else
+ units = ""
+ end
+
+ # Now we can return a nice human readable string.
+ return "#{multiplier * value}#{units}"
+end
+
+#######
+# Locale Configuration
+
+# Function to test the date order.
+# Used in recipes/config_initdb.rb to set this attribute:
+# node.default['postgresql']['config']['datestyle']
+def locale_date_order
+ # Test locale conversion of mon=11, day=22, year=33
+ testtime = DateTime.new(2033,11,22,0,0,0,"-00:00")
+ #=> #<DateTime: 2033-11-22T00:00:00-0000 ...>
+
+ # %x - Preferred representation for the date alone, no time
+ res = testtime.strftime("%x")
+
+ if res.nil?
+ return 'mdy'
+ end
+
+ posM = res.index("11")
+ posD = res.index("22")
+ posY = res.index("33")
+
+ if (posM.nil? || posD.nil? || posY.nil?)
+ return 'mdy'
+ elseif (posY < posM && posM < posD)
+ return 'ymd'
+ elseif (posD < posM)
+ return 'dmy'
+ else
+ return 'mdy'
+ end
+end
+
+#######
+# Timezone Configuration
+require 'find'
+
+# Function to determine where the system stored shared timezone data.
+# Used in recipes/config_initdb.rb to detemine where it should have
+# select_default_timezone(tzdir) search.
+def pg_TZDIR()
+ # System time zone conversions are controlled by a timezone data file
+ # identified through environment variables (TZ and TZDIR) and/or file
+ # and directory naming conventions specific to the Linux distribution.
+ # Each of these timezone names will have been loaded into the PostgreSQL
+ # pg_timezone_names view by the package maintainer.
+ #
+ # Instead of using the timezone name configured as the system default,
+ # the PostgreSQL server uses ones named in postgresql.conf settings
+ # (timezone and log_timezone). The initdb utility does initialize those
+ # settings to the timezone name that corresponds to the system default.
+ #
+ # The system's timezone name is actually a filename relative to the
+ # shared zoneinfo directory. That is usually /usr/share/zoneinfo, but
+ # it was /usr/lib/zoneinfo in older distributions and can be anywhere
+ # if specified by the environment variable TZDIR. The tzset(3) manpage
+ # seems to indicate the following precedence:
+ tzdir = nil
+ if ::File.directory?("/usr/lib/zoneinfo")
+ tzdir = "/usr/lib/zoneinfo"
+ else
+ share_path = [ ENV['TZDIR'], "/usr/share/zoneinfo" ].compact.first
+ if ::File.directory?(share_path)
+ tzdir = share_path
+ end
+ end
+ return tzdir
+end
+
+#######
+# Function to support select_default_timezone(tzdir), which is
+# used in recipes/config_initdb.rb.
+def validate_zone(tzname)
+ # PostgreSQL does not support leap seconds, so this function tests
+ # the usual Linux tzname convention to avoid a misconfiguration.
+ # Assume that the tzdata package maintainer has kept all timezone
+ # data files with support for leap seconds is kept under the
+ # so-named "right/" subdir of the shared zoneinfo directory.
+ #
+ # The original PostgreSQL initdb is not Unix-specific, so it did a
+ # very complicated, thorough test in its pg_tz_acceptable() function
+ # that I could not begin to understand how to do in ruby :).
+ #
+ # Testing the tzname is good enough, since a misconfiguration
+ # will result in an immediate fatal error when the PostgreSQL
+ # service is started, with pgstartup.log messages such as:
+ # LOG: time zone "right/US/Eastern" appears to use leap seconds
+ # DETAIL: PostgreSQL does not support leap seconds.
+
+ if tzname.index("right/") == 0
+ return false
+ else
+ return true
+ end
+end
+
+# Function to support select_default_timezone(tzdir), which is
+# used in recipes/config_initdb.rb.
+def scan_available_timezones(tzdir)
+ # There should be an /etc/localtime zoneinfo file that is a link to
+ # (or a copy of) a timezone data file under tzdir, which should have
+ # been installed under the "share" directory by the tzdata package.
+ #
+ # The initdb utility determines which shared timezone file is being
+ # used as the system's default /etc/localtime. The timezone name is
+ # the timezone file path relative to the tzdir.
+
+ bestzonename = nil
+
+ if (tzdir.nil?)
+ Chef::Log.error("The zoneinfo directory not found (looked for /usr/share/zoneinfo and /usr/lib/zoneinfo)")
+ elsif !::File.exists?("/etc/localtime")
+ Chef::Log.error("The system zoneinfo file not found (looked for /etc/localtime)")
+ elsif ::File.directory?("/etc/localtime")
+ Chef::Log.error("The system zoneinfo file not found (/etc/localtime is a directory instead)")
+ elsif ::File.symlink?("/etc/localtime")
+ # PostgreSQL initdb doesn't use the symlink target, but this
+ # certainly will make sense to any system administrator. A full
+ # scan of the tzdir to find the shortest filename could result
+ # "US/Eastern" instead of "America/New_York" as bestzonename,
+ # in spite of what the sysadmin had specified in the symlink.
+ # (There are many duplicates under tzdir, with the same timezone
+ # content appearing as an average of 2-3 different file names.)
+ path = ::File.readlink("/etc/localtime")
+ bestzonename = path.gsub("#{tzdir}/","")
+ else # /etc/localtime is a file, so scan for it under tzdir
+ localtime_content = File.read("/etc/localtime")
+
+ Find.find(tzdir) do |path|
+ # Only consider files (skip directories or symlinks)
+ if !::File.directory?(path) && !::File.symlink?(path)
+ # Ignore any file named "posixrules" or "localtime"
+ if ::File.basename(path) != "posixrules" && ::File.basename(path) != "localtime"
+ # Do consider if content exactly matches /etc/localtime.
+ if localtime_content == File.read(path)
+ tzname = path.gsub("#{tzdir}/","")
+ if validate_zone(tzname)
+ if (bestzonename.nil? ||
+ tzname.length < bestzonename.length ||
+ (tzname.length == bestzonename.length &&
+ (tzname <=> bestzonename) < 0)
+ )
+ bestzonename = tzname
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+
+ return bestzonename
+end
+
+# Function to support select_default_timezone(tzdir), which is
+# used in recipes/config_initdb.rb.
+def identify_system_timezone(tzdir)
+ resultbuf = scan_available_timezones(tzdir)
+
+ if !resultbuf.nil?
+ # Ignore Olson's rather silly "Factory" zone; use GMT instead
+ if (resultbuf <=> "Factory") == 0
+ resultbuf = nil
+ end
+
+ else
+ # Did not find the timezone. Fallback to use a GMT zone. Note that the
+ # Olson timezone database names the GMT-offset zones in POSIX style: plus
+ # is west of Greenwich.
+ testtime = DateTime.now
+ std_ofs = testtime.strftime("%:z").split(":")[0].to_i
+
+ resultbuf = [
+ "Etc/GMT",
+ (-std_ofs > 0) ? "+" : "",
+ (-std_ofs).to_s
+ ].join('')
+ end
+
+ return resultbuf
+end
+
+#######
+# Function to determine the name of the system's default timezone.
+# Used in recipes/config_initdb.rb to set these attributes:
+# node.default['postgresql']['config']['log_timezone']
+# node.default['postgresql']['config']['timezone']
+def select_default_timezone(tzdir)
+
+ system_timezone = nil
+
+ # Check TZ environment variable
+ tzname = ENV['TZ']
+ if !tzname.nil? && !tzname.empty? && validate_zone(tzname)
+ system_timezone = tzname
+
+ else
+ # Nope, so try to identify system timezone from /etc/localtime
+ tzname = identify_system_timezone(tzdir)
+ if validate_zone(tzname)
+ system_timezone = tzname
+ end
+ end
+
+ return system_timezone
+end
+
+# End the Opscode::PostgresqlHelper module
+ end
+end
View
193 recipes/config_initdb.rb
@@ -1,6 +1,7 @@
#
# Cookbook Name:: postgresql
# Recipe:: config_initdb
+# Author:: David Crane (<davidc@donorschoose.org>)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,6 +17,11 @@
#
#######
+# Load the locale_date_order() and select_default_timezone(tzdir)
+# methods from libraries/default.rb
+::Chef::Recipe.send(:include, Opscode::PostgresqlHelpers)
+
+#######
# This recipe is derived from the setup_config() source code in the
# PostgreSQL initdb utility. It determines postgresql.conf settings that
# conform to the system's locale and timezone configuration, and also
@@ -62,34 +68,7 @@
#######
# Locale Configuration
-# Function to test the date order.
-def locale_date_order
- # Test locale conversion of mon=11, day=22, year=33
- testtime = DateTime.new(2033,11,22,0,0,0,"-00:00")
- #=> #<DateTime: 2033-11-22T00:00:00-0000 ...>
-
- # %x - Preferred representation for the date alone, no time
- res = testtime.strftime("%x")
-
- if res.nil?
- return 'mdy'
- end
-
- posM = res.index("11")
- posD = res.index("22")
- posY = res.index("33")
-
- if (posM.nil? || posD.nil? || posY.nil?)
- return 'mdy'
- elseif (posY < posM && posM < posD)
- return 'ymd'
- elseif (posD < posM)
- return 'dmy'
- else
- return 'mdy'
- end
-end
-
+# See libraries/default.rb for the locale_date_order() method.
node.default['postgresql']['config']['datestyle'] = "iso, #{locale_date_order()}"
# According to the locale(1) manpage, the locale settings are determined
@@ -143,168 +122,16 @@ def locale_date_order
else
nil
end
-
+
#######
# Timezone Configuration
-require 'find'
-
-def validate_zone(tzname)
- # PostgreSQL does not support leap seconds, so this function tests
- # the usual Linux tzname convention to avoid a misconfiguration.
- # Assume that the tzdata package maintainer has kept all timezone
- # data files with support for leap seconds is kept under the
- # so-named "right/" subdir of the shared zoneinfo directory.
- #
- # The original PostgreSQL initdb is not Unix-specific, so it did a
- # very complicated, thorough test in its pg_tz_acceptable() function
- # that I could not begin to understand how to do in ruby :).
- #
- # Testing the tzname is good enough, since a misconfiguration
- # will result in an immediate fatal error when the PostgreSQL
- # service is started, with pgstartup.log messages such as:
- # LOG: time zone "right/US/Eastern" appears to use leap seconds
- # DETAIL: PostgreSQL does not support leap seconds.
-
- if tzname.index("right/") == 0
- return false
- else
- return true
- end
-end
-
-def scan_available_timezones(tzdir)
- # There should be an /etc/localtime zoneinfo file that is a link to
- # (or a copy of) a timezone data file under tzdir, which should have
- # been installed under the "share" directory by the tzdata package.
- #
- # The initdb utility determines which shared timezone file is being
- # used as the system's default /etc/localtime. The timezone name is
- # the timezone file path relative to the tzdir.
-
- bestzonename = nil
-
- if (tzdir.nil?)
- Chef::Log.error("The zoneinfo directory not found (looked for /usr/share/zoneinfo and /usr/lib/zoneinfo)")
- elsif !::File.exists?("/etc/localtime")
- Chef::Log.error("The system zoneinfo file not found (looked for /etc/localtime)")
- elsif ::File.directory?("/etc/localtime")
- Chef::Log.error("The system zoneinfo file not found (/etc/localtime is a directory instead)")
- elsif ::File.symlink?("/etc/localtime")
- # PostgreSQL initdb doesn't use the symlink target, but this
- # certainly will make sense to any system administrator. A full
- # scan of the tzdir to find the shortest filename could result
- # "US/Eastern" instead of "America/New_York" as bestzonename,
- # in spite of what the sysadmin had specified in the symlink.
- # (There are many duplicates under tzdir, with the same timezone
- # content appearing as an average of 2-3 different file names.)
- path = ::File.readlink("/etc/localtime")
- bestzonename = path.gsub("#{tzdir}/","")
- else # /etc/localtime is a file, so scan for it under tzdir
- localtime_content = File.read("/etc/localtime")
-
- Find.find(tzdir) do |path|
- # Only consider files (skip directories or symlinks)
- if !::File.directory?(path) && !::File.symlink?(path)
- # Ignore any file named "posixrules" or "localtime"
- if ::File.basename(path) != "posixrules" && ::File.basename(path) != "localtime"
- # Do consider if content exactly matches /etc/localtime.
- if localtime_content == File.read(path)
- tzname = path.gsub("#{tzdir}/","")
- if validate_zone(tzname)
- if (bestzonename.nil? ||
- tzname.length < bestzonename.length ||
- (tzname.length == bestzonename.length &&
- (tzname <=> bestzonename) < 0)
- )
- bestzonename = tzname
- end
- end
- end
- end
- end
- end
- end
-
- return bestzonename
-end
-
-def identify_system_timezone(tzdir)
- resultbuf = scan_available_timezones(tzdir)
-
- if !resultbuf.nil?
- # Ignore Olson's rather silly "Factory" zone; use GMT instead
- if (resultbuf <=> "Factory") == 0
- resultbuf = nil
- end
-
- else
- # Did not find the timezone. Fallback to use a GMT zone. Note that the
- # Olson timezone database names the GMT-offset zones in POSIX style: plus
- # is west of Greenwich.
- testtime = DateTime.now
- std_ofs = testtime.strftime("%:z").split(":")[0].to_i
-
- resultbuf = [
- "Etc/GMT",
- (-std_ofs > 0) ? "+" : "",
- (-std_ofs).to_s
- ].join('')
- end
-
- return resultbuf
-end
-
-def select_default_timezone(tzdir)
-
- system_timezone = nil
-
- # Check TZ environment variable
- tzname = ENV['TZ']
- if !tzname.nil? && !tzname.empty? && validate_zone(tzname)
- system_timezone = tzname
-
- else
- # Nope, so try to identify system timezone from /etc/localtime
- tzname = identify_system_timezone(tzdir)
- if validate_zone(tzname)
- system_timezone = tzname
- end
- end
-
- return system_timezone
-end
-
-# System time zone conversions are controlled by a timezone data file
-# identified through environment variables (TZ and TZDIR) and/or file
-# and directory naming conventions specific to the Linux distribution.
-# Each of these timezone names will have been loaded into the PostgreSQL
-# pg_timezone_names view by the package maintainer.
-#
-# Instead of using the timezone name configured as the system default,
-# the PostgreSQL server uses ones named in postgresql.conf settings
-# (timezone and log_timezone). The initdb utility does initialize those
-# settings to the timezone name that corresponds to the system default.
-#
-# The system's timezone name is actually a filename relative to the
-# shared zoneinfo directory. That is usually /usr/share/zoneinfo, but
-# it was /usr/lib/zoneinfo in older distributions and can be anywhere
-# if specified by the environment variable TZDIR. The tzset(3) manpage
-# seems to indicate the following precedence:
-tzdirpath = nil
-if ::File.directory?("/usr/lib/zoneinfo")
- tzdirpath = "/usr/lib/zoneinfo"
-else
- share_path = [ ENV['TZDIR'], "/usr/share/zoneinfo" ].compact.first
- if ::File.directory?(share_path)
- tzdirpath = share_path
- end
-end
# Determine the name of the system's default timezone and specify node
# defaults for the postgresql.cof settings. If the timezone cannot be
# identified, do as initdb would do: leave it unspecified so PostgreSQL
# uses it's internal default of GMT.
-default_timezone = select_default_timezone(tzdirpath)
+tzdirpath = pg_TZDIR() # See libraries/default.rb
+default_timezone = select_default_timezone(tzdirpath) # See libraries/default.rb
if !default_timezone.nil?
node.default['postgresql']['config']['log_timezone'] = default_timezone
node.default['postgresql']['config']['timezone'] = default_timezone
View
65 recipes/config_pgtune.rb
@@ -1,6 +1,7 @@
#
# Cookbook Name:: postgresql
# Recipe:: config_pgtune
+# Author:: David Crane (<davidc@donorschoose.org>)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -16,6 +17,10 @@
#
#######
+# Load the binaryround(value) method from libraries/default.rb
+::Chef::Recipe.send(:include, Opscode::PostgresqlHelpers)
+
+#######
# This recipe is based on Greg Smith's pgtune script (the Feb 1, 2012
# version at https://github.com/gregs1104/pgtune). Introduction: pgtune
# takes the wimpy default postgresql.conf and expands the database
@@ -133,66 +138,6 @@
mem = total_memory.split("kB")[0].to_i / 1024 # in MB
#######
-# The memory settings (shared_buffers, effective_cache_size, work_mem,
-# maintenance_work_mem and wal_buffers) will be rounded down to keep
-# the 4 most significant bits, so that SHOW will be likely to use a
-# larger divisor. The output is actually a human readable string that
-# ends with "GB", "MB" or "kB" if over 1023, exactly what Postgresql
-# will expect in a postgresql.conf setting. The output may be up to
-# 6.25% less than the original value because of the rounding.
-def binaryround(value)
-
- # Keep a multiplier which grows through powers of 1
- multiplier = 1
-
- # Truncate value to 4 most significant bits
- while value >= 16
- value = (value / 2).floor
- multiplier = multiplier * 2
- end
-
- # Factor any remaining powers of 2 into the multiplier
- while value == 2*((value / 2).floor)
- value = (value / 2).floor
- multiplier = multiplier * 2
- end
-
- # Factor enough powers of 2 back into the value to
- # leave the multiplier as a power of 1024 that can
- # be represented as units of "GB", "MB" or "kB".
- if multiplier >= 1024*1024*1024
- while multiplier > 1024*1024*1024
- value = 2*value
- multiplier = (multiplier/2).floor
- end
- multiplier = 1
- units = "GB"
-
- elsif multiplier >= 1024*1024
- while multiplier > 1024*1024
- value = 2*value
- multiplier = (multiplier/2).floor
- end
- multiplier = 1
- units = "MB"
-
- elsif multiplier >= 1024
- while multiplier > 1024
- value = 2*value
- multiplier = (multiplier/2).floor
- end
- multiplier = 1
- units = "kB"
-
- else
- units = ""
- end
-
- # Now we can return a nice human readable string.
- return "#{multiplier * value}#{units}"
-end
-
-#######
# RAM-related settings computed as in Greg Smith's pgtune script.
# Remember that con and mem were either chosen above based on the
# db_type or the actual total memory, or were passed in attributes.
Please sign in to comment.
Something went wrong with that request. Please try again.