jenkins-bot has submitted this change and it was merged.
Change subject: Import wmflib from operations/puppet
......................................................................
Import wmflib from operations/puppet
This should really be done via git submodule, but I'm tired of waiting
for that and needed a better version of the php_ini function for some
hhvm cleanup. Imported from state at
43bb05b469e2a0f36bfc964a71e65f6fe93a5052 in operations/puppet.git.
Change-Id: I0069ce1210aeefca1ac45f2a997bcb3e089d4185
---
D puppet/modules/hhvm/lib/puppet/parser/functions/php_ini.rb
D puppet/modules/lib/lib/puppet/parser/functions/require_package.rb
A puppet/modules/wmflib/README.md
A puppet/modules/wmflib/lib/hiera/backend/mwyaml_backend.rb
A puppet/modules/wmflib/lib/hiera/backend/nuyaml_backend.rb
A puppet/modules/wmflib/lib/hiera/mwcache.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/apply_format.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/array_concat.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ensure_directory.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ensure_link.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ensure_service.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ordered_json.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ordered_yaml.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/php_ini.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/require_package.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/requires_realm.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/requires_ubuntu.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/shell_exports.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ssl_ciphersuite.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/to_milliseconds.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/to_seconds.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/ubuntu_version.rb
A puppet/modules/wmflib/lib/puppet/parser/functions/validate_ensure.rb
23 files changed, 1,319 insertions(+), 72 deletions(-)
Approvals:
Ori.livneh: Looks good to me, approved
jenkins-bot: Verified
diff --git a/puppet/modules/hhvm/lib/puppet/parser/functions/php_ini.rb
b/puppet/modules/hhvm/lib/puppet/parser/functions/php_ini.rb
deleted file mode 100644
index 98b5903..0000000
--- a/puppet/modules/hhvm/lib/puppet/parser/functions/php_ini.rb
+++ /dev/null
@@ -1,45 +0,0 @@
-# == Function: php_ini
-#
-# Serialize a hash into php.ini-style format. Takes one or more hashes
-# as arguments. If the argument list contains more than one hash, they
-# are merged together. In case of duplicate keys, hashes to the right
-# win.
-#
-# === Example
-#
-# php_ini({'server' => {'port' => 80}}) # => server.port = 80
-#
-
-def flatten_map(map, prefix=nil)
- map.inject({}) do |flat,(k,v)|
- k = [prefix, k].compact.join('.')
- v = v.include?('.') ? Float(v) : Integer(v) rescue v
- flat.merge! v.is_a?(Hash) ? flatten_map(v, k) : Hash[k, v]
- end
-end
-
-module Puppet::Parser::Functions
- newfunction(
- :php_ini,
- :type => :rvalue,
- :doc => <<-END
- Serialize a hash into php.ini-style format. Takes one or more hashes
- as arguments. If the argument list contains more than one hash, they
- are merged together. In case of duplicate keys, hashes to the right
- win.
-
- Example:
-
- php_ini({'server' => {'port' => 80}}) # => server.port = 80
-
- END
- ) do |args|
- fail 'php_ini() operates on hashes' if args.map(&:class).uniq != [Hash]
- args.map { |arg| flatten_map(arg) }.
- inject(:merge).
- sort.
- map { |kv| kv.join(' = ') }.
- push('').
- join("\n")
- end
-end
diff --git a/puppet/modules/lib/lib/puppet/parser/functions/require_package.rb
b/puppet/modules/lib/lib/puppet/parser/functions/require_package.rb
deleted file mode 100644
index 87781cb..0000000
--- a/puppet/modules/lib/lib/puppet/parser/functions/require_package.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-# == Function: require_package
-#
-# Declare a package as a dependency for the current scope.
-#
-# === Examples
-#
-# require_package('python-redis')
-#
-module Puppet::Parser::Functions
- newfunction(:require_package, :arity => 1) do |args|
- unless args.first.is_a?(String)
- fail(ArgumentError, 'require_package(): string argument required.')
- end
-
- package_name = args.first
- class_name = 'packages::' + package_name.tr('-', '_')
-
- unless compiler.topscope.find_hostclass(class_name)
- host = Puppet::Resource::Type.new(:hostclass, class_name)
- known_resource_types.add_hostclass(host)
- send Puppet::Parser::Functions.function(:create_resources),
- ['package', { package_name => { :ensure => :present } }]
- end
-
- send Puppet::Parser::Functions.function(:require), [class_name]
- end
-end
diff --git a/puppet/modules/wmflib/README.md b/puppet/modules/wmflib/README.md
new file mode 100644
index 0000000..882a17a
--- /dev/null
+++ b/puppet/modules/wmflib/README.md
@@ -0,0 +1,328 @@
+# wmflib
+
+Custom Puppet functions that help you get things done.
+
+
+## apply_format
+
+`apply_format( string $format, array $items )`
+
+Apply a format string to each element of an array.
+
+### Examples
+
+ $languages = [ 'finnish', 'french', 'greek', 'hebrew' ]
+ $packages = apply_format('texlive-lang-%s', $languages)
+
+
+## ensure_directory
+
+`ensure_directory( string|bool $ensure )`
+
+Takes a generic 'ensure' parameter value and convert it to an
+appropriate value for use with a directory declaration.
+
+If $ensure is 'true' or 'present', the return value is 'directory'.
+Otherwise, the return value is the unmodified $ensure parameter.
+
+### Examples
+
+ # Sample class which creates or removes '/srv/redis'
+ # based on the class's generic $ensure parameter:
+ class redis( $ensure = present ) {
+ package { 'redis-server':
+ ensure => $ensure,
+ }
+
+ file { '/srv/redis':
+ ensure => ensure_directory($ensure),
+ }
+ }
+
+
+## ensure_link
+
+`ensure_link( string|bool $ensure )`
+
+Takes a generic 'ensure' parameter value and convert it to an
+appropriate value for use with a symlink file declaration.
+
+If $ensure is 'true' or 'present', the return value is 'link'.
+Otherwise, the return value is the unmodified $ensure parameter.
+
+### Examples
+
+ # Sample class which creates or remove a symlink
+ # based on the class's generic $ensure parameter:
+ class rsyslog( $ensure = present ) {
+ package { 'rsyslog':
+ ensure => $ensure,
+ }
+
+ file { '/etc/rsyslog.d/50-default.conf':
+ ensure => ensure_link($ensure),
+ target => '/usr/share/rsyslog/50-default.conf',
+ }
+ }
+
+
+## ensure_service
+
+`ensure_service( string|bool $ensure )`
+
+Takes a generic 'ensure' parameter value and convert it to an
+appropriate value for use with a service declaration.
+
+If $ensure is 'true' or 'present', the return value is 'running'.
+Otherwise, the return value is 'stopped'.
+
+### Examples
+
+ # Sample class which starts or stops the redis service
+ # based on the class's generic $ensure parameter:
+ class redis( $ensure = present ) {
+ package { 'redis-server':
+ ensure => $ensure,
+ }
+ service { 'redis':
+ ensure => ensure_service($ensure),
+ require => Package['redis-server'],
+ }
+ }
+
+
+## ordered_json
+
+`ordered_json( hash $data [, hash $... ] )`
+
+Serialize a hash into JSON with lexicographically sorted keys.
+
+Because the order of keys in Ruby 1.8 hashes is undefined, 'to_pson'
+is not idempotent: i.e., the serialized form of the same hash object
+can vary from one invocation to the next. This causes problems
+whenever a JSON-serialized hash is included in a file template,
+because the variations in key order are picked up as file updates by
+Puppet, causing Puppet to replace the file and refresh dependent
+resources on every run.
+
+### Examples
+
+ # Render a Puppet hash as a configuration file:
+ $options = { 'useGraphite' => true, 'minVal' => '0.1' }
+ file { '/etc/kibana/config.json':
+ content => ordered_json($options),
+ }
+
+
+## ordered_yaml
+
+`ordered_yaml( mixed $data )`
+
+Emit a hash as YAML with keys (both shallow and deep) in sorted order.
+
+### Examples
+
+ # Render a Puppet hash as a configuration file:
+ $options = { 'useGraphite' => true, 'minVal' => '0.1' }
+ file { '/etc/kibana/config.yaml':
+ content => ordered_yaml($options),
+ }
+
+
+## php_ini
+
+`php_ini( hash $ini_settings [, hash $... ] )`
+
+Serialize a hash into php.ini-style format. Takes one or more hashes as
+arguments. If the argument list contains more than one hash, they are
+merged together. In case of duplicate keys, hashes to the right win.
+
+### Example
+
+ php_ini({'server' => {'port' => 80}}) # => server.port = 80
+
+
+## require_package
+
+`require_package( string $package_name [, string $... ] )`
+
+Declare one or more packages a dependency for the current scope.
+This is equivalent to declaring and requiring the package resources.
+In other words, it ensures the package(s) are installed before
+evaluating any of the resources in the current scope.
+
+### Examples
+
+ # Single package
+ require_package('python-redis')
+
+ # Multiple packages as arguments
+ require_package('redis-server', 'python-redis')
+
+ # Multiple packages as array
+ $deps = [ 'redis-server', 'python-redis' ]
+ require_package($deps)
+
+
+## requires_realm
+
+`requires_realm( string $realm, [ string $message ] )`
+
+Validate that the host realm is equal to some value.
+Abort catalog compilation if it is not.
+
+### Examples
+
+ # Fail unless running in Labs:
+ requires_realm('labs')
+
+
+## requires_ubuntu
+
+`requires_ubuntu( string $version_predicate )`
+
+Validate that the host Ubuntu version satisfies a version
+check. Abort catalog compilation if not.
+
+See the documentation for ubuntu_version() for supported
+predicate syntax.
+
+### Examples
+
+ # Fail unless version is Trusty
+ requires_ubuntu('trusty')
+
+ # Fail unless Trusty or newer
+ requires_ubuntu('> trusty')
+
+
+
+## shell_exports
+
+`shell_exports( hash $variables [, bool $uppercase_keys = true ] )`
+
+Generate shell environment variable declarations out of a Puppet hash.
+
+The hash keys are used as the variable names, and the values as
+the variable's values. Values are automatically quoted with double
+quotes. If the second parameter is true (the default), keys are
+automatically uppercased.
+
+### Examples
+
+Invocation:
+
+ shell_exports({
+ apache_run_user => 'apache',
+ apache_pid_file => '/var/run/apache2/apache2.pid',
+ })
+
+Output:
+
+ export APACHE_RUN_USER="apache"
+ export APACHE_PID_FILE="/var/run/apache2/apache2.pid"
+
+
+## ssl_ciphersuite
+
+`ssl_ciphersuite( string $servercode, string $encryption_type, int $hsts_days
)`
+
+Outputs the ssl configuration directives for use with either Nginx
+or Apache using our selection of ciphers and SSL options.
+
+Takes three arguments:
+
+- The servercode, or which browser-version combination to
+ support. At the moment only 'apache-2.2', 'apache-2.4' and 'nginx'
+ are supported.
+- The compatibility mode,indicating the degree of compatibility we
+ want to retain with older browsers (basically, IE6, IE7 and
+ Android prior to 3.0)
+- An optional argument, that if non-nil will set HSTS to max-age of
+ N days
+
+Whenever called, this function will output a list of strings that
+can be safely used in your configuration file as the ssl
+configuration part.
+
+### Examples
+
+ ssl_ciphersuite('apache-2.4', 'compat')
+ ssl_ciphersuite('nginx', 'strong')
+
+
+## to_milliseconds
+`to_milliseconds( string $time_spec )`
+
+Convert a unit of time expressed as a string to milliseconds.
+
+### Examples
+
+ to_milliseconds('1s') # 1000
+ to_milliseconds('1 second') # 1000
+
+
+## to_seconds
+`to_seconds( string $time_spec )`
+
+Convert a unit of time expressed as a string to seconds.
+
+### Examples
+
+ to_seconds('9000ms') # 9
+ to_seconds('1hr') # 3600
+ to_seconds('2 days') # 172800
+
+
+## ubuntu_version
+
+`ubuntu_version( string $version_predicate )`
+
+Performs semantic Ubuntu version comparison.
+
+Takes a single string argument containing a comparison operator
+followed by an optional space, followed by a comparison target,
+provided as Ubuntu version number or release name.
+
+The host's Ubuntu version will be compared to to the comparison target
+using the specified operator, returning a boolean. If no operator is
+present, the equality operator is assumed.
+
+Release names are case-insensitive. The comparison operator and
+comparison target can be provided as two separate arguments, if you
+prefer.
+
+### Examples
+
+ # True if Precise or newer
+ ubuntu_version('>= precise')
+ ubuntu_version('>= 12.04.4')
+
+ # True if older than Utopic
+ ubuntu_version('< utopic')
+
+ # True if newer than Precise
+ ubuntu_version('> precise')
+
+ # True if Trusty or older
+ ubuntu_version('<= trusty')
+
+ # True if exactly Trusty
+ ubuntu_version('trusty')
+ ubuntu_version('== trusty')
+
+ # True if anything but Trusty
+ ubuntu_version('!trusty')
+ ubuntu_version('!= trusty')
+
+
+## validate_ensure
+
+`validate_ensure( string $ensure )`
+
+Throw an error if the $ensure argument is not 'present' or 'absent'.
+
+### Examples
+
+ # Abort compilation if $ensure is invalid
+ validate_ensure($ensure)
diff --git a/puppet/modules/wmflib/lib/hiera/backend/mwyaml_backend.rb
b/puppet/modules/wmflib/lib/hiera/backend/mwyaml_backend.rb
new file mode 100644
index 0000000..6e0c638
--- /dev/null
+++ b/puppet/modules/wmflib/lib/hiera/backend/mwyaml_backend.rb
@@ -0,0 +1,49 @@
+require "hiera/mwcache"
+class Hiera
+ module Backend
+ class Mwyaml_backend
+ def initialize(cache=nil)
+ @cache = cache || Mwcache.new
+ end
+
+ def lookup(key, scope, order_override, resolution_type)
+ answer = nil
+ Hiera.debug("Looking up #{key}")
+
+ Backend.datasources(scope, order_override) do |source|
+ # Small hack: - we don't want to search any datasource but the
+ # labs/%{::instanceproject} hierarchy here; so we plainly exit
+ # in any other case
+ if m = /labs\/([^\/]+)$/.match(source)
+ source = m[1].capitalize
+ else
+ next
+ end
+ data = @cache.read(source, Hash, {}) do |content|
+ YAML.load(content)
+ end
+
+ next if data.empty?
+ next unless data.include?(key)
+
+ new_answer = Backend.parse_answer(data[key], scope)
+ case resolution_type
+ when :array
+ raise Exception, "Hiera type mismatch: expected Array and got
#{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of?
String
+ answer ||= []
+ answer << new_answer
+ when :hash
+ raise Exception, "Hiera type mismatch: expected Hash and got
#{new_answer.class}" unless new_answer.kind_of? Hash
+ answer ||= {}
+ answer = Backend.merge_answer(new_answer,answer)
+ else
+ answer = new_answer
+ break
+ end
+ end
+
+ return answer
+ end
+ end
+ end
+end
diff --git a/puppet/modules/wmflib/lib/hiera/backend/nuyaml_backend.rb
b/puppet/modules/wmflib/lib/hiera/backend/nuyaml_backend.rb
new file mode 100644
index 0000000..2f4e376
--- /dev/null
+++ b/puppet/modules/wmflib/lib/hiera/backend/nuyaml_backend.rb
@@ -0,0 +1,183 @@
+# Nuyaml Hiera backend - the yaml backend with some sugar on top
+#
+# Based on the original yaml_backend from hiera distribution, any
+# modification/addition:
+# Author: Giuseppe Lavagetto <[email protected]>
+# Copyright (c) 2014 Wikimedia Foundation
+#
+#
+# This backend allows some more flexibility over the vanilla yaml
+# backend, as path expansion in the lookup and even dynamic lookups.
+#
+# == Private path
+#
+# If you define a 'private' data source in hiera, we will look up
+# in a data.yaml file in the data dir we've specified as the datadir
+# for a 'private' backend, or in the default datadir as a fallback.
+#
+# == Path expansion
+#
+# Any hierarchy named in the backend-configuration section
+# :expand_path be expanded when looking the file up on disk. This
+# allows both to have a more granular set of files, but also to avoid
+# unnecessary cache evictions for cached data.
+#
+# === Example
+#
+# Say your hiera.yaml has defined
+#
+# :nuyaml:
+# :expand_path:
+# - module_data
+#
+# :hierarchy:
+# - common
+# - module_data
+#
+# then when searching hiera for say passwords::mysql::s1, hiera will
+# first load the #{datadir}/common.yaml file and search for
+# passwords::mysql::s1, then if not found, it will search for 's1'
+# inside the file #{datadir}/module_data/passwords/mysql.yaml
+#
+# Unless very small, all files should be split up like this.
+#
+# == Dynamic lookup
+#
+# Sometimes we want to search for data based on variables... that are
+# hosted within hiera! Dynamic lookup allows to define hierachies that
+# will determine the full path based on results from hiera
+# itself. Tricky? Let's see with an example
+#
+# === Example
+#
+# Say you have in your hiera config
+# :nuyaml:
+# :dynamic_lookup:
+# - role
+# :hierarchy:
+# - "host/%{fqdn}"
+# - role
+#
+# What will happen will be that any key we search (say $cluster) will
+# be first searched in the specific file for that host
+# (host/hostname.yaml), if not found, it will be searched as follows:
+# - if host/hostname.yaml contains a value for role, say
+# 'refrigerator', then lookup will continue in the
+# 'role/refrigerator.yaml' file
+# - else it will looked up in the 'role/default.yaml'
+#
+# Note that for added fun you may declare one part of the hierarchy to
+# be both dynamically looked up and expanded. It works!
+class Hiera
+ module Backend
+ class Nuyaml_backend
+
+ def initialize(cache=nil)
+ require 'yaml'
+ @cache = cache || Filecache.new
+ config = Config[:nuyaml]
+ @dynlookup = config[:dynlookup] || []
+ @expand_path = config[:expand_path] || []
+ end
+
+ def get_path(key, scope, source)
+ config_section = :nuyaml
+ # Special case: 'private' repository.
+ # We use a different datadir in this case.
+ # Example: private/common will search in the common source
+ # within the private datadir
+ if m = /private\/(.*)/.match(source)
+ config_section = :private
+ source = m[1]
+ end
+
+ Hiera.debug("The source is: #{source}")
+ # If the source is in the expand_path list, perform path
+ # expansion. This is thought to allow large codebases to live
+ # with fairly small yaml files as opposed to a very large one.
+ # Example:
+ # $apache::mpm::worker => 'worker' in common/apache/mpm.yaml
+ if @expand_path.include? source
+ namespaces = key.gsub(/^::/,'').split('::')
+ newkey = namespaces.pop
+
+ unless namespaces.empty?
+ source += "/".concat(namespaces.join('/'))
+ key = newkey
+ end
+ end
+
+ return key, Backend.datafile(config_section, scope, source, "yaml")
+ end
+
+ def lookup(key, scope, order_override, resolution_type)
+ answer = nil
+
+ Hiera.debug("Looking up #{key}")
+
+ Backend.datasources(scope, order_override) do |source|
+ # Yes this is kind of hacky. We look it up again on hiera,
+ # and build a source based on the lookup.
+ if @dynlookup.include? source
+ Hiera.debug("Dynamic lookup for source #{source}")
+ if key == source
+ next
+ end
+ dynsource = lookup(source, scope, order_override, :priority)
+ dynsource ||= 'default'
+ source += "/#{dynsource}"
+ end
+
+ Hiera.debug("Loading info from #{source} for #{key}")
+
+ lookup_key, yamlfile = get_path(key, scope, source)
+
+ Hiera.debug("Searching for #{lookup_key} in #{yamlfile}")
+
+ next if yamlfile.nil?
+
+ Hiera.debug("Loading file #{yamlfile}")
+
+ next unless File.exist?(yamlfile)
+
+ data = @cache.read(yamlfile, Hash) do |content|
+ YAML.load(content)
+ end
+
+ next if data.empty?
+ next unless data.include?(lookup_key)
+
+ # Extra logging that we found the key. This can be outputted
+ # multiple times if the resolution type is array or hash but that
+ # should be expected as the logging will then tell the user ALL the
+ # places where the key is found.
+ Hiera.debug("Found #{lookup_key} in #{source}")
+
+ # for array resolution we just append to the array whatever
+ # we find, we then goes onto the next file and keep adding to
+ # the array
+ #
+ # for priority searches we break after the first found data
+ # item
+
+ new_answer = Backend.parse_answer(data[lookup_key], scope)
+ case resolution_type
+ when :array
+ raise Exception, "Hiera type mismatch: expected Array and got
#{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of?
String
+ answer ||= []
+ answer << new_answer
+ when :hash
+ raise Exception, "Hiera type mismatch: expected Hash and got
#{new_answer.class}" unless new_answer.kind_of? Hash
+ answer ||= {}
+ answer = Backend.merge_answer(new_answer,answer)
+ else
+ answer = new_answer
+ break
+ end
+ end
+
+ return answer
+ end
+ end
+ end
+end
diff --git a/puppet/modules/wmflib/lib/hiera/mwcache.rb
b/puppet/modules/wmflib/lib/hiera/mwcache.rb
new file mode 100644
index 0000000..485f167
--- /dev/null
+++ b/puppet/modules/wmflib/lib/hiera/mwcache.rb
@@ -0,0 +1,100 @@
+class Hiera
+ class Mwcache < Filecache
+ def initialize
+ super
+ require 'httpclient'
+ require 'yaml'
+ require 'json'
+ config = Config[:mwyaml]
+ @httphost = config[:host] || 'https://wikitech.wikimedia.org'
+ @endpoint = config[:endpoint] || '/w/api.php'
+ @http = HTTPClient.new(:agent_name => 'HieraMwCache/0.1')
+ @stat_ttl = config[:cache_ttl] || 60
+ if defined? @http.ssl_config.ssl_version
+ @http.ssl_config.ssl_version = 'TLSv1_2'
+ else
+ # Note: this seem to work in later versions of the library,
+ # but has no effect. How cute, I <3 ruby.
+ @http.ssl_config.options = OpenSSL::SSL::OP_NO_SSLv3
+ end
+ end
+
+ def read_file(path, expected_type = Object, &block)
+ if stale?(path)
+ resp = get_from_mediawiki(path, true)
+ data = resp["*"]
+ @cache[path][:data] = block_given? ? yield(data) : data
+
+ if !@cache[path][:data].is_a?(expected_type)
+ raise TypeError, "Data retrieved from #{path} is #{data.class} not
#{expected_type}"
+ end
+ end
+
+ @cache[path][:data]
+ end
+
+ private
+
+ def stale?(path)
+ # Performs a request for the revision only
+ meta = path_metadata(path)
+
+ if @cache[path][:meta].nil?
+ @cache[path][:meta] = meta
+ return true
+ end
+ if @cache[path][:meta][:revision] == meta[:revision]
+ @cache[path][:meta][:ts] = meta[:ts]
+ return false
+ else
+ @cache[path][:meta] = meta
+ return true
+ end
+ rescue => detail
+ error = "Retrieving metadata from #{path} failed: #{detail}"
+ Hiera.warn(error)
+ # Fill this up with very safe defaults - we cache non-existence
+ # for cache_ttl as well.
+ @cache[path][:meta] = {:ts => Time.now.to_i, :revision => 0}
+ return true
+ end
+
+ def path_metadata(path)
+ now = Time.now.to_i
+ if @cache[path].nil?
+ @cache[path] = {:data => nil, :meta => nil}
+ elsif (now - @cache[path][:meta][:ts]) <= @stat_ttl
+ # if we already fetched the result within the last stat_ttl seconds,
+ # we don't bother killing the mediawiki instance with a flood of
requests
+ return @cache[path][:meta]
+ end
+ # TODO: add some locking mechanism for requests? Maybe overkill, maybe
not.
+ revision = get_from_mediawiki(path, false)["revid"]
+
+ return {:ts => now, :revision => revision}
+ end
+
+
+ def get_from_mediawiki(path,want_content)
+ what = want_content ? 'content' : 'ids'
+ query_string =
"action=query&prop=revisions&format=json&rvprop=#{what}&titles=Hiera:#{path}"
+ url = "#{@httphost}#{@endpoint}?#{query_string}"
+ Hiera.debug("Fetching #{url}")
+ res = @http.get(url)
+ if res.status_code != 200
+ raise IOError, "Could not correctly fetch revision for #{path}, HTTP
status code #{res.status_code}"
+ end
+ # We shamelessly throw exceptions here, and catch them upper in the chain
+ # specifically in stale? and Filecache.read
+ body = JSON.parse(res.body)
+ pages = body["query"]["pages"]
+ # Quoting Yuvi: "MediaWiki API doesn't give a fuck about HTTP status
codes"
+ if pages.keys.include? "-1"
+ raise IndexError, "The mediawiki page was non-existent"
+ end
+ #yes, it's that convoluted.
+ key = pages.keys[0]
+ return pages[key]["revisions"][0]
+ end
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/apply_format.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/apply_format.rb
new file mode 100644
index 0000000..d948a73
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/apply_format.rb
@@ -0,0 +1,16 @@
+# == Function: apply_format( string $format, array $items )
+#
+# Apply a format string to each element of an array.
+#
+# === Examples
+#
+# $languages = [ 'finnish', 'french', 'greek', 'hebrew' ]
+# $packages = apply_format('texlive-lang-%s', $languages)
+#
+module Puppet::Parser::Functions
+ newfunction(:apply_format, :type => :rvalue, :arity => 2) do |args|
+ format, *items = args
+ fail(ArgumentError, 'apply_format(): a format string is required') unless
format.is_a?(String)
+ items.flatten.map { |item| format % item }
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/array_concat.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/array_concat.rb
new file mode 100644
index 0000000..df9f60b
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/array_concat.rb
@@ -0,0 +1,28 @@
+# == Function: array_concat( $args... )
+#
+# Concatenates things into an array.
+# Array arguments are concatenated together
+# Other types (e.g. Hashes, Strings) are included as whole single elements
+#
+# === Examples
+#
+# $a1 = [ 'a', 'b', 'c' ]
+# $a2 = [ 'd', 'e' ]
+# $a3 = 'f'
+# $a4 = { 'g' => 'h' }
+# $all = array_concat($a1, $a2, $a3, $a4)
+# ### $all == [ 'a', 'b', 'c', 'd', 'e', 'f', { 'g' => 'h' } ]
+#
+module Puppet::Parser::Functions
+ newfunction(:array_concat, :type => :rvalue) do |args|
+ retval = Array.new
+ args.each do |arg|
+ if arg.is_a? Array
+ retval += arg
+ else
+ retval += [ arg ]
+ end
+ end
+ retval
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_directory.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_directory.rb
new file mode 100644
index 0000000..ef0f4ac
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_directory.rb
@@ -0,0 +1,31 @@
+# == Function: ensure_directory( string|bool $ensure )
+#
+# Takes a generic 'ensure' parameter value and convert it to an
+# appropriate value for use with a directory declaration.
+#
+# If $ensure is 'true' or 'present', the return value is 'directory'.
+# Otherwise, the return value is the unmodified $ensure parameter.
+#
+# === Examples
+#
+# # Sample class which creates or removes '/srv/redis'
+# # based on the class's generic $ensure parameter:
+# class redis( $ensure = present ) {
+# package { 'redis-server':
+# ensure => $ensure,
+# }
+# file { '/srv/redis':
+# ensure => ensure_directory($ensure),
+# }
+# }
+#
+module Puppet::Parser::Functions
+ newfunction(:ensure_directory, :type => :rvalue, :arity => 1) do |args|
+ ensure_param = args.first
+ case ensure_param
+ when 'present', 'true', true then 'directory'
+ when 'absent', 'false', false then ensure_param
+ else fail(ArgumentError, "ensure_directory(): invalid argument:
'#{ensure_param}'.")
+ end
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_link.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_link.rb
new file mode 100644
index 0000000..fd21f5a
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_link.rb
@@ -0,0 +1,32 @@
+# == Function: ensure_link( string|bool $ensure )
+#
+# Takes a generic 'ensure' parameter value and convert it to an
+# appropriate value for use with a symlink file declaration.
+#
+# If $ensure is 'true' or 'present', the return value is 'link'.
+# Otherwise, the return value is the unmodified $ensure parameter.
+#
+# === Examples
+#
+# # Sample class which creates or remove a symlink
+# # based on the class's generic $ensure parameter:
+# class rsyslog( $ensure = present ) {
+# package { 'rsyslog':
+# ensure => $ensure,
+# }
+# file { '/etc/rsyslog.d/50-default.conf':
+# ensure => ensure_link($ensure),
+# target => '/usr/share/rsyslog/50-default.conf',
+# }
+# }
+#
+module Puppet::Parser::Functions
+ newfunction(:ensure_link, :type => :rvalue, :arity => 1) do |args|
+ ensure_param = args.first
+ case ensure_param
+ when 'present', 'true', true then 'link'
+ when 'absent', 'false', false then ensure_param
+ else fail(ArgumentError, "ensure_link(): invalid argument:
'#{ensure_param}'.")
+ end
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_service.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_service.rb
new file mode 100644
index 0000000..7b42086
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ensure_service.rb
@@ -0,0 +1,32 @@
+# == Function: ensure_service( string|bool $ensure )
+#
+# Takes a generic 'ensure' parameter value and convert it to an
+# appropriate value for use with a service declaration.
+#
+# If $ensure is 'true' or 'present', the return value is 'running'.
+# Otherwise, the return value is 'stopped'.
+#
+# === Examples
+#
+# # Sample class which starts or stops the redis service
+# # based on the class's generic $ensure parameter:
+# class redis( $ensure = present ) {
+# package { 'redis-server':
+# ensure => $ensure,
+# }
+# service { 'redis':
+# ensure => ensure_service($ensure),
+# require => Package['redis-server'],
+# }
+# }
+#
+module Puppet::Parser::Functions
+ newfunction(:ensure_service, :type => :rvalue, :arity => 1) do |args|
+ ensure_param = args.first
+ case ensure_param
+ when 'running', 'present', 'true', true then 'running'
+ when 'stopped', 'absent', 'false', false then 'stopped'
+ else fail(ArgumentError, "ensure_service(): invalid argument:
'#{ensure_param}'.")
+ end
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/ordered_json.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ordered_json.rb
new file mode 100644
index 0000000..74a4e2c
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ordered_json.rb
@@ -0,0 +1,36 @@
+# == Function: ordered_json( hash $data [, hash $... ] )
+#
+# Serialize a hash into JSON with lexicographically sorted keys.
+#
+# Because the order of keys in Ruby 1.8 hashes is undefined, 'to_pson'
+# is not idempotent: i.e., the serialized form of the same hash object
+# can vary from one invocation to the next. This causes problems
+# whenever a JSON-serialized hash is included in a file template,
+# because the variations in key order are picked up as file updates by
+# Puppet, causing Puppet to replace the file and refresh dependent
+# resources on every run.
+#
+# === Examples
+#
+# # Render a Puppet hash as a configuration file:
+# $options = { 'useGraphite' => true, 'minVal' => '0.1' }
+# file { '/etc/kibana/config.json':
+# content => ordered_json($options),
+# }
+#
+def ordered_json(o)
+ case o
+ when Array
+ '[' + o.map { |x| ordered_json(x) }.join(', ') + ']'
+ when Hash
+ '{' + o.sort.map { |k, v| k.to_pson + ': ' + ordered_json(v) }.join(', ')
+ '}'
+ else
+ o.include?('.') ? Float(o).to_s : Integer(o).to_s rescue o.to_pson
+ end
+end
+
+module Puppet::Parser::Functions
+ newfunction(:ordered_json, :type => :rvalue, :arity => -2) do |args|
+ ordered_json(args.reduce(&:merge))
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/ordered_yaml.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ordered_yaml.rb
new file mode 100644
index 0000000..64973f4
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ordered_yaml.rb
@@ -0,0 +1,49 @@
+# == Function: ordered_yaml( mixed $data )
+#
+# Emit a hash as YAML with keys (both shallow and deep) in sorted order.
+#
+# === Examples
+#
+# # Render a Puppet hash as a configuration file:
+# $options = { 'useGraphite' => true, 'minVal' => '0.1' }
+# file { '/etc/kibana/config.yaml':
+# content => ordered_yaml($options),
+# }
+#
+require 'puppet/util/zaml.rb'
+
+def sort_keys_recursive(value)
+ # Prepare a value for YAML serialization by sorting its keys (if it is
+ # a hash) and the keys of any hash object that is contained within the
+ # value. Returns a new value.
+ case value
+ when Array
+ value.map { |elem| sort_keys_recursive(elem) }
+ when Hash
+ map = {}
+ def map.each_pair
+ map.sort.each { |p| yield p }
+ end
+ value.sort.reduce(map) { |h, (k, v)| h[k] = sort_keys_recursive(v); h }
+ when 'true', 'false'
+ value == 'true'
+ when :undef
+ nil
+ else
+ value.include?('.') ? Float(value) : Integer(value) rescue value
+ end
+end
+
+def dedent_string(string)
+ lines = string.split("\n")
+ return string if lines.empty?
+ min_indent = lines.map { |line| line.start_with?(" ") ? line.match(/^
+/).offset(0)[1] : 0 }.min
+ return string if min_indent.zero?
+ lines.map { |line| line.gsub(/^ {#{min_indent}}/, "") }.join("\n")
+end
+
+module Puppet::Parser::Functions
+ newfunction(:ordered_yaml, :type => :rvalue, :arity => 1) do |args|
+ dedent_string(ZAML.dump(sort_keys_recursive(args.first)).gsub(/^---.*?\n/,
'')) << "\n"
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/php_ini.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/php_ini.rb
new file mode 100644
index 0000000..6593989
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/php_ini.rb
@@ -0,0 +1,37 @@
+# == Function: php_ini( hash $ini_settings [, hash $... ] )
+#
+# Serialize a hash into php.ini-style format. Takes one or more hashes
+# as arguments. If the argument list contains more than one hash, they
+# are merged together. In case of duplicate keys, hashes to the right
+# win.
+#
+# === Example
+#
+# php_ini({'server' => {'port' => 80}}) # => server.port = 80
+#
+def ini_flatten(map, prefix = nil)
+ map.reduce({}) do |flat, (k, v)|
+ k = [prefix, k].compact.join('.')
+ flat.merge! v.is_a?(Hash) ? ini_flatten(v, k) : Hash[k, v]
+ end
+end
+
+def ini_cast(v)
+ v.include?('.') ? Float(v) : Integer(v) rescue v
+end
+
+module Puppet::Parser::Functions
+ newfunction(:php_ini, :type => :rvalue, :arity => -2) do |args|
+ if args.map(&:class).uniq != [Hash]
+ fail(ArgumentError, 'php_ini(): hash arguments required')
+ end
+ flat = args.map { |arg| ini_flatten(arg) }.inject(:merge)
+ options = flat.map do |k, vs|
+ case vs
+ when Array then vs.map { |v| "#{k}[#{v}] = #{ini_cast(v)}" }
+ else "#{k} = #{ini_cast(vs)}"
+ end
+ end
+ options.flatten.sort.push('').join("\n")
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/require_package.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/require_package.rb
new file mode 100644
index 0000000..91d69cd
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/require_package.rb
@@ -0,0 +1,33 @@
+# == Function: require_package( string $package_name [, string $... ] )
+#
+# Declare one or more packages a dependency for the current scope.
+# This is equivalent to declaring and requiring the package resources.
+# In other words, it ensures the package(s) are installed before
+# evaluating any of the resources in the current scope.
+#
+# === Examples
+#
+# # Single package
+# require_package('python-redis')
+#
+# # Multiple packages as arguments
+# require_package('redis-server', 'python-redis')
+#
+# # Multiple packages as array
+# $deps = [ 'redis-server', 'python-redis' ]
+# require_package($deps)
+#
+module Puppet::Parser::Functions
+ newfunction(:require_package, :arity => -2) do |args|
+ args.each do |package_name|
+ class_name = 'packages::' + package_name.tr('-', '_')
+ unless compiler.topscope.find_hostclass(class_name)
+ host = Puppet::Resource::Type.new(:hostclass, class_name)
+ known_resource_types.add_hostclass(host)
+ send Puppet::Parser::Functions.function(:create_resources),
+ ['package', { package_name => { :ensure => :present } }]
+ end
+ send Puppet::Parser::Functions.function(:require), [class_name]
+ end
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/requires_realm.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/requires_realm.rb
new file mode 100644
index 0000000..37ddab9
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/requires_realm.rb
@@ -0,0 +1,17 @@
+# == Function: requires_realm( string $realm, [ string $message ] )
+#
+# Validate that the host realm is equal to some value.
+# Abort catalog compilation if it is not.
+#
+# === Examples
+#
+# # Fail unless running in Labs:
+# requires_realm('labs')
+#
+module Puppet::Parser::Functions
+ newfunction(:requires_realm, :arity => 1) do |args|
+ realm, message = args
+ fail(ArgumentError, 'requires_realm(): string argument required') unless
realm.is_a?(String)
+ fail(Puppet::ParseError, message || "Realm '#{realm}' required.") unless
realm == lookupvar('realm')
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/requires_ubuntu.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/requires_ubuntu.rb
new file mode 100644
index 0000000..3e68cdc
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/requires_ubuntu.rb
@@ -0,0 +1,23 @@
+# == Function: requires_ubuntu( string $version_predicate )
+#
+# Validate that the host Ubuntu version satisfies a version
+# check. Abort catalog compilation if not.
+#
+# See the documentation for ubuntu_version() for supported
+# predicate syntax.
+#
+# === Examples
+#
+# # Fail unless version is Trusty
+# requires_ubuntu('trusty')
+#
+# # Fail unless Trusty or newer
+# requires_ubuntu('> trusty')
+#
+module Puppet::Parser::Functions
+ newfunction(:requires_ubuntu, :arity => 1) do |args|
+ Puppet::Parser::Functions.function(:ubuntu_version)
+ fail(ArgumentError, 'requires_ubuntu(): string argument required') unless
args.first.is_a?(String)
+ fail(Puppet::ParseError, "Ubuntu #{args.first} required.") unless
function_ubuntu_version(args)
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/shell_exports.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/shell_exports.rb
new file mode 100644
index 0000000..233ef3b
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/shell_exports.rb
@@ -0,0 +1,31 @@
+# == Function: shell_exports( hash $variables [, bool $uppercase_keys = true ]
)
+#
+# Generate shell environment variable declarations out of a Puppet hash.
+#
+# The hash keys are used as the variable names, and the values as
+# the variable's values. Values are automatically quoted with double
+# quotes. If the second parameter is true (the default), keys are
+# automatically uppercased.
+#
+# === Examples
+#
+# Invocation:
+#
+# shell_exports({
+# apache_run_user => 'apache',
+# apache_pid_file => '/var/run/apache2/apache2.pid',
+# })
+#
+# Output:
+#
+# export APACHE_RUN_USER="apache"
+# export APACHE_PID_FILE="/var/run/apache2/apache2.pid"
+#
+module Puppet::Parser::Functions
+ newfunction(:shell_exports, :type => :rvalue, :arity => 1) do |args|
+ vars, uppercase_keys = args
+ fail(ArgumentError, 'validate_ensure(): hash argument required') unless
vars.is_a?(Hash)
+ vars = Hash[vars.map { |k, v| [k.upcase, v] }] unless uppercase_keys ==
false
+ vars.sort.map { |k, v| "export #{k}=#{v.to_pson}" }.push('').join("\n")
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/ssl_ciphersuite.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ssl_ciphersuite.rb
new file mode 100644
index 0000000..26d0bf7
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ssl_ciphersuite.rb
@@ -0,0 +1,139 @@
+# == Function: ssl_ciphersuite( string $servercode, string $encryption_type,
int $hsts_days )
+#
+# Outputs the ssl configuration directives for use with either Nginx
+# or Apache using our selection of ciphers and SSL options.
+#
+# === Arguments
+#
+# Takes three arguments:
+#
+# - The servercode, or which browser-version combination to
+# support. At the moment only 'apache-2.2', 'apache-2.4' and 'nginx'
+# are supported.
+# - The compatibility mode,indicating the degree of compatibility we
+# want to retain with older browsers (basically, IE6, IE7 and
+# Android prior to 3.0)
+# - An optional argument, that if non-nil will set HSTS to max-age of
+# N days
+#
+# Whenever called, this function will output a list of strings that
+# can be safely used in your configuration file as the ssl
+# configuration part.
+#
+# == Examples
+#
+# ssl_ciphersuite('apache-2.4', 'compat')
+# ssl_ciphersuite('nginx', 'strong')
+#
+# == License
+#
+# Author: Giuseppe Lavagetto
+# Copyright 2014 Wikimedia Foundation
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+require 'puppet/util/package'
+
+module Puppet::Parser::Functions
+ ciphersuites = {
+ 'compat' =>
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!DH',
+ 'strong' =>
'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!DH'
+ }
+ newfunction(
+ :ssl_ciphersuite,
+ :type => :rvalue,
+ :doc => <<-END
+Outputs the ssl configuration part of the webserver config.
+Function parameters are:
+ servercode - either nginx, apache-2.2 or apache-2.4
+ encryption_type - either strong for PFS only, or compat for maximum
compatibility
+ hsts_days - how many days should the STS header live. If not expressed, HSTS
will
+ be disabled
+
+Examples:
+
+ ssl_ciphersuite('apache-2.4', 'compat') # Compatible config for apache 2.4
+ ssl_ciphersuite('nginx', 'strong', '365') # PFS-only, use HSTS for 365 days
+END
+ ) do |args|
+
+
+ if args.length < 2 || args.length > 3
+ fail(ArgumentError, 'ssl_ciphersuite() requires at least 2 arguments')
+ end
+
+ servercode = args.shift
+ case servercode
+ when 'apache-2.4' then
+ server = 'apache'
+ server_version = 24
+ when 'apache-2.2' then
+ server = 'apache'
+ server_version = 22
+ when 'nginx' then
+ server = 'nginx'
+ server_version = nil
+ else
+ fail(ArgumentError, "ssl_ciphersuite(): unknown server string
'#{servercode}'")
+ end
+
+ ciphersuite = args.shift
+ unless ciphersuites.has_key?(ciphersuite)
+ fail(ArgumentError, "ssl_ciphersuite(): unknown ciphersuite
'#{ciphersuite}'")
+ end
+
+ cipherlist = ciphersuites[ciphersuite]
+
+ if ciphersuite == 'strong' && server == 'apache' && server_version < 24
+ fail(ArgumentError, 'ssl_ciphersuite(): apache 2.2 cannot work in strong
PFS mode')
+ end
+ if args.length == 1
+ hsts_days = args.shift.to_i
+ else
+ hsts_days = nil
+ end
+
+ output = []
+
+ if server == 'apache'
+ case ciphersuite
+ when 'strong' then
+ output.push('SSLProtocol all -SSLv2 -SSLv3 -TLSv1')
+ when 'compat' then
+ output.push('SSLProtocol all -SSLv2 -SSLv3')
+ end
+ output.push("SSLCipherSuite #{cipherlist}")
+ output.push('SSLHonorCipherOrder On')
+ unless hsts_days.nil?
+ hsts_seconds = hsts_days * 86400
+ output.push("Header set Strict-Transport-Security
\"max-age=#{hsts_seconds}\"")
+ end
+ else
+ # nginx
+ case ciphersuite
+ when 'strong' then
+ output.push('ssl_protocols TLSv1.1 TLSv1.2;')
+ when 'compat' then
+ output.push('ssl_protocols TLSv1 TLSv1.1 TLSv1.2;')
+ end
+ output.push("ssl_ciphers #{cipherlist};")
+ output.push('ssl_prefer_server_ciphers on;')
+ unless hsts_days.nil?
+ hsts_seconds = hsts_days * 86400
+ output.push("add_header Strict-Transport-Security
\"max-age=#{hsts_seconds}\";")
+ end
+ end
+ return output
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/to_milliseconds.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/to_milliseconds.rb
new file mode 100644
index 0000000..2421afe
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/to_milliseconds.rb
@@ -0,0 +1,33 @@
+# -*- coding: UTF-8 -*-
+#
+# == Function: to_milliseconds( string $time_spec )
+#
+# Convert a unit of time expressed as a string to milliseconds.
+#
+# === Examples
+#
+# to_milliseconds('1s') # 1000
+# to_milliseconds('1 second') # 1000
+#
+module Puppet::Parser::Functions
+ newfunction(:to_milliseconds, :type => :rvalue, :arity => 1) do |args|
+ time_spec = args.first
+ /^([0-9.+e]+)\s*(.*).?$/ =~ time_spec.downcase
+ count, unit = $1, $2
+ factor = case unit
+ when /^n/ then 1.0e-6 # nanoseconds
+ when /^u/ then 1.0e-3 # microseconds
+ when /^(ms|mil)/ then 1.0 # milliseconds
+ when /^s/ then 1.0e3 # seconds
+ when /^(m|min)/ then 6.0e4 # minutes
+ when /^h/ then 3.6e6 # hours
+ when /^d/ then 8.64e7 # days
+ when /^w/ then 6.048e8 # weeks
+ when /^mo/ then 2.62974e9 # months
+ when /^y/ then 3.15569e10 # years
+ else fail(ArgumentError, "to_milliseconds(): Invalid time spec
#{time_spec.inspect}")
+ end
+ ms = factor * Float(count)
+ ms.to_i == ms ? ms.to_i : ms
+ end
+end
diff --git a/puppet/modules/wmflib/lib/puppet/parser/functions/to_seconds.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/to_seconds.rb
new file mode 100644
index 0000000..9340396
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/to_seconds.rb
@@ -0,0 +1,16 @@
+# == Function: to_seconds( string $time_spec )
+#
+# Convert a unit of time expressed as a string to seconds.
+#
+# === Examples
+#
+# to_seconds('9000ms') # 9
+# to_seconds('1hr') # 3600
+# to_seconds('2 days') # 172800
+#
+module Puppet::Parser::Functions
+ newfunction(:to_seconds, :type => :rvalue, :arity => 1) do |args|
+ s = send(Puppet::Parser::Functions.function(:to_milliseconds), args) /
1000.0
+ s.to_i == s ? s.to_i : s
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/ubuntu_version.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/ubuntu_version.rb
new file mode 100644
index 0000000..6c998b3
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/ubuntu_version.rb
@@ -0,0 +1,90 @@
+# == Function: ubuntu_version( string $version_predicate )
+#
+# Performs semantic Ubuntu version comparison.
+#
+# Takes a single string argument containing a comparison operator
+# followed by an optional space, followed by a comparison target,
+# provided as Ubuntu version number or release name.
+#
+# The host's Ubuntu version will be compared to to the comparison target
+# using the specified operator, returning a boolean. If no operator is
+# present, the equality operator is assumed.
+#
+# Release names are case-insensitive. The comparison operator and
+# comparison target can be provided as two separate arguments, if you
+# prefer.
+#
+# === Examples
+#
+# # True if Precise or newer
+# ubuntu_version('>= precise')
+# ubuntu_version('>= 12.04.4')
+#
+# # True if older than Utopic
+# ubuntu_version('< utopic')
+#
+# # True if newer than Precise
+# ubuntu_version('> precise')
+#
+# # True if Trusty or older
+# ubuntu_version('<= trusty')
+#
+# # True if exactly Trusty
+# ubuntu_version('trusty')
+# ubuntu_version('== trusty')
+#
+# # True if anything but Trusty
+# ubuntu_version('!trusty')
+# ubuntu_version('!= trusty')
+#
+require 'puppet/util/package'
+
+module Puppet::Parser::Functions
+ ubuntu_releases = {
+ 'hardy' => '8.04',
+ 'intrepid' => '8.10',
+ 'jaunty' => '9.04',
+ 'karmic' => '9.10',
+ 'lucid' => '10.04.4',
+ 'maverick' => '10.10',
+ 'natty' => '11.04',
+ 'oneiric' => '11.10',
+ 'precise' => '12.04.4',
+ 'quantal' => '12.10',
+ 'raring' => '13.04',
+ 'saucy' => '13.10',
+ 'trusty' => '14.04',
+ 'utopic' => '14.10'
+ }
+
+ newfunction(:ubuntu_version, :type => :rvalue, :arity => 1) do |args|
+ return false unless lookupvar('lsbdistid') == 'Ubuntu'
+
+ unless args.length <= 2 && args.map(&:class).uniq == [String]
+ fail(ArgumentError, 'ubuntu_version() requires a string argument')
+ end
+
+ expr = args.join(' ')
+ unless expr =~ /^([<>=]*) *([\w\.]+)$/
+ fail(ArgumentError, "ubuntu_version(): invalid expression '#{expr}'")
+ end
+
+ current = lookupvar('lsbdistrelease')
+ operator = $1
+ other = ubuntu_releases[$2.downcase] || $2
+ unless /^[\d.]+$/ =~ other
+ fail(ArgumentError, "ubuntu_version(): unknown release '#{other}'")
+ end
+
+ cmp = Puppet::Util::Package.versioncmp(current, other)
+ case operator
+ when '', '=', '==' then cmp == 0
+ when '!=', '!' then cmp != 0
+ when '>' then cmp == 1
+ when '<' then cmp == -1
+ when '>=' then cmp >= 0
+ when '<=' then cmp <= 0
+ else fail(ArgumentError, "ubuntu_version(): unknown comparison operator
'#{operator}'")
+ end
+ end
+end
diff --git
a/puppet/modules/wmflib/lib/puppet/parser/functions/validate_ensure.rb
b/puppet/modules/wmflib/lib/puppet/parser/functions/validate_ensure.rb
new file mode 100644
index 0000000..f17a9dd
--- /dev/null
+++ b/puppet/modules/wmflib/lib/puppet/parser/functions/validate_ensure.rb
@@ -0,0 +1,16 @@
+# == Function: validate_ensure( string $ensure )
+#
+# Throw an error if the $ensure argument is not 'present' or 'absent'.
+#
+# === Examples
+#
+# # Abort compilation if $ensure is invalid
+# validate_ensure($ensure)
+#
+module Puppet::Parser::Functions
+ newfunction(:validate_ensure, :arity => 1) do |args|
+ unless %w(present absent).include?(args.first)
+ fail(Puppet::ParseError, "$ensure must be \"present\" or \"absent\"
(got: #{args.first.inspect}).")
+ end
+ end
+end
--
To view, visit https://gerrit.wikimedia.org/r/172004
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I0069ce1210aeefca1ac45f2a997bcb3e089d4185
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/vagrant
Gerrit-Branch: master
Gerrit-Owner: BryanDavis <[email protected]>
Gerrit-Reviewer: BryanDavis <[email protected]>
Gerrit-Reviewer: Ori.livneh <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits