File Manager
Viewing File: locale.rb
#
# Copyright:: 2011-2016, Heavy Water Software Inc.
# Copyright:: Copyright (c) Chef Software Inc.
#
# 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.
#
require_relative "../resource"
require_relative "../dist"
class Chef
class Resource
class Locale < Chef::Resource
unified_mode true
provides :locale
description "Use the **locale** resource to set the system's locale on Debian and Windows systems. Windows support was added in Chef Infra Client 16.0"
introduced "14.5"
examples <<~DOC
Set the lang to 'en_US.UTF-8'
```ruby
locale 'set system locale' do
lang 'en_US.UTF-8'
end
```
DOC
LC_VARIABLES ||= %w{LC_ADDRESS LC_COLLATE LC_CTYPE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME}.freeze
LOCALE_CONF ||= "/etc/locale.conf".freeze
LOCALE_REGEX ||= /\A\S+/.freeze
LOCALE_PLATFORM_FAMILIES ||= %w{debian windows}.freeze
property :lang, String,
description: "Sets the default system language.",
regex: [LOCALE_REGEX],
validation_message: "The provided lang is not valid. It should be a non-empty string without any leading whitespace."
property :lc_env, Hash,
description: "A Hash of LC_* env variables in the form of `({ 'LC_ENV_VARIABLE' => 'VALUE' })`.",
default: lazy { {} },
coerce: proc { |h|
if h.respond_to?(:keys)
invalid_keys = h.keys - LC_VARIABLES
unless invalid_keys.empty?
error_msg = "Key of option lc_env must be equal to one of: \"#{LC_VARIABLES.join('", "')}\"! You passed \"#{invalid_keys.join(", ")}\"."
raise Chef::Exceptions::ValidationFailed, error_msg
end
end
unless h.values.all? { |x| x =~ LOCALE_REGEX }
error_msg = "Values of option lc_env should be non-empty string without any leading whitespace."
raise Chef::Exceptions::ValidationFailed, error_msg
end
h
}
# @deprecated Use {#lc_env} instead of this property.
# {#lc_env} uses Hash with specific LC var as key.
# @raise [Chef::Deprecated]
#
def lc_all(arg = nil)
unless arg.nil?
Chef.deprecated(:locale_lc_all, "Changing LC_ALL can break #{Chef::Dist::PRODUCT}'s parsing of command output in unexpected ways.\n Use one of the more specific LC_ properties as needed.")
end
end
load_current_value do
if windows?
lang get_system_locale_windows
else
begin
old_content = ::File.read(LOCALE_CONF)
locale_values = Hash[old_content.split("\n").map { |v| v.split("=") }]
lang locale_values["LANG"]
rescue Errno::ENOENT => e
false
end
end
end
# Gets the System-locale setting for the current computer.
# @see https://docs.microsoft.com/en-us/powershell/module/international/get-winsystemlocale
# @return [String] the current value of the System-locale setting.
#
def get_system_locale_windows
powershell_exec("Get-WinSystemLocale").result["Name"]
end
action :update do
description "Update the system's locale."
converge_if_changed do
set_system_locale
end
end
action_class do
# Avoid running this resource on platforms that don't use /etc/locale.conf
#
def define_resource_requirements
requirements.assert(:all_actions) do |a|
a.assertion { LOCALE_PLATFORM_FAMILIES.include?(node[:platform_family]) }
a.failure_message(Chef::Exceptions::ProviderNotFound, "The locale resource is not supported on platform family: #{node[:platform_family]}")
end
requirements.assert(:all_actions) do |a|
# RHEL/CentOS type platforms don't have locale-gen
a.assertion { shell_out("locale-gen") }
a.failure_message(Chef::Exceptions::ProviderNotFound, "The locale resource requires the locale-gen tool")
end
end
# Generates the localization files from templates using locale-gen.
# @see http://manpages.ubuntu.com/manpages/cosmic/man8/locale-gen.8.html
# @raise [Mixlib::ShellOut::ShellCommandFailed] not a supported language or locale
#
def generate_locales
shell_out!("locale-gen #{unavailable_locales.join(" ")}")
end
# Sets the system locale for the current computer.
#
def set_system_locale
if windows?
# Sets the system locale for the current computer.
# @see https://docs.microsoft.com/en-us/powershell/module/internationalcmdlets/set-winsystemlocale
#
response = powershell_exec("Set-WinSystemLocale -SystemLocale #{new_resource.lang}")
raise response.errors.join(" ") if response.error?
else
generate_locales unless unavailable_locales.empty?
update_locale
end
end
# Updates system locale by appropriately writing them in /etc/locale.conf
# @note This locale change won't affect the current run. At this time it is an exercise
# left to the user to restart or reboot if the locale change is required at
# later part of the client run.
# @see https://wiki.archlinux.org/index.php/locale#Setting_the_system_locale
#
def update_locale
file "Updating system locale" do
path LOCALE_CONF
content new_content
end
end
# @return [Array<String>] Locales that user wants to set but are not available on
# the system. They are required to be generated.
#
def unavailable_locales
@unavailable_locales ||= begin
available = shell_out!("locale -a").stdout.split("\n")
required = [new_resource.lang, new_resource.lc_env.values].flatten.compact.uniq
required - available
end
end
# @return [String] Contents that are required to be
# updated in /etc/locale.conf
#
def new_content
@new_content ||= begin
content = {}
content = new_resource.lc_env.dup if new_resource.lc_env
content["LANG"] = new_resource.lang if new_resource.lang
content.sort.map { |t| t.join("=") }.join("\n") + "\n"
end
end
end
end
end
end