diff --git a/lib/puppet/type/scheduled_task.rb b/lib/puppet/type/scheduled_task.rb index 4a0fe4e66..f69035a0d 100644 --- a/lib/puppet/type/scheduled_task.rb +++ b/lib/puppet/type/scheduled_task.rb @@ -1,168 +1,174 @@ require 'puppet/util' Puppet::Type.newtype(:scheduled_task) do include Puppet::Util @doc = "Installs and manages Windows Scheduled Tasks. All attributes except `name`, `command`, and `trigger` are optional; see the description of the `trigger` attribute for details on setting schedules." ensurable newproperty(:enabled) do desc "Whether the triggers for this task should be enabled. This attribute affects every trigger for the task; triggers cannot be enabled or disabled individually." newvalue(:true, :event => :task_enabled) newvalue(:false, :event => :task_disabled) defaultto(:true) end newparam(:name) do desc "The name assigned to the scheduled task. This will uniquely identify the task on the system." isnamevar end newproperty(:command) do desc "The full path to the application to run, without any arguments." validate do |value| raise Puppet::Error.new('Must be specified using an absolute path.') unless absolute_path?(value) end + munge do |value| + # windows converts slashes to backslashes, so the *is* value + # has backslashes. Do the same for the *should* value, so that + # we are slash-insensitive. See #13009 + File.expand_path(value).gsub(/\//, '\\') + end end newproperty(:working_dir) do desc "The full path of the directory in which to start the command." validate do |value| raise Puppet::Error.new('Must be specified using an absolute path.') unless absolute_path?(value) end end newproperty(:arguments, :array_matching => :all) do desc "Any arguments or flags that should be passed to the command. Multiple arguments can be specified as an array or as a space-separated string." end newproperty(:user) do desc "The user to run the scheduled task as. Please note that not all security configurations will allow running a scheduled task as 'SYSTEM', and saving the scheduled task under these conditions will fail with a reported error of 'The operation completed successfully'. It is recommended that you either choose another user to run the scheduled task, or alter the security policy to allow v1 scheduled tasks to run as the 'SYSTEM' account. Defaults to 'SYSTEM'." defaultto :system def insync?(current) provider.user_insync?(current, @should) end end newparam(:password) do desc "The password for the user specified in the 'user' attribute. This is only used if specifying a user other than 'SYSTEM'. Since there is no way to retrieve the password used to set the account information for a task, this parameter will not be used to determine if a scheduled task is in sync or not." end newproperty(:trigger, :array_matching => :all) do desc <<-EOT One or more triggers defining when the task should run. A single trigger is represented as a hash, and multiple triggers can be specified with an array of hashes. A trigger can contain the following keys: * For all triggers: * `schedule` **(Required)** --- The schedule type. Valid values are `daily`, `weekly`, `monthly`, or `once`. * `start_time` **(Required)** --- The time of day when the trigger should first become active. Several time formats will work, but we suggest 24-hour time formatted as HH:MM. * `start_date` --- The date when the trigger should first become active. Defaults to "today." Several date formats will work, including special dates like "today," but we suggest formatting dates as YYYY-MM-DD. * For daily triggers: * `every` --- How often the task should run, as a number of days. Defaults to 1. ("2" means every other day, "3" means every three days, etc.) * For weekly triggers: * `every` --- How often the task should run, as a number of weeks. Defaults to 1. ("2" means every other week, "3" means every three weeks, etc.) * `day_of_week` --- Which days of the week the task should run, as an array. Defaults to all days. Each day must be one of `mon`, `tues`, `wed`, `thurs`, `fri`, `sat`, `sun`, or `all`. * For monthly-by-date triggers: * `months` --- Which months the task should run, as an array. Defaults to all months. Each month must be an integer between 1 and 12. * `on` **(Required)** --- Which days of the month the task should run, as an array. Each day must beeither an integer between 1 and 31, or the special value `last,` which is always the last day of the month. * For monthly-by-weekday triggers: * `months` --- Which months the task should run, as an array. Defaults to all months. Each month must be an integer between 1 and 12. * `day_of_week` **(Required)** --- Which day of the week the task should run, as an array with only one element. Each day must be one of `mon`, `tues`, `wed`, `thurs`, `fri`, `sat`, `sun`, or `all`. * `which_occurrence` **(Required)** --- The occurrence of the chosen weekday when the task should run. Must be one of `first`, `second`, `third`, `fourth`, `fifth`, or `last`. Examples: # Run at 8am on the 1st, 15th, and last day of the month in January, March, # May, July, September, and November, starting after August 31st, 2011. trigger => { schedule => monthly, start_date => '2011-08-31', # Defaults to 'today' start_time => '08:00', # Must be specified months => [1,3,5,7,9,11], # Defaults to all on => [1, 15, last], # Must be specified } # Run at 8am on the first Monday of the month for January, March, and May, # starting after August 31st, 2011. trigger => { schedule => monthly, start_date => '2011-08-31', # Defaults to 'today' start_time => '08:00', # Must be specified months => [1,3,5], # Defaults to all which_occurrence => first, # Must be specified day_of_week => [mon], # Must be specified } EOT validate do |value| provider.validate_trigger(value) end def insync?(current) provider.trigger_insync?(current, @should) end def should_to_s(new_value=@should) self.class.format_value_for_display(new_value) end def is_to_s(current_value=@is) self.class.format_value_for_display(current_value) end end validate do return true if self[:ensure] == :absent if self[:arguments] and !(self[:arguments].is_a?(Array) and self[:arguments].length == 1) self.fail('Parameter arguments failed: Must be specified as a single string') end end end diff --git a/spec/unit/type/scheduled_task_spec.rb b/spec/unit/type/scheduled_task_spec.rb index 17d84900e..7c128177b 100644 --- a/spec/unit/type/scheduled_task_spec.rb +++ b/spec/unit/type/scheduled_task_spec.rb @@ -1,102 +1,116 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Type.type(:scheduled_task), :if => Puppet.features.microsoft_windows? do it 'should use name as the namevar' do described_class.new( :title => 'Foo', :command => 'C:\Windows\System32\notepad.exe' ).name.must == 'Foo' end describe 'when setting the command' do it 'should accept an absolute path to the command' do described_class.new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe')[:command].should == 'C:\Windows\System32\notepad.exe' end + it 'should convert forward slashes to backslashes' do + described_class.new( + :name => 'Test Task', + :command => 'C:/Windows/System32/notepad.exe' + )[:command].should == 'C:\Windows\System32\notepad.exe' + end + + it 'should normalize backslashes' do + described_class.new( + :name => 'Test Task', + :command => 'C:\Windows\\System32\\\\notepad.exe' + )[:command].should == 'C:\Windows\System32\notepad.exe' + end + it 'should fail if the path to the command is not absolute' do expect { described_class.new(:name => 'Test Task', :command => 'notepad.exe') }.to raise_error( Puppet::Error, /Parameter command failed: Must be specified using an absolute path\./ ) end end describe 'when setting the command arguments' do it 'should fail if provided an array' do expect { described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :arguments => ['/a', '/b', '/c'] ) }.to raise_error( Puppet::Error, /Parameter arguments failed: Must be specified as a single string/ ) end it 'should accept a string' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :arguments => '/a /b /c' )[:arguments].should == ['/a /b /c'] end it 'should allow not specifying any command arguments' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe' )[:arguments].should_not be end end describe 'when setting whether the task is enabled or not' do end describe 'when setting the working directory' do it 'should accept an absolute path to the working directory' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :working_dir => 'C:\Windows\System32' )[:working_dir].should == 'C:\Windows\System32' end it 'should fail if the path to the working directory is not absolute' do expect { described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :working_dir => 'Windows\System32' ) }.to raise_error( Puppet::Error, /Parameter working_dir failed: Must be specified using an absolute path/ ) end it 'should allow not specifying any working directory' do described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe' )[:working_dir].should_not be end end describe 'when setting the trigger' do it 'should delegate to the provider to validate the trigger' do described_class.defaultprovider.any_instance.expects(:validate_trigger).returns(true) described_class.new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :trigger => {'schedule' => 'once', 'start_date' => '2011-09-16', 'start_time' => '13:20'} ) end end end