summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNate Walck <nate.walck@gmail.com>2015-10-05 16:07:02 -0700
committerNate Walck <nate.walck@gmail.com>2015-12-17 08:21:26 -0500
commite05e7b485463ea2c9346a83ad1bbc095cc95d4df (patch)
tree4deb5d987e59ec0854e6ce72c50b88d205e2e84f
parent4b0531742b7e560d04a2fda7bcc24d6da9ea1c60 (diff)
downloadchef-e05e7b485463ea2c9346a83ad1bbc095cc95d4df.tar.gz
Added profile provider for OS X
Fixed resource to use let
-rw-r--r--chef.gemspec1
-rw-r--r--lib/chef/provider/osx_profile.rb254
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource/osx_profile.rb74
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--spec/unit/provider/osx_profile_spec.rb267
-rw-r--r--spec/unit/provider_resolver_spec.rb1
-rw-r--r--spec/unit/resource/osx_profile_spec.rb61
8 files changed, 660 insertions, 0 deletions
diff --git a/chef.gemspec b/chef.gemspec
index dad33623af..5200f7cf3e 100644
--- a/chef.gemspec
+++ b/chef.gemspec
@@ -42,6 +42,7 @@ Gem::Specification.new do |s|
s.add_dependency "specinfra", "~> 2.10"
s.add_dependency "syslog-logger", "~> 1.6"
+ s.add_dependency "uuidtools", "~> 2.1.5"
s.add_dependency "proxifier", "~> 1.0"
diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb
new file mode 100644
index 0000000000..ee355fd38e
--- /dev/null
+++ b/lib/chef/provider/osx_profile.rb
@@ -0,0 +1,254 @@
+#
+# Author:: Nate Walck (<nate.walck@gmail.com>)
+# Copyright:: Copyright (c) 2015 Facebook, 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/log'
+require 'chef/provider'
+require 'chef/resource'
+require 'chef/resource/file'
+require 'uuidtools'
+
+class Chef
+ class Provider
+ class OsxProfile < Chef::Provider
+ include Chef::Mixin::Command
+ provides :osx_profile, os: "darwin"
+ provides :osx_config_profile, os: "darwin"
+
+ def whyrun_supported?
+ true
+ end
+
+ def load_current_resource
+ @current_resource = Chef::Resource::OsxProfile.new(@new_resource.name)
+ @current_resource.profile_name(@new_resource.profile_name)
+
+ all_profiles = get_installed_profiles
+ @new_resource.profile(
+ @new_resource.profile ||
+ @new_resource.profile_name
+ )
+
+ @new_profile_hash = get_profile_hash(@new_resource.profile)
+ @new_profile_hash['PayloadUUID'] =
+ config_uuid(@new_profile_hash) if @new_profile_hash
+
+ if @new_profile_hash
+ @new_profile_identifier = @new_profile_hash['PayloadIdentifier']
+ else
+ @new_profile_identifier = @new_resource.identifier ||
+ @new_resource.profile_name
+ end
+
+ current_profile = all_profiles['_computerlevel'].find {
+ |item| item['ProfileIdentifier'] ==
+ @new_profile_identifier
+ }
+ @current_resource.profile(current_profile)
+
+ end
+
+ def define_resource_requirements
+ requirements.assert(:remove) do |a|
+ if @new_profile_identifier
+ a.assertion {
+ !@new_profile_identifier.nil? and
+ !@new_profile_identifier.end_with?('.mobileconfig') and
+ /^\w+(?:\.\w+)+$/.match(@new_profile_identifier)
+ }
+ a.failure_message RuntimeError, "when removing using the identifier attribute, it must match the profile identifier"
+ else
+ new_profile_name = @new_resource.profile_name
+ a.assertion {
+ !new_profile_name.end_with?('.mobileconfig') and
+ /^\w+(?:\.\w+)+$/.match(new_profile_name)
+ }
+ a.failure_message RuntimeError, "When removing by resource name, it must match the profile identifier "
+ end
+ end
+
+ requirements.assert(:install) do |a|
+ if @new_profile_hash.is_a?(Hash)
+ a.assertion {
+ @new_profile_hash.include?('PayloadIdentifier')
+ }
+ a.failure_message RuntimeError, "The specified profile does not seem to be valid"
+ end
+ if @new_profile_hash.is_a?(String)
+ a.assertion {
+ @new_profile_hash.end_with?('.mobileconfig')
+ }
+ a.failure_message RuntimeError, "#{new_profile_hash}' is not a valid profile"
+ end
+ end
+ end
+
+ def action_install
+ unless profile_installed?
+ converge_by("install profile #{@new_profile_identifier}") do
+ profile_path = write_profile_to_disk
+ install_profile(profile_path)
+ get_installed_profiles(true)
+ end
+ end
+ end
+
+ def action_remove
+ # Clean up profile after removing it
+ if profile_installed?
+ converge_by("remove profile #{@new_profile_identifier}") do
+ remove_profile
+ get_installed_profiles(true)
+ end
+ end
+ end
+
+ def load_profile_hash(new_profile)
+ # file must exist in cookbook
+ if new_profile.end_with?('.mobileconfig')
+ unless cookbook_file_available?(new_profile)
+ error_string = "#{self.to_s}: '#{new_profile}' not found in cookbook"
+ raise Chef::Exceptions::FileNotFound, error_string
+ end
+ cookbook_profile = cache_cookbook_profile(new_profile)
+ return read_plist(cookbook_profile)
+ else
+ return nil
+ end
+ end
+
+ def cookbook_file_available?(cookbook_file)
+ run_context.has_cookbook_file_in_cookbook?(
+ @new_resource.cookbook_name, cookbook_file
+ )
+ end
+
+ def get_cache_dir
+ cache_dir = Chef::FileCache.create_cache_path(
+ "profiles/#{@new_resource.cookbook_name}"
+ )
+ end
+
+ def cache_cookbook_profile(cookbook_file)
+ Chef::FileCache.create_cache_path(
+ ::File.join(
+ "profiles",
+ @new_resource.cookbook_name,
+ ::File.dirname(cookbook_file)
+ )
+ )
+ remote_file = Chef::Resource::CookbookFile.new(
+ ::File.join(
+ get_cache_dir,
+ "#{cookbook_file}.remote"
+ ),
+ run_context
+ )
+ remote_file.cookbook_name = @new_resource.cookbook_name
+ remote_file.source(cookbook_file)
+ remote_file.backup(false)
+ remote_file.run_action(:create)
+ remote_file.path
+ end
+
+ def get_profile_hash(new_profile)
+ if new_profile.is_a?(Hash)
+ return new_profile
+ elsif new_profile.is_a?(String)
+ return load_profile_hash(new_profile)
+ end
+ end
+
+ def config_uuid(profile)
+ # Make a UUID of the profile contents and return as string
+ UUIDTools::UUID.sha1_create(
+ UUIDTools::UUID_DNS_NAMESPACE,
+ profile.to_s
+ ).to_s
+ end
+
+ def write_profile_to_disk
+ @new_resource.path(Chef::FileCache.create_cache_path("profiles"))
+ tempfile = Chef::FileContentManagement::Tempfile.new(@new_resource).tempfile
+ tempfile.write(@new_profile_hash.to_plist)
+ tempfile.close
+ tempfile.path
+ end
+
+ def install_profile(profile_path)
+ cmd = "profiles -I -F '#{profile_path}'"
+ Chef::Log.debug("cmd: #{cmd}")
+ shellout_results = shell_out(cmd)
+ shellout_results.exitstatus
+ end
+
+ def remove_profile
+ cmd = "profiles -R -p '#{@new_profile_identifier}'"
+ Chef::Log.debug("cmd: #{cmd}")
+ shellout_results = shell_out(cmd)
+ shellout_results.exitstatus
+ end
+
+ def get_installed_profiles(update=nil)
+ if update
+ node.run_state[:config_profiles] = query_installed_profiles
+ else
+ node.run_state[:config_profiles] ||= query_installed_profiles
+ end
+ end
+
+ def query_installed_profiles
+ # Dump all profile metadata to a tempfile
+ tempfile = generate_tempfile
+ write_installed_profiles(tempfile)
+ installed_profiles = read_plist(tempfile)
+ Chef::Log.debug("Saved profiles to run_state")
+ # Clean up the temp file as we do not need it anymore
+ ::File.unlink(tempfile)
+ installed_profiles
+ end
+
+ def generate_tempfile
+ tempfile = ::Dir::Tmpname.create("allprofiles.plist") {}
+ end
+
+ def write_installed_profiles(tempfile)
+ cmd = "profiles -P -o '#{tempfile}'"
+ shell_out!(cmd)
+ end
+
+ def read_plist(xml_file)
+ Plist::parse_xml(xml_file)
+ end
+
+ def profile_installed?
+ # Profile Identifier and UUID must match a currently installed profile
+ if @current_resource.profile.nil? or @current_resource.profile.empty?
+ false
+ else
+ if @new_resource.action.include?(:remove)
+ true
+ else
+ @current_resource.profile['ProfileUUID'] ==
+ @new_profile_hash['PayloadUUID']
+ end
+ end
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 18500d4669..f5e7a0f989 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -41,6 +41,7 @@ require 'chef/provider/mdadm'
require 'chef/provider/mount'
require 'chef/provider/package'
require 'chef/provider/powershell_script'
+require 'chef/provider/osx_profile'
require 'chef/provider/reboot'
require 'chef/provider/remote_directory'
require 'chef/provider/remote_file'
diff --git a/lib/chef/resource/osx_profile.rb b/lib/chef/resource/osx_profile.rb
new file mode 100644
index 0000000000..26b834a9c0
--- /dev/null
+++ b/lib/chef/resource/osx_profile.rb
@@ -0,0 +1,74 @@
+#
+# Author:: Nate Walck (<nate.walck@gmail.com>)
+# Copyright:: Copyright (c) 2015 Facebook, 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 OsxProfile < Chef::Resource
+ provides :osx_profile, os: "darwin"
+ provides :osx_config_profile, os: "darwin"
+
+ identity_attr :profile_name
+
+ default_action :install
+ allowed_actions :install, :remove
+
+ def initialize(name, run_context=nil)
+ super
+ @profile_name = name
+ @profile = nil
+ @identifier = nil
+ @path = nil
+ end
+
+ def profile_name(arg=nil)
+ set_or_return(
+ :profile_name,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def profile(arg=nil)
+ set_or_return(
+ :profile,
+ arg,
+ :kind_of => [ String, Hash ]
+ )
+ end
+
+ def identifier(arg=nil)
+ set_or_return(
+ :identifier,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ def path(arg=nil)
+ set_or_return(
+ :path,
+ arg,
+ :kind_of => [ String ]
+ )
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index 6db0fc9d8d..f699d95ace 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -57,6 +57,7 @@ require 'chef/resource/paludis_package'
require 'chef/resource/perl'
require 'chef/resource/portage_package'
require 'chef/resource/powershell_script'
+require 'chef/resource/osx_profile'
require 'chef/resource/python'
require 'chef/resource/reboot'
require 'chef/resource/registry_key'
diff --git a/spec/unit/provider/osx_profile_spec.rb b/spec/unit/provider/osx_profile_spec.rb
new file mode 100644
index 0000000000..85f9d56e16
--- /dev/null
+++ b/spec/unit/provider/osx_profile_spec.rb
@@ -0,0 +1,267 @@
+#
+# Author:: Nate Walck (<nate.walck@gmail.com>)
+# Copyright:: Copyright (c) 2015 Chef, 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::Provider::OsxProfile do
+ let(:shell_out_success) do
+ double('shell_out', :exitstatus => 0, :error? => false)
+ end
+ describe 'action_create' do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) { Chef::Resource::OsxProfile.new("Profile Test", run_context) }
+ let(:provider) { Chef::Provider::OsxProfile.new(new_resource, run_context) }
+ let(:all_profiles) do
+ {"_computerlevel"=>
+ [{"ProfileDisplayName"=>"Finder Settings",
+ "ProfileIdentifier"=>"com.apple.finder",
+ "ProfileInstallDate"=>"2015-11-08 23:15:21 +0000",
+ "ProfileItems"=>
+ [{"PayloadContent"=>
+ {"PayloadContentManagedPreferences"=>
+ {"com.apple.finder"=>
+ {"Forced"=>[{"mcx_preference_settings"=>{"ShowExternalHardDrivesOnDesktop"=>false}}]}}},
+ "PayloadDisplayName"=>"Custom: (com.apple.finder)",
+ "PayloadIdentifier"=>"com.apple.finder",
+ "PayloadType"=>"com.apple.ManagedClient.preferences",
+ "PayloadUUID"=>"a017048f-684b-4e81-baa3-43afe316d739",
+ "PayloadVersion"=>1}],
+ "ProfileOrganization"=>"Chef",
+ "ProfileRemovalDisallowed"=>"false",
+ "ProfileType"=>"Configuration",
+ "ProfileUUID"=>"e2e09bef-e673-44a6-bcbe-ecb5f1c1b740",
+ "ProfileVerificationState"=>"unsigned",
+ "ProfileVersion"=>1},
+ {"ProfileDisplayName"=>"ScreenSaver Settings",
+ "ProfileIdentifier"=>"com.testprofile.screensaver",
+ "ProfileInstallDate"=>"2015-10-05 23:15:21 +0000",
+ "ProfileItems"=>
+ [{"PayloadContent"=>
+ {"PayloadContentManagedPreferences"=>
+ {"com.apple.screensaver"=>
+ {"Forced"=>[{"mcx_preference_settings"=>{"idleTime"=>0}}]}}},
+ "PayloadDisplayName"=>"Custom: (com.apple.screensaver)",
+ "PayloadIdentifier"=>"com.apple.screensaver",
+ "PayloadType"=>"com.apple.ManagedClient.preferences",
+ "PayloadUUID"=>"73fc30e0-1e57-0131-c32d-000c2944c110",
+ "PayloadVersion"=>1}],
+ "ProfileOrganization"=>"Chef",
+ "ProfileRemovalDisallowed"=>"false",
+ "ProfileType"=>"Configuration",
+ "ProfileUUID"=>"6e95927c-f200-54b4-85c7-52ab99b61c47",
+ "ProfileVerificationState"=>"unsigned",
+ "ProfileVersion"=>1}],
+ }
+ end
+ # If anything is changed within this profile, be sure to update the
+ # ProfileUUID in all_profiles to match the new config specific UUID
+ let(:test_profile) do
+ {
+ 'PayloadIdentifier' => 'com.testprofile.screensaver',
+ 'PayloadRemovalDisallowed' => false,
+ 'PayloadScope' => 'System',
+ 'PayloadType' => 'Configuration',
+ 'PayloadUUID' => '1781fbec-3325-565f-9022-8aa28135c3cc',
+ 'PayloadOrganization' => 'Chef',
+ 'PayloadVersion' => 1,
+ 'PayloadDisplayName' => 'Screensaver Settings',
+ 'PayloadContent'=> [
+ {
+ 'PayloadType' => 'com.apple.ManagedClient.preferences',
+ 'PayloadVersion' => 1,
+ 'PayloadIdentifier' => 'com.testprofile.screensaver',
+ 'PayloadUUID' => '73fc30e0-1e57-0131-c32d-000c2944c108',
+ 'PayloadEnabled' => true,
+ 'PayloadDisplayName' => 'com.apple.screensaver',
+ 'PayloadContent' => {
+ 'com.apple.screensaver' => {
+ 'Forced' => [
+ {
+ 'mcx_preference_settings' => {
+ 'idleTime' => 0,
+ }
+ }
+ ]
+ }
+ }
+ }
+ ]
+ }
+ end
+ let(:no_profiles) do
+ { "_computerlevel"=> [] }
+ end
+
+ before(:each) do
+ allow(provider).to receive(:cookbook_file_available?).and_return(true)
+ allow(provider).to receive(:cache_cookbook_profile).and_return('/tmp/test.mobileconfig.remote')
+ allow(provider).to receive(:get_new_profile_hash).and_return(test_profile)
+ allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
+ allow(provider).to receive(:read_plist).and_return(all_profiles)
+ allow(::File).to receive(:unlink).and_return(true)
+ end
+
+ it 'should build the get all profiles shellout command correctly' do
+ profile_name = 'com.testprofile.screensaver.mobileconfig'
+ tempfile = '/tmp/allprofiles.plist'
+ new_resource.profile_name profile_name
+ allow(provider).to receive(:generate_tempfile).and_return(tempfile)
+ allow(provider).to receive(:get_installed_profiles).and_call_original
+ allow(provider).to receive(:read_plist).and_return(all_profiles)
+ expect(provider).to receive(:shell_out!).with("profiles -P -o '/tmp/allprofiles.plist'")
+ provider.load_current_resource
+ end
+
+ it 'should use profile name as profile when no profile is set' do
+ profile_name = 'com.testprofile.screensaver.mobileconfig'
+ new_resource.profile_name profile_name
+ provider.load_current_resource
+ expect(new_resource.profile_name).to eql(profile_name)
+ end
+
+ it 'should use identifier from specified profile' do
+ new_resource.profile test_profile
+ provider.load_current_resource
+ expect(
+ provider.instance_variable_get(:@new_profile_identifier)
+ ).to eql(test_profile['PayloadIdentifier'])
+ end
+
+ it 'should install when not installed' do
+ new_resource.profile test_profile
+ allow(provider).to receive(:get_installed_profiles).and_return(no_profiles)
+ provider.load_current_resource
+ expect { provider.run_action(:install) }.to_not raise_error
+ end
+
+ it 'does not install if the profile is already installed' do
+ new_resource.profile test_profile
+ allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
+ provider.load_current_resource
+ expect(provider).to_not receive(:install_profile)
+ expect { provider.action_install }.to_not raise_error
+ end
+
+ it 'should install when installed but uuid differs' do
+ new_resource.profile test_profile
+ all_profiles['_computerlevel'][1]['ProfileUUID'] = '1781fbec-3325-565f-9022-9bb39245d4dd'
+ provider.load_current_resource
+ expect { provider.run_action(:install) }.to_not raise_error
+ end
+
+ it 'should build the shellout install command correctly' do
+ profile_path = '/tmp/test.mobileconfig'
+ new_resource.profile test_profile
+ # Change the profile so it triggers an install
+ all_profiles['_computerlevel'][1]['ProfileUUID'] = '1781fbec-3325-565f-9022-9bb39245d4dd'
+ provider.load_current_resource
+ allow(provider).to receive(:write_profile_to_disk).and_return(profile_path)
+ expect(provider).to receive(:shell_out).with("profiles -I -F '#{profile_path}'").and_return(shell_out_success)
+ provider.action_install()
+ end
+
+ it 'should fail if there is no identifier inside the profile' do
+ test_profile.delete('PayloadIdentifier')
+ new_resource.profile test_profile
+ error_message = 'The specified profile does not seem to be valid'
+ expect{provider.run_action(:install)}.to raise_error(RuntimeError, error_message)
+ end
+
+ end
+
+ describe 'action_remove' do
+ let(:node) { Chef::Node.new }
+ let(:events) { Chef::EventDispatch::Dispatcher.new }
+ let(:run_context) { Chef::RunContext.new(node, {}, events) }
+ let(:new_resource) { Chef::Resource::OsxProfile.new("Profile Test", run_context) }
+ let(:provider) { Chef::Provider::OsxProfile.new(new_resource, run_context) }
+ let(:current_resource) { Chef::Resource::OsxProfile.new('Profile Test') }
+ let(:all_profiles) do
+ {"_computerlevel"=>
+ [{"ProfileDisplayName"=>"ScreenSaver Settings",
+ "ProfileIdentifier"=>"com.apple.screensaver",
+ "ProfileInstallDate"=>"2015-10-05 23:15:21 +0000",
+ "ProfileItems"=>
+ [{"PayloadContent"=>
+ {"PayloadContentManagedPreferences"=>
+ {"com.apple.screensaver"=>
+ {"Forced"=>[{"mcx_preference_settings"=>{"idleTime"=>0}}]}}},
+ "PayloadDisplayName"=>"Custom: (com.apple.screensaver)",
+ "PayloadIdentifier"=>"com.apple.screensaver",
+ "PayloadType"=>"com.apple.ManagedClient.preferences",
+ "PayloadUUID"=>"73fc30e0-1e57-0131-c32d-000c2944c108",
+ "PayloadVersion"=>1}],
+ "ProfileOrganization"=>"Chef",
+ "ProfileRemovalDisallowed"=>"false",
+ "ProfileType"=>"Configuration",
+ "ProfileUUID"=>"1781fbec-3325-565f-9022-8aa28135c3cc",
+ "ProfileVerificationState"=>"unsigned",
+ "ProfileVersion"=>1},
+ {"ProfileDisplayName"=>"ScreenSaver Settings",
+ "ProfileIdentifier"=>"com.testprofile.screensaver",
+ "ProfileInstallDate"=>"2015-10-05 23:15:21 +0000",
+ "ProfileItems"=>
+ [{"PayloadContent"=>
+ {"PayloadContentManagedPreferences"=>
+ {"com.apple.screensaver"=>
+ {"Forced"=>[{"mcx_preference_settings"=>{"idleTime"=>0}}]}}},
+ "PayloadDisplayName"=>"Custom: (com.apple.screensaver)",
+ "PayloadIdentifier"=>"com.apple.screensaver",
+ "PayloadType"=>"com.apple.ManagedClient.preferences",
+ "PayloadUUID"=>"73fc30e0-1e57-0131-c32d-000c2944c110",
+ "PayloadVersion"=>1}],
+ "ProfileOrganization"=>"Chef",
+ "ProfileRemovalDisallowed"=>"false",
+ "ProfileType"=>"Configuration",
+ "ProfileUUID"=>"1781fbec-3325-565f-9022-8aa28135c3cc",
+ "ProfileVerificationState"=>"unsigned",
+ "ProfileVersion"=>1}],
+ }
+ end
+ before(:each) do
+ provider.current_resource = current_resource
+ allow(provider).to receive(:get_installed_profiles).and_return(all_profiles)
+ end
+
+ it 'should use resource name for identifier when not specified' do
+ new_resource.profile_name 'com.testprofile.screensaver'
+ new_resource.action(:remove)
+ provider.load_current_resource
+ expect(provider.instance_variable_get(:@new_profile_identifier)
+ ).to eql(new_resource.profile_name)
+ end
+
+ it 'should use specified identifier' do
+ new_resource.identifier 'com.testprofile.screensaver'
+ new_resource.action(:remove)
+ provider.load_current_resource
+ expect(provider.instance_variable_get(:@new_profile_identifier)
+ ).to eql(new_resource.identifier)
+ end
+
+ it 'should build the shellout remove command correctly' do
+ new_resource.identifier 'com.testprofile.screensaver'
+ new_resource.action(:remove)
+ provider.load_current_resource
+ expect(provider).to receive(:shell_out).with("profiles -R -p '#{new_resource.identifier}'").and_return(shell_out_success)
+ provider.action_remove()
+ end
+ end
+end
diff --git a/spec/unit/provider_resolver_spec.rb b/spec/unit/provider_resolver_spec.rb
index c1256180af..8c087cf3f3 100644
--- a/spec/unit/provider_resolver_spec.rb
+++ b/spec/unit/provider_resolver_spec.rb
@@ -708,6 +708,7 @@ describe Chef::ProviderResolver do
%w(mac_os_x mac_os_x_server) => {
group: [ Chef::Resource::Group, Chef::Provider::Group::Dscl ],
package: [ Chef::Resource::HomebrewPackage, Chef::Provider::Package::Homebrew ],
+ osx_profile: [ Chef::Resource::OsxProfile, Chef::Provider::OsxProfile],
user: [ Chef::Resource::User, Chef::Provider::User::Dscl ],
"mac_os_x" => {
diff --git a/spec/unit/resource/osx_profile_spec.rb b/spec/unit/resource/osx_profile_spec.rb
new file mode 100644
index 0000000000..d7d72e5836
--- /dev/null
+++ b/spec/unit/resource/osx_profile_spec.rb
@@ -0,0 +1,61 @@
+#
+# Author:: Nate Walck (<nate.walck@gmail.com>)
+# Copyright:: Copyright (c) 2015 Facebook, 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::OsxProfile do
+ let(:resource) { Chef::Resource::OsxProfile.new(
+ "Test Profile Resource",
+ run_context)
+ }
+
+ it "should create a new Chef::Resource::OsxProfile" do
+ expect(resource).to be_a_kind_of(Chef::Resource)
+ expect(resource).to be_a_kind_of(Chef::Resource::OsxProfile)
+ end
+
+ it "should have a resource name of profile" do
+ expect(resource.resource_name).to eql(:osx_profile)
+ end
+
+ it "should have a default action of install" do
+ expect(resource.action).to eql([:install])
+ end
+
+ it "should accept install and remove as actions" do
+ expect { resource.action :install}.not_to raise_error
+ expect { resource.action :remove}.not_to raise_error
+ end
+
+ it "should allow you to set the profile attribute" do
+ resource.profile "com.testprofile.screensaver"
+ expect(resource.profile).to eql("com.testprofile.screensaver")
+ end
+
+ it "should allow you to set the profile attribute to a string" do
+ resource.profile "com.testprofile.screensaver"
+ expect(resource.profile).to be_a(String)
+ expect(resource.profile).to eql("com.testprofile.screensaver")
+ end
+
+ it "should allow you to set the profile attribute to a hash" do
+ test_profile = { 'profile' => false }
+ resource.profile test_profile
+ expect(resource.profile).to be_a(Hash)
+ end
+end