diff options
author | Tim Smith <tsmith@chef.io> | 2020-04-10 15:08:31 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-10 15:08:31 -0700 |
commit | 59779bd4a5405a26da025e2772a7878133df2baa (patch) | |
tree | af61b55656a91b5be5770bb89bc4ba7f1eade0b4 /lib/chef | |
parent | d2cfd047bd3b77f56230efef9f1bef569992800c (diff) | |
parent | e009583afea176de8a9619aca17cd162b01db4ea (diff) | |
download | chef-59779bd4a5405a26da025e2772a7878133df2baa.tar.gz |
Merge pull request #9642 from chef/macos-resources
Add the plist resource from the macos cookbook
Diffstat (limited to 'lib/chef')
-rw-r--r-- | lib/chef/provider/launchd.rb | 2 | ||||
-rw-r--r-- | lib/chef/provider/osx_profile.rb | 3 | ||||
-rw-r--r-- | lib/chef/provider/user/dscl.rb | 6 | ||||
-rw-r--r-- | lib/chef/provider/user/mac.rb | 5 | ||||
-rw-r--r-- | lib/chef/resource/build_essential.rb | 2 | ||||
-rw-r--r-- | lib/chef/resource/plist.rb | 207 | ||||
-rw-r--r-- | lib/chef/resources.rb | 1 |
7 files changed, 218 insertions, 8 deletions
diff --git a/lib/chef/provider/launchd.rb b/lib/chef/provider/launchd.rb index 3516cadda7..fbb9307712 100644 --- a/lib/chef/provider/launchd.rb +++ b/lib/chef/provider/launchd.rb @@ -161,7 +161,7 @@ class Chef def content plist_hash = new_resource.plist_hash || gen_hash - Plist::Emit.dump(plist_hash) unless plist_hash.nil? + ::Plist::Emit.dump(plist_hash) unless plist_hash.nil? end def gen_hash diff --git a/lib/chef/provider/osx_profile.rb b/lib/chef/provider/osx_profile.rb index ba63d4b02a..07d35e633c 100644 --- a/lib/chef/provider/osx_profile.rb +++ b/lib/chef/provider/osx_profile.rb @@ -21,6 +21,7 @@ require_relative "../provider" require_relative "../resource" require_relative "../resource/file" require "uuidtools" +require "plist" class Chef class Provider @@ -232,7 +233,7 @@ class Chef end def read_plist(xml_file) - Plist.parse_xml(xml_file) + ::Plist.parse_xml(xml_file) end def profile_installed? diff --git a/lib/chef/provider/user/dscl.rb b/lib/chef/provider/user/dscl.rb index 687fc021da..a46ecb3a62 100644 --- a/lib/chef/provider/user/dscl.rb +++ b/lib/chef/provider/user/dscl.rb @@ -118,7 +118,7 @@ in 'password', with the associated 'salt' and 'iterations'.") # Calling shell_out directly since we want to give an input stream shadow_hash_xml = convert_binary_plist_to_xml(shadow_hash_binary.string) - shadow_hash = Plist.parse_xml(shadow_hash_xml) + shadow_hash = ::Plist.parse_xml(shadow_hash_xml) if shadow_hash["SALTED-SHA512-PBKDF2"] # 10.7+ contains this, but we retain the check in case it goes away in the future @password_shadow_conversion_algorithm = "SALTED-SHA512-PBKDF2" @@ -541,7 +541,7 @@ in 'password', with the associated 'salt' and 'iterations'.") begin user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist" user_plist_info = run_plutil("convert", "xml1", "-o", "-", user_plist_file) - user_info = Plist.parse_xml(user_plist_info) + user_info = ::Plist.parse_xml(user_plist_info) rescue Chef::Exceptions::PlistUtilCommandFailed end @@ -554,7 +554,7 @@ in 'password', with the associated 'salt' and 'iterations'.") # def save_user_info(user_info) user_plist_file = "#{USER_PLIST_DIRECTORY}/#{new_resource.username}.plist" - Plist::Emit.save_plist(user_info, user_plist_file) + ::Plist::Emit.save_plist(user_info, user_plist_file) run_plutil("convert", "binary1", user_plist_file) end diff --git a/lib/chef/provider/user/mac.rb b/lib/chef/provider/user/mac.rb index 312d2a7b47..104cf51abe 100644 --- a/lib/chef/provider/user/mac.rb +++ b/lib/chef/provider/user/mac.rb @@ -22,6 +22,7 @@ require_relative "../../mixin/shell_out" require_relative "../../mixin/which" require_relative "../user" require_relative "../../resource/user/mac_user" +require "plist" class Chef class Provider @@ -79,7 +80,7 @@ class Chef admin_group_xml = run_dscl("read", "/Groups/admin") return nil unless admin_group_xml && admin_group_xml != "" - @admin_group_plist = Plist.new(::Plist.parse_xml(admin_group_xml)) + @admin_group_plist = ::Plist.new(::Plist.parse_xml(admin_group_xml)) end def reload_user_plist @@ -94,7 +95,7 @@ class Chef return nil if user_xml.nil? || user_xml == "" - @user_plist = Plist.new(::Plist.parse_xml(user_xml)) + @user_plist = ::Plist.new(::Plist.parse_xml(user_xml)) return unless user_plist[:shadow_hash] diff --git a/lib/chef/resource/build_essential.rb b/lib/chef/resource/build_essential.rb index 5a41f3895b..ab1be3bcfd 100644 --- a/lib/chef/resource/build_essential.rb +++ b/lib/chef/resource/build_essential.rb @@ -161,7 +161,7 @@ class Chef # # @return [true, false] def xcode_cli_installed? - packages = Plist.parse_xml(::File.open("/Library/Receipts/InstallHistory.plist", "r")) + packages = ::Plist.parse_xml(::File.open("/Library/Receipts/InstallHistory.plist", "r")) packages.select! { |package| package["displayName"].match? "Command Line Tools" } !packages.empty? end diff --git a/lib/chef/resource/plist.rb b/lib/chef/resource/plist.rb new file mode 100644 index 0000000000..d5d2ce3c6c --- /dev/null +++ b/lib/chef/resource/plist.rb @@ -0,0 +1,207 @@ +# +# Copyright:: Copyright 2017-2020, Microsoft Corporation +# Copyright:: Copyright 2020, Chef Software Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +require_relative "../resource" +require "plist" + +class Chef + class Resource + + class PlistResource < Chef::Resource # we name this PlistResource to avoid confusion with Plist from the plist gem + unified_mode true + + provides :plist + + description "Use the plist resource to set config values in plist files on macOS systems." + introduced "16.0" + + property :path, String, name_property: true + property :entry, String + property :value, [TrueClass, FalseClass, String, Integer, Float, Hash] + property :encoding, String, default: "binary" + property :owner, String, default: "root" + property :group, String, default: "wheel" + property :mode, [String, Integer] + + PLISTBUDDY_EXECUTABLE = "/usr/libexec/PlistBuddy".freeze + DEFAULTS_EXECUTABLE = "/usr/bin/defaults".freeze + PLUTIL_EXECUTABLE = "/usr/bin/plutil".freeze + PLUTIL_FORMAT_MAP = { "us-ascii" => "xml1", + "text/xml" => "xml1", + "utf-8" => "xml1", + "binary" => "binary1" }.freeze + + load_current_value do |desired| + current_value_does_not_exist! unless ::File.exist? desired.path + entry desired.entry if entry_in_plist? desired.entry, desired.path + + setting = setting_from_plist desired.entry, desired.path + value convert_to_data_type_from_string(setting[:key_type], setting[:key_value]) + + file_type_cmd = shell_out "/usr/bin/file", "--brief", "--mime-encoding", "--preserve-date", desired.path + encoding file_type_cmd.stdout.chomp + + file_owner_cmd = shell_out("/usr/bin/stat", "-f", "%Su", desired.path) + owner file_owner_cmd.stdout.chomp + + file_group_cmd = shell_out("/usr/bin/stat", "-f", "%Sg", desired.path) + group file_group_cmd.stdout.chomp + end + + action :set do + converge_if_changed :path do + converge_by "create new plist: '#{new_resource.path}'" do + file new_resource.path do + content {}.to_plist + owner new_resource.owner + group new_resource.group + mode new_resource.mode if property_is_set?(:mode) + end + end + end + + plist_file_name = ::File.basename(new_resource.path) + + converge_if_changed :entry do + converge_by "add entry \"#{new_resource.entry}\" to #{plist_file_name}" do + shell_out!(plistbuddy_command(:add, new_resource.entry, new_resource.path, new_resource.value)) + end + end + + converge_if_changed :value do + converge_by "#{plist_file_name}: set #{new_resource.entry} to #{new_resource.value}" do + shell_out!(plistbuddy_command(:set, new_resource.entry, new_resource.path, new_resource.value)) + end + end + + converge_if_changed :encoding do + converge_by "change format" do + unless PLUTIL_FORMAT_MAP.key?(new_resource.encoding) + Chef::Application.fatal!( + "Option encoding must be equal to one of: #{PLUTIL_FORMAT_MAP.keys}! You passed \"#{new_resource.encoding}\"." + ) + end + shell_out!(PLUTIL_EXECUTABLE, "-convert", PLUTIL_FORMAT_MAP[new_resource.encoding], new_resource.path) + end + end + + converge_if_changed :owner do + converge_by "update owner to #{new_resource.owner}" do + file new_resource.path do + owner new_resource.owner + end + end + end + + converge_if_changed :group do + converge_by "update group to #{new_resource.group}" do + file new_resource.path do + group new_resource.group + end + end + end + end + + ### Question: Should I refactor these methods into an action_class? + ### Answer: NO + ### Why: We need them in both the action and in load_current_value. If you put them in the + ### action class then they're only in the Provider class and are not available to load_current_value + + def convert_to_data_type_from_string(type, value) + case type + when "boolean" + # Since we've determined this is a boolean data type, we can assume that: + # If the value as an int is 1, return true + # If the value as an int is 0 (not 1), return false + value.to_i == 1 + when "integer" + value.to_i + when "float" + value.to_f + when "string" + value + when "dictionary" + value + when nil + "" + else + raise "Unknown or unsupported data type: #{type.class}" + end + end + + def type_to_commandline_string(value) + case value + when Array + "array" + when Integer + "integer" + when FalseClass + "bool" + when TrueClass + "bool" + when Hash + "dict" + when String + "string" + when Float + "float" + else + raise "Unknown or unsupported data type: #{value} of #{value.class}" + end + end + + def entry_in_plist?(entry, path) + print_entry = plistbuddy_command :print, entry, path + cmd = shell_out print_entry + cmd.exitstatus == 0 + end + + def plistbuddy_command(subcommand, entry, path, value = nil) + sep = " " + arg = case subcommand.to_s + when "add" + type_to_commandline_string(value) + when "set" + if value.is_a?(Hash) + sep = ":" + value.map { |k, v| "#{k} #{v}" } + else + value + end + else + "" + end + entry_with_arg = ["\"#{entry}\"", arg].join(sep).strip + subcommand = "#{subcommand.capitalize} :#{entry_with_arg}" + [PLISTBUDDY_EXECUTABLE, "-c", "\'#{subcommand}\'", "\"#{path}\""].join(" ") + end + + def setting_from_plist(entry, path) + defaults_read_type_output = shell_out(DEFAULTS_EXECUTABLE, "read-type", path, entry).stdout + data_type = defaults_read_type_output.split.last + + if value.class == Hash + plutil_output = shell_out(PLUTIL_EXECUTABLE, "-extract", entry, "xml1", "-o", "-", path).stdout.chomp + { key_type: data_type, key_value: ::Plist.parse_xml(plutil_output) } + else + defaults_read_output = shell_out(DEFAULTS_EXECUTABLE, "read", path, entry).stdout + { key_type: data_type, key_value: defaults_read_output.strip } + end + end + end + end +end diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb index 0d1ffeb273..c7191ef69f 100644 --- a/lib/chef/resources.rb +++ b/lib/chef/resources.rb @@ -87,6 +87,7 @@ require_relative "resource/package" require_relative "resource/pacman_package" require_relative "resource/paludis_package" require_relative "resource/perl" +require_relative "resource/plist" require_relative "resource/portage_package" require_relative "resource/powershell_package_source" require_relative "resource/powershell_script" |