diff options
author | Vasu1105 <vasundhara.jagdale@msystechnologies.com> | 2018-01-31 11:47:09 +0000 |
---|---|---|
committer | vasu1105 <vasundhara.jagdale@msystechnologies.com> | 2018-04-24 11:32:31 +0530 |
commit | 2c665b3e889381062b2faf1f28d4a41ca1a698cb (patch) | |
tree | 2d8e1fdc98a9d267c6fa403732d421e6feb6d684 /lib/chef/resource/windows_task.rb | |
parent | 5ef14cc77226fe16b734ec90aead1de5d49ca7fb (diff) | |
download | chef-2c665b3e889381062b2faf1f28d4a41ca1a698cb.tar.gz |
[MSYS-752] windows task rewrite using wind32-taskscheduler
Signed-off-by: Vasu1105 <vasundhara.jagdale@msystechnologies.com>
Diffstat (limited to 'lib/chef/resource/windows_task.rb')
-rw-r--r-- | lib/chef/resource/windows_task.rb | 180 |
1 files changed, 123 insertions, 57 deletions
diff --git a/lib/chef/resource/windows_task.rb b/lib/chef/resource/windows_task.rb index 8b4743eecc..38f651d670 100644 --- a/lib/chef/resource/windows_task.rb +++ b/lib/chef/resource/windows_task.rb @@ -28,7 +28,7 @@ class Chef " scheduled task. Requires Windows Server 2008 or later due to API usage." introduced "13.0" - allowed_actions :create, :delete, :run, :end, :enable, :disable + allowed_actions :create, :delete, :run, :end, :enable, :disable, :change default_action :create property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/], name_property: true @@ -49,7 +49,7 @@ class Chef :on_logon, :onstart, :on_idle, - :none], default: :hourly + :none] property :start_day, String property :start_time, String property :day, [String, Integer] @@ -57,36 +57,59 @@ class Chef property :idle_time, Integer property :random_delay, [String, Integer] property :execution_time_limit, [String, Integer], default: "PT72H" # 72 hours in ISO8601 duration format + property :minutes_duration, [String, Integer] + property :minutes_interval, [String, Integer] - attr_accessor :exists, :status, :enabled + attr_accessor :exists, :task + + SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze + VALID_WEEK_DAYS = %w{ mon tue wed thu fri sat sun * } + VALID_DAYS_OF_MONTH = ("1".."31").to_a << "last" << "lastday" + VALID_MONTHS = %w{JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC *} + VALID_WEEKS = %w{FIRST SECOND THIRD FOURTH LAST LASTDAY} def after_created if random_delay validate_random_delay(random_delay, frequency) - duration = sec_to_dur(random_delay) - random_delay(duration) + random_delay(sec_to_min(random_delay)) end if execution_time_limit - unless execution_time_limit == "PT72H" # don't double convert an ISO8601 format duration - 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) - duration = sec_to_dur(execution_time_limit) - execution_time_limit(duration) - end + execution_time_limit(259200) if execution_time_limit == "PT72H" + 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) + execution_time_limit(sec_to_min(execution_time_limit)) end + # Removed the default value :hourly for frequency in Chef 14 following code added for backward compatibility + chef_version = ::Chef::VERSION.split(".") + if chef_version[0].to_i > 13 || (chef_version[0].to_i >= 0) + validate_frequency(frequency) if action.include?(:create) || action.include?(:change) + else + frequency(:hourly) if frequency.nil? + end validate_start_time(start_time, frequency) validate_start_day(start_day, frequency) if start_day validate_user_and_password(user, password) validate_interactive_setting(interactive_enabled, password) - validate_create_frequency_modifier(frequency, frequency_modifier) - validate_create_day(day, frequency) if day + validate_create_frequency_modifier(frequency, frequency_modifier) if frequency_modifier + validate_create_day(day, frequency, frequency_modifier) if day validate_create_months(months, frequency) if months + validate_frequency_monthly(frequency_modifier, months, day) if frequency == :monthly validate_idle_time(idle_time, frequency) + idempotency_warning_for_frequency_weekly(day, start_day) if frequency == :weekly end private + ## Resource is not idempotent when day, start_day is not provided with frequency :weekly + ## 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 .. + ## 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 + def idempotency_warning_for_frequency_weekly(day, start_day) + if start_day.nil? && day.nil? + logger.warn "To maintain idempotency for frequency :weekly provide start_day, start_time and day." + end + end + # Validate the passed value is numeric values only if it is a string def numeric_value_in_string?(val) return true if Integer(val) @@ -94,9 +117,35 @@ class Chef false end + def validate_frequency(frequency) + if frequency.nil? || !([:minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none].include?(frequency)) + raise ArgumentError, "Frequency needs to be provided. Valid frequencies are :minute, :hourly, :daily, :weekly, :monthly, :once, :on_logon, :onstart, :on_idle, :none." + end + end + + def validate_frequency_monthly(frequency_modifier, months, day) + # validates the frequency :monthly and raises error if frequency_modifier is first, second, thrid etc and day is not provided + if (frequency_modifier != 1) && (frequency_modifier_includes_days_of_weeks?(frequency_modifier)) && !(day) + raise ArgumentError, "Please select day on which you want to run the task e.g. 'Mon, Tue'. Multiple values must be seprated by comma." + end + + # 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. + # 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 + if (frequency_modifier.to_i.between?(2, 12)) && !(months.nil?) + raise ArgumentError, "For frequency :monthly either use property months or frequency_modifier to set months." + end + end + + # returns true if frequency_modifer has values First, second, third, fourth, last, lastday + def frequency_modifier_includes_days_of_weeks?(frequency_modifier) + frequency_modifier = frequency_modifier.to_s.split(",") + frequency_modifier.map! { |value| value.strip.upcase } + (frequency_modifier - VALID_WEEKS).empty? + end + def validate_random_delay(random_delay, frequency) - if [:once, :on_logon, :onstart, :on_idle, :none].include? frequency - raise ArgumentError, "`random_delay` property is supported only for frequency :minute, :hourly, :daily, :weekly and :monthly" + if [:on_logon, :onstart, :on_idle, :none].include? frequency + raise ArgumentError, "`random_delay` property is supported only for frequency :once, :minute, :hourly, :daily, :weekly and :monthly" end 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) @@ -104,12 +153,14 @@ class Chef # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~f def validate_start_day(start_day, frequency) - if [:once, :on_logon, :onstart, :on_idle, :none].include? frequency + if start_day && frequency == :none raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}" end # make sure the start_day is in MM/DD/YYYY format: http://rubular.com/r/cgjHemtWl5 - 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 + if start_day + 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 + end end # @todo when we drop ruby 2.3 support this should be converted to .match?() instead of =~ @@ -122,8 +173,6 @@ class Chef end end - SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze - def validate_user_and_password(user, password) if password_required?(user) && password.nil? raise ArgumentError, %q{Cannot specify a user other than the system users without specifying a password!. Valid passwordless users: 'NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', 'USERS'} @@ -136,65 +185,79 @@ class Chef end def validate_interactive_setting(interactive_enabled, password) - if interactive_enabled && password.nil? - raise ArgumentError, "Please provide the password when attempting to set interactive/non-interactive." - end + raise ArgumentError, "Please provide the password when attempting to set interactive/non-interactive." if interactive_enabled && password.nil? end def validate_create_frequency_modifier(frequency, frequency_modifier) - # Currently is handled in create action 'frequency_modifier_allowed' line. Does not allow for frequency_modifier for once,onstart,onlogon,onidle,none - # Note that 'OnEvent' is not a supported frequency. - unless frequency.nil? || frequency_modifier.nil? - case frequency - when :minute - unless frequency_modifier.to_i > 0 && frequency_modifier.to_i <= 1439 - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :minute frequency are 1 - 1439." - end - when :hourly - unless frequency_modifier.to_i > 0 && frequency_modifier.to_i <= 23 - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :hourly frequency are 1 - 23." - end - when :daily - unless frequency_modifier.to_i > 0 && frequency_modifier.to_i <= 365 - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :daily frequency are 1 - 365." - end - when :weekly - unless frequency_modifier.to_i > 0 && frequency_modifier.to_i <= 52 - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :weekly frequency are 1 - 52." - end - when :monthly - unless ("1".."12").to_a.push("FIRST", "SECOND", "THIRD", "FOURTH", "LAST", "LASTDAY").include?(frequency_modifier.to_s.upcase) - raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY'." + if ([:on_logon, :onstart, :on_idle, :none].include?(frequency)) && ( frequency_modifier != 1) + raise ArgumentError, "frequency_modifier property not supported with frequency :#{frequency}" + end + + if frequency == :monthly + unless (1..12).cover?(frequency_modifier.to_i) || frequency_modifier_includes_days_of_weeks?(frequency_modifier) + raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST'." + end + else + unless frequency.nil? || frequency_modifier.nil? + frequency_modifier = frequency_modifier.to_i + min = 1 + max = case frequency + when :minute + 1439 + when :hourly + 23 + when :daily + 365 + when :weekly + 52 + else + min + end + unless frequency_modifier.between?(min, max) + raise ArgumentError, "frequency_modifier value #{frequency_modifier} is invalid. Valid values for :#{frequency} frequency are #{min} - #{max}." end end end end - def validate_create_day(day, frequency) - unless [:weekly, :monthly].include?(frequency) - raise "day property is only valid for tasks that run monthly or weekly" - end + def validate_create_day(day, frequency, frequency_modifier) + raise ArgumentError, "day property is only valid for tasks that run monthly or weekly" unless [:weekly, :monthly].include?(frequency) + + # This has been verified with schtask.exe https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks#d-dayday-- + # verified with earlier code if day "*" is given with frequency it raised exception Invalid value for /D option + raise ArgumentError, "day wild card (*) is only valid with frequency :weekly" if frequency == :monthly && day == "*" + if day.is_a?(String) && day.to_i.to_s != day days = day.split(",") - days.each do |d| - unless ["mon", "tue", "wed", "thu", "fri", "sat", "sun", "*"].include?(d.strip.downcase) - raise ArgumentError, "day property invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN and *. Multiple values must be separated by a comma." + if days_includes_days_of_months?(days) + # 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 + raise ArgumentError, "day values 1-31 or last is only valid with frequency :monthly" if frequency == :weekly + else + days.map! { |day| day.to_s.strip.downcase } + unless (days - VALID_WEEK_DAYS).empty? + raise ArgumentError, "day property invalid. Only valid values are: #{VALID_WEEK_DAYS.map(&:upcase).join(', ')}. Multiple values must be separated by a comma." end end end end def validate_create_months(months, frequency) - raise ArgumentError, "months property is only valid for tasks that run monthly" unless frequency == :monthly - if months.is_a? String - months.split(",").each do |month| - unless ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC", "*"].include?(month.strip.upcase) - raise ArgumentError, "months property invalid. Only valid values are: JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC and *. Multiple values must be separated by a comma." - end + raise ArgumentError, "months property is only valid for tasks that run monthly" if frequency != :monthly + if months.is_a?(String) + months = months.split(",") + months.map! { |month| month.strip.upcase } + unless (months - VALID_MONTHS).empty? + raise ArgumentError, "months property invalid. Only valid values are: #{VALID_MONTHS.join(', ')}. Multiple values must be separated by a comma." end end end + # This method returns true if day has values from 1-31 which is a days of moths and used with frequency :monthly + def days_includes_days_of_months?(days) + days.map! { |day| day.to_s.strip.downcase } + (days - VALID_DAYS_OF_MONTH).empty? + end + def validate_idle_time(idle_time, frequency) if !idle_time.nil? && frequency != :on_idle raise ArgumentError, "idle_time property is only valid for tasks that run on_idle" @@ -216,6 +279,9 @@ class Chef ISO8601::Duration.new(seconds.to_i).to_s end + def sec_to_min(seconds) + seconds.to_i / 60 + end end end end |