chef 14.5.33 → 14.6.47
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -3
- data/chef-universal-mingw32.gemspec +3 -2
- data/chef.gemspec +3 -4
- data/lib/chef/api_client.rb +5 -3
- data/lib/chef/api_client_v1.rb +6 -4
- data/lib/chef/application.rb +1 -1
- data/lib/chef/audit/audit_reporter.rb +1 -1
- data/lib/chef/audit/control_group_data.rb +12 -6
- data/lib/chef/chef_fs/chef_fs_data_store.rb +2 -2
- data/lib/chef/chef_fs/data_handler/data_handler_base.rb +1 -1
- data/lib/chef/cookbook/manifest_v0.rb +34 -29
- data/lib/chef/cookbook/manifest_v2.rb +15 -11
- data/lib/chef/cookbook/metadata.rb +4 -2
- data/lib/chef/cookbook_manifest.rb +8 -5
- data/lib/chef/cookbook_version.rb +1 -1
- data/lib/chef/data_bag.rb +4 -2
- data/lib/chef/data_bag_item.rb +5 -3
- data/lib/chef/data_collector.rb +2 -2
- data/lib/chef/data_collector/resource_report.rb +4 -4
- data/lib/chef/encrypted_data_bag_item.rb +4 -2
- data/lib/chef/environment.rb +4 -2
- data/lib/chef/file_content_management/deploy/mv_unix.rb +5 -4
- data/lib/chef/handler.rb +2 -2
- data/lib/chef/json_compat.rb +1 -1
- data/lib/chef/key.rb +7 -5
- data/lib/chef/knife/bootstrap.rb +7 -1
- data/lib/chef/knife/client_edit.rb +2 -2
- data/lib/chef/knife/data_bag_show.rb +2 -2
- data/lib/chef/knife/osc_user_edit.rb +2 -2
- data/lib/chef/knife/user_edit.rb +2 -2
- data/lib/chef/mixin/params_validate.rb +4 -2
- data/lib/chef/node/attribute.rb +4 -4
- data/lib/chef/org.rb +6 -4
- data/lib/chef/policy_builder/policyfile.rb +5 -3
- data/lib/chef/provider/package.rb +9 -4
- data/lib/chef/provider/package/windows.rb +23 -1
- data/lib/chef/provider/package/yum/yum_helper.py +3 -2
- data/lib/chef/provider/package/zypper.rb +12 -8
- data/lib/chef/provider/registry_key.rb +15 -6
- data/lib/chef/provider/user/windows.rb +4 -3
- data/lib/chef/provider/windows_task.rb +11 -2
- data/lib/chef/resource.rb +3 -1
- data/lib/chef/resource/locale.rb +1 -1
- data/lib/chef/resource/ohai_hint.rb +4 -4
- data/lib/chef/resource/rhsm_errata_level.rb +1 -1
- data/lib/chef/resource/timezone.rb +91 -0
- data/lib/chef/resource/user/windows_user.rb +4 -0
- data/lib/chef/resource/windows_task.rb +240 -238
- data/lib/chef/resource/zypper_package.rb +5 -0
- data/lib/chef/resource_collection/resource_collection_serialization.rb +4 -2
- data/lib/chef/resources.rb +1 -0
- data/lib/chef/role.rb +4 -2
- data/lib/chef/run_list/run_list_expansion.rb +5 -3
- data/lib/chef/run_status.rb +4 -2
- data/lib/chef/user.rb +7 -5
- data/lib/chef/user_v1.rb +8 -6
- data/lib/chef/version.rb +1 -1
- data/lib/chef/win32/security/sid.rb +39 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.aarch64.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.i686.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.ppc64le.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.s390x.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.src.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.10-1.x86_64.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.aarch64.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.i686.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.ppc64le.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.s390x.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.src.rpm +0 -0
- data/spec/functional/assets/zypprepo/chef_rpm-1.2-1.x86_64.rpm +0 -0
- data/spec/functional/http/simple_spec.rb +2 -2
- data/spec/functional/resource/remote_file_spec.rb +2 -2
- data/spec/functional/resource/user/windows_spec.rb +1 -1
- data/spec/functional/resource/windows_task_spec.rb +1 -1
- data/spec/functional/resource/zypper_package_spec.rb +233 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/audit/audit_reporter_spec.rb +4 -4
- data/spec/unit/audit/control_group_data_spec.rb +17 -17
- data/spec/unit/environment_spec.rb +1 -1
- data/spec/unit/file_content_management/deploy/mv_unix_spec.rb +13 -1
- data/spec/unit/node_spec.rb +33 -0
- data/spec/unit/provider/package/rpm_spec.rb +5 -5
- data/spec/unit/provider/package/zypper_spec.rb +51 -0
- data/spec/unit/provider/package_spec.rb +32 -2
- data/spec/unit/provider/registry_key_spec.rb +74 -0
- data/spec/unit/provider/user/windows_spec.rb +12 -3
- data/spec/unit/provider/windows_task_spec.rb +1 -0
- data/spec/unit/resource/timezone.rb +39 -0
- data/spec/unit/resource/windows_task_spec.rb +1 -1
- data/spec/unit/resource_collection_spec.rb +1 -1
- data/spec/unit/run_context/child_run_context_spec.rb +3 -3
- data/spec/unit/shell/shell_session_spec.rb +3 -2
- metadata +21 -43
- data/CONTRIBUTING.md +0 -152
- data/VERSION +0 -1
- data/distro/powershell/chef/chef.psm1 +0 -459
- data/distro/ruby_bin_folder/Chef.PowerShell.Wrapper.dll +0 -0
- data/distro/ruby_bin_folder/Chef.PowerShell.dll +0 -0
- data/distro/ruby_bin_folder/Newtonsoft.Json.dll +0 -0
@@ -361,9 +361,14 @@ class Chef
|
|
361
361
|
#
|
362
362
|
# By default, this function will use Gem::Version comparison. Subclasses can reimplement this method
|
363
363
|
# for package-management system specific versions.
|
364
|
+
#
|
365
|
+
# (In other words, pull requests to introduce domain specific mangling of versions into this method
|
366
|
+
# will be closed -- that logic must go into the subclass -- we understand that this is far from perfect
|
367
|
+
# but it is a better default than outright buggy things like v1.to_f <=> v2.to_f)
|
368
|
+
#
|
364
369
|
def version_compare(v1, v2)
|
365
|
-
gem_v1 = Gem::Version.new(v1)
|
366
|
-
gem_v2 = Gem::Version.new(v2)
|
370
|
+
gem_v1 = Gem::Version.new(v1.gsub(/\A\s*(#{Gem::Version::VERSION_PATTERN}).*/, '\1'))
|
371
|
+
gem_v2 = Gem::Version.new(v2.gsub(/\A\s*(#{Gem::Version::VERSION_PATTERN}).*/, '\1'))
|
367
372
|
|
368
373
|
gem_v1 <=> gem_v2
|
369
374
|
end
|
@@ -491,7 +496,7 @@ class Chef
|
|
491
496
|
elsif current_version.nil?
|
492
497
|
logger.trace("#{new_resource} has no existing installed version. Installing install #{candidate_version}")
|
493
498
|
target_version_array.push(candidate_version)
|
494
|
-
elsif version_compare(current_version, candidate_version) == 1
|
499
|
+
elsif !allow_downgrade && version_compare(current_version, candidate_version) == 1
|
495
500
|
logger.trace("#{new_resource} #{package_name} has installed version #{current_version}, which is newer than available version #{candidate_version}. Skipping...)")
|
496
501
|
target_version_array.push(nil)
|
497
502
|
else
|
@@ -666,7 +671,7 @@ class Chef
|
|
666
671
|
if new_resource.respond_to?("allow_downgrade")
|
667
672
|
new_resource.allow_downgrade
|
668
673
|
else
|
669
|
-
|
674
|
+
true
|
670
675
|
end
|
671
676
|
end
|
672
677
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
#
|
2
2
|
# Author:: Bryan McLellan <btm@loftninjas.org>
|
3
|
-
# Copyright:: Copyright 2014-
|
3
|
+
# Copyright:: Copyright 2014-2018, Chef Software Inc.
|
4
4
|
# License:: Apache License, Version 2.0
|
5
5
|
#
|
6
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
@@ -54,6 +54,16 @@ class Chef
|
|
54
54
|
@current_resource = Chef::Resource::WindowsPackage.new(new_resource.name)
|
55
55
|
if downloadable_file_missing?
|
56
56
|
logger.trace("We do not know the version of #{new_resource.source} because the file is not downloaded")
|
57
|
+
# FIXME: this label should not be used. It could be set to nil. Probably what should happen is that
|
58
|
+
# if the file hasn't been downloaded then load_current_resource must download the file here, and then
|
59
|
+
# the else clause to set current_resource.version can always be run. Relying on a side-effect here
|
60
|
+
# produces at least less readable code, if not outright buggy... (and I'm assuming that this isn't
|
61
|
+
# wholly just a bug -- since if we only need the package_name to determine if its installed then we
|
62
|
+
# need this, so I'm assuming we need to download the file to pull out the name in order to check
|
63
|
+
# the registry -- which it still feels like we get wrong in the sense we're forcing always downloading
|
64
|
+
# and then always installing(?) which violates idempotency -- and I'm having to think way too hard
|
65
|
+
# about this and would need to go surfing around the code to determine what actually happens, probably
|
66
|
+
# in every different package_provider...)
|
57
67
|
current_resource.version(:unknown.to_s)
|
58
68
|
else
|
59
69
|
current_resource.version(package_provider.installed_version)
|
@@ -164,6 +174,18 @@ class Chef
|
|
164
174
|
# this package provider does not support package arrays
|
165
175
|
# However, There may be multiple versions for a single
|
166
176
|
# package so the first element may be a nested array
|
177
|
+
#
|
178
|
+
# FIXME: this breaks the semantics of the superclass and needs to get unwound. Since these package
|
179
|
+
# providers don't support multipackage they can't put multiple versions into this array. The windows
|
180
|
+
# package managers need this in order to uninstall multiple installed version, and they should track
|
181
|
+
# that in something like an `uninstall_version_array` of their own. The superclass does not implement
|
182
|
+
# this kind of feature. Doing this here breaks LSP and will create bugs since the superclass will not
|
183
|
+
# expect it at all. The `current_resource.version` also MUST NOT be an array if the package provider
|
184
|
+
# is not multipackage. The existing implementation of package_provider.installed_version should probably
|
185
|
+
# be what `uninstall_version_array` is, and then that list should be sorted and last/first'd into the
|
186
|
+
# current_resource.version. The current_version_array method was not intended to be overwritten by
|
187
|
+
# sublasses (but ruby provides no feature to block doing so -- it is already marked as private).
|
188
|
+
#
|
167
189
|
def current_version_array
|
168
190
|
[ current_resource.version ]
|
169
191
|
end
|
@@ -125,8 +125,9 @@ def query(command):
|
|
125
125
|
# - in order to fix this, something would have to happen where getProvides was called first and
|
126
126
|
# then the result was searchNevra'd. please be extremely careful if attempting to fix that
|
127
127
|
# since searchNevra does not support prco tuples.
|
128
|
-
if
|
129
|
-
# handles flags (<, >, =, etc) and versions, but no wildcareds
|
128
|
+
if bool(re.search('\\s+', command['provides'])):
|
129
|
+
# handles flags (<, >, =, etc) and versions, but no wildcareds
|
130
|
+
# raises error for any invalid input like: 'FOO BAR BAZ'
|
130
131
|
pkgs = obj.getProvides(*string_to_prco_tuple(command['provides']))
|
131
132
|
elif do_nevra:
|
132
133
|
# now if we're given version or arch properties explicitly, then we do a SearchNevra.
|
@@ -109,7 +109,7 @@ class Chef
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def install_package(name, version)
|
112
|
-
zypper_package("install", *options, "--auto-agree-with-licenses", allow_downgrade, name, version)
|
112
|
+
zypper_package("install", global_options, *options, "--auto-agree-with-licenses", allow_downgrade, name, version)
|
113
113
|
end
|
114
114
|
|
115
115
|
def upgrade_package(name, version)
|
@@ -118,19 +118,19 @@ class Chef
|
|
118
118
|
end
|
119
119
|
|
120
120
|
def remove_package(name, version)
|
121
|
-
zypper_package("remove", *options, name, version)
|
121
|
+
zypper_package("remove", global_options, *options, name, version)
|
122
122
|
end
|
123
123
|
|
124
124
|
def purge_package(name, version)
|
125
|
-
zypper_package("remove", *options, "--clean-deps", name, version)
|
125
|
+
zypper_package("remove", global_options, *options, "--clean-deps", name, version)
|
126
126
|
end
|
127
127
|
|
128
128
|
def lock_package(name, version)
|
129
|
-
zypper_package("addlock", *options, name, version)
|
129
|
+
zypper_package("addlock", global_options, *options, name, version)
|
130
130
|
end
|
131
131
|
|
132
132
|
def unlock_package(name, version)
|
133
|
-
zypper_package("removelock", *options, name, version)
|
133
|
+
zypper_package("removelock", global_options, *options, name, version)
|
134
134
|
end
|
135
135
|
|
136
136
|
private
|
@@ -141,12 +141,12 @@ class Chef
|
|
141
141
|
end
|
142
142
|
end
|
143
143
|
|
144
|
-
def zypper_package(command, *options, names, versions)
|
144
|
+
def zypper_package(command, global_options, *options, names, versions)
|
145
145
|
zipped_names = zip(names, versions)
|
146
146
|
if zypper_version < 1.0
|
147
|
-
shell_out!("zypper", gpg_checks, command, *options, "-y", names)
|
147
|
+
shell_out!("zypper", global_options, gpg_checks, command, *options, "-y", names)
|
148
148
|
else
|
149
|
-
shell_out!("zypper", "--non-interactive", gpg_checks, command, *options, zipped_names)
|
149
|
+
shell_out!("zypper", global_options, "--non-interactive", gpg_checks, command, *options, zipped_names)
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
@@ -157,6 +157,10 @@ class Chef
|
|
157
157
|
def allow_downgrade
|
158
158
|
"--oldpackage" if new_resource.allow_downgrade
|
159
159
|
end
|
160
|
+
|
161
|
+
def global_options
|
162
|
+
new_resource.global_options if new_resource.global_options
|
163
|
+
end
|
160
164
|
end
|
161
165
|
end
|
162
166
|
end
|
@@ -126,16 +126,22 @@ class Chef
|
|
126
126
|
value[:data] = value[:data].to_i
|
127
127
|
end
|
128
128
|
unless current_value[:type] == value[:type] && current_value[:data] == value[:data]
|
129
|
-
converge_by_value =
|
130
|
-
|
129
|
+
converge_by_value = if new_resource.sensitive
|
130
|
+
value.merge(data: "*sensitive value suppressed*")
|
131
|
+
else
|
132
|
+
value
|
133
|
+
end
|
131
134
|
|
132
135
|
converge_by("set value #{converge_by_value}") do
|
133
136
|
registry.set_value(new_resource.key, value)
|
134
137
|
end
|
135
138
|
end
|
136
139
|
else
|
137
|
-
converge_by_value =
|
138
|
-
|
140
|
+
converge_by_value = if new_resource.sensitive
|
141
|
+
value.merge(data: "*sensitive value suppressed*")
|
142
|
+
else
|
143
|
+
value
|
144
|
+
end
|
139
145
|
|
140
146
|
converge_by("set value #{converge_by_value}") do
|
141
147
|
registry.set_value(new_resource.key, value)
|
@@ -152,8 +158,11 @@ class Chef
|
|
152
158
|
end
|
153
159
|
new_resource.unscrubbed_values.each do |value|
|
154
160
|
unless @name_hash.key?(value[:name].downcase)
|
155
|
-
converge_by_value =
|
156
|
-
|
161
|
+
converge_by_value = if new_resource.sensitive
|
162
|
+
value.merge(data: "*sensitive value suppressed*")
|
163
|
+
else
|
164
|
+
value
|
165
|
+
end
|
157
166
|
|
158
167
|
converge_by("create value #{converge_by_value}") do
|
159
168
|
registry.set_value(new_resource.key, value)
|
@@ -39,12 +39,12 @@ class Chef
|
|
39
39
|
logger.warn("The 'gid' (or 'group') property is not implemented on the Windows platform. Please use the `members` property of the 'group' resource to assign a user to a group.")
|
40
40
|
end
|
41
41
|
|
42
|
-
@current_resource = Chef::Resource::User.new(new_resource.name)
|
42
|
+
@current_resource = Chef::Resource::User::WindowsUser.new(new_resource.name)
|
43
43
|
current_resource.username(new_resource.username)
|
44
44
|
begin
|
45
45
|
user_info = @net_user.get_info
|
46
|
-
|
47
46
|
current_resource.uid(user_info[:user_id])
|
47
|
+
current_resource.full_name(user_info[:full_name])
|
48
48
|
current_resource.comment(user_info[:comment])
|
49
49
|
current_resource.home(user_info[:home_dir])
|
50
50
|
current_resource.shell(user_info[:script_path])
|
@@ -67,7 +67,7 @@ class Chef
|
|
67
67
|
logger.trace("#{new_resource} password has changed")
|
68
68
|
return true
|
69
69
|
end
|
70
|
-
[ :uid, :comment, :home, :shell ].any? do |user_attrib|
|
70
|
+
[ :uid, :comment, :home, :shell, :full_name ].any? do |user_attrib|
|
71
71
|
!new_resource.send(user_attrib).nil? && new_resource.send(user_attrib) != current_resource.send(user_attrib)
|
72
72
|
end
|
73
73
|
end
|
@@ -100,6 +100,7 @@ class Chef
|
|
100
100
|
opts = { name: new_resource.username }
|
101
101
|
|
102
102
|
field_list = {
|
103
|
+
"full_name" => "full_name",
|
103
104
|
"comment" => "comment",
|
104
105
|
"home" => "home_dir",
|
105
106
|
"uid" => "user_id",
|
@@ -18,7 +18,7 @@
|
|
18
18
|
|
19
19
|
require "chef/mixin/shell_out"
|
20
20
|
require "rexml/document"
|
21
|
-
require "iso8601"
|
21
|
+
require "iso8601" if Chef::Platform.windows?
|
22
22
|
require "chef/mixin/powershell_out"
|
23
23
|
require "chef/provider"
|
24
24
|
require "chef/util/path_helper"
|
@@ -570,7 +570,16 @@ class Chef
|
|
570
570
|
def logon_type
|
571
571
|
# Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/aa383566(v=vs.85).aspx
|
572
572
|
# if nothing is passed as logon_type the TASK_LOGON_SERVICE_ACCOUNT is getting set as default so using that for comparision.
|
573
|
-
new_resource.
|
573
|
+
user_id = new_resource.user
|
574
|
+
if Chef::ReservedNames::Win32::Security::SID.service_account_user?(user_id)
|
575
|
+
TaskScheduler::TASK_LOGON_SERVICE_ACCOUNT
|
576
|
+
elsif Chef::ReservedNames::Win32::Security::SID.group_user?(user_id)
|
577
|
+
TaskScheduler::TASK_LOGON_GROUP
|
578
|
+
elsif !new_resource.password.to_s.empty? # password is present
|
579
|
+
TaskScheduler::TASK_LOGON_PASSWORD
|
580
|
+
else
|
581
|
+
TaskScheduler::TASK_LOGON_INTERACTIVE_TOKEN
|
582
|
+
end
|
574
583
|
end
|
575
584
|
|
576
585
|
# This method checks if task and command properties exist since those two are mandatory properties to create a schedules task.
|
data/lib/chef/resource.rb
CHANGED
@@ -682,7 +682,7 @@ class Chef
|
|
682
682
|
Chef::JSONCompat.to_json(results, *a)
|
683
683
|
end
|
684
684
|
|
685
|
-
def
|
685
|
+
def to_h
|
686
686
|
# Grab all current state, then any other ivars (backcompat)
|
687
687
|
result = {}
|
688
688
|
self.class.state_properties.each do |p|
|
@@ -697,6 +697,8 @@ class Chef
|
|
697
697
|
result
|
698
698
|
end
|
699
699
|
|
700
|
+
alias_method :to_hash, :to_h
|
701
|
+
|
700
702
|
def self.from_hash(o)
|
701
703
|
resource = new(o["instance_vars"]["@name"])
|
702
704
|
o["instance_vars"].each do |k, v|
|
data/lib/chef/resource/locale.rb
CHANGED
@@ -61,7 +61,7 @@ class Chef
|
|
61
61
|
end
|
62
62
|
|
63
63
|
execute "reload root's lang profile script" do
|
64
|
-
command "source
|
64
|
+
command "source /etc/sysconfig/i18n; source /etc/profile.d/lang.sh"
|
65
65
|
not_if { updated }
|
66
66
|
end
|
67
67
|
elsif ::File.exist?("/usr/sbin/update-locale")
|
@@ -40,12 +40,12 @@ class Chef
|
|
40
40
|
action :create do
|
41
41
|
description "Create an Ohai hint file."
|
42
42
|
|
43
|
-
|
43
|
+
directory ::Ohai::Config.ohai.hints_path.first do
|
44
44
|
action :create
|
45
45
|
recursive true
|
46
46
|
end
|
47
47
|
|
48
|
-
|
48
|
+
file ohai_hint_file_path(new_resource.hint_name) do
|
49
49
|
action :create
|
50
50
|
content format_content(new_resource.content)
|
51
51
|
end
|
@@ -54,12 +54,12 @@ class Chef
|
|
54
54
|
action :delete do
|
55
55
|
description "Delete an Ohai hint file."
|
56
56
|
|
57
|
-
|
57
|
+
file ohai_hint_file_path(new_resource.hint_name) do
|
58
58
|
action :delete
|
59
59
|
notifies :reload, ohai[reload ohai post hint removal]
|
60
60
|
end
|
61
61
|
|
62
|
-
|
62
|
+
ohai "reload ohai post hint removal" do
|
63
63
|
action :nothing
|
64
64
|
end
|
65
65
|
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Kirill Kouznetsov <agon.smith@gmail.com>
|
3
|
+
#
|
4
|
+
# Copyright 2018, Kirill Kouznetsov.
|
5
|
+
# Copyright 2018, Chef Software, Inc.
|
6
|
+
#
|
7
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
8
|
+
# you may not use this file except in compliance with the License.
|
9
|
+
# You may obtain a copy of the License at
|
10
|
+
#
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
12
|
+
#
|
13
|
+
# Unless required by applicable law or agreed to in writing, software
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
15
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
16
|
+
# See the License for the specific language governing permissions and
|
17
|
+
# limitations under the License.
|
18
|
+
#
|
19
|
+
|
20
|
+
require "chef/resource"
|
21
|
+
|
22
|
+
class Chef
|
23
|
+
class Resource
|
24
|
+
class Timezone < Chef::Resource
|
25
|
+
preview_resource true
|
26
|
+
resource_name :timezone
|
27
|
+
|
28
|
+
description "Use the timezone resource to change the system timezone."
|
29
|
+
introduced "14.6"
|
30
|
+
|
31
|
+
property :timezone, String,
|
32
|
+
description: "The timezone value to set.",
|
33
|
+
name_property: true
|
34
|
+
|
35
|
+
action :set do
|
36
|
+
description "Set the timezone."
|
37
|
+
|
38
|
+
package "tzdata" do
|
39
|
+
package_name platform_family?("suse") ? "timezone" : "tzdata"
|
40
|
+
end
|
41
|
+
|
42
|
+
if node["init_package"] == "systemd"
|
43
|
+
# Modern Amazon, Fedora, CentOS, RHEL, Ubuntu & Debian
|
44
|
+
cmd_set_tz = "/usr/bin/timedatectl --no-ask-password set-timezone #{new_resource.timezone}"
|
45
|
+
|
46
|
+
cmd_check_if_set = "/usr/bin/timedatectl status"
|
47
|
+
cmd_check_if_set += " | /usr/bin/awk '/Time.*zone/{print}'"
|
48
|
+
cmd_check_if_set += " | grep -q #{new_resource.timezone}"
|
49
|
+
|
50
|
+
execute cmd_set_tz do
|
51
|
+
action :run
|
52
|
+
not_if cmd_check_if_set
|
53
|
+
end
|
54
|
+
elsif platform_family?("rhel", "amazon")
|
55
|
+
# Old version of RHEL & CentOS
|
56
|
+
file "/etc/sysconfig/clock" do
|
57
|
+
owner "root"
|
58
|
+
group "root"
|
59
|
+
mode "0644"
|
60
|
+
action :create
|
61
|
+
content %{ZONE="#{new_resource.timezone}"\nUTC="true"\n}
|
62
|
+
end
|
63
|
+
|
64
|
+
execute "tzdata-update" do
|
65
|
+
command "/usr/sbin/tzdata-update"
|
66
|
+
action :nothing
|
67
|
+
only_if { ::File.executable?("/usr/sbin/tzdata-update") }
|
68
|
+
subscribes :run, "file[/etc/sysconfig/clock]", :immediately
|
69
|
+
end
|
70
|
+
|
71
|
+
link "/etc/localtime" do
|
72
|
+
to "/usr/share/zoneinfo/#{new_resource.timezone}"
|
73
|
+
not_if { ::File.executable?("/usr/sbin/tzdata-update") }
|
74
|
+
end
|
75
|
+
elsif platform_family?("debian")
|
76
|
+
file "/etc/timezone" do
|
77
|
+
action :create
|
78
|
+
content "#{new_resource.timezone}\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
bash "dpkg-reconfigure tzdata" do
|
82
|
+
user "root"
|
83
|
+
code "/usr/sbin/dpkg-reconfigure -f noninteractive tzdata"
|
84
|
+
action :nothing
|
85
|
+
subscribes :run, "file[/etc/timezone]", :immediately
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -17,318 +17,320 @@
|
|
17
17
|
#
|
18
18
|
|
19
19
|
require "chef/resource"
|
20
|
+
require "chef/win32/security" if Chef::Platform.windows?
|
20
21
|
|
21
22
|
class Chef
|
22
23
|
class Resource
|
23
24
|
class WindowsTask < Chef::Resource
|
24
|
-
|
25
|
-
|
25
|
+
if Chef::Platform.windows?
|
26
|
+
resource_name :windows_task
|
27
|
+
provides(:windows_task) { true }
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
+
description "Use the windows_task resource to create, delete or run a Windows scheduled task. Requires Windows Server 2008 or later due to API usage."
|
30
|
+
introduced "13.0"
|
29
31
|
|
30
|
-
|
31
|
-
|
32
|
+
allowed_actions :create, :delete, :run, :end, :enable, :disable, :change
|
33
|
+
default_action :create
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/],
|
36
|
+
description: "The task name, such as 'Task Name' or '/Task Name'",
|
37
|
+
name_property: true
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
+
property :command, String,
|
40
|
+
description: "The command to be executed by the windows scheduled task."
|
39
41
|
|
40
|
-
|
41
|
-
|
42
|
+
property :cwd, String,
|
43
|
+
description: "The directory the task will be run from."
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
45
|
+
property :user, String,
|
46
|
+
description: "The user to run the task as.",
|
47
|
+
default: Chef::ReservedNames::Win32::Security::SID.LocalSystem.account_simple_name
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
+
property :password, String,
|
50
|
+
description: "The user’s password. The user property must be set if using this property."
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
property :run_level, Symbol, equal_to: [:highest, :limited],
|
53
|
+
description: "Run with ':limited' or ':highest' privileges.",
|
54
|
+
default: :limited
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
property :force, [TrueClass, FalseClass],
|
57
|
+
description: "When used with create, will update the task.",
|
58
|
+
default: false
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
60
|
+
property :interactive_enabled, [TrueClass, FalseClass],
|
61
|
+
description: "Allow task to run interactively or non-interactively. Requires user and password to also be set.",
|
62
|
+
default: false
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
+
property :frequency_modifier, [Integer, String],
|
65
|
+
default: 1
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
67
|
+
property :frequency, Symbol, equal_to: [:minute,
|
68
|
+
:hourly,
|
69
|
+
:daily,
|
70
|
+
:weekly,
|
71
|
+
:monthly,
|
72
|
+
:once,
|
73
|
+
:on_logon,
|
74
|
+
:onstart,
|
75
|
+
:on_idle,
|
76
|
+
:none],
|
77
|
+
description: "The frequency with which to run the task."
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
+
property :start_day, String,
|
80
|
+
description: "Specifies the first date on which the task runs in MM/DD/YYYY format."
|
79
81
|
|
80
|
-
|
81
|
-
|
82
|
+
property :start_time, String,
|
83
|
+
description: "Specifies the start time to run the task, in HH:mm format."
|
82
84
|
|
83
|
-
|
84
|
-
|
85
|
+
property :day, [String, Integer],
|
86
|
+
description: "The day(s) on which the task runs."
|
85
87
|
|
86
|
-
|
87
|
-
|
88
|
+
property :months, String,
|
89
|
+
description: "The Months of the year on which the task runs, such as: 'JAN, FEB' or '\*'. Multiple months should be comma delimited. e.g. 'Jan, Feb, Mar, Dec'."
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
+
property :idle_time, Integer,
|
92
|
+
description: "For :on_idle frequency, the time (in minutes) without user activity that must pass to trigger the task, from 1 - 999."
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
+
property :random_delay, [String, Integer],
|
95
|
+
description: "Delays the task up to a given time (in seconds)."
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
property :execution_time_limit, [String, Integer],
|
98
|
+
description: "The maximum time (in seconds) the task will run.",
|
99
|
+
default: "PT72H" # 72 hours in ISO8601 duration format
|
98
100
|
|
99
|
-
|
100
|
-
|
101
|
+
property :minutes_duration, [String, Integer],
|
102
|
+
description: ""
|
101
103
|
|
102
|
-
|
103
|
-
|
104
|
+
property :minutes_interval, [String, Integer],
|
105
|
+
description: ""
|
104
106
|
|
105
|
-
|
106
|
-
|
107
|
-
|
107
|
+
property :priority, Integer,
|
108
|
+
description: "Use to set Priority Levels range from 0 to 10.",
|
109
|
+
default: 7, callbacks: { "should be in range of 0 to 10" => proc { |v| v >= 0 && v <= 10 } }
|
108
110
|
|
109
|
-
|
110
|
-
|
111
|
-
|
111
|
+
property :disallow_start_if_on_batteries, [TrueClass, FalseClass],
|
112
|
+
introduced: "14.4", default: false,
|
113
|
+
description: "Disallow start of the task if the system is running on battery power."
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
-
|
115
|
+
property :stop_if_going_on_batteries, [TrueClass, FalseClass],
|
116
|
+
introduced: "14.4", default: false,
|
117
|
+
description: "Scheduled task option when system is switching on battery."
|
116
118
|
|
117
|
-
|
119
|
+
attr_accessor :exists, :task, :command_arguments
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY}.freeze
|
121
|
+
VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * }.freeze
|
122
|
+
VALID_DAYS_OF_MONTH = ("1".."31").to_a << "last" << "lastday"
|
123
|
+
VALID_MONTHS = %w{JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC *}.freeze
|
124
|
+
VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY}.freeze
|
124
125
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
126
|
+
def after_created
|
127
|
+
if random_delay
|
128
|
+
validate_random_delay(random_delay, frequency)
|
129
|
+
random_delay(sec_to_min(random_delay))
|
130
|
+
end
|
130
131
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
132
|
+
if execution_time_limit
|
133
|
+
execution_time_limit(259200) if execution_time_limit == "PT72H"
|
134
|
+
raise ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(execution_time_limit)
|
135
|
+
execution_time_limit(sec_to_min(execution_time_limit))
|
136
|
+
end
|
136
137
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
138
|
+
validate_frequency(frequency) if action.include?(:create) || action.include?(:change)
|
139
|
+
validate_start_time(start_time, frequency)
|
140
|
+
validate_start_day(start_day, frequency) if start_day
|
141
|
+
validate_user_and_password(user, password)
|
142
|
+
validate_interactive_setting(interactive_enabled, password)
|
143
|
+
validate_create_frequency_modifier(frequency, frequency_modifier) if frequency_modifier
|
144
|
+
validate_create_day(day, frequency, frequency_modifier) if day
|
145
|
+
validate_create_months(months, frequency) if months
|
146
|
+
validate_frequency_monthly(frequency_modifier, months, day) if frequency == :monthly
|
147
|
+
validate_idle_time(idle_time, frequency)
|
148
|
+
idempotency_warning_for_frequency_weekly(day, start_day) if frequency == :weekly
|
149
|
+
end
|
149
150
|
|
150
|
-
|
151
|
+
private
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
153
|
+
## Resource is not idempotent when day, start_day is not provided with frequency :weekly
|
154
|
+
## we set start_day when not given by user as current date based on which we set the day property for current current date day is monday ..
|
155
|
+
## we set the monday as the day so at next run when new_resource.day is nil and current_resource day is monday due to which udpate gets called
|
156
|
+
def idempotency_warning_for_frequency_weekly(day, start_day)
|
157
|
+
if start_day.nil? && day.nil?
|
158
|
+
logger.warn "To maintain idempotency for frequency :weekly provide start_day, start_time and day."
|
159
|
+
end
|
158
160
|
end
|
159
|
-
end
|
160
161
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
162
|
+
# Validate the passed value is numeric values only if it is a string
|
163
|
+
def numeric_value_in_string?(val)
|
164
|
+
return true if Integer(val)
|
165
|
+
rescue ArgumentError
|
166
|
+
false
|
167
|
+
end
|
167
168
|
|
168
|
-
|
169
|
-
|
170
|
-
|
169
|
+
def validate_frequency(frequency)
|
170
|
+
if frequency.nil? || !([:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none].include?(frequency))
|
171
|
+
raise ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none."
|
172
|
+
end
|
171
173
|
end
|
172
|
-
end
|
173
174
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
175
|
+
def validate_frequency_monthly(frequency_modifier, months, day)
|
176
|
+
# validates the frequency :monthly and raises error if frequency_modifier is first, second, thrid etc and day is not provided
|
177
|
+
if (frequency_modifier != 1) && (frequency_modifier_includes_days_of_weeks?(frequency_modifier)) && !(day)
|
178
|
+
raise ArgumentError, "Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be seprated by comma."
|
179
|
+
end
|
180
|
+
|
181
|
+
# frequency_modifer 2-12 is used to set every (n) months, so using :months propety with frequency_modifer is not valid since they both used to set months.
|
182
|
+
# Not checking value 1 here for frequecy_modifier since we are setting that as default value it won't break anything since preference is given to months property
|
183
|
+
if (frequency_modifier.to_i.between?(2, 12)) && !(months.nil?)
|
184
|
+
raise ArgumentError, "For frequency :monthly either use property months or frequency_modifier to set months."
|
185
|
+
end
|
178
186
|
end
|
179
187
|
|
180
|
-
#
|
181
|
-
|
182
|
-
|
183
|
-
|
188
|
+
# returns true if frequency_modifer has values First, second, third, fourth, last, lastday
|
189
|
+
def frequency_modifier_includes_days_of_weeks?(frequency_modifier)
|
190
|
+
frequency_modifier = frequency_modifier.to_s.split(",")
|
191
|
+
frequency_modifier.map! { |value| value.strip.upcase }
|
192
|
+
(frequency_modifier - VALID_WEEKS).empty?
|
184
193
|
end
|
185
|
-
end
|
186
194
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
(frequency_modifier - VALID_WEEKS).empty?
|
192
|
-
end
|
195
|
+
def validate_random_delay(random_delay, frequency)
|
196
|
+
if [:on_logon, :onstart, :on_idle, :none].include? frequency
|
197
|
+
raise ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly"
|
198
|
+
end
|
193
199
|
|
194
|
-
|
195
|
-
if [:on_logon, :onstart, :on_idle, :none].include? frequency
|
196
|
-
raise ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly"
|
200
|
+
raise ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as an Integer (e.g. 60) or a String with numeric values only (e.g. '60')." unless numeric_value_in_string?(random_delay)
|
197
201
|
end
|
198
202
|
|
199
|
-
|
200
|
-
|
203
|
+
# @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~f
|
204
|
+
def validate_start_day(start_day, frequency)
|
205
|
+
if start_day && frequency == :none
|
206
|
+
raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}"
|
207
|
+
end
|
201
208
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
209
|
+
# make sure the start_day is in MM/DD/YYYY format: http://rubular.com/r/cgjHemtWl5
|
210
|
+
if start_day
|
211
|
+
raise ArgumentError, "`start_day` property must be in the MM/DD/YYYY format." unless /^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d$/ =~ start_day
|
212
|
+
end
|
206
213
|
end
|
207
214
|
|
208
|
-
#
|
209
|
-
|
210
|
-
|
215
|
+
# @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~
|
216
|
+
def validate_start_time(start_time, frequency)
|
217
|
+
if start_time
|
218
|
+
raise ArgumentError, "`start_time` property is not supported with `frequency :none`" if frequency == :none
|
219
|
+
raise ArgumentError, "`start_time` property must be in the HH:mm format (e.g. 6:20pm -> 18:20)." unless /^[0-2][0-9]:[0-5][0-9]$/ =~ start_time
|
220
|
+
else
|
221
|
+
raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" if frequency == :once
|
222
|
+
end
|
211
223
|
end
|
212
|
-
end
|
213
224
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
raise ArgumentError, "`start_time` property must be in the HH:mm format (e.g. 6:20pm -> 18:20)." unless /^[0-2][0-9]:[0-5][0-9]$/ =~ start_time
|
219
|
-
else
|
220
|
-
raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" if frequency == :once
|
225
|
+
def validate_user_and_password(user, password)
|
226
|
+
if password_required?(user) && password.nil?
|
227
|
+
raise ArgumentError, "Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: '#{Chef::ReservedNames::Win32::Security::SID::SYSTEM_USER.join("', '")}'"
|
228
|
+
end
|
221
229
|
end
|
222
|
-
end
|
223
230
|
|
224
|
-
|
225
|
-
|
226
|
-
|
231
|
+
def password_required?(user)
|
232
|
+
return false if user.nil?
|
233
|
+
@password_required ||= !Chef::ReservedNames::Win32::Security::SID.system_user?(user)
|
227
234
|
end
|
228
|
-
end
|
229
|
-
|
230
|
-
def password_required?(user)
|
231
|
-
return false if user.nil?
|
232
|
-
@password_required ||= !SYSTEM_USERS.include?(user.upcase)
|
233
|
-
end
|
234
235
|
|
235
|
-
|
236
|
-
|
237
|
-
end
|
238
|
-
|
239
|
-
def validate_create_frequency_modifier(frequency, frequency_modifier)
|
240
|
-
if ([:on_logon, :onstart, :on_idle, :none].include?(frequency)) && ( frequency_modifier != 1)
|
241
|
-
raise ArgumentError, "frequency_modifier property not supported with frequency :#{frequency}"
|
236
|
+
def validate_interactive_setting(interactive_enabled, password)
|
237
|
+
raise ArgumentError, "Please provide the password when attempting to set interactive/non-interactive." if interactive_enabled && password.nil?
|
242
238
|
end
|
243
239
|
|
244
|
-
|
245
|
-
|
246
|
-
raise ArgumentError, "frequency_modifier
|
240
|
+
def validate_create_frequency_modifier(frequency, frequency_modifier)
|
241
|
+
if ([:on_logon, :onstart, :on_idle, :none].include?(frequency)) && ( frequency_modifier != 1)
|
242
|
+
raise ArgumentError, "frequency_modifier property not supported with frequency :#{frequency}"
|
247
243
|
end
|
248
|
-
|
249
|
-
|
250
|
-
frequency_modifier
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
244
|
+
|
245
|
+
if frequency == :monthly
|
246
|
+
unless (1..12).cover?(frequency_modifier.to_i) || frequency_modifier_includes_days_of_weeks?(frequency_modifier)
|
247
|
+
raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'."
|
248
|
+
end
|
249
|
+
else
|
250
|
+
unless frequency.nil? || frequency_modifier.nil?
|
251
|
+
frequency_modifier = frequency_modifier.to_i
|
252
|
+
min = 1
|
253
|
+
max = case frequency
|
254
|
+
when :minute
|
255
|
+
1439
|
256
|
+
when :hourly
|
257
|
+
23
|
258
|
+
when :daily
|
259
|
+
365
|
260
|
+
when :weekly
|
261
|
+
52
|
262
|
+
else
|
263
|
+
min
|
264
|
+
end
|
265
|
+
unless frequency_modifier.between?(min, max)
|
266
|
+
raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :#{frequency} frequency are #{min} - #{max}."
|
267
|
+
end
|
266
268
|
end
|
267
269
|
end
|
268
270
|
end
|
269
|
-
end
|
270
|
-
|
271
|
-
def validate_create_day(day, frequency, frequency_modifier)
|
272
|
-
raise ArgumentError, "day property is only valid for tasks that run monthly or weekly" unless [:weekly, :monthly].include?(frequency)
|
273
271
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
272
|
+
def validate_create_day(day, frequency, frequency_modifier)
|
273
|
+
raise ArgumentError, "day property is only valid for tasks that run monthly or weekly" unless [:weekly, :monthly].include?(frequency)
|
274
|
+
|
275
|
+
# This has been verified with schtask.exe https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks#d-dayday--
|
276
|
+
# verified with earlier code if day "*" is given with frequency it raised exception Invalid value for /D option
|
277
|
+
raise ArgumentError, "day wild card (*) is only valid with frequency :weekly" if frequency == :monthly && day == "*"
|
278
|
+
|
279
|
+
if day.is_a?(String) && day.to_i.to_s != day
|
280
|
+
days = day.split(",")
|
281
|
+
if days_includes_days_of_months?(days)
|
282
|
+
# Following error will be raise if day is set as 1-31 and frequency is selected as :weekly since those values are valid with only frequency :monthly
|
283
|
+
raise ArgumentError, "day values 1-31 or last is only valid with frequency :monthly" if frequency == :weekly
|
284
|
+
else
|
285
|
+
days.map! { |day| day.to_s.strip.downcase }
|
286
|
+
unless (days - VALID_WEEK_DAYS).empty?
|
287
|
+
raise ArgumentError, "day property invalid. Only valid values are: #{VALID_WEEK_DAYS.map(&:upcase).join(', ')}. Multiple values must be separated by a comma."
|
288
|
+
end
|
287
289
|
end
|
288
290
|
end
|
289
291
|
end
|
290
|
-
end
|
291
292
|
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
293
|
+
def validate_create_months(months, frequency)
|
294
|
+
raise ArgumentError, "months property is only valid for tasks that run monthly" if frequency != :monthly
|
295
|
+
if months.is_a?(String)
|
296
|
+
months = months.split(",")
|
297
|
+
months.map! { |month| month.strip.upcase }
|
298
|
+
unless (months - VALID_MONTHS).empty?
|
299
|
+
raise ArgumentError, "months property invalid. Only valid values are: #{VALID_MONTHS.join(', ')}. Multiple values must be separated by a comma."
|
300
|
+
end
|
299
301
|
end
|
300
302
|
end
|
301
|
-
end
|
302
|
-
|
303
|
-
# This method returns true if day has values from 1-31 which is a days of moths and used with frequency :monthly
|
304
|
-
def days_includes_days_of_months?(days)
|
305
|
-
days.map! { |day| day.to_s.strip.downcase }
|
306
|
-
(days - VALID_DAYS_OF_MONTH).empty?
|
307
|
-
end
|
308
303
|
|
309
|
-
|
310
|
-
|
311
|
-
|
304
|
+
# This method returns true if day has values from 1-31 which is a days of moths and used with frequency :monthly
|
305
|
+
def days_includes_days_of_months?(days)
|
306
|
+
days.map! { |day| day.to_s.strip.downcase }
|
307
|
+
(days - VALID_DAYS_OF_MONTH).empty?
|
312
308
|
end
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
309
|
+
|
310
|
+
def validate_idle_time(idle_time, frequency)
|
311
|
+
if !idle_time.nil? && frequency != :on_idle
|
312
|
+
raise ArgumentError, "idle_time property is only valid for tasks that run on_idle"
|
313
|
+
end
|
314
|
+
if idle_time.nil? && frequency == :on_idle
|
315
|
+
raise ArgumentError, "idle_time value should be set for :on_idle frequency."
|
316
|
+
end
|
317
|
+
unless idle_time.nil? || idle_time > 0 && idle_time <= 999
|
318
|
+
raise ArgumentError, "idle_time value #{idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999."
|
319
|
+
end
|
318
320
|
end
|
319
|
-
end
|
320
321
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
322
|
+
# Converts the number of seconds to an ISO8601 duration format and returns it.
|
323
|
+
# Ref : https://github.com/arnau/ISO8601/blob/master/lib/iso8601/duration.rb#L18-L23
|
324
|
+
# e.g.
|
325
|
+
# ISO8601::Duration.new(65707200).to_s
|
326
|
+
# returns 'PT65707200S'
|
327
|
+
def sec_to_dur(seconds)
|
328
|
+
ISO8601::Duration.new(seconds.to_i).to_s
|
329
|
+
end
|
329
330
|
|
330
|
-
|
331
|
-
|
331
|
+
def sec_to_min(seconds)
|
332
|
+
seconds.to_i / 60
|
333
|
+
end
|
332
334
|
end
|
333
335
|
end
|
334
336
|
end
|