summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornimisha <nimisha.sharad@msystechnologies.com>2017-03-29 15:46:29 +0530
committerBryan McLellan <btm@chef.io>2017-04-03 13:06:20 -0400
commit20153869c604c570febfaed5bd7ec860b6050a9d (patch)
tree6e7084271fa9139f023e8e45ebc931af9fadf785
parent9a71a7a4459ed45a11ac5e590c53a06b2ec19d13 (diff)
downloadchef-20153869c604c570febfaed5bd7ec860b6050a9d.tar.gz
Fixed Rubocop style issues and review comments
Signed-off-by: nimisha <nimisha.sharad@msystechnologies.com>
-rw-r--r--lib/chef/provider/windows_task.rb809
-rw-r--r--lib/chef/resource/windows_task.rb474
-rw-r--r--spec/unit/resource/windows_task_spec.rb426
3 files changed, 861 insertions, 848 deletions
diff --git a/lib/chef/provider/windows_task.rb b/lib/chef/provider/windows_task.rb
index 0147e47a55..0621da6d45 100644
--- a/lib/chef/provider/windows_task.rb
+++ b/lib/chef/provider/windows_task.rb
@@ -1,398 +1,411 @@
-#
-# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# 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 'chef/mixin/shell_out'
-require 'rexml/document'
-require 'iso8601'
-
-class Chef
- class Provider
- class WindowsTask < Chef::Provider
- use_inline_resources
- include Chef::Mixin::ShellOut
-
- provides :windows_task, os: "windows"
-
- def load_current_resource
- @current_resource = Chef::Resource::WindowsTask.new(new_resource.name)
- pathed_task_name = new_resource.task_name.start_with?('\\') ? new_resource.task_name : "\\#{new_resource.task_name}"
-
- @current_resource.task_name(pathed_task_name)
- task_hash = load_task_hash(pathed_task_name)
-
- set_current_resource(task_hash) if task_hash.respond_to?(:[]) && task_hash[:TaskName] == pathed_task_name
- end
-
- def set_current_resource task_hash
- @current_resource.exists = true
- @current_resource.command(task_hash[:TaskToRun])
- @current_resource.cwd(task_hash[:StartIn]) unless task_hash[:StartIn] == 'N/A'
- @current_resource.user(task_hash[:RunAsUser])
- set_current_run_level task_hash[:run_level]
- set_current_frequency task_hash
- @current_resource.day(task_hash[:day]) if task_hash[:day]
- @current_resource.months(task_hash[:months]) if task_hash[:months]
- set_current_idle_time(task_hash[:idle_time]) if task_hash[:idle_time]
- @current_resource.random_delay(task_hash[:random_delay]) if task_hash[:random_delay]
- @current_resource.execution_time_limit(task_hash[:execution_time_limit]) if task_hash[:execution_time_limit]
-
- @current_resource.status = :running if task_hash[:Status] == 'Running'
- @current_resource.enabled = true if task_hash[:ScheduledTaskState] == 'Enabled'
- end
-
- def action_create
- if @current_resource.exists && !(task_need_update? || @new_resource.force)
- Chef::Log.info "#{@new_resource} task already exists - nothing to do"
- else
- options = {}
- options['F'] = '' if @new_resource.force || task_need_update?
- options['SC'] = schedule
- options['MO'] = @new_resource.frequency_modifier if frequency_modifier_allowed
- options['I'] = @new_resource.idle_time unless @new_resource.idle_time.nil?
- options['SD'] = @new_resource.start_day unless @new_resource.start_day.nil?
- options['ST'] = @new_resource.start_time unless @new_resource.start_time.nil?
- options['TR'] = @new_resource.command
- options['RU'] = @new_resource.user
- options['RP'] = @new_resource.password if use_password?
- options['RL'] = 'HIGHEST' if @new_resource.run_level == :highest
- options['IT'] = '' if @new_resource.interactive_enabled
- options['D'] = @new_resource.day if @new_resource.day
- options['M'] = @new_resource.months unless @new_resource.months.nil?
-
- run_schtasks 'CREATE', options
- xml_options = []
- xml_options << "cwd" if new_resource.cwd
- xml_options << "random_delay" if new_resource.random_delay
- xml_options << "execution_time_limit" if new_resource.execution_time_limit
- update_task_xml(xml_options) unless xml_options.empty?
-
- new_resource.updated_by_last_action true
- Chef::Log.info "#{@new_resource} task created"
- end
- end
-
- def action_run
- if @current_resource.exists
- if @current_resource.status == :running
- Chef::Log.info "#{@new_resource} task is currently running, skipping run"
- else
- run_schtasks 'RUN'
- new_resource.updated_by_last_action true
- Chef::Log.info "#{@new_resource} task ran"
- end
- else
- Chef::Log.warn "#{@new_resource} task doesn't exists - nothing to do"
- end
- end
-
- def action_delete
- if @current_resource.exists
- # always need to force deletion
- run_schtasks 'DELETE', 'F' => ''
- new_resource.updated_by_last_action true
- Chef::Log.info "#{@new_resource} task deleted"
- else
- Chef::Log.warn "#{@new_resource} task doesn't exists - nothing to do"
- end
- end
-
- def action_end
- if @current_resource.exists
- if @current_resource.status != :running
- Chef::Log.debug "#{@new_resource} is not running - nothing to do"
- else
- run_schtasks 'END'
- @new_resource.updated_by_last_action true
- Chef::Log.info "#{@new_resource} task ended"
- end
- else
- Chef::Log.warn "#{@new_resource} task doesn't exist - nothing to do"
- end
- end
-
- def action_enable
- if @current_resource.exists
- if @current_resource.enabled
- Chef::Log.debug "#{@new_resource} already enabled - nothing to do"
- else
- run_schtasks 'CHANGE', 'ENABLE' => ''
- @new_resource.updated_by_last_action true
- Chef::Log.info "#{@new_resource} task enabled"
- end
- else
- Chef::Log.fatal "#{@new_resource} task doesn't exist - nothing to do"
- raise Errno::ENOENT, "#{@new_resource}: task does not exist, cannot enable"
- end
- end
-
- def action_disable
- if @current_resource.exists
- if @current_resource.enabled
- run_schtasks 'CHANGE', 'DISABLE' => ''
- @new_resource.updated_by_last_action true
- Chef::Log.info "#{@new_resource} task disabled"
- else
- Chef::Log.warn "#{@new_resource} already disabled - nothing to do"
- end
- else
- Chef::Log.warn "#{@new_resource} task doesn't exist - nothing to do"
- end
- end
-
- private
-
- # rubocop:disable Style/StringLiteralsInInterpolation
- def run_schtasks(task_action, options = {})
- cmd = "schtasks /#{task_action} /TN \"#{@new_resource.task_name}\" "
- options.keys.each do |option|
- cmd += "/#{option} "
- cmd += "\"#{options[option].to_s.gsub('"', "\\\"")}\" " unless options[option] == ''
- end
- Chef::Log.debug('running: ')
- Chef::Log.debug(" #{cmd}")
- shell_out!(cmd, returns: [0])
- end
- # rubocop:enable Style/StringLiteralsInInterpolation
-
- def task_need_update?
- # gsub needed as schtasks converts single quotes to double quotes on creation
- @current_resource.command != @new_resource.command.tr("'", '"') ||
- @current_resource.user.encode('external') != @new_resource.user ||
- @current_resource.run_level != @new_resource.run_level ||
- @current_resource.cwd != @new_resource.cwd ||
- @current_resource.frequency_modifier != @new_resource.frequency_modifier ||
- @current_resource.frequency != @new_resource.frequency ||
- @new_resource.day.casecmp(@current_resource.day) != 0 rescue false ||
- @new_resource.months.casecmp(@current_resource.months) != 0 rescue false ||
- @current_resource.idle_time != @new_resource.idle_time ||
- @current_resource.random_delay != @new_resource.random_delay ||
- @current_resource.execution_time_limit != @new_resource.execution_time_limit ||
- !@new_resource.start_day.nil? || !@new_resource.start_time.nil?
- end
-
- def update_task_xml(options = [])
- # random_delay xml element is different for different frequencies
- random_delay_xml_element = {
- :minute => "Triggers/TimeTrigger/RandomDelay",
- :hourly => "Triggers/TimeTrigger/RandomDelay",
- :once => "Triggers/TimeTrigger/RandomDelay",
- :daily => "Triggers/CalendarTrigger/RandomDelay",
- :weekly => "Triggers/CalendarTrigger/RandomDelay",
- :monthly => "Triggers/CalendarTrigger/RandomDelay",
- }
-
- xml_element_mapping = {
- "cwd" => "Actions/Exec/WorkingDirectory",
- "random_delay" => random_delay_xml_element[@new_resource.frequency],
- "execution_time_limit" => "Settings/ExecutionTimeLimit"
- }
-
- Chef::Log.debug 'looking for existing tasks'
-
- # we use shell_out here instead of shell_out! because a failure implies that the task does not exist
- xml_cmd = shell_out("schtasks /Query /TN \"#{@new_resource.task_name}\" /XML")
-
- return if xml_cmd.exitstatus != 0
-
- doc = REXML::Document.new(xml_cmd.stdout)
-
- options.each do |option|
- Chef::Log.debug 'Removing former #{option} if any'
- doc.root.elements.delete(xml_element_mapping[option])
- option_value = @new_resource.send("#{option}")
-
- if option_value
- Chef::Log.debug 'Setting #option as #option_value'
- split_xml_path = xml_element_mapping[option].split("/") # eg. if xml_element_mapping[option] = "Actions/Exec/WorkingDirectory"
- element_name = split_xml_path.last # element_name = "WorkingDirectory"
- cwd_element = REXML::Element.new(element_name)
- cwd_element.add_text(option_value)
- element_root = (split_xml_path - [element_name]).join("/") # element_root = 'Actions/Exec'
- exec_element = doc.root.elements[element_root]
- exec_element.add_element(cwd_element)
- end
- end
-
- temp_task_file = ::File.join(ENV['TEMP'], 'windows_task.xml')
- begin
- ::File.open(temp_task_file, 'w:UTF-16LE') do |f|
- doc.write(f)
- end
-
- options = {}
- options['RU'] = @new_resource.user if @new_resource.user
- options['RP'] = @new_resource.password if @new_resource.password
- options['IT'] = '' if @new_resource.interactive_enabled
- options['XML'] = temp_task_file
-
- run_schtasks('DELETE', 'F' => '')
- run_schtasks('CREATE', options)
- ensure
- ::File.delete(temp_task_file)
- end
- end
-
- def load_task_hash(task_name)
- Chef::Log.debug 'Looking for existing tasks'
-
- # we use shell_out here instead of shell_out! because a failure implies that the task does not exist
- output = shell_out("chcp 65001 >nul 2>&1 && schtasks /Query /FO LIST /V /TN \"#{task_name}\"").stdout.force_encoding('UTF-8')
- if output.empty?
- task = false
- else
- task = {}
-
- output.split("\n").map! do |line|
- line.split(': ').map!(&:strip)
- end.each do |field|
- if field.is_a?(Array) && field[0].respond_to?(:to_sym)
- key = (field - [field.last]).join(": ")
- task[key.gsub(/\s+/, '').to_sym] = field.last
- end
- end
- end
-
- task_xml = load_task_xml task_name
- task.merge!(task_xml) if task && task_xml
-
- task
- end
-
- def load_task_xml task_name
- xml_cmd = shell_out("chcp 65001 >nul 2>&1 && schtasks /Query /TN \"#{task_name}\" /XML")
- return if xml_cmd.exitstatus != 0
-
- doc = REXML::Document.new(xml_cmd.stdout)
- root = doc.root
-
- task = {}
- task[:run_level] = root.elements["Principals/Principal/RunLevel"].text if root.elements["Principals/Principal/RunLevel"]
-
- # for frequency = :minutes, :hourly
- task[:repetition_interval] = root.elements["Triggers/TimeTrigger/Repetition/Interval"].text if root.elements["Triggers/TimeTrigger/Repetition/Interval"]
-
- # for frequency = :daily
- task[:schedule_by_day] = root.elements["Triggers/CalendarTrigger/ScheduleByDay/DaysInterval"].text if root.elements["Triggers/CalendarTrigger/ScheduleByDay/DaysInterval"]
-
- # for frequency = :weekly
- task[:schedule_by_week] = root.elements["Triggers/CalendarTrigger/ScheduleByWeek/WeeksInterval"].text if root.elements["Triggers/CalendarTrigger/ScheduleByWeek/WeeksInterval"]
- if root.elements["Triggers/CalendarTrigger/ScheduleByWeek/DaysOfWeek"]
- task[:day] = []
- root.elements["Triggers/CalendarTrigger/ScheduleByWeek/DaysOfWeek"].elements.each do |e|
- task[:day] << e.to_s[0..3].delete("<").delete("/>")
- end
- task[:day] = task[:day].join(", ")
- end
-
- # for frequency = :monthly
- task[:schedule_by_month] = root.elements["Triggers/CalendarTrigger/ScheduleByMonth/DaysOfMonth/Day"].text if root.elements["Triggers/CalendarTrigger/ScheduleByMonth/DaysOfMonth/Day"]
- if root.elements["Triggers/CalendarTrigger/ScheduleByMonth/Months"]
- task[:months] = []
- root.elements["Triggers/CalendarTrigger/ScheduleByMonth/Months"].elements.each do |e|
- task[:months] << e.to_s[0..3].delete("<").delete("/>")
- end
- task[:months] = task[:months].join(", ")
- end
-
- task[:on_logon] = true if root.elements["Triggers/LogonTrigger"]
- task[:onstart] = true if root.elements["Triggers/BootTrigger"]
- task[:on_idle] = true if root.elements["Triggers/IdleTrigger"]
-
- task[:idle_time] = root.elements["Settings/IdleSettings/Duration"].text if root.elements["Settings/IdleSettings/Duration"] if task[:on_idle]
-
- task[:once] = true if !(task[:repetition_interval] || task[:schedule_by_day] || task[:schedule_by_week] || task[:schedule_by_month] || task[:on_logon] || task[:onstart] || task[:on_idle])
- task[:execution_time_limit] = root.elements["Settings/ExecutionTimeLimit"].text if root.elements["Settings/ExecutionTimeLimit"] #by default PT72H
- task[:random_delay] = root.elements["Triggers/TimeTrigger/RandomDelay"].text if root.elements["Triggers/TimeTrigger/RandomDelay"]
- task[:random_delay] = root.elements["Triggers/CalendarTrigger/RandomDelay"].text if root.elements["Triggers/CalendarTrigger/RandomDelay"]
- task
- end
-
- SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE'].freeze
-
- def use_password?
- @use_password ||= !SYSTEM_USERS.include?(@new_resource.user.upcase)
- end
-
- def schedule
- case @new_resource.frequency
- when :on_logon
- 'ONLOGON'
- when :on_idle
- 'ONIDLE'
- else
- @new_resource.frequency
- end
- end
-
- def frequency_modifier_allowed
- case @new_resource.frequency
- when :minute, :hourly, :daily, :weekly
- true
- when :monthly
- @new_resource.months.nil? || %w(FIRST SECOND THIRD FOURTH LAST LASTDAY).include?(@new_resource.frequency_modifier)
- else
- false
- end
- end
-
- def set_current_run_level run_level
- case run_level
- when "HighestAvailable"
- @current_resource.run_level(:highest)
- when "LeastPrivilege"
- @current_resource.run_level(:limited)
- end
- end
-
- def set_current_frequency(task_hash)
- if task_hash[:repetition_interval]
- duration = ISO8601::Duration.new(task_hash[:repetition_interval])
- if task_hash[:repetition_interval].include?("M")
- @current_resource.frequency(:minute)
- @current_resource.frequency_modifier(duration.minutes.atom.to_i)
- elsif task_hash[:repetition_interval].include?("H")
- @current_resource.frequency(:hourly)
- @current_resource.frequency_modifier(duration.hours.atom.to_i)
- end
- end
-
- if task_hash[:schedule_by_day]
- @current_resource.frequency(:daily)
- @current_resource.frequency_modifier(task_hash[:schedule_by_day].to_i)
- end
-
- if task_hash[:schedule_by_week]
- @current_resource.frequency(:weekly)
- @current_resource.frequency_modifier(task_hash[:schedule_by_week].to_i)
- end
-
- @current_resource.frequency(:monthly) if task_hash[:schedule_by_month]
- @current_resource.frequency(:on_logon) if task_hash[:on_logon]
- @current_resource.frequency(:onstart) if task_hash[:onstart]
- @current_resource.frequency(:on_idle) if task_hash[:on_idle]
- @current_resource.frequency(:once) if task_hash[:once]
- end
-
- def set_current_idle_time(idle_time)
- duration = ISO8601::Duration.new(idle_time)
- @current_resource.idle_time(duration.minutes.atom.to_i)
- end
-
- end
- end
-end
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 "chef/mixin/shell_out"
+require "rexml/document"
+require "iso8601"
+require "chef/mixin/powershell_out"
+
+class Chef
+ class Provider
+ class WindowsTask < Chef::Provider
+ use_inline_resources
+ include Chef::Mixin::ShellOut
+ include Chef::Mixin::PowershellOut
+
+ provides :windows_task, os: "windows"
+
+ def load_current_resource
+ @current_resource = Chef::Resource::WindowsTask.new(new_resource.name)
+ pathed_task_name = new_resource.task_name.start_with?('\\') ? new_resource.task_name : "\\#{new_resource.task_name}"
+
+ @current_resource.task_name(pathed_task_name)
+ task_hash = load_task_hash(pathed_task_name)
+
+ set_current_resource(task_hash) if task_hash.respond_to?(:[]) && task_hash[:TaskName] == pathed_task_name
+ @current_resource
+ end
+
+ def set_current_resource(task_hash)
+ @current_resource.exists = true
+ @current_resource.command(task_hash[:TaskToRun])
+ @current_resource.cwd(task_hash[:StartIn]) unless task_hash[:StartIn] == "N/A"
+ @current_resource.user(task_hash[:RunAsUser])
+ set_current_run_level task_hash[:run_level]
+ set_current_frequency task_hash
+ @current_resource.day(task_hash[:day]) if task_hash[:day]
+ @current_resource.months(task_hash[:months]) if task_hash[:months]
+ set_current_idle_time(task_hash[:idle_time]) if task_hash[:idle_time]
+ @current_resource.random_delay(task_hash[:random_delay]) if task_hash[:random_delay]
+ @current_resource.execution_time_limit(task_hash[:execution_time_limit]) if task_hash[:execution_time_limit]
+
+ @current_resource.status = :running if task_hash[:Status] == "Running"
+ @current_resource.enabled = true if task_hash[:ScheduledTaskState] == "Enabled"
+ end
+
+ def action_create
+ if @current_resource.exists && !(task_need_update? || @new_resource.force)
+ Chef::Log.info "#{@new_resource} task already exists - nothing to do"
+ else
+ options = {}
+ options["F"] = "" if @new_resource.force || task_need_update?
+ options["SC"] = schedule
+ options["MO"] = @new_resource.frequency_modifier if frequency_modifier_allowed
+ options["I"] = @new_resource.idle_time unless @new_resource.idle_time.nil?
+ options["SD"] = @new_resource.start_day unless @new_resource.start_day.nil?
+ options["ST"] = @new_resource.start_time unless @new_resource.start_time.nil?
+ options["TR"] = @new_resource.command
+ options["RU"] = @new_resource.user
+ options["RP"] = @new_resource.password if use_password?
+ options["RL"] = "HIGHEST" if @new_resource.run_level == :highest
+ options["IT"] = "" if @new_resource.interactive_enabled
+ options["D"] = @new_resource.day if @new_resource.day
+ options["M"] = @new_resource.months unless @new_resource.months.nil?
+
+ run_schtasks "CREATE", options
+ xml_options = []
+ xml_options << "cwd" if new_resource.cwd
+ xml_options << "random_delay" if new_resource.random_delay
+ xml_options << "execution_time_limit" if new_resource.execution_time_limit
+ update_task_xml(xml_options) unless xml_options.empty?
+
+ new_resource.updated_by_last_action true
+ Chef::Log.info "#{@new_resource} task created"
+ end
+ end
+
+ def action_run
+ if @current_resource.exists
+ if @current_resource.status == :running
+ Chef::Log.info "#{@new_resource} task is currently running, skipping run"
+ else
+ run_schtasks "RUN"
+ new_resource.updated_by_last_action true
+ Chef::Log.info "#{@new_resource} task ran"
+ end
+ else
+ Chef::Log.warn "#{@new_resource} task doesn't exists - nothing to do"
+ end
+ end
+
+ def action_delete
+ if @current_resource.exists
+ # always need to force deletion
+ run_schtasks "DELETE", "F" => ""
+ new_resource.updated_by_last_action true
+ Chef::Log.info "#{@new_resource} task deleted"
+ else
+ Chef::Log.warn "#{@new_resource} task doesn't exists - nothing to do"
+ end
+ end
+
+ def action_end
+ if @current_resource.exists
+ if @current_resource.status != :running
+ Chef::Log.debug "#{@new_resource} is not running - nothing to do"
+ else
+ run_schtasks "END"
+ @new_resource.updated_by_last_action true
+ Chef::Log.info "#{@new_resource} task ended"
+ end
+ else
+ Chef::Log.warn "#{@new_resource} task doesn't exist - nothing to do"
+ end
+ end
+
+ def action_enable
+ if @current_resource.exists
+ if @current_resource.enabled
+ Chef::Log.debug "#{@new_resource} already enabled - nothing to do"
+ else
+ run_schtasks "CHANGE", "ENABLE" => ""
+ @new_resource.updated_by_last_action true
+ Chef::Log.info "#{@new_resource} task enabled"
+ end
+ else
+ Chef::Log.fatal "#{@new_resource} task doesn't exist - nothing to do"
+ raise Errno::ENOENT, "#{@new_resource}: task does not exist, cannot enable"
+ end
+ end
+
+ def action_disable
+ if @current_resource.exists
+ if @current_resource.enabled
+ run_schtasks "CHANGE", "DISABLE" => ""
+ @new_resource.updated_by_last_action true
+ Chef::Log.info "#{@new_resource} task disabled"
+ else
+ Chef::Log.warn "#{@new_resource} already disabled - nothing to do"
+ end
+ else
+ Chef::Log.warn "#{@new_resource} task doesn't exist - nothing to do"
+ end
+ end
+
+ private
+
+ # rubocop:disable Style/StringLiteralsInInterpolation
+ def run_schtasks(task_action, options = {})
+ cmd = "schtasks /#{task_action} /TN \"#{@new_resource.task_name}\" "
+ options.keys.each do |option|
+ cmd += "/#{option} "
+ cmd += "\"#{options[option].to_s.gsub('"', "\\\"")}\" " unless options[option] == ""
+ end
+ Chef::Log.debug("running: ")
+ Chef::Log.debug(" #{cmd}")
+ shell_out!(cmd, returns: [0])
+ end
+ # rubocop:enable Style/StringLiteralsInInterpolation
+
+ def task_need_update?
+ return true if @current_resource.command != @new_resource.command.tr("'", '"') ||
+ @current_resource.user != @new_resource.user ||
+ @current_resource.run_level != @new_resource.run_level ||
+ @current_resource.cwd != @new_resource.cwd ||
+ @current_resource.frequency_modifier != @new_resource.frequency_modifier ||
+ @current_resource.frequency != @new_resource.frequency ||
+ @current_resource.idle_time != @new_resource.idle_time ||
+ @current_resource.random_delay != @new_resource.random_delay ||
+ @current_resource.execution_time_limit != @new_resource.execution_time_limit ||
+ !@new_resource.start_day.nil? || !@new_resource.start_time.nil?
+
+ begin
+ return true if @new_resource.day.to_s.casecmp(@current_resource.day.to_s) != 0 ||
+ @new_resource.months.to_s.casecmp(@current_resource.months.to_s) != 0
+ rescue
+ Chef::Log.debug "caught a raise in task_needs_update?"
+ end
+
+ false
+ end
+
+ def update_task_xml(options = [])
+ # random_delay xml element is different for different frequencies
+ random_delay_xml_element = {
+ :minute => "Triggers/TimeTrigger/RandomDelay",
+ :hourly => "Triggers/TimeTrigger/RandomDelay",
+ :once => "Triggers/TimeTrigger/RandomDelay",
+ :daily => "Triggers/CalendarTrigger/RandomDelay",
+ :weekly => "Triggers/CalendarTrigger/RandomDelay",
+ :monthly => "Triggers/CalendarTrigger/RandomDelay",
+ }
+
+ xml_element_mapping = {
+ "cwd" => "Actions/Exec/WorkingDirectory",
+ "random_delay" => random_delay_xml_element[@new_resource.frequency],
+ "execution_time_limit" => "Settings/ExecutionTimeLimit",
+ }
+
+ Chef::Log.debug "looking for existing tasks"
+
+ # we use shell_out here instead of shell_out! because a failure implies that the task does not exist
+ xml_cmd = shell_out("schtasks /Query /TN \"#{@new_resource.task_name}\" /XML")
+
+ return if xml_cmd.exitstatus != 0
+
+ doc = REXML::Document.new(xml_cmd.stdout)
+
+ options.each do |option|
+ Chef::Log.debug 'Removing former #{option} if any'
+ doc.root.elements.delete(xml_element_mapping[option])
+ option_value = @new_resource.send("#{option}")
+
+ if option_value
+ Chef::Log.debug "Setting #option as #option_value"
+ split_xml_path = xml_element_mapping[option].split("/") # eg. if xml_element_mapping[option] = "Actions/Exec/WorkingDirectory"
+ element_name = split_xml_path.last # element_name = "WorkingDirectory"
+ cwd_element = REXML::Element.new(element_name)
+ cwd_element.add_text(option_value)
+ element_root = (split_xml_path - [element_name]).join("/") # element_root = 'Actions/Exec'
+ exec_element = doc.root.elements[element_root]
+ exec_element.add_element(cwd_element)
+ end
+ end
+
+ temp_task_file = ::File.join(ENV["TEMP"], "windows_task.xml")
+ begin
+ ::File.open(temp_task_file, "w:UTF-16LE") do |f|
+ doc.write(f)
+ end
+
+ options = {}
+ options["RU"] = @new_resource.user if @new_resource.user
+ options["RP"] = @new_resource.password if @new_resource.password
+ options["IT"] = "" if @new_resource.interactive_enabled
+ options["XML"] = temp_task_file
+
+ run_schtasks("DELETE", "F" => "")
+ run_schtasks("CREATE", options)
+ ensure
+ ::File.delete(temp_task_file)
+ end
+ end
+
+ def load_task_hash(task_name)
+ Chef::Log.debug "Looking for existing tasks"
+
+ task_script = <<-EOH
+ [Console]::OutputEncoding = [Text.UTF8Encoding]::UTF8
+ schtasks /Query /FO LIST /V /TN \"#{task_name}\"
+ EOH
+
+ output = powershell_out("#{task_script}").stdout.force_encoding("UTF-8")
+ if output.empty?
+ task = false
+ else
+ task = {}
+
+ output.split("\n").map! do |line|
+ line.split(": ").map!(&:strip)
+ end.each do |field|
+ if field.is_a?(Array) && field[0].respond_to?(:to_sym)
+ key = (field - [field.last]).join(": ")
+ task[key.gsub(/\s+/, "").to_sym] = field.last
+ end
+ end
+ end
+
+ task_xml = load_task_xml task_name
+ task.merge!(task_xml) if task && task_xml
+
+ task
+ end
+
+ def load_task_xml(task_name)
+ xml_cmd = shell_out("chcp 65001 >nul 2>&1 && schtasks /Query /TN \"#{task_name}\" /XML")
+ return if xml_cmd.exitstatus != 0
+
+ doc = REXML::Document.new(xml_cmd.stdout)
+ root = doc.root
+
+ task = {}
+ task[:run_level] = root.elements["Principals/Principal/RunLevel"].text if root.elements["Principals/Principal/RunLevel"]
+
+ # for frequency = :minutes, :hourly
+ task[:repetition_interval] = root.elements["Triggers/TimeTrigger/Repetition/Interval"].text if root.elements["Triggers/TimeTrigger/Repetition/Interval"]
+
+ # for frequency = :daily
+ task[:schedule_by_day] = root.elements["Triggers/CalendarTrigger/ScheduleByDay/DaysInterval"].text if root.elements["Triggers/CalendarTrigger/ScheduleByDay/DaysInterval"]
+
+ # for frequency = :weekly
+ task[:schedule_by_week] = root.elements["Triggers/CalendarTrigger/ScheduleByWeek/WeeksInterval"].text if root.elements["Triggers/CalendarTrigger/ScheduleByWeek/WeeksInterval"]
+ if root.elements["Triggers/CalendarTrigger/ScheduleByWeek/DaysOfWeek"]
+ task[:day] = []
+ root.elements["Triggers/CalendarTrigger/ScheduleByWeek/DaysOfWeek"].elements.each do |e|
+ task[:day] << e.to_s[0..3].delete("<").delete("/>")
+ end
+ task[:day] = task[:day].join(", ")
+ end
+
+ # for frequency = :monthly
+ task[:schedule_by_month] = root.elements["Triggers/CalendarTrigger/ScheduleByMonth/DaysOfMonth/Day"].text if root.elements["Triggers/CalendarTrigger/ScheduleByMonth/DaysOfMonth/Day"]
+ if root.elements["Triggers/CalendarTrigger/ScheduleByMonth/Months"]
+ task[:months] = []
+ root.elements["Triggers/CalendarTrigger/ScheduleByMonth/Months"].elements.each do |e|
+ task[:months] << e.to_s[0..3].delete("<").delete("/>")
+ end
+ task[:months] = task[:months].join(", ")
+ end
+
+ task[:on_logon] = true if root.elements["Triggers/LogonTrigger"]
+ task[:onstart] = true if root.elements["Triggers/BootTrigger"]
+ task[:on_idle] = true if root.elements["Triggers/IdleTrigger"]
+
+ task[:idle_time] = root.elements["Settings/IdleSettings/Duration"].text if root.elements["Settings/IdleSettings/Duration"] && task[:on_idle]
+
+ task[:once] = true if !(task[:repetition_interval] || task[:schedule_by_day] || task[:schedule_by_week] || task[:schedule_by_month] || task[:on_logon] || task[:onstart] || task[:on_idle])
+ task[:execution_time_limit] = root.elements["Settings/ExecutionTimeLimit"].text if root.elements["Settings/ExecutionTimeLimit"] #by default PT72H
+ task[:random_delay] = root.elements["Triggers/TimeTrigger/RandomDelay"].text if root.elements["Triggers/TimeTrigger/RandomDelay"]
+ task[:random_delay] = root.elements["Triggers/CalendarTrigger/RandomDelay"].text if root.elements["Triggers/CalendarTrigger/RandomDelay"]
+ task
+ end
+
+ SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', "SYSTEM", 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE', 'BUILTIN\USERS', "USERS"].freeze
+
+ def use_password?
+ @use_password ||= !SYSTEM_USERS.include?(@new_resource.user.upcase)
+ end
+
+ def schedule
+ case @new_resource.frequency
+ when :on_logon
+ "ONLOGON"
+ when :on_idle
+ "ONIDLE"
+ else
+ @new_resource.frequency
+ end
+ end
+
+ def frequency_modifier_allowed
+ case @new_resource.frequency
+ when :minute, :hourly, :daily, :weekly
+ true
+ when :monthly
+ @new_resource.months.nil? || %w{ FIRST SECOND THIRD FOURTH LAST LASTDAY }.include?(@new_resource.frequency_modifier)
+ else
+ false
+ end
+ end
+
+ def set_current_run_level(run_level)
+ case run_level
+ when "HighestAvailable"
+ @current_resource.run_level(:highest)
+ when "LeastPrivilege"
+ @current_resource.run_level(:limited)
+ end
+ end
+
+ def set_current_frequency(task_hash)
+ if task_hash[:repetition_interval]
+ duration = ISO8601::Duration.new(task_hash[:repetition_interval])
+ if task_hash[:repetition_interval].include?("M")
+ @current_resource.frequency(:minute)
+ @current_resource.frequency_modifier(duration.minutes.atom.to_i)
+ elsif task_hash[:repetition_interval].include?("H")
+ @current_resource.frequency(:hourly)
+ @current_resource.frequency_modifier(duration.hours.atom.to_i)
+ end
+ end
+
+ if task_hash[:schedule_by_day]
+ @current_resource.frequency(:daily)
+ @current_resource.frequency_modifier(task_hash[:schedule_by_day].to_i)
+ end
+
+ if task_hash[:schedule_by_week]
+ @current_resource.frequency(:weekly)
+ @current_resource.frequency_modifier(task_hash[:schedule_by_week].to_i)
+ end
+
+ @current_resource.frequency(:monthly) if task_hash[:schedule_by_month]
+ @current_resource.frequency(:on_logon) if task_hash[:on_logon]
+ @current_resource.frequency(:onstart) if task_hash[:onstart]
+ @current_resource.frequency(:on_idle) if task_hash[:on_idle]
+ @current_resource.frequency(:once) if task_hash[:once]
+ end
+
+ def set_current_idle_time(idle_time)
+ duration = ISO8601::Duration.new(idle_time)
+ @current_resource.idle_time(duration.minutes.atom.to_i)
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/resource/windows_task.rb b/lib/chef/resource/windows_task.rb
index c8701eacbf..ff34b9f1e3 100644
--- a/lib/chef/resource/windows_task.rb
+++ b/lib/chef/resource/windows_task.rb
@@ -1,237 +1,237 @@
-#
-# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
-# Copyright:: Copyright 2008-2016, Chef Software Inc.
-# License:: Apache License, Version 2.0
-#
-# 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 "chef/resource"
-
-class Chef
- class Resource
- class WindowsTask < Chef::Resource
-
- provides :windows_task, os: "windows"
-
- allowed_actions :create, :delete, :run, :end, :change, :enable, :disable
- default_action :create
-
- def initialize(name, run_context = nil)
- super
- @resource_name = :windows_task
- @task_name = name
- @action = :create
- end
-
- property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/]
- property :command, String
- property :cwd, String
- property :user, String, default: 'SYSTEM'
- property :password, String
- property :run_level, equal_to: [:highest, :limited], default: :limited
- property :force, [TrueClass, FalseClass], default: false
- property :interactive_enabled, [TrueClass, FalseClass], default: false
- property :frequency_modifier, [Integer, String], default: 1
- property :frequency, equal_to: [:minute,
- :hourly,
- :daily,
- :weekly,
- :monthly,
- :once,
- :on_logon,
- :onstart,
- :on_idle], default: :hourly
- property :start_day, String
- property :start_time, String
- property :day, [String, Integer]
- property :months, String
- property :idle_time, Integer
- property :random_delay, String
- property :execution_time_limit
-
- attr_accessor :exists, :status, :enabled
-
- def after_created
- if random_delay
- validate_random_delay(random_delay, frequency)
- duration = sec_to_dur(random_delay)
- random_delay(duration)
- end
-
- if execution_time_limit
- raise ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as a String e.g. '60'." if execution_time_limit.to_i == 0
- duration = sec_to_dur(execution_time_limit)
- execution_time_limit(duration)
- else
- # schtask sets execution_time_limit as PT72H by default
- # We are setting the default value here so that we can do idempotency check later
- # Note: We can't use `default` in the property
- # because it will raise error for Invalid values passed as "PT72H" is not in seconds
- execution_time_limit("PT72H")
- end
-
- validate_start_time(start_time) if frequency == :once
- 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_months(months, frequency) if months
- validate_idle_time(idle_time, frequency) if idle_time
- end
-
- private
-
- def validate_random_delay(random_delay, frequency)
- if [:once, :on_logon, :onstart, :on_idle].include? frequency
- raise ArgumentError, "`random_delay` property is supported only for frequency :minute, :hourly, :daily, :weekly and :monthly"
- end
-
- raise ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as a String e.g. '60'." if random_delay.to_i == 0
- end
-
- def validate_start_day(start_day, frequency)
- if [:once, :on_logon, :onstart, :on_idle].include? frequency
- raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}"
- end
- end
-
- def validate_start_time(start_time)
- raise ArgumentError,"`start_time` needs to be provided with `frequency :once`" unless start_time
- end
-
- SYSTEM_USERS = ['NT AUTHORITY\SYSTEM', 'SYSTEM', 'NT AUTHORITY\LOCALSERVICE', 'NT AUTHORITY\NETWORKSERVICE'].freeze
-
- def validate_user_and_password(user,password)
- if user && use_password?(user)
- if password.nil?
- raise ArgumentError, "Can't specify a non-system user without a password!"
- end
- end
- end
-
- def use_password?(user)
- @use_password ||= !SYSTEM_USERS.include?(user.upcase)
- 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
- 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
- # 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'."
- end
- end
- end
- end
-
- def validate_create_day(day, frequency)
- unless [:weekly].include?(frequency)
- raise 'day attribute is only valid for tasks that run weekly'
- end
- 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 'day attribute invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN and *. Multiple values must be separated by a comma.'
- end
- end
- end
- end
-
- def validate_create_months(months, frequency)
- unless [:monthly].include?(frequency)
- raise 'months attribute is only valid for tasks that run monthly'
- end
- 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 'months attribute 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
- end
- end
- end
-
- def validate_idle_time(idle_time, frequency)
- unless [:on_idle].include?(frequency)
- raise 'idle_time attribute is only valid for tasks that run on_idle'
- end
-
- unless idle_time.to_i > 0 && idle_time.to_i <= 999
- raise "idle_time value #{idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999."
- end
- end
-
- # Convert the number of seconds to an ISO8601 duration format
- # @see http://tools.ietf.org/html/rfc2445#section-4.3.6
- # @param [Integer] seconds The amount of seconds for this duration
- def sec_to_dur(seconds)
- seconds = seconds.to_i
- return if seconds == 0
- iso_str = 'P'
- if seconds > 604_800 # more than a week
- weeks = seconds / 604_800
- seconds -= (604_800 * weeks)
- iso_str << "#{weeks}W"
- end
- if seconds > 86_400 # more than a day
- days = seconds / 86_400
- seconds -= (86_400 * days)
- iso_str << "#{days}D"
- end
- if seconds > 0
- iso_str << 'T'
- if seconds > 3600 # more than an hour
- hours = seconds / 3600
- seconds -= (3600 * hours)
- iso_str << "#{hours}H"
- end
- if seconds > 60 # more than a minute
- minutes = seconds / 60
- seconds -= (60 * minutes)
- iso_str << "#{minutes}M"
- end
- iso_str << "#{seconds}S"
- end
-
- iso_str
- end
-
- end
- end
-end
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2016, Chef Software Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 "chef/resource"
+
+class Chef
+ class Resource
+ class WindowsTask < Chef::Resource
+
+ provides :windows_task, os: "windows"
+
+ allowed_actions :create, :delete, :run, :end, :change, :enable, :disable
+ default_action :create
+
+ def initialize(name, run_context = nil)
+ super
+ @resource_name = :windows_task
+ @task_name = name
+ @action = :create
+ end
+
+ property :task_name, String, regex: [/\A[^\/\:\*\?\<\>\|]+\z/]
+ property :command, String
+ property :cwd, String
+ property :user, String, default: "SYSTEM"
+ property :password, String
+ property :run_level, equal_to: [:highest, :limited], default: :limited
+ property :force, [TrueClass, FalseClass], default: false
+ property :interactive_enabled, [TrueClass, FalseClass], default: false
+ property :frequency_modifier, [Integer, String], default: 1
+ property :frequency, equal_to: [:minute,
+ :hourly,
+ :daily,
+ :weekly,
+ :monthly,
+ :once,
+ :on_logon,
+ :onstart,
+ :on_idle], default: :hourly
+ property :start_day, String
+ property :start_time, String
+ property :day, [String, Integer]
+ property :months, String
+ property :idle_time, Integer
+ property :random_delay, String
+ property :execution_time_limit
+
+ attr_accessor :exists, :status, :enabled
+
+ def after_created
+ if random_delay
+ validate_random_delay(random_delay, frequency)
+ duration = sec_to_dur(random_delay)
+ random_delay(duration)
+ end
+
+ if execution_time_limit
+ raise ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as a String e.g. '60'." if execution_time_limit.to_i == 0
+ duration = sec_to_dur(execution_time_limit)
+ execution_time_limit(duration)
+ else
+ # schtask sets execution_time_limit as PT72H by default
+ # We are setting the default value here so that we can do idempotency check later
+ # Note: We can't use `default` in the property
+ # because it will raise error for Invalid values passed as "PT72H" is not in seconds
+ execution_time_limit("PT72H")
+ end
+
+ validate_start_time(start_time) if frequency == :once
+ 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_months(months, frequency) if months
+ validate_idle_time(idle_time, frequency) if idle_time
+ end
+
+ private
+
+ def validate_random_delay(random_delay, frequency)
+ if [:once, :on_logon, :onstart, :on_idle].include? frequency
+ raise ArgumentError, "`random_delay` property is supported only for frequency :minute, :hourly, :daily, :weekly and :monthly"
+ end
+
+ raise ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as a String e.g. '60'." if random_delay.to_i == 0
+ end
+
+ def validate_start_day(start_day, frequency)
+ if [:once, :on_logon, :onstart, :on_idle].include? frequency
+ raise ArgumentError, "`start_day` property is not supported with frequency: #{frequency}"
+ end
+ end
+
+ def validate_start_time(start_time)
+ raise ArgumentError, "`start_time` needs to be provided with `frequency :once`" unless start_time
+ 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 user && use_password?(user)
+ if password.nil?
+ raise ArgumentError, "Can't specify a non-system user without a password!"
+ end
+ end
+ end
+
+ def use_password?(user)
+ @use_password ||= !SYSTEM_USERS.include?(user.upcase)
+ 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
+ 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
+ # 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'."
+ end
+ end
+ end
+ end
+
+ def validate_create_day(day, frequency)
+ unless [:weekly].include?(frequency)
+ raise "day attribute is only valid for tasks that run weekly"
+ end
+ 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 "day attribute invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN and *. Multiple values must be separated by a comma."
+ end
+ end
+ end
+ end
+
+ def validate_create_months(months, frequency)
+ unless [:monthly].include?(frequency)
+ raise "months attribute is only valid for tasks that run monthly"
+ end
+ 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 "months attribute 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
+ end
+ end
+ end
+
+ def validate_idle_time(idle_time, frequency)
+ unless [:on_idle].include?(frequency)
+ raise "idle_time attribute is only valid for tasks that run on_idle"
+ end
+
+ unless idle_time.to_i > 0 && idle_time.to_i <= 999
+ raise "idle_time value #{idle_time} is invalid. Valid values for :on_idle frequency are 1 - 999."
+ end
+ end
+
+ # Convert the number of seconds to an ISO8601 duration format
+ # @see http://tools.ietf.org/html/rfc2445#section-4.3.6
+ # @param [Integer] seconds The amount of seconds for this duration
+ def sec_to_dur(seconds)
+ seconds = seconds.to_i
+ return if seconds == 0
+ iso_str = "P"
+ if seconds > 604_800 # more than a week
+ weeks = seconds / 604_800
+ seconds -= (604_800 * weeks)
+ iso_str << "#{weeks}W"
+ end
+ if seconds > 86_400 # more than a day
+ days = seconds / 86_400
+ seconds -= (86_400 * days)
+ iso_str << "#{days}D"
+ end
+ if seconds > 0
+ iso_str << "T"
+ if seconds > 3600 # more than an hour
+ hours = seconds / 3600
+ seconds -= (3600 * hours)
+ iso_str << "#{hours}H"
+ end
+ if seconds > 60 # more than a minute
+ minutes = seconds / 60
+ seconds -= (60 * minutes)
+ iso_str << "#{minutes}M"
+ end
+ iso_str << "#{seconds}S"
+ end
+
+ iso_str
+ end
+
+ end
+ end
+end
diff --git a/spec/unit/resource/windows_task_spec.rb b/spec/unit/resource/windows_task_spec.rb
index e158d9fe82..fa2d458bbb 100644
--- a/spec/unit/resource/windows_task_spec.rb
+++ b/spec/unit/resource/windows_task_spec.rb
@@ -1,213 +1,213 @@
-#
-# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
-# Copyright:: Copyright 2008-2017, Chef Software, Inc.
-# License:: Apache License, Version 2.0
-#
-# 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 "spec_helper"
-
-describe Chef::Resource::MsuPackage do
- let(:resource) { Chef::Resource::WindowsTask.new("sample_task") }
-
- it "creates a new Chef::Resource::WindowsTask" do
- expect(resource).to be_a_kind_of(Chef::Resource)
- expect(resource).to be_a_instance_of(Chef::Resource::WindowsTask)
- end
-
- it "sets resource name as :windows_task" do
- expect(resource.resource_name).to eql(:windows_task)
- end
-
- it "sets the task_name as it's name" do
- expect(resource.task_name).to eql("sample_task")
- end
-
- it "sets the default action as :create" do
- expect(resource.action).to eql(:create)
- end
-
- it "sets the default user as System" do
- expect(resource.user).to eql('SYSTEM')
- end
-
- it "sets the default run_level as :limited" do
- expect(resource.run_level).to eql(:limited)
- end
-
- it "sets the default force as false" do
- expect(resource.force).to eql(false)
- end
-
- it "sets the default interactive_enabled as false" do
- expect(resource.interactive_enabled).to eql(false)
- end
-
- it "sets the default frequency_modifier as 1" do
- expect(resource.frequency_modifier).to eql(1)
- end
-
- it "sets the default frequency as :hourly" do
- expect(resource.frequency).to eql(:hourly)
- end
-
- context "when random_delay is passed" do
- it "raises error if frequency is `:once`" do
- resource.frequency :once
- resource.random_delay "20"
- expect { resource.after_created }.to raise_error(Chef::Exceptions::ArgumentError, "`random_delay` property is supported only for frequency :minute, :hourly, :daily, :weekly and :monthly")
- end
-
- it "raises error for invalid random_delay" do
- resource.frequency :monthly
- resource.random_delay "xyz"
- expect { resource.after_created }.to raise_error(Chef::Exceptions::ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as a String e.g. '60'.")
- end
-
- it "converts seconds into iso8601 format" do
- resource.frequency :monthly
- resource.random_delay "60"
- resource.after_created
- expect(resource.random_delay).to eq("PT60S")
- end
- end
-
- context "when execution_time_limit is passed" do
- it "sets the deafult value as PT72H" do
- resource.after_created
- expect(resource.execution_time_limit).to eq("PT72H")
- end
-
- it "raises error for invalid execution_time_limit" do
- resource.execution_time_limit "abc"
- expect { resource.after_created }.to raise_error(Chef::Exceptions::ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as a String e.g. '60'.")
- end
-
- it "converts seconds into iso8601 format" do
- resource.execution_time_limit "60"
- resource.after_created
- expect(resource.execution_time_limit).to eq("PT60S")
- end
- end
-
- context "#validate_start_time" do
- it "raises error if start_time is nil" do
- expect { resource.send(:validate_start_time, nil) }.to raise_error(Chef::Exceptions::ArgumentError, "`start_time` needs to be provided with `frequency :once`")
- end
- end
-
- context "#validate_start_day" do
- it "raise error if start_day is passed with frequency :on_logon" do
- resource.frequency :on_logon
- expect { resource.send(:validate_start_day, "Wed", :on_logon) }.to raise_error(Chef::Exceptions::ArgumentError, "`start_day` property is not supported with frequency: on_logon")
- end
- end
-
- context "#validate_user_and_password" do
- context "when password is not passed" do
- it "raises error with non-system users" do
- allow(resource).to receive(:use_password?).and_return(true)
- expect { resource.send(:validate_user_and_password, "Testuser", nil) }.to raise_error("Can't specify a non-system user without a password!")
- end
- end
- end
-
- context "#validate_interactive_setting" do
- it "raises error when interactive_enabled is passed without password" do
- expect { resource.send(:validate_interactive_setting, true, nil) }.to raise_error("Please provide the password when attempting to set interactive/non-interactive.")
- end
- end
-
- context "#validate_create_frequency_modifier" do
- context "when frequency is :minute" do
- it "raises error if frequency_modifier > 1439" do
- expect { resource.send(:validate_create_frequency_modifier, :minute, 1500) }.to raise_error("frequency_modifier value 1500 is invalid. Valid values for :minute frequency are 1 - 1439.")
- end
- end
-
- context "when frequency is :hourly" do
- it "raises error if frequency_modifier > 23" do
- expect { resource.send(:validate_create_frequency_modifier, :hourly, 24) }.to raise_error("frequency_modifier value 24 is invalid. Valid values for :hourly frequency are 1 - 23.")
- end
- end
-
- context "when frequency is :daily" do
- it "raises error if frequency_modifier > 365" do
- expect { resource.send(:validate_create_frequency_modifier, :daily, 366) }.to raise_error("frequency_modifier value 366 is invalid. Valid values for :daily frequency are 1 - 365.")
- end
- end
-
- context "when frequency is :weekly" do
- it "raises error if frequency_modifier > 52" do
- expect { resource.send(:validate_create_frequency_modifier, :weekly, 53) }.to raise_error("frequency_modifier value 53 is invalid. Valid values for :weekly frequency are 1 - 52.")
- end
- end
-
- context "when frequency is :monthly" do
- it "raises error if frequency_modifier > 12" do
- expect { resource.send(:validate_create_frequency_modifier, :monthly, 14) }.to raise_error("frequency_modifier value 14 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY'.")
- end
-
- it "raises error if frequency_modifier is invalid" do
- expect { resource.send(:validate_create_frequency_modifier, :monthly, "abc") }.to raise_error("frequency_modifier value abc is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY'.")
- end
- end
- end
-
- context "#validate_create_day" do
- it "raises error if frequency is not :weekly" do
- expect { resource.send(:validate_create_day, "Mon", :monthly) }.to raise_error("day attribute is only valid for tasks that run weekly")
- end
-
- it "accepts a valid single day" do
- expect { resource.send(:validate_create_day, "Mon", :weekly) }.not_to raise_error
- end
-
- it "accepts a comma separated list of valid days" do
- expect { resource.send(:validate_create_day, "Mon, tue, THU", :weekly) }.not_to raise_error
- end
-
- it "raises error for invalid day value" do
- expect { resource.send(:validate_create_day, "xyz", :weekly) }.to raise_error("day attribute invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN and *. Multiple values must be separated by a comma.")
- end
- end
-
- context "#validate_create_months" do
- it "raises error if frequency is not :monthly" do
- expect { resource.send(:validate_create_months, "Jan", :once) }.to raise_error("months attribute is only valid for tasks that run monthly")
- end
-
- it "accepts a valid single month" do
- expect { resource.send(:validate_create_months, "Feb", :monthly) }.not_to raise_error
- end
-
- it "accepts a comma separated list of valid months" do
- expect { resource.send(:validate_create_months, "Jan, mar, AUG", :monthly) }.not_to raise_error
- end
-
- it "raises error for invalid month value" do
- expect { resource.send(:validate_create_months, "xyz", :monthly) }.to raise_error("months attribute 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
- end
-
- context "#validate_idle_time" do
- it "raises error if frequency is not :on_idle" do
- expect { resource.send(:validate_idle_time, 5, :hourly) }.to raise_error("idle_time attribute is only valid for tasks that run on_idle")
- end
-
- it "raises error if idle_time > 999" do
- expect { resource.send(:validate_idle_time, 1000, :on_idle) }.to raise_error("idle_time value 1000 is invalid. Valid values for :on_idle frequency are 1 - 999.")
- end
- end
-end \ No newline at end of file
+#
+# Author:: Nimisha Sharad (<nimisha.sharad@msystechnologies.com>)
+# Copyright:: Copyright 2008-2017, Chef Software, Inc.
+# License:: Apache License, Version 2.0
+#
+# 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 "spec_helper"
+
+describe Chef::Resource::WindowsTask do
+ let(:resource) { Chef::Resource::WindowsTask.new("sample_task") }
+
+ it "creates a new Chef::Resource::WindowsTask" do
+ expect(resource).to be_a_kind_of(Chef::Resource)
+ expect(resource).to be_a_instance_of(Chef::Resource::WindowsTask)
+ end
+
+ it "sets resource name as :windows_task" do
+ expect(resource.resource_name).to eql(:windows_task)
+ end
+
+ it "sets the task_name as it's name" do
+ expect(resource.task_name).to eql("sample_task")
+ end
+
+ it "sets the default action as :create" do
+ expect(resource.action).to eql(:create)
+ end
+
+ it "sets the default user as System" do
+ expect(resource.user).to eql("SYSTEM")
+ end
+
+ it "sets the default run_level as :limited" do
+ expect(resource.run_level).to eql(:limited)
+ end
+
+ it "sets the default force as false" do
+ expect(resource.force).to eql(false)
+ end
+
+ it "sets the default interactive_enabled as false" do
+ expect(resource.interactive_enabled).to eql(false)
+ end
+
+ it "sets the default frequency_modifier as 1" do
+ expect(resource.frequency_modifier).to eql(1)
+ end
+
+ it "sets the default frequency as :hourly" do
+ expect(resource.frequency).to eql(:hourly)
+ end
+
+ context "when random_delay is passed" do
+ it "raises error if frequency is `:once`" do
+ resource.frequency :once
+ resource.random_delay "20"
+ expect { resource.after_created }.to raise_error(Chef::Exceptions::ArgumentError, "`random_delay` property is supported only for frequency :minute, :hourly, :daily, :weekly and :monthly")
+ end
+
+ it "raises error for invalid random_delay" do
+ resource.frequency :monthly
+ resource.random_delay "xyz"
+ expect { resource.after_created }.to raise_error(Chef::Exceptions::ArgumentError, "Invalid value passed for `random_delay`. Please pass seconds as a String e.g. '60'.")
+ end
+
+ it "converts seconds into iso8601 format" do
+ resource.frequency :monthly
+ resource.random_delay "60"
+ resource.after_created
+ expect(resource.random_delay).to eq("PT60S")
+ end
+ end
+
+ context "when execution_time_limit is passed" do
+ it "sets the deafult value as PT72H" do
+ resource.after_created
+ expect(resource.execution_time_limit).to eq("PT72H")
+ end
+
+ it "raises error for invalid execution_time_limit" do
+ resource.execution_time_limit "abc"
+ expect { resource.after_created }.to raise_error(Chef::Exceptions::ArgumentError, "Invalid value passed for `execution_time_limit`. Please pass seconds as a String e.g. '60'.")
+ end
+
+ it "converts seconds into iso8601 format" do
+ resource.execution_time_limit "60"
+ resource.after_created
+ expect(resource.execution_time_limit).to eq("PT60S")
+ end
+ end
+
+ context "#validate_start_time" do
+ it "raises error if start_time is nil" do
+ expect { resource.send(:validate_start_time, nil) }.to raise_error(Chef::Exceptions::ArgumentError, "`start_time` needs to be provided with `frequency :once`")
+ end
+ end
+
+ context "#validate_start_day" do
+ it "raise error if start_day is passed with frequency :on_logon" do
+ resource.frequency :on_logon
+ expect { resource.send(:validate_start_day, "Wed", :on_logon) }.to raise_error(Chef::Exceptions::ArgumentError, "`start_day` property is not supported with frequency: on_logon")
+ end
+ end
+
+ context "#validate_user_and_password" do
+ context "when password is not passed" do
+ it "raises error with non-system users" do
+ allow(resource).to receive(:use_password?).and_return(true)
+ expect { resource.send(:validate_user_and_password, "Testuser", nil) }.to raise_error("Can't specify a non-system user without a password!")
+ end
+ end
+ end
+
+ context "#validate_interactive_setting" do
+ it "raises error when interactive_enabled is passed without password" do
+ expect { resource.send(:validate_interactive_setting, true, nil) }.to raise_error("Please provide the password when attempting to set interactive/non-interactive.")
+ end
+ end
+
+ context "#validate_create_frequency_modifier" do
+ context "when frequency is :minute" do
+ it "raises error if frequency_modifier > 1439" do
+ expect { resource.send(:validate_create_frequency_modifier, :minute, 1500) }.to raise_error("frequency_modifier value 1500 is invalid. Valid values for :minute frequency are 1 - 1439.")
+ end
+ end
+
+ context "when frequency is :hourly" do
+ it "raises error if frequency_modifier > 23" do
+ expect { resource.send(:validate_create_frequency_modifier, :hourly, 24) }.to raise_error("frequency_modifier value 24 is invalid. Valid values for :hourly frequency are 1 - 23.")
+ end
+ end
+
+ context "when frequency is :daily" do
+ it "raises error if frequency_modifier > 365" do
+ expect { resource.send(:validate_create_frequency_modifier, :daily, 366) }.to raise_error("frequency_modifier value 366 is invalid. Valid values for :daily frequency are 1 - 365.")
+ end
+ end
+
+ context "when frequency is :weekly" do
+ it "raises error if frequency_modifier > 52" do
+ expect { resource.send(:validate_create_frequency_modifier, :weekly, 53) }.to raise_error("frequency_modifier value 53 is invalid. Valid values for :weekly frequency are 1 - 52.")
+ end
+ end
+
+ context "when frequency is :monthly" do
+ it "raises error if frequency_modifier > 12" do
+ expect { resource.send(:validate_create_frequency_modifier, :monthly, 14) }.to raise_error("frequency_modifier value 14 is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY'.")
+ end
+
+ it "raises error if frequency_modifier is invalid" do
+ expect { resource.send(:validate_create_frequency_modifier, :monthly, "abc") }.to raise_error("frequency_modifier value abc is invalid. Valid values for :monthly frequency are 1 - 12, 'FIRST', 'SECOND', 'THIRD', 'FOURTH', 'LAST', 'LASTDAY'.")
+ end
+ end
+ end
+
+ context "#validate_create_day" do
+ it "raises error if frequency is not :weekly" do
+ expect { resource.send(:validate_create_day, "Mon", :monthly) }.to raise_error("day attribute is only valid for tasks that run weekly")
+ end
+
+ it "accepts a valid single day" do
+ expect { resource.send(:validate_create_day, "Mon", :weekly) }.not_to raise_error
+ end
+
+ it "accepts a comma separated list of valid days" do
+ expect { resource.send(:validate_create_day, "Mon, tue, THU", :weekly) }.not_to raise_error
+ end
+
+ it "raises error for invalid day value" do
+ expect { resource.send(:validate_create_day, "xyz", :weekly) }.to raise_error("day attribute invalid. Only valid values are: MON, TUE, WED, THU, FRI, SAT, SUN and *. Multiple values must be separated by a comma.")
+ end
+ end
+
+ context "#validate_create_months" do
+ it "raises error if frequency is not :monthly" do
+ expect { resource.send(:validate_create_months, "Jan", :once) }.to raise_error("months attribute is only valid for tasks that run monthly")
+ end
+
+ it "accepts a valid single month" do
+ expect { resource.send(:validate_create_months, "Feb", :monthly) }.not_to raise_error
+ end
+
+ it "accepts a comma separated list of valid months" do
+ expect { resource.send(:validate_create_months, "Jan, mar, AUG", :monthly) }.not_to raise_error
+ end
+
+ it "raises error for invalid month value" do
+ expect { resource.send(:validate_create_months, "xyz", :monthly) }.to raise_error("months attribute 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
+ end
+
+ context "#validate_idle_time" do
+ it "raises error if frequency is not :on_idle" do
+ expect { resource.send(:validate_idle_time, 5, :hourly) }.to raise_error("idle_time attribute is only valid for tasks that run on_idle")
+ end
+
+ it "raises error if idle_time > 999" do
+ expect { resource.send(:validate_idle_time, 1000, :on_idle) }.to raise_error("idle_time value 1000 is invalid. Valid values for :on_idle frequency are 1 - 999.")
+ end
+ end
+end