diff --git a/lib/puppet/provider/group/windows_adsi.rb b/lib/puppet/provider/group/windows_adsi.rb index 6d086da26..5811fc593 100644 --- a/lib/puppet/provider/group/windows_adsi.rb +++ b/lib/puppet/provider/group/windows_adsi.rb @@ -1,54 +1,54 @@ require 'puppet/util/adsi' Puppet::Type.type(:group).provide :windows_adsi do desc "Local group management for Windows. Nested groups are not supported." defaultfor :operatingsystem => :windows confine :operatingsystem => :windows has_features :manages_members def group @group ||= Puppet::Util::ADSI::Group.new(@resource[:name]) end def members group.members end def members=(members) group.set_members(members) end def create @group = Puppet::Util::ADSI::Group.create(@resource[:name]) @group.commit self.members = @resource[:members] end def exists? Puppet::Util::ADSI::Group.exists?(@resource[:name]) end def delete Puppet::Util::ADSI::Group.delete(@resource[:name]) end # Only flush if we created or modified a group, not deleted def flush @group.commit if @group end def gid - Puppet::Util::ADSI.sid_for_account(@resource[:name]) + Puppet::Util::Windows::Security.name_to_sid(@resource[:name]) end def gid=(value) fail "gid is read-only" end def self.instances Puppet::Util::ADSI::Group.map { |g| new(:ensure => :present, :name => g.name) } end end diff --git a/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb b/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb index b9491294d..140ab1cd9 100644 --- a/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb +++ b/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb @@ -1,565 +1,565 @@ require 'puppet/parameter' if Puppet.features.microsoft_windows? require 'win32/taskscheduler' require 'puppet/util/adsi' end Puppet::Type.type(:scheduled_task).provide(:win32_taskscheduler) do desc %q{This provider uses the win32-taskscheduler gem to manage scheduled tasks on Windows. Puppet requires version 0.2.1 or later of the win32-taskscheduler gem; previous versions can cause "Could not evaluate: The operation completed successfully" errors.} defaultfor :operatingsystem => :windows confine :operatingsystem => :windows def self.instances Win32::TaskScheduler.new.tasks.collect do |job_file| job_title = File.basename(job_file, '.job') new( :provider => :win32_taskscheduler, :name => job_title ) end end def exists? Win32::TaskScheduler.new.exists? resource[:name] end def task return @task if @task @task ||= Win32::TaskScheduler.new @task.activate(resource[:name] + '.job') if exists? @task end def clear_task @task = nil @triggers = nil end def enabled task.flags & Win32::TaskScheduler::DISABLED == 0 ? :true : :false end def command task.application_name end def arguments task.parameters end def working_dir task.working_directory end def user account = task.account_information return 'system' if account == '' account end def trigger return @triggers if @triggers @triggers = [] task.trigger_count.times do |i| trigger = begin task.trigger(i) rescue Win32::TaskScheduler::Error => e # Win32::TaskScheduler can't handle all of the # trigger types Windows uses, so we need to skip the # unhandled types to prevent "puppet resource" from # blowing up. nil end next unless trigger and scheduler_trigger_types.include?(trigger['trigger_type']) puppet_trigger = {} case trigger['trigger_type'] when Win32::TaskScheduler::TASK_TIME_TRIGGER_DAILY puppet_trigger['schedule'] = 'daily' puppet_trigger['every'] = trigger['type']['days_interval'].to_s when Win32::TaskScheduler::TASK_TIME_TRIGGER_WEEKLY puppet_trigger['schedule'] = 'weekly' puppet_trigger['every'] = trigger['type']['weeks_interval'].to_s puppet_trigger['on'] = days_of_week_from_bitfield(trigger['type']['days_of_week']) when Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDATE puppet_trigger['schedule'] = 'monthly' puppet_trigger['months'] = months_from_bitfield(trigger['type']['months']) puppet_trigger['on'] = days_from_bitfield(trigger['type']['days']) when Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDOW puppet_trigger['schedule'] = 'monthly' puppet_trigger['months'] = months_from_bitfield(trigger['type']['months']) puppet_trigger['which_occurrence'] = occurrence_constant_to_name(trigger['type']['weeks']) puppet_trigger['day_of_week'] = days_of_week_from_bitfield(trigger['type']['days_of_week']) when Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE puppet_trigger['schedule'] = 'once' end puppet_trigger['start_date'] = self.class.normalized_date("#{trigger['start_year']}-#{trigger['start_month']}-#{trigger['start_day']}") puppet_trigger['start_time'] = self.class.normalized_time("#{trigger['start_hour']}:#{trigger['start_minute']}") puppet_trigger['enabled'] = trigger['flags'] & Win32::TaskScheduler::TASK_TRIGGER_FLAG_DISABLED == 0 puppet_trigger['index'] = i @triggers << puppet_trigger end @triggers = @triggers[0] if @triggers.length == 1 @triggers end def user_insync?(current, should) return false unless current # Win32::TaskScheduler can return the 'SYSTEM' account as the # empty string. current = 'system' if current == '' # By comparing account SIDs we don't have to worry about case # sensitivity, or canonicalization of the account name. - Puppet::Util::ADSI.sid_for_account(current) == Puppet::Util::ADSI.sid_for_account(should[0]) + Puppet::Util::Windows::Security.name_to_sid(current) == Puppet::Util::Windows::Security.name_to_sid(should[0]) end def trigger_insync?(current, should) should = [should] unless should.is_a?(Array) current = [current] unless current.is_a?(Array) return false unless current.length == should.length current_in_sync = current.all? do |c| should.any? {|s| triggers_same?(c, s)} end should_in_sync = should.all? do |s| current.any? {|c| triggers_same?(c,s)} end current_in_sync && should_in_sync end def command=(value) task.application_name = value end def arguments=(value) task.parameters = value end def working_dir=(value) task.working_directory = value end def enabled=(value) if value == :true task.flags = task.flags & ~Win32::TaskScheduler::DISABLED else task.flags = task.flags | Win32::TaskScheduler::DISABLED end end def trigger=(value) desired_triggers = value.is_a?(Array) ? value : [value] current_triggers = trigger.is_a?(Array) ? trigger : [trigger] extra_triggers = [] desired_to_search = desired_triggers.dup current_triggers.each do |current| if found = desired_to_search.find {|desired| triggers_same?(current, desired)} desired_to_search.delete(found) else extra_triggers << current['index'] end end needed_triggers = [] current_to_search = current_triggers.dup desired_triggers.each do |desired| if found = current_to_search.find {|current| triggers_same?(current, desired)} current_to_search.delete(found) else needed_triggers << desired end end extra_triggers.reverse_each do |index| task.delete_trigger(index) end needed_triggers.each do |trigger_hash| # Even though this is an assignment, the API for # Win32::TaskScheduler ends up appending this trigger to the # list of triggers for the task, while #add_trigger is only able # to replace existing triggers. *shrug* task.trigger = translate_hash_to_trigger(trigger_hash) end end def user=(value) - self.fail("Invalid user: #{value}") unless Puppet::Util::ADSI.sid_for_account(value) + self.fail("Invalid user: #{value}") unless Puppet::Util::Windows::Security.name_to_sid(value) if value.to_s.downcase != 'system' task.set_account_information(value, resource[:password]) else # Win32::TaskScheduler treats a nil/empty username & password as # requesting the SYSTEM account. task.set_account_information(nil, nil) end end def create clear_task @task = Win32::TaskScheduler.new(resource[:name], dummy_time_trigger) self.command = resource[:command] [:arguments, :working_dir, :enabled, :trigger, :user].each do |prop| send("#{prop}=", resource[prop]) if resource[prop] end end def destroy Win32::TaskScheduler.new.delete(resource[:name] + '.job') end def flush unless resource[:ensure] == :absent self.fail('Parameter command is required.') unless resource[:command] task.save @task = nil end end def triggers_same?(current_trigger, desired_trigger) return false unless current_trigger['schedule'] == desired_trigger['schedule'] return false if current_trigger.has_key?('enabled') && !current_trigger['enabled'] desired = desired_trigger.dup desired['every'] ||= current_trigger['every'] if current_trigger.has_key?('every') desired['months'] ||= current_trigger['months'] if current_trigger.has_key?('months') desired['on'] ||= current_trigger['on'] if current_trigger.has_key?('on') desired['day_of_week'] ||= current_trigger['day_of_week'] if current_trigger.has_key?('day_of_week') translate_hash_to_trigger(current_trigger) == translate_hash_to_trigger(desired) end def self.normalized_date(date_string) date = Date.parse("#{date_string}") "#{date.year}-#{date.month}-#{date.day}" end def self.normalized_time(time_string) Time.parse("#{time_string}").strftime('%H:%M') end def dummy_time_trigger now = Time.now { 'flags' => 0, 'random_minutes_interval' => 0, 'end_day' => 0, "end_year" => 0, "trigger_type" => 0, "minutes_interval" => 0, "end_month" => 0, "minutes_duration" => 0, 'start_year' => now.year, 'start_month' => now.month, 'start_day' => now.day, 'start_hour' => now.hour, 'start_minute' => now.min, 'trigger_type' => Win32::TaskScheduler::ONCE, } end def translate_hash_to_trigger(puppet_trigger, user_provided_input=false) trigger = dummy_time_trigger if user_provided_input self.fail "'enabled' is read-only on triggers" if puppet_trigger.has_key?('enabled') self.fail "'index' is read-only on triggers" if puppet_trigger.has_key?('index') end puppet_trigger.delete('index') if puppet_trigger.delete('enabled') == false trigger['flags'] |= Win32::TaskScheduler::TASK_TRIGGER_FLAG_DISABLED else trigger['flags'] &= ~Win32::TaskScheduler::TASK_TRIGGER_FLAG_DISABLED end extra_keys = puppet_trigger.keys.sort - ['schedule', 'start_date', 'start_time', 'every', 'months', 'on', 'which_occurrence', 'day_of_week'] self.fail "Unknown trigger option(s): #{Puppet::Parameter.format_value_for_display(extra_keys)}" unless extra_keys.empty? self.fail "Must specify 'start_time' when defining a trigger" unless puppet_trigger['start_time'] case puppet_trigger['schedule'] when 'daily' trigger['trigger_type'] = Win32::TaskScheduler::DAILY trigger['type'] = { 'days_interval' => Integer(puppet_trigger['every'] || 1) } when 'weekly' trigger['trigger_type'] = Win32::TaskScheduler::WEEKLY trigger['type'] = { 'weeks_interval' => Integer(puppet_trigger['every'] || 1) } trigger['type']['days_of_week'] = if puppet_trigger['day_of_week'] bitfield_from_days_of_week(puppet_trigger['day_of_week']) else scheduler_days_of_week.inject(0) {|day_flags,day| day_flags |= day} end when 'monthly' trigger['type'] = { 'months' => bitfield_from_months(puppet_trigger['months'] || (1..12).to_a), } if puppet_trigger.keys.include?('on') if puppet_trigger.has_key?('day_of_week') or puppet_trigger.has_key?('which_occurrence') self.fail "Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger" end trigger['trigger_type'] = Win32::TaskScheduler::MONTHLYDATE trigger['type']['days'] = bitfield_from_days(puppet_trigger['on']) elsif puppet_trigger.keys.include?('which_occurrence') or puppet_trigger.keys.include?('day_of_week') self.fail 'which_occurrence cannot be specified as an array' if puppet_trigger['which_occurrence'].is_a?(Array) %w{day_of_week which_occurrence}.each do |field| self.fail "#{field} must be specified when creating a monthly day-of-week based trigger" unless puppet_trigger.has_key?(field) end trigger['trigger_type'] = Win32::TaskScheduler::MONTHLYDOW trigger['type']['weeks'] = occurrence_name_to_constant(puppet_trigger['which_occurrence']) trigger['type']['days_of_week'] = bitfield_from_days_of_week(puppet_trigger['day_of_week']) else self.fail "Don't know how to create a 'monthly' schedule with the options: #{puppet_trigger.keys.sort.join(', ')}" end when 'once' self.fail "Must specify 'start_date' when defining a one-time trigger" unless puppet_trigger['start_date'] trigger['trigger_type'] = Win32::TaskScheduler::ONCE else self.fail "Unknown schedule type: #{puppet_trigger["schedule"].inspect}" end if start_date = puppet_trigger['start_date'] start_date = Date.parse(start_date) self.fail "start_date must be on or after 1753-01-01" unless start_date >= Date.new(1753, 1, 1) trigger['start_year'] = start_date.year trigger['start_month'] = start_date.month trigger['start_day'] = start_date.day end start_time = Time.parse(puppet_trigger['start_time']) trigger['start_hour'] = start_time.hour trigger['start_minute'] = start_time.min trigger end def validate_trigger(value) value = [value] unless value.is_a?(Array) # translate_hash_to_trigger handles the same validation that we # would be doing here at the individual trigger level. value.each {|t| translate_hash_to_trigger(t, true)} true end private def bitfield_from_months(months) bitfield = 0 months = [months] unless months.is_a?(Array) months.each do |month| integer_month = Integer(month) rescue nil self.fail 'Month must be specified as an integer in the range 1-12' unless integer_month == month.to_f and integer_month.between?(1,12) bitfield |= scheduler_months[integer_month - 1] end bitfield end def bitfield_from_days(days) bitfield = 0 days = [days] unless days.is_a?(Array) days.each do |day| # The special "day" of 'last' is represented by day "number" # 32. 'last' has the special meaning of "the last day of the # month", no matter how many days there are in the month. day = 32 if day == 'last' integer_day = Integer(day) self.fail "Day must be specified as an integer in the range 1-31, or as 'last'" unless integer_day = day.to_f and integer_day.between?(1,32) bitfield |= 1 << integer_day - 1 end bitfield end def bitfield_from_days_of_week(days_of_week) bitfield = 0 days_of_week = [days_of_week] unless days_of_week.is_a?(Array) days_of_week.each do |day_of_week| bitfield |= day_of_week_name_to_constant(day_of_week) end bitfield end def months_from_bitfield(bitfield) months = [] scheduler_months.each do |month| if bitfield & month != 0 months << month_constant_to_number(month) end end months end def days_from_bitfield(bitfield) days = [] i = 0 while bitfield > 0 if bitfield & 1 > 0 # Day 32 has the special meaning of "the last day of the # month", no matter how many days there are in the month. days << (i == 31 ? 'last' : i + 1) end bitfield = bitfield >> 1 i += 1 end days end def days_of_week_from_bitfield(bitfield) days_of_week = [] scheduler_days_of_week.each do |day_of_week| if bitfield & day_of_week != 0 days_of_week << day_of_week_constant_to_name(day_of_week) end end days_of_week end def scheduler_trigger_types [ Win32::TaskScheduler::TASK_TIME_TRIGGER_DAILY, Win32::TaskScheduler::TASK_TIME_TRIGGER_WEEKLY, Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDATE, Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDOW, Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE ] end def scheduler_days_of_week [ Win32::TaskScheduler::SUNDAY, Win32::TaskScheduler::MONDAY, Win32::TaskScheduler::TUESDAY, Win32::TaskScheduler::WEDNESDAY, Win32::TaskScheduler::THURSDAY, Win32::TaskScheduler::FRIDAY, Win32::TaskScheduler::SATURDAY ] end def scheduler_months [ Win32::TaskScheduler::JANUARY, Win32::TaskScheduler::FEBRUARY, Win32::TaskScheduler::MARCH, Win32::TaskScheduler::APRIL, Win32::TaskScheduler::MAY, Win32::TaskScheduler::JUNE, Win32::TaskScheduler::JULY, Win32::TaskScheduler::AUGUST, Win32::TaskScheduler::SEPTEMBER, Win32::TaskScheduler::OCTOBER, Win32::TaskScheduler::NOVEMBER, Win32::TaskScheduler::DECEMBER ] end def scheduler_occurrences [ Win32::TaskScheduler::FIRST_WEEK, Win32::TaskScheduler::SECOND_WEEK, Win32::TaskScheduler::THIRD_WEEK, Win32::TaskScheduler::FOURTH_WEEK, Win32::TaskScheduler::LAST_WEEK ] end def day_of_week_constant_to_name(constant) case constant when Win32::TaskScheduler::SUNDAY; 'sun' when Win32::TaskScheduler::MONDAY; 'mon' when Win32::TaskScheduler::TUESDAY; 'tues' when Win32::TaskScheduler::WEDNESDAY; 'wed' when Win32::TaskScheduler::THURSDAY; 'thurs' when Win32::TaskScheduler::FRIDAY; 'fri' when Win32::TaskScheduler::SATURDAY; 'sat' end end def day_of_week_name_to_constant(name) case name when 'sun'; Win32::TaskScheduler::SUNDAY when 'mon'; Win32::TaskScheduler::MONDAY when 'tues'; Win32::TaskScheduler::TUESDAY when 'wed'; Win32::TaskScheduler::WEDNESDAY when 'thurs'; Win32::TaskScheduler::THURSDAY when 'fri'; Win32::TaskScheduler::FRIDAY when 'sat'; Win32::TaskScheduler::SATURDAY end end def month_constant_to_number(constant) month_num = 1 while constant >> month_num - 1 > 1 month_num += 1 end month_num end def occurrence_constant_to_name(constant) case constant when Win32::TaskScheduler::FIRST_WEEK; 'first' when Win32::TaskScheduler::SECOND_WEEK; 'second' when Win32::TaskScheduler::THIRD_WEEK; 'third' when Win32::TaskScheduler::FOURTH_WEEK; 'fourth' when Win32::TaskScheduler::LAST_WEEK; 'last' end end def occurrence_name_to_constant(name) case name when 'first'; Win32::TaskScheduler::FIRST_WEEK when 'second'; Win32::TaskScheduler::SECOND_WEEK when 'third'; Win32::TaskScheduler::THIRD_WEEK when 'fourth'; Win32::TaskScheduler::FOURTH_WEEK when 'last'; Win32::TaskScheduler::LAST_WEEK end end end diff --git a/lib/puppet/provider/user/windows_adsi.rb b/lib/puppet/provider/user/windows_adsi.rb index 5210fb077..14b905d3b 100644 --- a/lib/puppet/provider/user/windows_adsi.rb +++ b/lib/puppet/provider/user/windows_adsi.rb @@ -1,99 +1,99 @@ require 'puppet/util/adsi' Puppet::Type.type(:user).provide :windows_adsi do desc "Local user management for Windows." defaultfor :operatingsystem => :windows confine :operatingsystem => :windows has_features :manages_homedir, :manages_passwords def user @user ||= Puppet::Util::ADSI::User.new(@resource[:name]) end def groups user.groups.join(',') end def groups=(groups) user.set_groups(groups, @resource[:membership] == :minimum) end def create @user = Puppet::Util::ADSI::User.create(@resource[:name]) @user.password = @resource[:password] @user.commit [:comment, :home, :groups].each do |prop| send("#{prop}=", @resource[prop]) if @resource[prop] end if @resource.managehome? Puppet::Util::Windows::User.load_profile(@resource[:name], @resource[:password]) end end def exists? Puppet::Util::ADSI::User.exists?(@resource[:name]) end def delete # lookup sid before we delete account sid = uid if @resource.managehome? Puppet::Util::ADSI::User.delete(@resource[:name]) if sid Puppet::Util::ADSI::UserProfile.delete(sid) end end # Only flush if we created or modified a user, not deleted def flush @user.commit if @user end def comment user['Description'] end def comment=(value) user['Description'] = value end def home user['HomeDirectory'] end def home=(value) user['HomeDirectory'] = value end def password user.password_is?( @resource[:password] ) ? @resource[:password] : :absent end def password=(value) user.password = value end def uid - Puppet::Util::ADSI.sid_for_account(@resource[:name]) + Puppet::Util::Windows::Security.name_to_sid(@resource[:name]) end def uid=(value) fail "uid is read-only" end [:gid, :shell].each do |prop| define_method(prop) { nil } define_method("#{prop}=") do |v| fail "No support for managing property #{prop} of user #{@resource[:name]} on Windows" end end def self.instances Puppet::Util::ADSI::User.map { |u| new(:ensure => :present, :name => u.name) } end end diff --git a/spec/unit/provider/group/windows_adsi_spec.rb b/spec/unit/provider/group/windows_adsi_spec.rb index f798d1eac..d052b73b7 100644 --- a/spec/unit/provider/group/windows_adsi_spec.rb +++ b/spec/unit/provider/group/windows_adsi_spec.rb @@ -1,100 +1,100 @@ #!/usr/bin/env ruby require 'spec_helper' describe Puppet::Type.type(:group).provider(:windows_adsi) do let(:resource) do Puppet::Type.type(:group).new( :title => 'testers', :provider => :windows_adsi ) end let(:provider) { resource.provider } let(:connection) { stub 'connection' } before :each do Puppet::Util::ADSI.stubs(:computer_name).returns('testcomputername') Puppet::Util::ADSI.stubs(:connect).returns connection end describe ".instances" do it "should enumerate all groups" do names = ['group1', 'group2', 'group3'] stub_groups = names.map{|n| stub(:name => n)} connection.stubs(:execquery).with("select * from win32_group").returns stub_groups described_class.instances.map(&:name).should =~ names end end describe "when managing members" do it "should be able to provide a list of members" do provider.group.stubs(:members).returns ['user1', 'user2', 'user3'] provider.members.should =~ ['user1', 'user2', 'user3'] end it "should be able to set group members" do provider.group.stubs(:members).returns ['user1', 'user2'] provider.group.expects(:remove_members).with('user1') provider.group.expects(:add_members).with('user3') provider.members = ['user2', 'user3'] end end describe 'when creating groups' do it "should be able to create a group" do resource[:members] = ['user1', 'user2'] group = stub 'group' Puppet::Util::ADSI::Group.expects(:create).with('testers').returns group create = sequence('create') group.expects(:commit).in_sequence(create) group.expects(:set_members).with(['user1', 'user2']).in_sequence(create) provider.create end it 'should not create a group if a user by the same name exists' do Puppet::Util::ADSI::Group.expects(:create).with('testers').raises( Puppet::Error.new("Cannot create group if user 'testers' exists.") ) expect{ provider.create }.to raise_error( Puppet::Error, /Cannot create group if user 'testers' exists./ ) end it 'should commit a newly created group' do provider.group.expects( :commit ) provider.flush end end it "should be able to test whether a group exists" do Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists Puppet::Util::ADSI.stubs(:connect).returns nil provider.should_not be_exists end it "should be able to delete a group" do connection.expects(:Delete).with('group', 'testers') provider.delete end - it "should report the group's SID as gid" do - Puppet::Util::ADSI.expects(:sid_for_account).with('testers').returns('S-1-5-32-547') + it "should report the group's SID as gid", :if => Puppet.features.microsoft_windows? do + Puppet::Util::Windows::Security.expects(:name_to_sid).with('testers').returns('S-1-5-32-547') provider.gid.should == 'S-1-5-32-547' end it "should fail when trying to manage the gid property" do provider.expects(:fail).with { |msg| msg =~ /gid is read-only/ } provider.send(:gid=, 500) end end diff --git a/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb b/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb index 069a92a32..65b447f98 100644 --- a/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +++ b/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb @@ -1,1571 +1,1571 @@ #!/usr/bin/env rspec require 'spec_helper' require 'win32/taskscheduler' if Puppet.features.microsoft_windows? shared_examples_for "a trigger that handles start_date and start_time" do let(:trigger) do described_class.new( :name => 'Shared Test Task', :command => 'C:\Windows\System32\notepad.exe' ).translate_hash_to_trigger(trigger_hash) end before :each do Win32::TaskScheduler.any_instance.stubs(:save) end describe 'the given start_date' do before :each do trigger_hash['start_time'] = '00:00' end def date_component { 'start_year' => trigger['start_year'], 'start_month' => trigger['start_month'], 'start_day' => trigger['start_day'] } end it 'should be able to be specified in ISO 8601 calendar date format' do trigger_hash['start_date'] = '2011-12-31' date_component.should == { 'start_year' => 2011, 'start_month' => 12, 'start_day' => 31 } end it 'should fail if before 1753-01-01' do trigger_hash['start_date'] = '1752-12-31' expect { date_component }.to raise_error( Puppet::Error, 'start_date must be on or after 1753-01-01' ) end it 'should succeed if on 1753-01-01' do trigger_hash['start_date'] = '1753-01-01' date_component.should == { 'start_year' => 1753, 'start_month' => 1, 'start_day' => 1 } end it 'should succeed if after 1753-01-01' do trigger_hash['start_date'] = '1753-01-02' date_component.should == { 'start_year' => 1753, 'start_month' => 1, 'start_day' => 2 } end end describe 'the given start_time' do before :each do trigger_hash['start_date'] = '2011-12-31' end def time_component { 'start_hour' => trigger['start_hour'], 'start_minute' => trigger['start_minute'] } end it 'should be able to be specified as a 24-hour "hh:mm"' do trigger_hash['start_time'] = '17:13' time_component.should == { 'start_hour' => 17, 'start_minute' => 13 } end it 'should be able to be specified as a 12-hour "hh:mm am"' do trigger_hash['start_time'] = '3:13 am' time_component.should == { 'start_hour' => 3, 'start_minute' => 13 } end it 'should be able to be specified as a 12-hour "hh:mm pm"' do trigger_hash['start_time'] = '3:13 pm' time_component.should == { 'start_hour' => 15, 'start_minute' => 13 } end end end describe Puppet::Type.type(:scheduled_task).provider(:win32_taskscheduler), :if => Puppet.features.microsoft_windows? do before :each do Puppet::Type.type(:scheduled_task).stubs(:defaultprovider).returns(described_class) end describe 'when retrieving' do before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) described_class.any_instance.stubs(:task).returns(@mock_task) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end let(:resource) { Puppet::Type.type(:scheduled_task).new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } describe 'the triggers for a task' do describe 'with only one trigger' do before :each do @mock_task.expects(:trigger_count).returns(1) end it 'should handle a single daily trigger' do @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_DAILY, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'days_interval' => 2 }, }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'daily', 'every' => '2', 'enabled' => true, 'index' => 0, } end it 'should handle a single weekly trigger' do scheduled_days_of_week = Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::FRIDAY | Win32::TaskScheduler::SUNDAY @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_WEEKLY, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'weeks_interval' => 2, 'days_of_week' => scheduled_days_of_week } }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'weekly', 'every' => '2', 'on' => ['sun', 'mon', 'wed', 'fri'], 'enabled' => true, 'index' => 0, } end it 'should handle a single monthly date-based trigger' do scheduled_months = Win32::TaskScheduler::JANUARY | Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::AUGUST | Win32::TaskScheduler::SEPTEMBER | Win32::TaskScheduler::DECEMBER # 1 3 5 15 'last' scheduled_days = 1 | 1 << 2 | 1 << 4 | 1 << 14 | 1 << 31 @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDATE, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'months' => scheduled_months, 'days' => scheduled_days } }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'monthly', 'months' => [1, 2, 8, 9, 12], 'on' => [1, 3, 5, 15, 'last'], 'enabled' => true, 'index' => 0, } end it 'should handle a single monthly day-of-week-based trigger' do scheduled_months = Win32::TaskScheduler::JANUARY | Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::AUGUST | Win32::TaskScheduler::SEPTEMBER | Win32::TaskScheduler::DECEMBER scheduled_days_of_week = Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::FRIDAY | Win32::TaskScheduler::SUNDAY @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_MONTHLYDOW, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, 'type' => { 'months' => scheduled_months, 'weeks' => Win32::TaskScheduler::FIRST_WEEK, 'days_of_week' => scheduled_days_of_week } }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'monthly', 'months' => [1, 2, 8, 9, 12], 'which_occurrence' => 'first', 'day_of_week' => ['sun', 'mon', 'wed', 'fri'], 'enabled' => true, 'index' => 0, } end it 'should handle a single one-time trigger' do @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 9, 'start_day' => 12, 'start_hour' => 13, 'start_minute' => 20, 'flags' => 0, }) resource.provider.trigger.should == { 'start_date' => '2011-9-12', 'start_time' => '13:20', 'schedule' => 'once', 'enabled' => true, 'index' => 0, } end end it 'should handle multiple triggers' do @mock_task.expects(:trigger_count).returns(3) @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @mock_task.expects(:trigger).with(1).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2012, 'start_month' => 11, 'start_day' => 14, 'start_hour' => 15, 'start_minute' => 22, 'flags' => 0, }) @mock_task.expects(:trigger).with(2).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2013, 'start_month' => 12, 'start_day' => 15, 'start_hour' => 16, 'start_minute' => 23, 'flags' => 0, }) resource.provider.trigger.should =~ [ { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, }, { 'start_date' => '2012-11-14', 'start_time' => '15:22', 'schedule' => 'once', 'enabled' => true, 'index' => 1, }, { 'start_date' => '2013-12-15', 'start_time' => '16:23', 'schedule' => 'once', 'enabled' => true, 'index' => 2, } ] end it 'should skip triggers Win32::TaskScheduler cannot handle' do @mock_task.expects(:trigger_count).returns(3) @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @mock_task.expects(:trigger).with(1).raises( Win32::TaskScheduler::Error.new('Unhandled trigger type!') ) @mock_task.expects(:trigger).with(2).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2013, 'start_month' => 12, 'start_day' => 15, 'start_hour' => 16, 'start_minute' => 23, 'flags' => 0, }) resource.provider.trigger.should =~ [ { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, }, { 'start_date' => '2013-12-15', 'start_time' => '16:23', 'schedule' => 'once', 'enabled' => true, 'index' => 2, } ] end it 'should skip trigger types Puppet does not handle' do @mock_task.expects(:trigger_count).returns(3) @mock_task.expects(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @mock_task.expects(:trigger).with(1).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_EVENT_TRIGGER_AT_LOGON, }) @mock_task.expects(:trigger).with(2).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2013, 'start_month' => 12, 'start_day' => 15, 'start_hour' => 16, 'start_minute' => 23, 'flags' => 0, }) resource.provider.trigger.should =~ [ { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, }, { 'start_date' => '2013-12-15', 'start_time' => '16:23', 'schedule' => 'once', 'enabled' => true, 'index' => 2, } ] end end it 'should get the working directory from the working_directory on the task' do @mock_task.expects(:working_directory).returns('C:\Windows\System32') resource.provider.working_dir.should == 'C:\Windows\System32' end it 'should get the command from the application_name on the task' do @mock_task.expects(:application_name).returns('C:\Windows\System32\notepad.exe') resource.provider.command.should == 'C:\Windows\System32\notepad.exe' end it 'should get the command arguments from the parameters on the task' do @mock_task.expects(:parameters).returns('these are my arguments') resource.provider.arguments.should == 'these are my arguments' end it 'should get the user from the account_information on the task' do @mock_task.expects(:account_information).returns('this is my user') resource.provider.user.should == 'this is my user' end describe 'whether the task is enabled' do it 'should report tasks with the disabled bit set as disabled' do @mock_task.stubs(:flags).returns(Win32::TaskScheduler::DISABLED) resource.provider.enabled.should == :false end it 'should report tasks without the disabled bit set as enabled' do @mock_task.stubs(:flags).returns(~Win32::TaskScheduler::DISABLED) resource.provider.enabled.should == :true end it 'should not consider triggers for determining if the task is enabled' do @mock_task.stubs(:flags).returns(~Win32::TaskScheduler::DISABLED) @mock_task.stubs(:trigger_count).returns(1) @mock_task.stubs(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => Win32::TaskScheduler::TASK_TRIGGER_FLAG_DISABLED, }) resource.provider.enabled.should == :true end end end describe '#exists?' do before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) described_class.any_instance.stubs(:task).returns(@mock_task) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end let(:resource) { Puppet::Type.type(:scheduled_task).new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } it "should delegate to Win32::TaskScheduler using the resource's name" do @mock_task.expects(:exists?).with('Test Task').returns(true) resource.provider.exists?.should == true end end describe '#clear_task' do before :each do @mock_task = mock @new_mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @new_mock_task.responds_like(Win32::TaskScheduler.new) Win32::TaskScheduler.stubs(:new).returns(@mock_task, @new_mock_task) described_class.any_instance.stubs(:exists?).returns(false) end let(:resource) { Puppet::Type.type(:scheduled_task).new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } it 'should clear the cached task object' do resource.provider.task.should == @mock_task resource.provider.task.should == @mock_task resource.provider.clear_task resource.provider.task.should == @new_mock_task end it 'should clear the cached list of triggers for the task' do @mock_task.stubs(:trigger_count).returns(1) @mock_task.stubs(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2011, 'start_month' => 10, 'start_day' => 13, 'start_hour' => 14, 'start_minute' => 21, 'flags' => 0, }) @new_mock_task.stubs(:trigger_count).returns(1) @new_mock_task.stubs(:trigger).with(0).returns({ 'trigger_type' => Win32::TaskScheduler::TASK_TIME_TRIGGER_ONCE, 'start_year' => 2012, 'start_month' => 11, 'start_day' => 14, 'start_hour' => 15, 'start_minute' => 22, 'flags' => 0, }) mock_task_trigger = { 'start_date' => '2011-10-13', 'start_time' => '14:21', 'schedule' => 'once', 'enabled' => true, 'index' => 0, } resource.provider.trigger.should == mock_task_trigger resource.provider.trigger.should == mock_task_trigger resource.provider.clear_task resource.provider.trigger.should == { 'start_date' => '2012-11-14', 'start_time' => '15:22', 'schedule' => 'once', 'enabled' => true, 'index' => 0, } end end describe '.instances' do it 'should use the list of .job files to construct the list of scheduled_tasks' do job_files = ['foo.job', 'bar.job', 'baz.job'] Win32::TaskScheduler.any_instance.stubs(:tasks).returns(job_files) job_files.each do |job| job = File.basename(job, '.job') described_class.expects(:new).with(:provider => :win32_taskscheduler, :name => job) end described_class.instances end end - describe '#user_insync?' do + describe '#user_insync?', :if => Puppet.features.microsoft_windows? do let(:resource) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') } before :each do - Puppet::Util::ADSI.stubs(:sid_for_account).with('system').returns('SYSTEM SID') - Puppet::Util::ADSI.stubs(:sid_for_account).with('joe').returns('SID A') - Puppet::Util::ADSI.stubs(:sid_for_account).with('MACHINE\joe').returns('SID A') - Puppet::Util::ADSI.stubs(:sid_for_account).with('bob').returns('SID B') + Puppet::Util::Windows::Security.stubs(:name_to_account).with('system').returns('SYSTEM SID') + Puppet::Util::Windows::Security.stubs(:name_to_account).with('joe').returns('SID A') + Puppet::Util::Windows::Security.stubs(:name_to_account).with('MACHINE\joe').returns('SID A') + Puppet::Util::Windows::Security.stubs(:name_to_account).with('bob').returns('SID B') end it 'should consider the user as in sync if the name matches' do resource.should be_user_insync('joe', ['joe']) end it 'should consider the user as in sync if the current user is fully qualified' do resource.should be_user_insync('MACHINE\joe', ['joe']) end it 'should consider a current user of the empty string to be the same as the system user' do resource.should be_user_insync('', ['system']) end it 'should consider different users as being different' do resource.should_not be_user_insync('joe', ['bob']) end end describe '#trigger_insync?' do let(:resource) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') } it 'should not consider any extra current triggers as in sync' do current = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] desired = {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'} resource.should_not be_trigger_insync(current, desired) end it 'should not consider any extra desired triggers as in sync' do current = {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'} desired = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] resource.should_not be_trigger_insync(current, desired) end it 'should consider triggers to be in sync if the sets of current and desired triggers are equal' do current = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] desired = [ {'start_date' => '2011-09-12', 'start_time' => '15:15', 'schedule' => 'once'}, {'start_date' => '2012-10-13', 'start_time' => '16:16', 'schedule' => 'once'} ] resource.should be_trigger_insync(current, desired) end end describe '#triggers_same?' do let(:provider) { described_class.new(:name => 'foobar', :command => 'C:\Windows\System32\notepad.exe') } it "should not consider a disabled 'current' trigger to be the same" do current = {'schedule' => 'once', 'enabled' => false} desired = {'schedule' => 'once'} provider.should_not be_triggers_same(current, desired) end it 'should not consider triggers with different schedules to be the same' do current = {'schedule' => 'once'} desired = {'schedule' => 'weekly'} provider.should_not be_triggers_same(current, desired) end describe 'comparing daily triggers' do it "should consider 'desired' triggers not specifying 'every' to have the same value as the 'current' trigger" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30'} provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2012-09-12', 'start_time' => '15:30', 'every' => 3} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:31', 'every' => 3} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'weekly', 'start_date' => '2011-9-12', 'start_time' => '15:30', 'every' => 3} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '5:30', 'every' => 3} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '05:30', 'every' => 3} provider.should be_triggers_same(current, desired) end it "should consider different 'every' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 1} provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30', 'every' => 1} provider.should be_triggers_same(trigger, trigger) end end describe 'comparing one-time triggers' do it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30'} desired = {'schedule' => 'daily', 'start_date' => '2012-09-12', 'start_time' => '15:30'} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:30'} desired = {'schedule' => 'daily', 'start_date' => '2011-09-12', 'start_time' => '15:31'} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30'} desired = {'schedule' => 'weekly', 'start_date' => '2011-9-12', 'start_time' => '15:30'} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '1:30'} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30'} provider.should be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30'} provider.should be_triggers_same(trigger, trigger) end end describe 'comparing monthly date-based triggers' do it "should consider 'desired' triggers not specifying 'months' to have the same value as the 'current' trigger" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'on' => [1,'last']} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'on' => [1, 'last']} provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-10-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '22:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-9-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '5:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '05:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should be_triggers_same(current, desired) end it "should consider different 'months' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1], 'on' => [1, 3, 5, 7]} provider.should_not be_triggers_same(current, desired) end it "should consider different 'on' as different triggers" do current = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} desired = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 5, 7]} provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [1, 2], 'on' => [1, 3, 5, 7]} provider.should be_triggers_same(trigger, trigger) end end describe 'comparing monthly day-of-week-based triggers' do it "should consider 'desired' triggers not specifying 'months' to have the same value as the 'current' trigger" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-10-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '22:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'months' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3, 5, 7, 9], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'which_occurrence' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'last', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should_not be_triggers_same(current, desired) end it "should consider different 'day_of_week' as different triggers" do current = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } desired = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['fri'] } provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = { 'schedule' => 'monthly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'months' => [3], 'which_occurrence' => 'first', 'day_of_week' => ['mon', 'tues', 'sat'] } provider.should be_triggers_same(trigger, trigger) end end describe 'comparing weekly triggers' do it "should consider 'desired' triggers not specifying 'day_of_week' to have the same value as the 'current' trigger" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3} provider.should be_triggers_same(current, desired) end it "should consider different 'start_dates' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-10-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should_not be_triggers_same(current, desired) end it "should consider different 'start_times' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '22:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should_not be_triggers_same(current, desired) end it 'should not consider differences in date formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-9-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should be_triggers_same(current, desired) end it 'should not consider differences in time formatting to be different triggers' do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '1:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '01:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should be_triggers_same(current, desired) end it "should consider different 'every' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 1, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should_not be_triggers_same(current, desired) end it "should consider different 'day_of_week' as different triggers" do current = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} desired = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['fri']} provider.should_not be_triggers_same(current, desired) end it 'should consider triggers that are the same as being the same' do trigger = {'schedule' => 'weekly', 'start_date' => '2011-09-12', 'start_time' => '15:30', 'every' => 3, 'day_of_week' => ['mon', 'wed', 'fri']} provider.should be_triggers_same(trigger, trigger) end end end describe '#normalized_date' do it 'should format the date without leading zeros' do described_class.normalized_date('2011-01-01').should == '2011-1-1' end end describe '#normalized_time' do it 'should format the time as {24h}:{minutes}' do described_class.normalized_time('8:37 PM').should == '20:37' end end describe '#translate_hash_to_trigger' do before :each do @puppet_trigger = { 'start_date' => '2011-1-1', 'start_time' => '01:10' } end let(:provider) { described_class.new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } let(:trigger) { provider.translate_hash_to_trigger(@puppet_trigger) } describe 'when given a one-time trigger' do before :each do @puppet_trigger['schedule'] = 'once' end it 'should set the trigger_type to Win32::TaskScheduler::ONCE' do trigger['trigger_type'].should == Win32::TaskScheduler::ONCE end it 'should not set a type' do trigger.should_not be_has_key('type') end it "should require 'start_date'" do @puppet_trigger.delete('start_date') expect { trigger }.to raise_error( Puppet::Error, /Must specify 'start_date' when defining a one-time trigger/ ) end it "should require 'start_time'" do @puppet_trigger.delete('start_time') expect { trigger }.to raise_error( Puppet::Error, /Must specify 'start_time' when defining a trigger/ ) end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'once' }} end end describe 'when given a daily trigger' do before :each do @puppet_trigger['schedule'] = 'daily' end it "should default 'every' to 1" do trigger['type']['days_interval'].should == 1 end it "should use the specified value for 'every'" do @puppet_trigger['every'] = 5 trigger['type']['days_interval'].should == 5 end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'daily', 'every' => 1}} end end describe 'when given a weekly trigger' do before :each do @puppet_trigger['schedule'] = 'weekly' end it "should default 'every' to 1" do trigger['type']['weeks_interval'].should == 1 end it "should use the specified value for 'every'" do @puppet_trigger['every'] = 4 trigger['type']['weeks_interval'].should == 4 end it "should default 'day_of_week' to be every day of the week" do trigger['type']['days_of_week'].should == Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::TUESDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::THURSDAY | Win32::TaskScheduler::FRIDAY | Win32::TaskScheduler::SATURDAY | Win32::TaskScheduler::SUNDAY end it "should use the specified value for 'day_of_week'" do @puppet_trigger['day_of_week'] = ['mon', 'wed', 'fri'] trigger['type']['days_of_week'].should == Win32::TaskScheduler::MONDAY | Win32::TaskScheduler::WEDNESDAY | Win32::TaskScheduler::FRIDAY end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'weekly', 'every' => 1, 'day_of_week' => 'mon'}} end end shared_examples_for 'a monthly schedule' do it "should default 'months' to be every month" do trigger['type']['months'].should == Win32::TaskScheduler::JANUARY | Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::MARCH | Win32::TaskScheduler::APRIL | Win32::TaskScheduler::MAY | Win32::TaskScheduler::JUNE | Win32::TaskScheduler::JULY | Win32::TaskScheduler::AUGUST | Win32::TaskScheduler::SEPTEMBER | Win32::TaskScheduler::OCTOBER | Win32::TaskScheduler::NOVEMBER | Win32::TaskScheduler::DECEMBER end it "should use the specified value for 'months'" do @puppet_trigger['months'] = [2, 8] trigger['type']['months'].should == Win32::TaskScheduler::FEBRUARY | Win32::TaskScheduler::AUGUST end end describe 'when given a monthly date-based trigger' do before :each do @puppet_trigger['schedule'] = 'monthly' @puppet_trigger['on'] = [7, 14] end it_behaves_like 'a monthly schedule' it "should not allow 'which_occurrence' to be specified" do @puppet_trigger['which_occurrence'] = 'first' expect {trigger}.to raise_error( Puppet::Error, /Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger/ ) end it "should not allow 'day_of_week' to be specified" do @puppet_trigger['day_of_week'] = 'mon' expect {trigger}.to raise_error( Puppet::Error, /Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger/ ) end it "should require 'on'" do @puppet_trigger.delete('on') expect {trigger}.to raise_error( Puppet::Error, /Don't know how to create a 'monthly' schedule with the options: schedule, start_date, start_time/ ) end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'monthly', 'months' => 1, 'on' => 1}} end end describe 'when given a monthly day-of-week-based trigger' do before :each do @puppet_trigger['schedule'] = 'monthly' @puppet_trigger['which_occurrence'] = 'first' @puppet_trigger['day_of_week'] = 'mon' end it_behaves_like 'a monthly schedule' it "should not allow 'on' to be specified" do @puppet_trigger['on'] = 15 expect {trigger}.to raise_error( Puppet::Error, /Neither 'day_of_week' nor 'which_occurrence' can be specified when creating a monthly date-based trigger/ ) end it "should require 'which_occurrence'" do @puppet_trigger.delete('which_occurrence') expect {trigger}.to raise_error( Puppet::Error, /which_occurrence must be specified when creating a monthly day-of-week based trigger/ ) end it "should require 'day_of_week'" do @puppet_trigger.delete('day_of_week') expect {trigger}.to raise_error( Puppet::Error, /day_of_week must be specified when creating a monthly day-of-week based trigger/ ) end it "should default 'start_date' to 'today'" do @puppet_trigger.delete('start_date') today = Time.now trigger['start_year'].should == today.year trigger['start_month'].should == today.month trigger['start_day'].should == today.day end it_behaves_like "a trigger that handles start_date and start_time" do let(:trigger_hash) {{'schedule' => 'monthly', 'months' => 1, 'which_occurrence' => 'first', 'day_of_week' => 'mon'}} end end end describe '#validate_trigger' do let(:provider) { described_class.new(:name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe') } it 'should succeed if all passed triggers translate from hashes to triggers' do triggers_to_validate = [ {'schedule' => 'once', 'start_date' => '2011-09-13', 'start_time' => '13:50'}, {'schedule' => 'weekly', 'start_date' => '2011-09-13', 'start_time' => '13:50', 'day_of_week' => 'mon'} ] provider.validate_trigger(triggers_to_validate).should == true end it 'should use the exception from translate_hash_to_trigger when it fails' do triggers_to_validate = [ {'schedule' => 'once', 'start_date' => '2011-09-13', 'start_time' => '13:50'}, {'schedule' => 'monthly', 'this is invalid' => true} ] expect {provider.validate_trigger(triggers_to_validate)}.to raise_error( Puppet::Error, /#{Regexp.escape("Unknown trigger option(s): ['this is invalid']")}/ ) end end describe '#flush' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :ensure => @ensure ) end before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) @command = 'C:\Windows\System32\notepad.exe' end describe 'when :ensure is :present' do before :each do @ensure = :present end it 'should save the task' do @mock_task.expects(:save) resource.provider.flush end it 'should fail if the command is not specified' do resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :ensure => @ensure ) expect { resource.provider.flush }.to raise_error( Puppet::Error, 'Parameter command is required.' ) end end describe 'when :ensure is :absent' do before :each do @ensure = :absent @mock_task.stubs(:activate) end it 'should not save the task if :ensure is :absent' do @mock_task.expects(:save).never resource.provider.flush end it 'should not fail if the command is not specified' do @mock_task.stubs(:save) resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :ensure => @ensure ) resource.provider.flush end end end describe 'property setter methods' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\dummy_task.exe' ) end before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end describe '#command=' do it 'should set the application_name on the task' do @mock_task.expects(:application_name=).with('C:\Windows\System32\notepad.exe') resource.provider.command = 'C:\Windows\System32\notepad.exe' end end describe '#arguments=' do it 'should set the parameters on the task' do @mock_task.expects(:parameters=).with(['/some /arguments /here']) resource.provider.arguments = ['/some /arguments /here'] end end describe '#working_dir=' do it 'should set the working_directory on the task' do @mock_task.expects(:working_directory=).with('C:\Windows\System32') resource.provider.working_dir = 'C:\Windows\System32' end end describe '#enabled=' do it 'should set the disabled flag if the task should be disabled' do @mock_task.stubs(:flags).returns(0) @mock_task.expects(:flags=).with(Win32::TaskScheduler::DISABLED) resource.provider.enabled = :false end it 'should clear the disabled flag if the task should be enabled' do @mock_task.stubs(:flags).returns(Win32::TaskScheduler::DISABLED) @mock_task.expects(:flags=).with(0) resource.provider.enabled = :true end end describe '#trigger=' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\Windows\System32\notepad.exe', :trigger => @trigger ) end before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end it 'should not consider all duplicate current triggers in sync with a single desired trigger' do @trigger = {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10'} current_triggers = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 0}, {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 1}, {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 2}, ] resource.provider.stubs(:trigger).returns(current_triggers) @mock_task.expects(:delete_trigger).with(1) @mock_task.expects(:delete_trigger).with(2) resource.provider.trigger = @trigger end it 'should remove triggers not defined in the resource' do @trigger = {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10'} current_triggers = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 0}, {'schedule' => 'once', 'start_date' => '2012-09-15', 'start_time' => '15:10', 'index' => 1}, {'schedule' => 'once', 'start_date' => '2013-09-15', 'start_time' => '15:10', 'index' => 2}, ] resource.provider.stubs(:trigger).returns(current_triggers) @mock_task.expects(:delete_trigger).with(1) @mock_task.expects(:delete_trigger).with(2) resource.provider.trigger = @trigger end it 'should add triggers defined in the resource, but not found on the system' do @trigger = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10'}, {'schedule' => 'once', 'start_date' => '2012-09-15', 'start_time' => '15:10'}, {'schedule' => 'once', 'start_date' => '2013-09-15', 'start_time' => '15:10'}, ] current_triggers = [ {'schedule' => 'once', 'start_date' => '2011-09-15', 'start_time' => '15:10', 'index' => 0}, ] resource.provider.stubs(:trigger).returns(current_triggers) @mock_task.expects(:trigger=).with(resource.provider.translate_hash_to_trigger(@trigger[1])) @mock_task.expects(:trigger=).with(resource.provider.translate_hash_to_trigger(@trigger[2])) resource.provider.trigger = @trigger end end - describe '#user=' do + describe '#user=', :if => Puppet.features.microsoft_windows? do before :each do @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) Win32::TaskScheduler.stubs(:new).returns(@mock_task) end it 'should use nil for user and password when setting the user to the SYSTEM account' do - Puppet::Util::ADSI.stubs(:sid_for_account).with('system').returns('SYSTEM SID') + Puppet::Util::Windows::Security.stubs(:name_to_sid).with('system').returns('SYSTEM SID') resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\dummy_task.exe', :user => 'system' ) @mock_task.expects(:set_account_information).with(nil, nil) resource.provider.user = 'system' end it 'should use the specified user and password when setting the user to anything other than SYSTEM' do - Puppet::Util::ADSI.stubs(:sid_for_account).with('my_user_name').returns('SID A') + Puppet::Util::Windows::Security.stubs(:name_to_sid).with('my_user_name').returns('SID A') resource = Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :command => 'C:\dummy_task.exe', :user => 'my_user_name', :password => 'my password' ) @mock_task.expects(:set_account_information).with('my_user_name', 'my password') resource.provider.user = 'my_user_name' end end end describe '#create' do let(:resource) do Puppet::Type.type(:scheduled_task).new( :name => 'Test Task', :enabled => @enabled, :command => @command, :arguments => @arguments, :working_dir => @working_dir, :trigger => { 'schedule' => 'once', 'start_date' => '2011-09-27', 'start_time' => '17:00' } ) end before :each do @enabled = :true @command = 'C:\Windows\System32\notepad.exe' @arguments = '/a /list /of /arguments' @working_dir = 'C:\Windows\Some\Directory' @mock_task = mock @mock_task.responds_like(Win32::TaskScheduler.new) @mock_task.stubs(:exists?).returns(true) @mock_task.stubs(:activate) @mock_task.stubs(:application_name=) @mock_task.stubs(:parameters=) @mock_task.stubs(:working_directory=) @mock_task.stubs(:set_account_information) @mock_task.stubs(:flags) @mock_task.stubs(:flags=) @mock_task.stubs(:trigger_count).returns(0) @mock_task.stubs(:trigger=) @mock_task.stubs(:save) Win32::TaskScheduler.stubs(:new).returns(@mock_task) described_class.any_instance.stubs(:sync_triggers) end it 'should set the command' do resource.provider.expects(:command=).with(@command) resource.provider.create end it 'should set the arguments' do resource.provider.expects(:arguments=).with(@arguments) resource.provider.create end it 'should set the working_dir' do resource.provider.expects(:working_dir=).with(@working_dir) resource.provider.create end it "should set the user" do resource.provider.expects(:user=).with(:system) resource.provider.create end it 'should set the enabled property' do resource.provider.expects(:enabled=) resource.provider.create end it 'should sync triggers' do resource.provider.expects(:trigger=) resource.provider.create end end end diff --git a/spec/unit/provider/user/windows_adsi_spec.rb b/spec/unit/provider/user/windows_adsi_spec.rb index 17ce7a613..d9eaa02bc 100755 --- a/spec/unit/provider/user/windows_adsi_spec.rb +++ b/spec/unit/provider/user/windows_adsi_spec.rb @@ -1,173 +1,173 @@ #!/usr/bin/env ruby require 'spec_helper' describe Puppet::Type.type(:user).provider(:windows_adsi) do let(:resource) do Puppet::Type.type(:user).new( :title => 'testuser', :comment => 'Test J. User', :provider => :windows_adsi ) end let(:provider) { resource.provider } let(:connection) { stub 'connection' } before :each do Puppet::Util::ADSI.stubs(:computer_name).returns('testcomputername') Puppet::Util::ADSI.stubs(:connect).returns connection end describe ".instances" do it "should enumerate all users" do names = ['user1', 'user2', 'user3'] stub_users = names.map{|n| stub(:name => n)} connection.stubs(:execquery).with("select * from win32_useraccount").returns(stub_users) described_class.instances.map(&:name).should =~ names end end it "should provide access to a Puppet::Util::ADSI::User object" do provider.user.should be_a(Puppet::Util::ADSI::User) end describe "when managing groups" do it 'should return the list of groups as a comma-separated list' do provider.user.stubs(:groups).returns ['group1', 'group2', 'group3'] provider.groups.should == 'group1,group2,group3' end it "should return absent if there are no groups" do provider.user.stubs(:groups).returns [] provider.groups.should == '' end it 'should be able to add a user to a set of groups' do resource[:membership] = :minimum provider.user.expects(:set_groups).with('group1,group2', true) provider.groups = 'group1,group2' resource[:membership] = :inclusive provider.user.expects(:set_groups).with('group1,group2', false) provider.groups = 'group1,group2' end end describe "when creating a user" do it "should create the user on the system and set its other properties" do resource[:groups] = ['group1', 'group2'] resource[:membership] = :inclusive resource[:comment] = 'a test user' resource[:home] = 'C:\Users\testuser' user = stub 'user' Puppet::Util::ADSI::User.expects(:create).with('testuser').returns user user.stubs(:groups).returns(['group2', 'group3']) create = sequence('create') user.expects(:password=).in_sequence(create) user.expects(:commit).in_sequence(create) user.expects(:set_groups).with('group1,group2', false).in_sequence(create) user.expects(:[]=).with('Description', 'a test user') user.expects(:[]=).with('HomeDirectory', 'C:\Users\testuser') provider.create end it "should load the profile if managehome is set", :if => Puppet.features.microsoft_windows? do resource[:password] = '0xDeadBeef' resource[:managehome] = true user = stub_everything 'user' Puppet::Util::ADSI::User.expects(:create).with('testuser').returns user Puppet::Util::Windows::User.expects(:load_profile).with('testuser', '0xDeadBeef') provider.create end it "should set a user's password" do provider.user.expects(:password=).with('plaintextbad') provider.password = "plaintextbad" end it "should test a valid user password" do resource[:password] = 'plaintext' provider.user.expects(:password_is?).with('plaintext').returns true provider.password.should == 'plaintext' end it "should test a bad user password" do resource[:password] = 'plaintext' provider.user.expects(:password_is?).with('plaintext').returns false provider.password.should == :absent end it 'should not create a user if a group by the same name exists' do Puppet::Util::ADSI::User.expects(:create).with('testuser').raises( Puppet::Error.new("Cannot create user if group 'testuser' exists.") ) expect{ provider.create }.to raise_error( Puppet::Error, /Cannot create user if group 'testuser' exists./ ) end end it 'should be able to test whether a user exists' do Puppet::Util::ADSI.stubs(:connect).returns stub('connection') provider.should be_exists Puppet::Util::ADSI.stubs(:connect).returns nil provider.should_not be_exists end it 'should be able to delete a user' do connection.expects(:Delete).with('user', 'testuser') provider.delete end - it 'should delete the profile if managehome is set' do + it 'should delete the profile if managehome is set', :if => Puppet.features.microsoft_windows? do resource[:managehome] = true sid = 'S-A-B-C' - Puppet::Util::ADSI.expects(:sid_for_account).with('testuser').returns(sid) + Puppet::Util::Windows::Security.expects(:name_to_sid).with('testuser').returns(sid) Puppet::Util::ADSI::UserProfile.expects(:delete).with(sid) connection.expects(:Delete).with('user', 'testuser') provider.delete end it "should commit the user when flushed" do provider.user.expects(:commit) provider.flush end - it "should return the user's SID as uid" do - Puppet::Util::ADSI.expects(:sid_for_account).with('testuser').returns('S-1-5-21-1362942247-2130103807-3279964888-1111') + it "should return the user's SID as uid", :if => Puppet.features.microsoft_windows? do + Puppet::Util::Windows::Security.expects(:name_to_sid).with('testuser').returns('S-1-5-21-1362942247-2130103807-3279964888-1111') provider.uid.should == 'S-1-5-21-1362942247-2130103807-3279964888-1111' end it "should fail when trying to manage the uid property" do provider.expects(:fail).with { |msg| msg =~ /uid is read-only/ } provider.send(:uid=, 500) end [:gid, :shell].each do |prop| it "should fail when trying to manage the #{prop} property" do provider.expects(:fail).with { |msg| msg =~ /No support for managing property #{prop}/ } provider.send("#{prop}=", 'foo') end end end