diff options
author | Tim Smith <tsmith@chef.io> | 2020-08-18 16:51:50 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-18 16:51:50 -0700 |
commit | 9d31f2ffd7ef8cd48e615153843eba12592c73e9 (patch) | |
tree | ba9404e0b5b66916105158a2e6c3759ef6bc4cb0 | |
parent | d1984935869a71289c89af442eff2371d9188bc1 (diff) | |
parent | 292ee1cc5b92b4f13b6ebe62cb027e4e32654495 (diff) | |
download | chef-9d31f2ffd7ef8cd48e615153843eba12592c73e9.tar.gz |
Merge pull request #10323 from chef/timezone
Signed-off-by: Tim Smith <tsmith@chef.io>
-rw-r--r-- | cspell.json | 12 | ||||
-rw-r--r-- | kitchen-tests/cookbooks/end_to_end/recipes/linux.rb | 2 | ||||
-rw-r--r-- | kitchen-tests/cookbooks/end_to_end/recipes/macos.rb | 2 | ||||
-rw-r--r-- | kitchen-tests/cookbooks/end_to_end/recipes/windows.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource/timezone.rb | 185 | ||||
-rw-r--r-- | spec/unit/resource/timezone_spec.rb | 63 |
6 files changed, 179 insertions, 87 deletions
diff --git a/cspell.json b/cspell.json index 3ecb709c52..b93f247e1e 100644 --- a/cspell.json +++ b/cspell.json @@ -411,27 +411,17 @@ "Dubey", "dup'd", "DUPEUX", - "duvian", - "dword", - "DWORD", "DWORDLONG", "DYNALINK", "DYNLINK", - "EACCES", "Eachern", "EASTEUROPE", "EBUSY", - "ecdsa", - "ECDSA", + "escapepath", "eckey", - "ECONNREFUSED", - "ECONNRESET", "ecparam", "edir", - "EEXIST", "egid", - "EINVAL", - "EISDIR", "elif", "ELOOP", "EMBEDDEDBIN", diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb index 051a7c9b52..012a9a7507 100644 --- a/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb +++ b/kitchen-tests/cookbooks/end_to_end/recipes/linux.rb @@ -23,7 +23,7 @@ execute "sensitive sleep" do sensitive true end -timezone "UTC" +timezone "America/Los_Angeles" include_recipe "::_yum" if platform_family?("rhel") diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb b/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb index 1fbc1f0aca..6ac2607caa 100644 --- a/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb +++ b/kitchen-tests/cookbooks/end_to_end/recipes/macos.rb @@ -19,7 +19,7 @@ execute "sensitive sleep" do sensitive true end -timezone "GMT" +timezone "America/Los_Angeles" include_recipe "ntp" diff --git a/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb b/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb index df176e8751..aa53ca6365 100644 --- a/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb +++ b/kitchen-tests/cookbooks/end_to_end/recipes/windows.rb @@ -23,7 +23,7 @@ powershell_script "sensitive sleep" do sensitive true end -timezone "UTC" +timezone "Pacific Standard time" include_recipe "ntp" diff --git a/lib/chef/resource/timezone.rb b/lib/chef/resource/timezone.rb index fe03940e1d..04e5884b88 100644 --- a/lib/chef/resource/timezone.rb +++ b/lib/chef/resource/timezone.rb @@ -26,7 +26,7 @@ class Chef provides :timezone - description "Use the **timezone** resource to change the system timezone on Windows, Linux, and macOS hosts. Timezones are specified in tz database format, with a complete list of available TZ values for Linux and macOS here: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones and for Windows here: https://ss64.com/nt/timezones.html." + description "Use the **timezone** resource to change the system timezone on Windows, Linux, and macOS hosts. Timezones are specified in tz database format, with a complete list of available TZ values for Linux and macOS here: <https://en.wikipedia.org/wiki/List_of_tz_database_time_zones>. On Windows systems run `tzutil /l` for a complete list of valid timezones." introduced "14.6" examples <<~DOC **Set the timezone to UTC** @@ -35,11 +35,19 @@ class Chef timezone 'UTC' ``` - **Set the timezone to UTC with a friendly resource name** + **Set the timezone to America/Los_Angeles with a friendly resource name on Linux/macOS** ```ruby - timezone 'Set the host's timezone to UTC' do - timezone 'UTC' + timezone 'Set the host's timezone to America/Los_Angeles' do + timezone 'America/Los_Angeles' + end + ``` + + **Set the timezone to PST with a friendly resource name on Windows** + + ```ruby + timezone 'Set the host's timezone to PST' do + timezone 'Pacific Standard time' end ``` DOC @@ -48,90 +56,121 @@ class Chef description: "An optional property to set the timezone value if it differs from the resource block's name.", name_property: true - action :set do - description "Set the timezone." - - # some linux systems may be missing the timezone data - if linux? - package "tzdata" do - package_name suse? ? "timezone" : "tzdata" - end + # detect the current TZ on darwin hosts + # + # @since 14.7 + # @return [String] TZ database value + def current_macos_tz + tz_shellout = shell_out!(["systemsetup", "-gettimezone"]) + if /You need administrator access/.match?(tz_shellout.stdout) + raise "The timezone resource requires administrative privileges to run on macOS hosts!" + else + /Time Zone: (.*)/.match(tz_shellout.stdout)[1] end + end - # Modern SUSE, Amazon, Fedora, RHEL, Ubuntu & Debian - if systemd? - cmd_set_tz = "/usr/bin/timedatectl --no-ask-password set-timezone #{new_resource.timezone}" + # detect the current timezone on windows hosts + # + # @since 14.7 + # @return [String] timezone id + def current_windows_tz + tz_shellout = shell_out("tzutil /g") + raise "There was an error running the tzutil command" if tz_shellout.error? - cmd_check_if_set = "/usr/bin/timedatectl status" - cmd_check_if_set += " | /usr/bin/awk '/Time.*zone/{print}'" - cmd_check_if_set += " | grep -q #{new_resource.timezone}" + tz_shellout.stdout.strip + end - execute cmd_set_tz do - action :run - not_if cmd_check_if_set - end + # detect the current timezone on systemd hosts + # + # @since 16.5 + # @return [String] timezone id + def current_systemd_tz + tz_shellout = shell_out(["/usr/bin/timedatectl", "status"]) + raise "There was an error running the timedatectl command" if tz_shellout.error? + + # https://rubular.com/r/eV68MX9XXbyG4k + /Time zone: (.*) \(.*/.match(tz_shellout.stdout)[1] + end + + # detect the current timezone on non-systemd RHEL-ish hosts + # + # @since 16.5 + # @return [String] timezone id + def current_rhel_tz + return nil unless ::File.exist?("/etc/sysconfig/clock") + + # https://rubular.com/r/aoj01L3bKBM7wh + /ZONE="(.*)"/.match(::File.read("/etc/sysconfig/clock"))[1] + end + + load_current_value do + if systemd? + timezone current_systemd_tz else case node["platform_family"] # Old version of RHEL < 7 and Amazon 201X when "rhel", "amazon" - file "/etc/sysconfig/clock" do - owner "root" - group "root" - mode "0644" - action :create - content %{ZONE="#{new_resource.timezone}"\nUTC="true"\n} - end - - execute "tzdata-update" do - command "/usr/sbin/tzdata-update" - action :nothing - only_if { ::File.executable?("/usr/sbin/tzdata-update") } - subscribes :run, "file[/etc/sysconfig/clock]", :immediately - end - - link "/etc/localtime" do - to "/usr/share/zoneinfo/#{new_resource.timezone}" - not_if { ::File.executable?("/usr/sbin/tzdata-update") } - end + timezone current_rhel_tz when "mac_os_x" - unless current_darwin_tz == new_resource.timezone - converge_by("set timezone to #{new_resource.timezone}") do - shell_out!("sudo systemsetup -settimezone #{new_resource.timezone}") - end - end + timezone current_macos_tz when "windows" - unless current_windows_tz.casecmp?(new_resource.timezone) - converge_by("setting timezone to \"#{new_resource.timezone}\"") do - shell_out!("tzutil /s \"#{new_resource.timezone}\"") - end - end + timezone current_windows_tz end end end - action_class do - # detect the current TZ on darwin hosts - # - # @since 14.7 - # @return [String] TZ database value - def current_darwin_tz - tz_shellout = shell_out!("systemsetup -gettimezone") - if /You need administrator access/.match?(tz_shellout.stdout) - raise "The timezone resource requires administrative privileges to run on macOS hosts!" - else - /Time Zone: (.*)/.match(tz_shellout.stdout)[1] - end - end - - # detect the current timezone on windows hosts - # - # @since 14.7 - # @return [String] timezone id - def current_windows_tz - tz_shellout = shell_out("tzutil /g") - raise "There was an error running the tzutil command" if tz_shellout.exitstatus == 1 + action :set do + description "Set the timezone." - tz_shellout.stdout.strip + # we have to check windows first since the value isn't case sensitive here + if windows? + unless current_windows_tz.casecmp?(new_resource.timezone) + converge_by("setting timezone to '#{new_resource.timezone}'") do + shell_out!(["tzutil", "/s", new_resource.timezone]) + end + end + else # linux / macos + converge_if_changed(:timezone) do + # Modern SUSE, Amazon, Fedora, RHEL, Ubuntu & Debian + if systemd? + # make sure we have the tzdata files + package suse? ? "timezone" : "tzdata" + + shell_out!(["/usr/bin/timedatectl", "--no-ask-password", "set-timezone", new_resource.timezone]) + else + case node["platform_family"] + # Old version of RHEL < 7 and Amazon 201X + when "rhel", "amazon" + # make sure we have the tzdata files + package "tzdata" + + file "/etc/sysconfig/clock" do + owner "root" + group "root" + mode "0644" + action :create + content <<~CONTENT + ZONE="#{new_resource.timezone}" + UTC="true" + CONTENT + end + + execute "tzdata-update" do + command "/usr/sbin/tzdata-update" + action :nothing + only_if { ::File.executable?("/usr/sbin/tzdata-update") } + subscribes :run, "file[/etc/sysconfig/clock]", :immediately + end + + link "/etc/localtime" do + to "/usr/share/zoneinfo/#{new_resource.timezone}" + not_if { ::File.executable?("/usr/sbin/tzdata-update") } + end + when "mac_os_x" + shell_out!(["sudo", "systemsetup", "-settimezone", new_resource.timezone]) + end + end + end end end end diff --git a/spec/unit/resource/timezone_spec.rb b/spec/unit/resource/timezone_spec.rb index da48ab7c16..9f27c28d4f 100644 --- a/spec/unit/resource/timezone_spec.rb +++ b/spec/unit/resource/timezone_spec.rb @@ -20,6 +20,31 @@ require "spec_helper" describe Chef::Resource::Timezone do let(:resource) { Chef::Resource::Timezone.new("fakey_fakerton") } + let(:shellout_tzutil) do + double("shell_out", stdout: "UTC\n", exitstatus: 0, error?: false) + end + + # note: This weird indention is correct + let(:shellout_timedatectl) do + double("shell_out", exitstatus: 0, error?: false, stdout: <<-OUTPUT) + Local time: Tue 2020-08-18 20:55:05 UTC + Universal time: Tue 2020-08-18 20:55:05 UTC + RTC time: Tue 2020-08-18 20:55:05 + Time zone: Etc/UTC (UTC, +0000) +System clock synchronized: yes +systemd-timesyncd.service active: yes + RTC in local TZ: no + OUTPUT + end + + let(:shellout_systemsetup_fail) do + double("shell_out!", stdout: "You need administrator access to run this tool... exiting!", exitstatus: 0, error?: false) # yes it's a non-error exit + end + + let(:shellout_systemsetup) do + double("shell_out!", stdout: "Time Zone: UTC", exitstatus: 0, error?: false) + end + it "sets resource name as :timezone" do expect(resource.resource_name).to eql(:timezone) end @@ -36,4 +61,42 @@ describe Chef::Resource::Timezone do expect { resource.action :set }.not_to raise_error expect { resource.action :unset }.to raise_error(Chef::Exceptions::ValidationFailed) end + + describe "#current_macos_tz?" do + context "with admin privs" do + it "returns the TZ" do + expect(resource).to receive(:shell_out!).and_return(shellout_systemsetup) + expect(resource.current_macos_tz).to eql("UTC") + end + end + + context "without admin privs" do + it "returns the TZ" do + expect(resource).to receive(:shell_out!).and_return(shellout_systemsetup_fail) + expect { resource.current_macos_tz }.to raise_error(RuntimeError, "The timezone resource requires administrative privileges to run on macOS hosts!") + end + end + end + + describe "#current_systemd_tz?" do + it "returns the TZ" do + expect(resource).to receive(:shell_out).and_return(shellout_timedatectl) + expect(resource.current_systemd_tz).to eql("Etc/UTC") + end + end + + describe "#current_windows_tz?" do + it "returns the TZ" do + expect(resource).to receive(:shell_out).and_return(shellout_tzutil) + expect(resource.current_windows_tz).to eql("UTC") + end + end + + describe "#current_rhel_tz?" do + it "returns the TZ" do + allow(File).to receive(:exist?).with("/etc/sysconfig/clock").and_return true + expect(File).to receive(:read).with("/etc/sysconfig/clock").and_return 'ZONE="UTC"' + expect(resource.current_rhel_tz).to eql("UTC") + end + end end |