summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorsawanoboly <sawanoboriyu@higanworks.com>2014-08-12 15:56:16 +0900
committersawanoboly <sawanoboriyu@higanworks.com>2014-08-12 15:56:16 +0900
commit5c94009b6053e7ae71d528485f3560e2bf8a7851 (patch)
tree4070625ed40df73783c3fda3149ed526fbc34447 /lib
parentbea385b45620c5c5769d8680758181770b3c148f (diff)
parentf71cb384f3bbdca9093af76830b8ae3949d54c4e (diff)
downloadchef-5c94009b6053e7ae71d528485f3560e2bf8a7851.tar.gz
Merge remote-tracking branch 'upstream/master' into prevew_archive_before_site_share
Diffstat (limited to 'lib')
-rw-r--r--lib/chef/api_client.rb2
-rw-r--r--lib/chef/chef_fs/chef_fs_data_store.rb2
-rw-r--r--lib/chef/chef_fs/command_line.rb2
-rw-r--r--lib/chef/chef_fs/file_system.rb2
-rw-r--r--lib/chef/chef_fs/file_system/acl_entry.rb2
-rw-r--r--lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb6
-rw-r--r--lib/chef/chef_fs/file_system/rest_list_dir.rb4
-rw-r--r--lib/chef/chef_fs/file_system/rest_list_entry.rb8
-rw-r--r--lib/chef/config.rb3
-rw-r--r--lib/chef/config_fetcher.rb2
-rw-r--r--lib/chef/cookbook/cookbook_version_loader.rb8
-rw-r--r--lib/chef/cookbook/metadata.rb37
-rw-r--r--lib/chef/cookbook/syntax_check.rb2
-rw-r--r--lib/chef/cookbook_version.rb5
-rw-r--r--lib/chef/data_bag.rb43
-rw-r--r--lib/chef/data_bag_item.rb12
-rw-r--r--lib/chef/encrypted_data_bag_item.rb1
-rw-r--r--lib/chef/encrypted_data_bag_item/assertions.rb57
-rw-r--r--lib/chef/encrypted_data_bag_item/decryptor.rb78
-rw-r--r--lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb37
-rw-r--r--lib/chef/encrypted_data_bag_item/encryption_failure.rb22
-rw-r--r--lib/chef/encrypted_data_bag_item/encryptor.rb87
-rw-r--r--lib/chef/environment.rb4
-rw-r--r--lib/chef/exceptions.rb18
-rw-r--r--lib/chef/handler/json_file.rb1
-rw-r--r--lib/chef/json_compat.rb30
-rw-r--r--lib/chef/knife/bootstrap.rb4
-rw-r--r--lib/chef/knife/client_delete.rb2
-rw-r--r--lib/chef/knife/cookbook_site_share.rb2
-rw-r--r--lib/chef/knife/deps.rb5
-rw-r--r--lib/chef/mixin/shell_out.rb2
-rw-r--r--lib/chef/node.rb3
-rw-r--r--lib/chef/platform/provider_mapping.rb39
-rw-r--r--lib/chef/provider/cookbook_file/content.rb2
-rw-r--r--lib/chef/provider/deploy.rb5
-rw-r--r--lib/chef/provider/deploy/revision.rb2
-rw-r--r--lib/chef/provider/env.rb2
-rw-r--r--lib/chef/provider/env/windows.rb14
-rw-r--r--lib/chef/provider/file.rb117
-rw-r--r--lib/chef/provider/git.rb2
-rw-r--r--lib/chef/provider/group/aix.rb19
-rw-r--r--lib/chef/provider/group/dscl.rb36
-rw-r--r--lib/chef/provider/log.rb4
-rw-r--r--lib/chef/provider/mount/mount.rb4
-rw-r--r--lib/chef/provider/package/paludis.rb4
-rw-r--r--lib/chef/provider/package/rpm.rb4
-rw-r--r--lib/chef/provider/package/zypper.rb2
-rw-r--r--lib/chef/provider/remote_file/cache_control_data.rb2
-rw-r--r--lib/chef/provider/user/aix.rb95
-rw-r--r--lib/chef/provider/user/useradd.rb1
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/resource.rb4
-rw-r--r--lib/chef/resource/paludis_package.rb1
-rw-r--r--lib/chef/resource/user.rb9
-rw-r--r--lib/chef/resource_collection.rb10
-rw-r--r--lib/chef/resource_reporter.rb20
-rw-r--r--lib/chef/role.rb6
-rw-r--r--lib/chef/run_list.rb9
-rw-r--r--lib/chef/user.rb2
-rw-r--r--lib/chef/util/diff.rb3
-rw-r--r--lib/chef/version_constraint.rb8
61 files changed, 692 insertions, 228 deletions
diff --git a/lib/chef/api_client.rb b/lib/chef/api_client.rb
index 7b7fd99ff7..334fb23f38 100644
--- a/lib/chef/api_client.rb
+++ b/lib/chef/api_client.rb
@@ -121,7 +121,7 @@ class Chef
#
# @return [String] the JSON string.
def to_json(*a)
- to_hash.to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def self.json_create(o)
diff --git a/lib/chef/chef_fs/chef_fs_data_store.rb b/lib/chef/chef_fs/chef_fs_data_store.rb
index b84fc1945d..b2435d8201 100644
--- a/lib/chef/chef_fs/chef_fs_data_store.rb
+++ b/lib/chef/chef_fs/chef_fs_data_store.rb
@@ -269,7 +269,7 @@ class Chef
# Create a little Chef::ChefFS memory filesystem with the data
cookbook_fs = Chef::ChefFS::FileSystem::MemoryRoot.new('uploading')
- cookbook = JSON.parse(data, :create_additions => false)
+ cookbook = Chef::JSONCompat.parse(data)
cookbook.each_pair do |key, value|
if value.is_a?(Array)
value.each do |file|
diff --git a/lib/chef/chef_fs/command_line.rb b/lib/chef/chef_fs/command_line.rb
index 967c59ecae..43e8b276e0 100644
--- a/lib/chef/chef_fs/command_line.rb
+++ b/lib/chef/chef_fs/command_line.rb
@@ -251,7 +251,7 @@ class Chef
end
def self.canonicalize_json(json_text)
- parsed_json = JSON.parse(json_text, :create_additions => false)
+ parsed_json = Chef::JSONCompat.parse(json_text)
sorted_json = sort_keys(parsed_json)
JSON.pretty_generate(sorted_json)
end
diff --git a/lib/chef/chef_fs/file_system.rb b/lib/chef/chef_fs/file_system.rb
index ffbe274864..4d15d7af33 100644
--- a/lib/chef/chef_fs/file_system.rb
+++ b/lib/chef/chef_fs/file_system.rb
@@ -369,7 +369,7 @@ class Chef
end
else
if dest_entry.dir?
- ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui
+ ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui
return
else
diff --git a/lib/chef/chef_fs/file_system/acl_entry.rb b/lib/chef/chef_fs/file_system/acl_entry.rb
index 8edc02d5c5..1bd03a6095 100644
--- a/lib/chef/chef_fs/file_system/acl_entry.rb
+++ b/lib/chef/chef_fs/file_system/acl_entry.rb
@@ -37,7 +37,7 @@ class Chef
def write(file_contents)
# ACL writes are fun.
- acls = data_handler.normalize(JSON.parse(file_contents, :create_additions => false), self)
+ acls = data_handler.normalize(Chef::JSONCompat.parse(file_contents), self)
PERMISSIONS.each do |permission|
begin
rest.put("#{api_path}/#{permission}", { permission => acls[permission] })
diff --git a/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb b/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb
index 3d3f58201e..6ccdc2cf5f 100644
--- a/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb
+++ b/lib/chef/chef_fs/file_system/chef_repository_file_system_entry.rb
@@ -41,7 +41,7 @@ class Chef
def chef_object
begin
- return data_handler.chef_object(JSON.parse(read, :create_additions => false))
+ return data_handler.chef_object(Chef::JSONCompat.parse(read))
rescue
Chef::Log.error("Could not read #{path_for_printing} into a Chef object: #{$!}")
end
@@ -60,10 +60,10 @@ class Chef
end
def minimize(file_contents, entry)
- object = JSONCompat.from_json(file_contents, :create_additions => false)
+ object = Chef::JSONCompat.from_json(file_contents)
object = data_handler.normalize(object, entry)
object = data_handler.minimize(object, entry)
- JSONCompat.to_json_pretty(object)
+ Chef::JSONCompat.to_json_pretty(object)
end
def children
diff --git a/lib/chef/chef_fs/file_system/rest_list_dir.rb b/lib/chef/chef_fs/file_system/rest_list_dir.rb
index b7ee51d284..672fa444f1 100644
--- a/lib/chef/chef_fs/file_system/rest_list_dir.rb
+++ b/lib/chef/chef_fs/file_system/rest_list_dir.rb
@@ -61,8 +61,8 @@ class Chef
def create_child(name, file_contents)
begin
- object = JSON.parse(file_contents, :create_additions => false)
- rescue JSON::ParserError => e
+ object = Chef::JSONCompat.parse(file_contents)
+ rescue Chef::Exceptions::JSON::ParseError => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Parse error reading JSON creating child '#{name}': #{e}"
end
diff --git a/lib/chef/chef_fs/file_system/rest_list_entry.rb b/lib/chef/chef_fs/file_system/rest_list_entry.rb
index 0d5557de1d..67252a6f2f 100644
--- a/lib/chef/chef_fs/file_system/rest_list_entry.rb
+++ b/lib/chef/chef_fs/file_system/rest_list_entry.rb
@@ -128,8 +128,8 @@ class Chef
value = minimize_value(value)
value_json = Chef::JSONCompat.to_json_pretty(value)
begin
- other_value = JSON.parse(other_value_json, :create_additions => false)
- rescue JSON::ParserError => e
+ other_value = Chef::JSONCompat.parse(other_value_json)
+ rescue Chef::Exceptions::JSON::ParseError => e
Chef::Log.warn("Parse error reading #{other.path_for_printing} as JSON: #{e}")
return [ nil, value_json, other_value_json ]
end
@@ -145,8 +145,8 @@ class Chef
def write(file_contents)
begin
- object = JSON.parse(file_contents, :create_additions => false)
- rescue JSON::ParserError => e
+ object = Chef::JSONCompat.parse(file_contents)
+ rescue Chef::Exceptions::JSON::ParseError => e
raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Parse error reading JSON: #{e}"
end
diff --git a/lib/chef/config.rb b/lib/chef/config.rb
index 93cd05d0ef..9d465a3cea 100644
--- a/lib/chef/config.rb
+++ b/lib/chef/config.rb
@@ -432,7 +432,8 @@ class Chef
# format. Version "2" is available which adds encrypt-then-mac protection.
# To maintain compatibility, versions other than 1 must be opt-in.
#
- # Set this to `2` if you have chef-client 11.6.0+ in your infrastructure:
+ # Set this to `2` if you have chef-client 11.6.0+ in your infrastructure.
+ # Set this to `3` if you have chef-client 11.?.0+, ruby 2 and OpenSSL >= 1.0.1 in your infrastructure. (TODO)
default :data_bag_encrypt_version, 1
# When reading data bag items, any supported version is accepted. However,
diff --git a/lib/chef/config_fetcher.rb b/lib/chef/config_fetcher.rb
index c1fd262656..1d0693eaa2 100644
--- a/lib/chef/config_fetcher.rb
+++ b/lib/chef/config_fetcher.rb
@@ -18,7 +18,7 @@ class Chef
config_data = read_config
begin
Chef::JSONCompat.from_json(config_data)
- rescue FFI_Yajl::ParseError => error
+ rescue Chef::Exceptions::JSON::ParseError => error
Chef::Application.fatal!("Could not parse the provided JSON file (#{config_location}): " + error.message, 2)
end
end
diff --git a/lib/chef/cookbook/cookbook_version_loader.rb b/lib/chef/cookbook/cookbook_version_loader.rb
index 4e92b74ae9..fac8c80993 100644
--- a/lib/chef/cookbook/cookbook_version_loader.rb
+++ b/lib/chef/cookbook/cookbook_version_loader.rb
@@ -170,7 +170,7 @@ class Chef
def apply_ruby_metadata(file)
begin
@metadata.from_file(file)
- rescue JSON::ParserError
+ rescue Chef::Exceptions::JSON::ParseError
Chef::Log.error("Error evaluating metadata.rb for #@cookbook_name in " + file)
raise
end
@@ -179,7 +179,7 @@ class Chef
def apply_json_metadata(file)
begin
@metadata.from_json(IO.read(file))
- rescue JSON::ParserError
+ rescue Chef::Exceptions::JSON::ParseError
Chef::Log.error("Couldn't parse cookbook metadata JSON for #@cookbook_name in " + file)
raise
end
@@ -189,7 +189,7 @@ class Chef
begin
data = Chef::JSONCompat.from_json(IO.read(file), :create_additions => false)
@metadata.from_hash(data['metadata'])
- rescue JSON::ParserError
+ rescue Chef::Exceptions::JSON::ParseError
Chef::Log.error("Couldn't parse cookbook metadata JSON for #@cookbook_name in " + file)
raise
end
@@ -200,7 +200,7 @@ class Chef
begin
data = Chef::JSONCompat.from_json(IO.read(uploaded_cookbook_version_file), :create_additions => false)
@frozen = data['frozen?']
- rescue JSON::ParserError
+ rescue Chef::Exceptions::JSON::ParseError
Chef::Log.error("Couldn't parse cookbook metadata JSON for #@cookbook_name in #{uploaded_cookbook_version_file}")
raise
end
diff --git a/lib/chef/cookbook/metadata.rb b/lib/chef/cookbook/metadata.rb
index 7da1ae70de..8d3f8b84aa 100644
--- a/lib/chef/cookbook/metadata.rb
+++ b/lib/chef/cookbook/metadata.rb
@@ -24,6 +24,7 @@ require 'chef/mixin/params_validate'
require 'chef/log'
require 'chef/version_class'
require 'chef/version_constraint'
+require 'chef/json_compat'
class Chef
class Cookbook
@@ -242,8 +243,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def supports(platform, *version_args)
version = new_args_format(:supports, platform, version_args)
- normalized_version = normalize_version_constraint(:supports, platform, version)
- @platforms[platform] = normalized_version
+ constraint = validate_version_constraint(:supports, platform, version)
+ @platforms[platform] = constraint.to_s
@platforms[platform]
end
@@ -259,8 +260,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def depends(cookbook, *version_args)
version = new_args_format(:depends, cookbook, version_args)
- normalized_version = normalize_version_constraint(:depends, cookbook, version)
- @dependencies[cookbook] = normalized_version
+ constraint = validate_version_constraint(:depends, cookbook, version)
+ @dependencies[cookbook] = constraint.to_s
@dependencies[cookbook]
end
@@ -276,8 +277,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def recommends(cookbook, *version_args)
version = new_args_format(:recommends, cookbook, version_args)
- normalized_version = normalize_version_constraint(:recommends, cookbook, version)
- @recommendations[cookbook] = normalized_version
+ constraint = validate_version_constraint(:recommends, cookbook, version)
+ @recommendations[cookbook] = constraint.to_s
@recommendations[cookbook]
end
@@ -293,8 +294,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def suggests(cookbook, *version_args)
version = new_args_format(:suggests, cookbook, version_args)
- normalized_version = normalize_version_constraint(:suggests, cookbook, version)
- @suggestions[cookbook] = normalized_version
+ constraint = validate_version_constraint(:suggests, cookbook, version)
+ @suggestions[cookbook] = constraint.to_s
@suggestions[cookbook]
end
@@ -310,8 +311,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def conflicts(cookbook, *version_args)
version = new_args_format(:conflicts, cookbook, version_args)
- normalized_version = normalize_version_constraint(:conflicts, cookbook, version)
- @conflicting[cookbook] = normalized_version
+ constraint = validate_version_constraint(:conflicts, cookbook, version)
+ @conflicting[cookbook] = constraint.to_s
@conflicting[cookbook]
end
@@ -331,8 +332,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def provides(cookbook, *version_args)
version = new_args_format(:provides, cookbook, version_args)
- normalized_version = normalize_version_constraint(:provides, cookbook, version)
- @providing[cookbook] = normalized_version
+ constraint = validate_version_constraint(:provides, cookbook, version)
+ @providing[cookbook] = constraint.to_s
@providing[cookbook]
end
@@ -347,8 +348,8 @@ class Chef
# versions<Array>:: Returns the list of versions for the platform
def replaces(cookbook, *version_args)
version = new_args_format(:replaces, cookbook, version_args)
- normalized_version = normalize_version_constraint(:replaces, cookbook, version)
- @replacing[cookbook] = normalized_version
+ constraint = validate_version_constraint(:replaces, cookbook, version)
+ @replacing[cookbook] = constraint.to_s
@replacing[cookbook]
end
@@ -441,7 +442,7 @@ class Chef
end
def to_json(*a)
- self.to_hash.to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def self.from_hash(o)
@@ -533,11 +534,6 @@ INVALID
raise Exceptions::InvalidVersionConstraint, msg
end
- def normalize_version_constraint(caller_name, dep_name, constraint_str)
- version_constraint = validate_version_constraint(caller_name, dep_name, constraint_str)
- "#{version_constraint.op} #{version_constraint.raw_version}"
- end
-
# Verify that the given array is an array of strings
#
# Raise an exception if the members of the array are not Strings
@@ -656,6 +652,5 @@ INVALID
end
end
-
end
end
diff --git a/lib/chef/cookbook/syntax_check.rb b/lib/chef/cookbook/syntax_check.rb
index 9fee7c2a54..092fb47eba 100644
--- a/lib/chef/cookbook/syntax_check.rb
+++ b/lib/chef/cookbook/syntax_check.rb
@@ -125,7 +125,7 @@ class Chef
end
def template_files
- remove_ignored_files Dir[File.join(cookbook_path, '**', '*.erb')]
+ remove_ignored_files Dir[File.join(cookbook_path, '**/templates/**', '*.erb')]
end
def untested_template_files
diff --git a/lib/chef/cookbook_version.rb b/lib/chef/cookbook_version.rb
index 778a6043bf..4482f778c1 100644
--- a/lib/chef/cookbook_version.rb
+++ b/lib/chef/cookbook_version.rb
@@ -405,7 +405,6 @@ class Chef
records_by_pref[best_pref]
end
-
# Given a node, segment and path (filename or directory name),
# return the priority-ordered list of preference locations to
# look.
@@ -458,9 +457,9 @@ class Chef
end
def to_json(*a)
- result = self.to_hash
+ result = to_hash
result['json_class'] = self.class.name
- result.to_json(*a)
+ Chef::JSONCompat.to_json(result, *a)
end
def self.json_create(o)
diff --git a/lib/chef/data_bag.rb b/lib/chef/data_bag.rb
index 639d71a74d..99808d0609 100644
--- a/lib/chef/data_bag.rb
+++ b/lib/chef/data_bag.rb
@@ -54,16 +54,16 @@ class Chef
def to_hash
result = {
- "name" => @name,
+ 'name' => @name,
'json_class' => self.class.name,
- "chef_type" => "data_bag",
+ 'chef_type' => 'data_bag',
}
result
end
# Serialize this object as a hash
def to_json(*a)
- to_hash.to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def chef_server_rest
@@ -83,11 +83,15 @@ class Chef
def self.list(inflate=false)
if Chef::Config[:solo]
- unless File.directory?(Chef::Config[:data_bag_path])
- raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{Chef::Config[:data_bag_path]}' is invalid"
- end
+ paths = Array(Chef::Config[:data_bag_path])
+ names = []
+ paths.each do |path|
+ unless File.directory?(path)
+ raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid"
+ end
- names = Dir.glob(File.join(Chef::Config[:data_bag_path], "*")).map{|f|File.basename(f)}.sort
+ names += Dir.glob(File.join(path, "*")).map{|f|File.basename(f)}.sort
+ end
names.inject({}) {|h, n| h[n] = n; h}
else
if inflate
@@ -105,15 +109,25 @@ class Chef
# Load a Data Bag by name via either the RESTful API or local data_bag_path if run in solo mode
def self.load(name)
if Chef::Config[:solo]
- unless File.directory?(Chef::Config[:data_bag_path])
- raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{Chef::Config[:data_bag_path]}' is invalid"
- end
+ paths = Array(Chef::Config[:data_bag_path])
+ data_bag = {}
+ paths.each do |path|
+ unless File.directory?(path)
+ raise Chef::Exceptions::InvalidDataBagPath, "Data bag path '#{path}' is invalid"
+ end
+
+ Dir.glob(File.join(path, name.to_s, "*.json")).inject({}) do |bag, f|
+ item = Chef::JSONCompat.from_json(IO.read(f))
- Dir.glob(File.join(Chef::Config[:data_bag_path], "#{name}", "*.json")).inject({}) do |bag, f|
- item = Chef::JSONCompat.from_json(IO.read(f))
- bag[item['id']] = item
- bag
+ # Check if we have multiple items with similar names (ids) and raise if their content differs
+ if data_bag.has_key?(item["id"]) && data_bag[item["id"]] != item
+ raise Chef::Exceptions::DuplicateDataBagItem, "Data bag '#{name}' has items with the same name '#{item["id"]}' but different content."
+ else
+ data_bag[item["id"]] = item
+ end
+ end
end
+ return data_bag
else
Chef::REST.new(Chef::Config[:chef_server_url]).get_rest("data/#{name}")
end
@@ -150,4 +164,3 @@ class Chef
end
end
-
diff --git a/lib/chef/data_bag_item.rb b/lib/chef/data_bag_item.rb
index 07dd15a1dc..6f0ae65478 100644
--- a/lib/chef/data_bag_item.rb
+++ b/lib/chef/data_bag_item.rb
@@ -112,13 +112,13 @@ class Chef
# Serialize this object as a hash
def to_json(*a)
result = {
- "name" => self.object_name,
+ "name" => object_name,
"json_class" => self.class.name,
- "chef_type" => "data_bag_item",
- "data_bag" => self.data_bag,
- "raw_data" => self.raw_data
+ "chef_type" => "data_bag_item",
+ "data_bag" => data_bag,
+ "raw_data" => raw_data
}
- result.to_json(*a)
+ Chef::JSONCompat.to_json(result, *a)
end
def self.from_hash(h)
@@ -210,5 +210,3 @@ class Chef
end
end
-
-
diff --git a/lib/chef/encrypted_data_bag_item.rb b/lib/chef/encrypted_data_bag_item.rb
index b0d9337212..f722b5dc38 100644
--- a/lib/chef/encrypted_data_bag_item.rb
+++ b/lib/chef/encrypted_data_bag_item.rb
@@ -48,6 +48,7 @@ require 'open-uri'
#
class Chef::EncryptedDataBagItem
ALGORITHM = 'aes-256-cbc'
+ AEAD_ALGORITHM = 'aes-256-gcm'
#
# === Synopsis
diff --git a/lib/chef/encrypted_data_bag_item/assertions.rb b/lib/chef/encrypted_data_bag_item/assertions.rb
new file mode 100644
index 0000000000..e9acebbd29
--- /dev/null
+++ b/lib/chef/encrypted_data_bag_item/assertions.rb
@@ -0,0 +1,57 @@
+#
+# Author:: Xabier de Zuazo (<xabier@onddo.com>)
+# Copyright:: Copyright (c) 2014 Onddo Labs, SL.
+# 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/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format'
+require 'chef/encrypted_data_bag_item/unsupported_cipher'
+
+class Chef::EncryptedDataBagItem
+
+ class EncryptedDataBagRequirementsFailure < StandardError
+ end
+
+ module Assertions
+
+ def assert_format_version_acceptable!(format_version)
+ unless format_version.kind_of?(Integer) and format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
+ raise UnacceptableEncryptedDataBagItemFormat,
+ "The encrypted data bag item has format version `#{format_version}', " +
+ "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
+ end
+ end
+
+ def assert_valid_cipher!(requested_cipher, algorithm)
+ # In the future, chef may support configurable ciphers. For now, only
+ # aes-256-cbc and aes-256-gcm are supported.
+ unless requested_cipher == algorithm
+ raise UnsupportedCipher,
+ "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}', '#{AEAD_ALGORITHM}']"
+ end
+ end
+
+ def assert_aead_requirements_met!(algorithm)
+ unless OpenSSL::Cipher.method_defined?(:auth_data=)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires Ruby >= 1.9"
+ end
+ unless OpenSSL::Cipher.ciphers.include?(algorithm)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support"
+ end
+ end
+
+ end
+
+end
diff --git a/lib/chef/encrypted_data_bag_item/decryptor.rb b/lib/chef/encrypted_data_bag_item/decryptor.rb
index 69b8d62e3b..97a166b932 100644
--- a/lib/chef/encrypted_data_bag_item/decryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/decryptor.rb
@@ -17,15 +17,14 @@
#
require 'yaml'
-require 'ffi_yajl'
+require 'chef/json_compat'
require 'openssl'
require 'base64'
require 'digest/sha2'
require 'chef/encrypted_data_bag_item'
require 'chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format'
-require 'chef/encrypted_data_bag_item/unacceptable_encrypted_data_bag_item_format'
require 'chef/encrypted_data_bag_item/decryption_failure'
-require 'chef/encrypted_data_bag_item/unsupported_cipher'
+require 'chef/encrypted_data_bag_item/assertions'
class Chef::EncryptedDataBagItem
@@ -37,6 +36,7 @@ class Chef::EncryptedDataBagItem
# to create an instance of the appropriate strategy for the given encrypted
# data bag value.
module Decryptor
+ extend Chef::EncryptedDataBagItem::Assertions
# Detects the encrypted data bag item format version and instantiates a
# decryptor object for that version. Call #for_decrypted_item on the
@@ -45,6 +45,8 @@ class Chef::EncryptedDataBagItem
format_version = format_version_of(encrypted_value)
assert_format_version_acceptable!(format_version)
case format_version
+ when 3
+ Version3Decryptor.new(encrypted_value, key)
when 2
Version2Decryptor.new(encrypted_value, key)
when 1
@@ -65,15 +67,8 @@ class Chef::EncryptedDataBagItem
end
end
- def self.assert_format_version_acceptable!(format_version)
- unless format_version.kind_of?(Integer) and format_version >= Chef::Config[:data_bag_decrypt_minimum_version]
- raise UnacceptableEncryptedDataBagItemFormat,
- "The encrypted data bag item has format version `#{format_version}', " +
- "but the config setting 'data_bag_decrypt_minimum_version' requires version `#{Chef::Config[:data_bag_decrypt_minimum_version]}'"
- end
- end
-
class Version0Decryptor
+ include Chef::EncryptedDataBagItem::Assertions
attr_reader :encrypted_data
attr_reader :key
@@ -83,6 +78,11 @@ class Chef::EncryptedDataBagItem
@key = key
end
+ # Returns the used decryption algorithm
+ def algorithm
+ ALGORITHM
+ end
+
def for_decrypted_item
YAML.load(decrypted_data)
end
@@ -102,7 +102,7 @@ class Chef::EncryptedDataBagItem
def openssl_decryptor
@openssl_decryptor ||= begin
- d = OpenSSL::Cipher::Cipher.new(ALGORITHM)
+ d = OpenSSL::Cipher.new(algorithm)
d.decrypt
d.pkcs5_keyivgen(key)
d
@@ -110,7 +110,7 @@ class Chef::EncryptedDataBagItem
end
end
- class Version1Decryptor
+ class Version1Decryptor < Version0Decryptor
attr_reader :encrypted_data
attr_reader :key
@@ -121,8 +121,8 @@ class Chef::EncryptedDataBagItem
end
def for_decrypted_item
- FFI_Yajl::Parser.parse(decrypted_data)["json_wrapper"]
- rescue FFI_Yajl::ParseError
+ Chef::JSONCompat.parse(decrypted_data)["json_wrapper"]
+ rescue Chef::Exceptions::JSON::ParseError
# convert to a DecryptionFailure error because the most likely scenario
# here is that the decryption step was unsuccessful but returned bad
# data rather than raising an error.
@@ -148,24 +148,16 @@ class Chef::EncryptedDataBagItem
def openssl_decryptor
@openssl_decryptor ||= begin
- assert_valid_cipher!
- d = OpenSSL::Cipher::Cipher.new(ALGORITHM)
+ assert_valid_cipher!(@encrypted_data["cipher"], algorithm)
+ d = OpenSSL::Cipher.new(algorithm)
d.decrypt
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
d.key = Digest::SHA256.digest(key)
d.iv = iv
d
end
end
- def assert_valid_cipher!
- # In the future, chef may support configurable ciphers. For now, only
- # aes-256-cbc is supported.
- requested_cipher = @encrypted_data["cipher"]
- unless requested_cipher == ALGORITHM
- raise UnsupportedCipher,
- "Cipher '#{requested_cipher}' is not supported by this version of Chef. Available ciphers: ['#{ALGORITHM}']"
- end
- end
end
class Version2Decryptor < Version1Decryptor
@@ -176,7 +168,7 @@ class Chef::EncryptedDataBagItem
end
def validate_hmac!
- digest = OpenSSL::Digest::Digest.new("sha256")
+ digest = OpenSSL::Digest.new("sha256")
raw_hmac = OpenSSL::HMAC.digest(digest, key, @encrypted_data["encrypted_data"])
if candidate_hmac_matches?(raw_hmac)
@@ -197,5 +189,37 @@ class Chef::EncryptedDataBagItem
valid == 0
end
end
+
+ class Version3Decryptor < Version1Decryptor
+
+ def initialize(encrypted_data, key)
+ super
+ assert_aead_requirements_met!(algorithm)
+ end
+
+ # Returns the used decryption algorithm
+ def algorithm
+ AEAD_ALGORITHM
+ end
+
+ def auth_tag
+ auth_tag_b64 = @encrypted_data["auth_tag"]
+ if auth_tag_b64.nil?
+ raise DecryptionFailure, "Error decrypting data bag value: invalid authentication tag. Most likely the data is corrupted"
+ end
+ Base64.decode64(auth_tag_b64)
+ end
+
+ def openssl_decryptor
+ @openssl_decryptor ||= begin
+ d = super
+ d.auth_tag = auth_tag
+ d.auth_data = ''
+ d
+ end
+ end
+
+ end
+
end
end
diff --git a/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb b/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb
new file mode 100644
index 0000000000..3567925844
--- /dev/null
+++ b/lib/chef/encrypted_data_bag_item/encrypted_data_bag_item_assertions.rb
@@ -0,0 +1,37 @@
+#
+# Author:: Xabier de Zuazo (<xabier@onddo.com>)
+# Copyright:: Copyright (c) 2014 Onddo Labs, SL.
+# 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.
+#
+
+class Chef::EncryptedDataBagItem
+
+ class EncryptedDataBagRequirementsFailure < StandardError
+ end
+
+ module Assertions
+
+ def assert_requirements_met!
+ unless OpenSSL::Cipher.method_defined?(:auth_data=)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires Ruby >= 1.9"
+ end
+ unless OpenSSL::Cipher.ciphers.include?(algorithm)
+ raise EncryptedDataBagRequirementsFailure, "The used Encrypted Data Bags version requires an OpenSSL version with \"#{algorithm}\" algorithm support"
+ end
+ end
+
+ end
+
+end
diff --git a/lib/chef/encrypted_data_bag_item/encryption_failure.rb b/lib/chef/encrypted_data_bag_item/encryption_failure.rb
new file mode 100644
index 0000000000..380e9bc1f7
--- /dev/null
+++ b/lib/chef/encrypted_data_bag_item/encryption_failure.rb
@@ -0,0 +1,22 @@
+#
+# Author:: Xabier de Zuazo (<xabier@onddo.com>)
+# Copyright:: Copyright (c) 2014 Onddo Labs, SL.
+# 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.
+#
+
+class Chef::EncryptedDataBagItem
+ class EncryptionFailure < StandardError
+ end
+end
diff --git a/lib/chef/encrypted_data_bag_item/encryptor.rb b/lib/chef/encrypted_data_bag_item/encryptor.rb
index 9686e84b34..673b52a3c3 100644
--- a/lib/chef/encrypted_data_bag_item/encryptor.rb
+++ b/lib/chef/encrypted_data_bag_item/encryptor.rb
@@ -22,6 +22,8 @@ require 'openssl'
require 'ffi_yajl'
require 'chef/encrypted_data_bag_item'
require 'chef/encrypted_data_bag_item/unsupported_encrypted_data_bag_item_format'
+require 'chef/encrypted_data_bag_item/encryption_failure'
+require 'chef/encrypted_data_bag_item/assertions'
class Chef::EncryptedDataBagItem
@@ -40,9 +42,11 @@ class Chef::EncryptedDataBagItem
Version1Encryptor.new(value, secret, iv)
when 2
Version2Encryptor.new(value, secret, iv)
+ when 3
+ Version3Encryptor.new(value, secret, iv)
else
raise UnsupportedEncryptedDataBagItemFormat,
- "Invalid encrypted data bag format version `#{format_version}'. Supported versions are '1', '2'"
+ "Invalid encrypted data bag format version `#{format_version}'. Supported versions are '1', '2', '3'"
end
end
@@ -50,6 +54,8 @@ class Chef::EncryptedDataBagItem
attr_reader :key
attr_reader :plaintext_data
+ include Chef::EncryptedDataBagItem::Assertions
+
# Create a new Encryptor for +data+, which will be encrypted with the given
# +key+.
#
@@ -65,6 +71,11 @@ class Chef::EncryptedDataBagItem
@iv = iv && Base64.decode64(iv)
end
+ # Returns the used encryption algorithm
+ def algorithm
+ ALGORITHM
+ end
+
# Returns a wrapped and encrypted version of +plaintext_data+ suitable for
# using as the value in an encrypted data bag item.
def for_encrypted_item
@@ -72,27 +83,28 @@ class Chef::EncryptedDataBagItem
"encrypted_data" => encrypted_data,
"iv" => Base64.encode64(iv),
"version" => 1,
- "cipher" => ALGORITHM
+ "cipher" => algorithm
}
end
# Generates or returns the IV.
def iv
- # Generated IV comes from OpenSSL::Cipher::Cipher#random_iv
+ # Generated IV comes from OpenSSL::Cipher#random_iv
# This gets generated when +openssl_encryptor+ gets created.
openssl_encryptor if @iv.nil?
@iv
end
- # Generates (and memoizes) an OpenSSL::Cipher::Cipher object and configures
+ # Generates (and memoizes) an OpenSSL::Cipher object and configures
# it for the specified iv and encryption key.
def openssl_encryptor
@openssl_encryptor ||= begin
- encryptor = OpenSSL::Cipher::Cipher.new(ALGORITHM)
+ encryptor = OpenSSL::Cipher.new(algorithm)
encryptor.encrypt
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
+ encryptor.key = Digest::SHA256.digest(key)
@iv ||= encryptor.random_iv
encryptor.iv = @iv
- encryptor.key = Digest::SHA256.digest(key)
encryptor
end
end
@@ -125,18 +137,77 @@ class Chef::EncryptedDataBagItem
"hmac" => hmac,
"iv" => Base64.encode64(iv),
"version" => 2,
- "cipher" => ALGORITHM
+ "cipher" => algorithm
}
end
# Generates an HMAC-SHA2-256 of the encrypted data (encrypt-then-mac)
def hmac
@hmac ||= begin
- digest = OpenSSL::Digest::Digest.new("sha256")
+ digest = OpenSSL::Digest.new("sha256")
raw_hmac = OpenSSL::HMAC.digest(digest, key, encrypted_data)
Base64.encode64(raw_hmac)
end
end
end
+
+ class Version3Encryptor < Version1Encryptor
+ include Chef::EncryptedDataBagItem::Assertions
+
+ def initialize(plaintext_data, key, iv=nil)
+ super
+ assert_aead_requirements_met!(algorithm)
+ @auth_tag = nil
+ end
+
+ # Returns a wrapped and encrypted version of +plaintext_data+ suitable for
+ # using as the value in an encrypted data bag item.
+ def for_encrypted_item
+ {
+ "encrypted_data" => encrypted_data,
+ "iv" => Base64.encode64(iv),
+ "auth_tag" => Base64.encode64(auth_tag),
+ "version" => 3,
+ "cipher" => algorithm
+ }
+ end
+
+ # Returns the used encryption algorithm
+ def algorithm
+ AEAD_ALGORITHM
+ end
+
+ # Returns a wrapped and encrypted version of +plaintext_data+ suitable for
+ # Returns the auth_tag.
+ def auth_tag
+ # Generated auth_tag comes from OpenSSL::Cipher#auth_tag
+ # This must be generated after the data is encrypted
+ if @auth_tag.nil?
+ raise EncryptionFailure, "Internal Error: GCM authentication tag read before encryption"
+ end
+ @auth_tag
+ end
+
+ # Generates (and memoizes) an OpenSSL::Cipher object and configures
+ # it for the specified iv and encryption key using AEAD
+ def openssl_encryptor
+ @openssl_encryptor ||= begin
+ encryptor = super
+ encryptor.auth_data = ''
+ encryptor
+ end
+ end
+
+ # Encrypts, Base64 encodes +serialized_data+ and gets the authentication tag
+ def encrypted_data
+ @encrypted_data ||= begin
+ enc_data_b64 = super
+ @auth_tag = openssl_encryptor.auth_tag
+ enc_data_b64
+ end
+ end
+
+ end
+
end
end
diff --git a/lib/chef/environment.rb b/lib/chef/environment.rb
index 5c719ca285..33dfb52403 100644
--- a/lib/chef/environment.rb
+++ b/lib/chef/environment.rb
@@ -129,7 +129,7 @@ class Chef
end
def to_json(*a)
- to_hash.to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def update_from!(o)
@@ -140,7 +140,6 @@ class Chef
self
end
-
def update_attributes_from_params(params)
unless params[:default_attributes].nil? || params[:default_attributes].size == 0
default_attributes(Chef::JSONCompat.from_json(params[:default_attributes]))
@@ -213,7 +212,6 @@ class Chef
end
end
-
def self.json_create(o)
environment = new
environment.name(o["name"])
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 22fafaa4dc..bd6d887884 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -115,6 +115,7 @@ class Chef
class Win32ArchitectureIncorrect < RuntimeError; end
class ObsoleteDependencySyntax < ArgumentError; end
class InvalidDataBagPath < ArgumentError; end
+ class DuplicateDataBagItem < RuntimeError; end
# A different version of a cookbook was added to a
# VersionedRecipeList than the one already there.
@@ -194,7 +195,6 @@ class Chef
end
end
-
end
# Exception class for collecting multiple failures. Used when running
# delayed notifications so that chef can process each delayed
@@ -262,7 +262,7 @@ class Chef
"non_existent_cookbooks" => non_existent_cookbooks,
"cookbooks_with_no_versions" => cookbooks_with_no_matching_versions
}
- result.to_json(*a)
+ Chef::JSONCompat.to_json(result, *a)
end
end
@@ -297,7 +297,7 @@ class Chef
"non_existent_cookbooks" => non_existent_cookbooks,
"most_constrained_cookbooks" => most_constrained_cookbooks
}
- result.to_json(*a)
+ Chef::JSONCompat.to_json(result, *a)
end
end
@@ -331,6 +331,18 @@ class Chef
end
end
+ class ChecksumMismatch < RuntimeError
+ def initialize(res_cksum, cont_cksum)
+ super "Checksum on resource (#{res_cksum}) does not match checksum on content (#{cont_cksum})"
+ end
+ end
+
class BadProxyURI < RuntimeError; end
+
+ # Raised by Chef::JSONCompat
+ class JSON
+ class EncodeError < RuntimeError; end
+ class ParseError < RuntimeError; end
+ end
end
end
diff --git a/lib/chef/handler/json_file.rb b/lib/chef/handler/json_file.rb
index 3473db1838..405c91795e 100644
--- a/lib/chef/handler/json_file.rb
+++ b/lib/chef/handler/json_file.rb
@@ -58,7 +58,6 @@ class Chef
end
end
-
end
end
end
diff --git a/lib/chef/json_compat.rb b/lib/chef/json_compat.rb
index 2dbb607d9b..ddccfe5fcb 100644
--- a/lib/chef/json_compat.rb
+++ b/lib/chef/json_compat.rb
@@ -18,7 +18,9 @@
# Wrapper class for interacting with JSON.
require 'ffi_yajl'
+require 'json'
require 'ffi_yajl/json_gem' # XXX: parts of chef require JSON gem's Hash#to_json monkeypatch
+require 'chef/exceptions'
class Chef
class JSONCompat
@@ -40,15 +42,24 @@ class Chef
class <<self
+ # API to use to avoid create_addtions
+ def parse(source, opts = {})
+ begin
+ FFI_Yajl::Parser.parse(source, opts)
+ rescue FFI_Yajl::ParseError => e
+ raise Chef::Exceptions::JSON::ParseError, e.message
+ end
+ end
+
# Just call the JSON gem's parse method with a modified :max_nesting field
def from_json(source, opts = {})
- obj = ::FFI_Yajl::Parser.parse(source)
+ obj = parse(source, opts)
# JSON gem requires top level object to be a Hash or Array (otherwise
# you get the "must contain two octets" error). Yajl doesn't impose the
# same limitation. For compatibility, we re-impose this condition.
unless obj.kind_of?(Hash) or obj.kind_of?(Array)
- raise JSON::ParserError, "Top level JSON object must be a Hash or Array. (actual: #{obj.class})"
+ raise Chef::Exceptions::JSON::ParseError, "Top level JSON object must be a Hash or Array. (actual: #{obj.class})"
end
# The old default in the json gem (which we are mimicing because we
@@ -88,14 +99,21 @@ class Chef
end
def to_json(obj, opts = nil)
- obj.to_json(opts)
+ begin
+ FFI_Yajl::Encoder.encode(obj, opts)
+ rescue FFI_Yajl::EndodeError => e
+ raise Chef::Exceptions::JSON::EncodeError, e.message
+ end
end
def to_json_pretty(obj, opts = nil)
- ::JSON.pretty_generate(obj, opts)
+ opts ||= {}
+ options_map = {}
+ options_map[:pretty] = true
+ options_map[:indent] = opts[:indent] if opts.has_key?(:indent)
+ to_json(obj, options_map).chomp
end
-
# Map +json_class+ to a Class object. We use a +case+ instead of a Hash
# assigned to a constant because otherwise this file could not be loaded
# until all the constants were defined, which means you'd have to load
@@ -130,7 +148,7 @@ class Chef
when /^Chef::Resource/
Chef::Resource.find_subclass_by_name(json_class)
else
- raise JSON::ParserError, "Unsupported `json_class` type '#{json_class}'"
+ raise Chef::Exceptions::JSON::ParseError, "Unsupported `json_class` type '#{json_class}'"
end
end
diff --git a/lib/chef/knife/bootstrap.rb b/lib/chef/knife/bootstrap.rb
index 46cacbd3e0..d3d45bad4b 100644
--- a/lib/chef/knife/bootstrap.rb
+++ b/lib/chef/knife/bootstrap.rb
@@ -126,7 +126,7 @@ class Chef
:short => "-j JSON_ATTRIBS",
:long => "--json-attributes",
:description => "A JSON string to be added to the first run of chef-client",
- :proc => lambda { |o| JSON.parse(o) },
+ :proc => lambda { |o| Chef::JSONCompat.parse(o) },
:default => {}
option :host_key_verify,
@@ -141,7 +141,7 @@ class Chef
:proc => Proc.new { |h|
Chef::Config[:knife][:hints] ||= Hash.new
name, path = h.split("=")
- Chef::Config[:knife][:hints][name] = path ? JSON.parse(::File.read(path)) : Hash.new }
+ Chef::Config[:knife][:hints][name] = path ? Chef::JSONCompat.parse(::File.read(path)) : Hash.new }
option :secret,
:short => "-s SECRET",
diff --git a/lib/chef/knife/client_delete.rb b/lib/chef/knife/client_delete.rb
index 1902145c8d..d7d302ee1d 100644
--- a/lib/chef/knife/client_delete.rb
+++ b/lib/chef/knife/client_delete.rb
@@ -47,7 +47,7 @@ class Chef
object = Chef::ApiClient.load(@client_name)
if object.validator
unless config[:delete_validators]
- ui.fatal("You must specify --force to delete the validator client #{@client_name}")
+ ui.fatal("You must specify --delete-validators to delete the validator client #{@client_name}")
exit 2
end
end
diff --git a/lib/chef/knife/cookbook_site_share.rb b/lib/chef/knife/cookbook_site_share.rb
index 0a8461937d..71bbb00abc 100644
--- a/lib/chef/knife/cookbook_site_share.rb
+++ b/lib/chef/knife/cookbook_site_share.rb
@@ -101,7 +101,7 @@ class Chef
def do_upload(cookbook_filename, cookbook_category, user_id, user_secret_filename)
uri = "http://cookbooks.opscode.com/api/v1/cookbooks"
- category_string = { 'category'=>cookbook_category }.to_json
+ category_string = Chef::JSONCompat.to_json({ 'category'=>cookbook_category })
http_resp = Chef::CookbookSiteStreamingUploader.post(uri, user_id, user_secret_filename, {
:tarball => File.open(cookbook_filename),
diff --git a/lib/chef/knife/deps.rb b/lib/chef/knife/deps.rb
index b2a39a0725..4b23468962 100644
--- a/lib/chef/knife/deps.rb
+++ b/lib/chef/knife/deps.rb
@@ -77,7 +77,7 @@ class Chef
return entry.chef_object.metadata.dependencies.keys.map { |cookbook| "/cookbooks/#{cookbook}" }
elsif entry.parent && entry.parent.path == '/nodes'
- node = JSON.parse(entry.read, :create_additions => false)
+ node = Chef::JSONCompat.parse(entry.read)
result = []
if node['chef_environment'] && node['chef_environment'] != '_default'
result << "/environments/#{node['chef_environment']}.json"
@@ -88,7 +88,7 @@ class Chef
result
elsif entry.parent && entry.parent.path == '/roles'
- role = JSON.parse(entry.read, :create_additions => false)
+ role = Chef::JSONCompat.parse(entry.read)
result = []
if role['run_list']
dependencies_from_runlist(role['run_list']).each do |dependency|
@@ -138,4 +138,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/mixin/shell_out.rb b/lib/chef/mixin/shell_out.rb
index 97aa7041fd..3f80290f90 100644
--- a/lib/chef/mixin/shell_out.rb
+++ b/lib/chef/mixin/shell_out.rb
@@ -33,7 +33,7 @@ class Chef
def shell_out(*command_args)
cmd = Mixlib::ShellOut.new(*run_command_compatible_options(command_args))
- cmd.live_stream = io_for_live_stream
+ cmd.live_stream ||= io_for_live_stream
cmd.run_command
cmd
end
diff --git a/lib/chef/node.rb b/lib/chef/node.rb
index 17ec1d0f0a..327da67b1c 100644
--- a/lib/chef/node.rb
+++ b/lib/chef/node.rb
@@ -181,7 +181,6 @@ class Chef
attributes.override
end
-
def override_attrs
attributes.override
end
@@ -416,7 +415,7 @@ class Chef
# Serialize this object as a hash
def to_json(*a)
- for_json.to_json(*a)
+ Chef::JSONCompat.to_json(for_json, *a)
end
def for_json
diff --git a/lib/chef/platform/provider_mapping.rb b/lib/chef/platform/provider_mapping.rb
index ebcc8c2fe3..d61298e182 100644
--- a/lib/chef/platform/provider_mapping.rb
+++ b/lib/chef/platform/provider_mapping.rb
@@ -142,11 +142,14 @@ class Chef
},
:centos => {
:default => {
- :service => Chef::Provider::Service::Redhat,
+ :service => Chef::Provider::Service::Systemd,
:cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
:mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
+ },
+ "< 7" => {
+ :service => Chef::Provider::Service::Redhat
}
},
:amazon => {
@@ -159,19 +162,25 @@ class Chef
},
:scientific => {
:default => {
- :service => Chef::Provider::Service::Redhat,
+ :service => Chef::Provider::Service::Systemd,
:cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
:mdadm => Chef::Provider::Mdadm
+ },
+ "< 7" => {
+ :service => Chef::Provider::Service::Redhat
}
},
:fedora => {
:default => {
- :service => Chef::Provider::Service::Redhat,
+ :service => Chef::Provider::Service::Systemd,
:cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
:mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
+ },
+ "< 15" => {
+ :service => Chef::Provider::Service::Redhat
}
},
:opensuse => {
@@ -196,19 +205,25 @@ class Chef
},
:oracle => {
:default => {
- :service => Chef::Provider::Service::Redhat,
+ :service => Chef::Provider::Service::Systemd,
:cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
:mdadm => Chef::Provider::Mdadm
+ },
+ "< 7" => {
+ :service => Chef::Provider::Service::Redhat
}
},
:redhat => {
:default => {
- :service => Chef::Provider::Service::Redhat,
+ :service => Chef::Provider::Service::Systemd,
:cron => Chef::Provider::Cron,
:package => Chef::Provider::Package::Yum,
:mdadm => Chef::Provider::Mdadm,
:ifconfig => Chef::Provider::Ifconfig::Redhat
+ },
+ "< 7" => {
+ :service => Chef::Provider::Service::Systemd
}
},
:ibm_powerkvm => {
@@ -229,6 +244,15 @@ class Chef
:ifconfig => Chef::Provider::Ifconfig::Redhat
}
},
+ :parallels => {
+ :default => {
+ :service => Chef::Provider::Service::Redhat,
+ :cron => Chef::Provider::Cron,
+ :package => Chef::Provider::Package::Yum,
+ :mdadm => Chef::Provider::Mdadm,
+ :ifconfig => Chef::Provider::Ifconfig::Redhat
+ }
+ },
:gentoo => {
:default => {
:package => Chef::Provider::Package::Portage,
@@ -365,7 +389,8 @@ class Chef
:mount => Chef::Provider::Mount::Aix,
:ifconfig => Chef::Provider::Ifconfig::Aix,
:cron => Chef::Provider::Cron::Aix,
- :package => Chef::Provider::Package::Aix
+ :package => Chef::Provider::Package::Aix,
+ :user => Chef::Provider::User::Aix
}
},
:exherbo => {
@@ -523,6 +548,8 @@ class Chef
if platforms.has_key?(args[:platform])
if platforms[args[:platform]].has_key?(:default)
platforms[args[:platform]][:default][args[:resource].to_sym] = args[:provider]
+ elsif args[:platform] == :default
+ platforms[:default][args[:resource].to_sym] = args[:provider]
else
platforms[args[:platform]] = { :default => { args[:resource].to_sym => args[:provider] } }
end
diff --git a/lib/chef/provider/cookbook_file/content.rb b/lib/chef/provider/cookbook_file/content.rb
index cb777dd916..9f49ba885c 100644
--- a/lib/chef/provider/cookbook_file/content.rb
+++ b/lib/chef/provider/cookbook_file/content.rb
@@ -28,7 +28,7 @@ class Chef
def file_for_provider
cookbook = run_context.cookbook_collection[resource_cookbook]
- file_cache_location = cookbook.preferred_filename_on_disk_location(run_context.node, :files, @new_resource.source, @new_resource.path)
+ file_cache_location = cookbook.preferred_filename_on_disk_location(run_context.node, :files, @new_resource.source)
if file_cache_location.nil?
nil
else
diff --git a/lib/chef/provider/deploy.rb b/lib/chef/provider/deploy.rb
index 63ee8685df..426e69644e 100644
--- a/lib/chef/provider/deploy.rb
+++ b/lib/chef/provider/deploy.rb
@@ -21,7 +21,7 @@ require "chef/mixin/from_file"
require "chef/monkey_patches/fileutils"
require "chef/provider/git"
require "chef/provider/subversion"
-require 'chef/dsl/recipe'
+require "chef/dsl/recipe"
class Chef
class Provider
@@ -31,7 +31,7 @@ class Chef
include Chef::Mixin::FromFile
include Chef::Mixin::Command
- attr_reader :scm_provider, :release_path, :previous_release_path
+ attr_reader :scm_provider, :release_path, :shared_path, :previous_release_path
def initialize(new_resource, run_context)
super(new_resource, run_context)
@@ -53,6 +53,7 @@ class Chef
def load_current_resource
@scm_provider.load_current_resource
@release_path = @new_resource.deploy_to + "/releases/#{release_slug}"
+ @shared_path = @new_resource.shared_path
end
def sudo(command,&block)
diff --git a/lib/chef/provider/deploy/revision.rb b/lib/chef/provider/deploy/revision.rb
index f1eb171cd7..89710088d1 100644
--- a/lib/chef/provider/deploy/revision.rb
+++ b/lib/chef/provider/deploy/revision.rb
@@ -97,7 +97,7 @@ class Chef
end
def save_cache(cache)
- Chef::FileCache.store("revision-deploys/#{new_resource.name}", cache.to_json)
+ Chef::FileCache.store("revision-deploys/#{new_resource.name}", Chef::JSONCompat.to_json(cache))
cache
end
diff --git a/lib/chef/provider/env.rb b/lib/chef/provider/env.rb
index e857d74d68..e91b8bbb97 100644
--- a/lib/chef/provider/env.rb
+++ b/lib/chef/provider/env.rb
@@ -143,7 +143,7 @@ class Chef
def modify_env
if @new_resource.delim
#e.g. add to PATH
- @new_resource.value << @new_resource.delim << @current_resource.value
+ @new_resource.value(@new_resource.value + @new_resource.delim + @current_resource.value)
end
create_env
end
diff --git a/lib/chef/provider/env/windows.rb b/lib/chef/provider/env/windows.rb
index 96b5d3871f..f73cb42f7e 100644
--- a/lib/chef/provider/env/windows.rb
+++ b/lib/chef/provider/env/windows.rb
@@ -51,17 +51,13 @@ class Chef
return obj ? obj.variablevalue : nil
end
- def find_env(environment_variables, key_name)
- environment_variables.find do | environment_variable |
- environment_variable['name'].downcase == key_name
- end
- end
-
def env_obj(key_name)
wmi = WmiLite::Wmi.new
- environment_variables = wmi.instances_of('Win32_Environment')
- existing_variable = find_env(environment_variables, key_name)
- existing_variable.nil? ? nil : existing_variable.wmi_ole_object
+ # Note that by design this query is case insensitive with regard to key_name
+ environment_variables = wmi.query("select * from Win32_Environment where name = '#{key_name}'")
+ if environment_variables && environment_variables.length > 0
+ environment_variables[0].wmi_ole_object
+ end
end
#see: http://msdn.microsoft.com/en-us/library/ms682653%28VS.85%29.aspx
diff --git a/lib/chef/provider/file.rb b/lib/chef/provider/file.rb
index 031d0fb005..256248f240 100644
--- a/lib/chef/provider/file.rb
+++ b/lib/chef/provider/file.rb
@@ -56,6 +56,10 @@ class Chef
attr_reader :deployment_strategy
+ attr_accessor :needs_creating
+ attr_accessor :needs_unlinking
+ attr_accessor :managing_symlink
+
def initialize(new_resource, run_context)
@content_class ||= Chef::Provider::File::Content
if new_resource.respond_to?(:atomic_update)
@@ -69,22 +73,47 @@ class Chef
end
def load_current_resource
+ # true if there is a symlink and we need to manage what it points at
+ @managing_symlink = file_class.symlink?(new_resource.path) && ( new_resource.manage_symlink_source || new_resource.manage_symlink_source.nil? )
+
+ # true if there is a non-file thing in the way that we need to unlink first
+ @needs_unlinking =
+ if ::File.exist?(new_resource.path)
+ if managing_symlink?
+ !symlink_to_real_file?(new_resource.path)
+ else
+ !real_file?(new_resource.path)
+ end
+ else
+ false
+ end
+
+ # true if we are going to be creating a new file
+ @needs_creating = !::File.exist?(new_resource.path) || needs_unlinking?
+
# Let children resources override constructing the @current_resource
- @current_resource ||= Chef::Resource::File.new(@new_resource.name)
- @current_resource.path(@new_resource.path)
- if ::File.exists?(@current_resource.path) && ::File.file?(::File.realpath(@current_resource.path))
+ @current_resource ||= Chef::Resource::File.new(new_resource.name)
+ current_resource.path(new_resource.path)
+
+ if !needs_creating?
+ # we are updating an existing file
if managing_content?
- Chef::Log.debug("#{@new_resource} checksumming file at #{@new_resource.path}.")
- @current_resource.checksum(checksum(@current_resource.path))
+ Chef::Log.debug("#{new_resource} checksumming file at #{new_resource.path}.")
+ current_resource.checksum(checksum(current_resource.path))
+ else
+ # if the file does not exist or is not a file, then the checksum is invalid/pointless
+ current_resource.checksum(nil)
end
- load_resource_attributes_from_file(@current_resource)
+ load_resource_attributes_from_file(current_resource)
end
- @current_resource
+
+ current_resource
end
def define_resource_requirements
# deep inside FAC we have to assert requirements, so call FACs hook to set that up
access_controls.define_resource_requirements
+
# Make sure the parent directory exists, otherwise fail. For why-run assume it would have been created.
requirements.assert(:create, :create_if_missing, :touch) do |a|
parent_directory = ::File.dirname(@new_resource.path)
@@ -94,7 +123,7 @@ class Chef
end
# Make sure the file is deletable if it exists, otherwise fail.
- if ::File.exists?(@new_resource.path)
+ if ::File.exist?(@new_resource.path)
requirements.assert(:delete) do |a|
a.assertion { ::File.writable?(@new_resource.path) }
a.failure_message(Chef::Exceptions::InsufficientPermissions,"File #{@new_resource.path} exists but is not writable so it cannot be deleted")
@@ -114,6 +143,8 @@ class Chef
end
def action_create
+ do_generate_content
+ do_validate_content
do_unlink
do_create_file
do_contents_changes
@@ -123,10 +154,10 @@ class Chef
end
def action_create_if_missing
- if ::File.exists?(@new_resource.path)
- Chef::Log.debug("#{@new_resource} exists at #{@new_resource.path} taking no action.")
- else
+ unless ::File.exist?(@new_resource.path)
action_create
+ else
+ Chef::Log.debug("#{@new_resource} exists at #{@new_resource.path} taking no action.")
end
end
@@ -275,6 +306,15 @@ class Chef
!file_class.symlink?(path) && ::File.file?(path)
end
+ # like real_file? that follows (sane) symlinks
+ def symlink_to_real_file?(path)
+ begin
+ real_file?(::File.realpath(path))
+ rescue Errno::ELOOP, Errno::ENOENT
+ false
+ end
+ end
+
# Similar to File.exist?, but also returns true in the case that the
# named file is a broken symlink.
def l_exist?(path)
@@ -290,41 +330,42 @@ class Chef
end
end
+ def do_generate_content
+ # referencing the tempfile magically causes content to be generated
+ tempfile
+ end
+
+ def tempfile_checksum
+ @tempfile_checksum ||= checksum(tempfile.path)
+ end
+
+ def do_validate_content
+ if new_resource.checksum && tempfile && ( new_resource.checksum != tempfile_checksum )
+ raise Chef::Exceptions::ChecksumMismatch.new(short_cksum(new_resource.checksum), short_cksum(tempfile_checksum))
+ end
+ end
+
def do_unlink
- @file_unlinked = false
if @new_resource.force_unlink
- if l_exist?(@new_resource.path) && !real_file?(@new_resource.path)
+ if needs_unlinking?
# unlink things that aren't normal files
description = "unlink #{file_type_string(@new_resource.path)} at #{@new_resource.path}"
converge_by(description) do
unlink(@new_resource.path)
end
- @current_resource.checksum = nil
- @file_unlinked = true
end
end
end
- def file_unlinked?
- @file_unlinked == true
- end
-
def do_create_file
- @file_created = false
- if !::File.exists?(@new_resource.path) || file_unlinked?
+ if needs_creating?
converge_by("create new file #{@new_resource.path}") do
deployment_strategy.create(@new_resource.path)
Chef::Log.info("#{@new_resource} created file #{@new_resource.path}")
end
- @file_created = true
end
end
- # do_contents_changes needs to know if do_create_file created a file or not
- def file_created?
- @file_created == true
- end
-
def do_backup(file = nil)
Chef::Util::Backup.new(@new_resource, file).backup!
end
@@ -334,7 +375,7 @@ class Chef
end
def update_file_contents
- do_backup unless file_created?
+ do_backup unless needs_creating?
deployment_strategy.deploy(tempfile.path, ::File.realpath(@new_resource.path))
Chef::Log.info("#{@new_resource} updated file contents #{@new_resource.path}")
if managing_content?
@@ -353,7 +394,7 @@ class Chef
# the file? on the next line suppresses the case in why-run when we have a not-file here that would have otherwise been removed
if ::File.file?(@new_resource.path) && contents_changed?
description = [ "update content in file #{@new_resource.path} from \
-#{short_cksum(@current_resource.checksum)} to #{short_cksum(checksum(tempfile.path))}" ]
+#{short_cksum(@current_resource.checksum)} to #{short_cksum(tempfile_checksum)}" ]
# Hide the diff output if the resource is marked as a sensitive resource
if @new_resource.sensitive
@@ -361,7 +402,7 @@ class Chef
description << "suppressed sensitive resource"
else
diff.diff(@current_resource.path, tempfile.path)
- @new_resource.diff( diff.for_reporting ) unless file_created?
+ @new_resource.diff( diff.for_reporting ) unless needs_creating?
description << diff.for_output
end
@@ -401,11 +442,11 @@ class Chef
def contents_changed?
Chef::Log.debug "calculating checksum of #{tempfile.path} to compare with #{@current_resource.checksum}"
- checksum(tempfile.path) != @current_resource.checksum
+ tempfile_checksum != @current_resource.checksum
end
def tempfile
- content.tempfile
+ @tempfile ||= content.tempfile
end
def short_cksum(checksum)
@@ -414,7 +455,6 @@ class Chef
end
def load_resource_attributes_from_file(resource)
-
if Chef::Platform.windows?
# This is a work around for CHEF-3554.
# OC-6534: is tracking the real fix for this workaround.
@@ -426,6 +466,17 @@ class Chef
acl_scanner.set_all!
end
+ def managing_symlink?
+ !!@managing_symlink
+ end
+
+ def needs_creating?
+ !!@needs_creating
+ end
+
+ def needs_unlinking?
+ !!@needs_unlinking
+ end
end
end
end
diff --git a/lib/chef/provider/git.rb b/lib/chef/provider/git.rb
index 263014d229..525249a726 100644
--- a/lib/chef/provider/git.rb
+++ b/lib/chef/provider/git.rb
@@ -150,7 +150,7 @@ class Chef
converge_by("checkout ref #{sha_ref} branch #{@new_resource.revision}") do
# checkout into a local branch rather than a detached HEAD
- shell_out!("git checkout -b #{@new_resource.checkout_branch} #{sha_ref}", run_options(:cwd => @new_resource.destination))
+ shell_out!("git checkout -B #{@new_resource.checkout_branch} #{sha_ref}", run_options(:cwd => @new_resource.destination))
Chef::Log.info "#{@new_resource} checked out branch: #{@new_resource.revision} onto: #{@new_resource.checkout_branch} reference: #{sha_ref}"
end
end
diff --git a/lib/chef/provider/group/aix.rb b/lib/chef/provider/group/aix.rb
index 9dedef351a..6ac9d03357 100644
--- a/lib/chef/provider/group/aix.rb
+++ b/lib/chef/provider/group/aix.rb
@@ -16,16 +16,18 @@
# limitations under the License.
#
-require 'chef/provider/group/usermod'
+require 'chef/provider/group/groupadd'
+require 'chef/mixin/shell_out'
class Chef
class Provider
class Group
- class Aix < Chef::Provider::Group::Usermod
+ class Aix < Chef::Provider::Group::Groupadd
def required_binaries
[ "/usr/bin/mkgroup",
"/usr/bin/chgroup",
+ "/usr/bin/chgrpmem",
"/usr/sbin/rmgroup" ]
end
@@ -51,6 +53,19 @@ class Chef
run_command(:command => "rmgroup #{@new_resource.group_name}")
end
+ def add_member(member)
+ shell_out!("chgrpmem -m + #{member} #{@new_resource.group_name}")
+ end
+
+ def set_members(members)
+ return if members.empty?
+ shell_out!("chgrpmem -m = #{members.join(',')} #{@new_resource.group_name}")
+ end
+
+ def remove_member(member)
+ shell_out!("chgrpmem -m - #{member} #{@new_resource.group_name}")
+ end
+
def set_options
opts = ""
{ :gid => "id" }.sort { |a,b| a[0] <=> b[0] }.each do |field, option|
diff --git a/lib/chef/provider/group/dscl.rb b/lib/chef/provider/group/dscl.rb
index c204c09321..04ca9bc929 100644
--- a/lib/chef/provider/group/dscl.rb
+++ b/lib/chef/provider/group/dscl.rb
@@ -39,11 +39,33 @@ class Chef
return result[2]
end
- # This is handled in providers/group.rb by Etc.getgrnam()
- # def group_exists?(group)
- # groups = safe_dscl("list /Groups")
- # !! ( groups =~ Regexp.new("\n#{group}\n") )
- # end
+ def load_current_resource
+ @current_resource = Chef::Resource::Group.new(@new_resource.name)
+ @current_resource.group_name(@new_resource.name)
+ group_info = nil
+ begin
+ group_info = safe_dscl("read /Groups/#{@new_resource.name}")
+ rescue Chef::Exceptions::Group
+ @group_exists = false
+ Chef::Log.debug("#{@new_resource} group does not exist")
+ end
+
+ if group_info
+ group_info.each_line do |line|
+ key, val = line.split(': ')
+ val.strip! if val
+ case key.downcase
+ when 'primarygroupid'
+ @new_resource.gid(val) unless @new_resource.gid
+ @current_resource.gid(val)
+ when 'groupmembership'
+ @current_resource.members(val.split(' '))
+ end
+ end
+ end
+
+ @current_resource
+ end
# get a free GID greater than 200
def get_free_gid(search_limit=1000)
@@ -115,10 +137,6 @@ class Chef
end
end
- def load_current_resource
- super
- end
-
def create_group
dscl_create_group
set_gid
diff --git a/lib/chef/provider/log.rb b/lib/chef/provider/log.rb
index 0aee349705..9379ceeefa 100644
--- a/lib/chef/provider/log.rb
+++ b/lib/chef/provider/log.rb
@@ -25,6 +25,10 @@ class Chef
# Chef log provider, allows logging to chef's logs from recipes
class ChefLog < Chef::Provider
+ def whyrun_supported?
+ true
+ end
+
# No concept of a 'current' resource for logs, this is a no-op
#
# === Return
diff --git a/lib/chef/provider/mount/mount.rb b/lib/chef/provider/mount/mount.rb
index bb1b796290..207b0cde6c 100644
--- a/lib/chef/provider/mount/mount.rb
+++ b/lib/chef/provider/mount/mount.rb
@@ -85,7 +85,7 @@ class Chef
shell_out!("mount").stdout.each_line do |line|
case line
- when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(real_mount_point)}/
+ when /^#{device_mount_regex}\s+on\s+#{Regexp.escape(real_mount_point)}\s/
mounted = true
Chef::Log.debug("Special device #{device_logstring} mounted as #{real_mount_point}")
when /^([\/\w])+\son\s#{Regexp.escape(real_mount_point)}\s+/
@@ -167,7 +167,7 @@ class Chef
found = false
::File.readlines("/etc/fstab").reverse_each do |line|
- if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}/
+ if !found && line =~ /^#{device_fstab_regex}\s+#{Regexp.escape(@new_resource.mount_point)}\s/
found = true
Chef::Log.debug("#{@new_resource} is removed from fstab")
next
diff --git a/lib/chef/provider/package/paludis.rb b/lib/chef/provider/package/paludis.rb
index f40962d74d..7c5245fc97 100644
--- a/lib/chef/provider/package/paludis.rb
+++ b/lib/chef/provider/package/paludis.rb
@@ -34,7 +34,7 @@ class Chef
installed = false
re = Regexp.new('(.*)[[:blank:]](.*)[[:blank:]](.*)$')
- shell_out!("cave -L warning print-ids -m \"*/#{@new_resource.package_name.split('/').last}\" -f \"%c/%p %v %r\n\"").stdout.each_line do |line|
+ shell_out!("cave -L warning print-ids -M none -m \"*/#{@new_resource.package_name.split('/').last}\" -f \"%c/%p %v %r\n\"").stdout.each_line do |line|
res = re.match(line)
unless res.nil?
case res[3]
@@ -59,7 +59,7 @@ class Chef
else
pkg = "#{@new_resource.package_name}"
end
- shell_out!("cave -L warning resolve -x#{expand_options(@new_resource.options)} \"#{pkg}\"")
+ shell_out!("cave -L warning resolve -x#{expand_options(@new_resource.options)} \"#{pkg}\"",:timeout => @new_resource.timeout)
end
def upgrade_package(name, version)
diff --git a/lib/chef/provider/package/rpm.rb b/lib/chef/provider/package/rpm.rb
index 9cfd6bb010..bbb561bd15 100644
--- a/lib/chef/provider/package/rpm.rb
+++ b/lib/chef/provider/package/rpm.rb
@@ -60,7 +60,7 @@ class Chef
status = popen4("rpm -qp --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@new_resource.source}") do |pid, stdin, stdout, stderr|
stdout.each do |line|
case line
- when /([\w\d+_.-]+)\s([\w\d_.-]+)/
+ when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
@current_resource.package_name($1)
@new_resource.version($2)
@candidate_version = $2
@@ -78,7 +78,7 @@ class Chef
@rpm_status = popen4("rpm -q --queryformat '%{NAME} %{VERSION}-%{RELEASE}\n' #{@current_resource.package_name}") do |pid, stdin, stdout, stderr|
stdout.each do |line|
case line
- when /([\w\d+_.-]+)\s([\w\d_.-]+)/
+ when /^([\w\d+_.-]+)\s([\w\d_.-]+)$/
Chef::Log.debug("#{@new_resource} current version is #{$2}")
@current_resource.version($2)
end
diff --git a/lib/chef/provider/package/zypper.rb b/lib/chef/provider/package/zypper.rb
index 2639f18deb..b00bef0f92 100644
--- a/lib/chef/provider/package/zypper.rb
+++ b/lib/chef/provider/package/zypper.rb
@@ -103,7 +103,7 @@ class Chef
private
def zypper_package(command, pkgname, version)
- version = "=#{version}" unless version.empty?
+ version = "=#{version}" unless version.nil? || version.empty?
if zypper_version < 1.0
shell_out!("zypper#{gpg_checks} #{command} -y #{pkgname}")
else
diff --git a/lib/chef/provider/remote_file/cache_control_data.rb b/lib/chef/provider/remote_file/cache_control_data.rb
index 8331f3d31a..75b2a5535a 100644
--- a/lib/chef/provider/remote_file/cache_control_data.rb
+++ b/lib/chef/provider/remote_file/cache_control_data.rb
@@ -140,7 +140,7 @@ class Chef
def load_data
Chef::JSONCompat.from_json(load_json_data)
- rescue Chef::Exceptions::FileNotFound, FFI_Yajl::ParseError, JSON::ParserError
+ rescue Chef::Exceptions::FileNotFound, Chef::Exceptions::JSON::ParseError
false
end
diff --git a/lib/chef/provider/user/aix.rb b/lib/chef/provider/user/aix.rb
new file mode 100644
index 0000000000..af08ab4364
--- /dev/null
+++ b/lib/chef/provider/user/aix.rb
@@ -0,0 +1,95 @@
+#
+# Copyright:: Copyright (c) 2012 Opscode, 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.
+
+class Chef
+ class Provider
+ class User
+ class Aix < Chef::Provider::User::Useradd
+
+ UNIVERSAL_OPTIONS = [[:comment, "-c"], [:gid, "-g"], [:shell, "-s"], [:uid, "-u"]]
+
+ def create_user
+ super
+ add_password
+ end
+
+ def manage_user
+ add_password
+ manage_home
+ super
+ end
+
+ # Aix does not support -r like other unix, sytem account is created by adding to 'system' group
+ def useradd_options
+ opts = []
+ opts << "-g" << "system" if new_resource.system
+ opts
+ end
+
+ def check_lock
+ lock_info = shell_out!("lsuser -a account_locked #{new_resource.username}")
+ if whyrun_mode? && passwd_s.stdout.empty? && lock_info.stderr.match(/does not exist/)
+ # if we're in whyrun mode and the user is not yet created we assume it would be
+ return false
+ end
+ raise Chef::Exceptions::User, "Cannot determine if #{@new_resource} is locked!" if lock_info.stdout.empty?
+
+ status = /\S+\s+account_locked=(\S+)/.match(lock_info.stdout)
+ if status && status[1] == "true"
+ @locked = true
+ else
+ @locked = false
+ end
+
+ @locked
+ end
+
+ def lock_user
+ shell_out!("chuser account_locked=true #{new_resource.username}")
+ end
+
+ def unlock_user
+ shell_out!("chuser account_locked=false #{new_resource.username}")
+ end
+
+ private
+ def add_password
+ if @current_resource.password != @new_resource.password && @new_resource.password
+ Chef::Log.debug("#{@new_resource.username} setting password to #{@new_resource.password}")
+ command = "echo '#{@new_resource.username}:#{@new_resource.password}' | chpasswd -e"
+ shell_out!(command)
+ end
+ end
+
+ # Aix specific handling to update users home directory.
+ def manage_home
+ # -m option does not work on aix, so move dir.
+ if updating_home? and managing_home_dir?
+ universal_options.delete("-m")
+ if ::File.directory?(@current_resource.home)
+ Chef::Log.debug("Changing users home directory from #{@current_resource.home} to #{new_resource.home}")
+ shell_out!("mv #{@current_resource.home} #{new_resource.home}")
+ else
+ Chef::Log.debug("Creating users home directory #{new_resource.home}")
+ shell_out!("mkdir -p #{new_resource.home}")
+ end
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/chef/provider/user/useradd.rb b/lib/chef/provider/user/useradd.rb
index cad9d58e7e..cc770c0be2 100644
--- a/lib/chef/provider/user/useradd.rb
+++ b/lib/chef/provider/user/useradd.rb
@@ -46,6 +46,7 @@ class Chef
def remove_user
command = [ "userdel" ]
command << "-r" if managing_home_dir?
+ command << "-f" if new_resource.force
command << new_resource.username
shell_out!(*command)
end
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index c89037df31..3c9e94e6f7 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -91,6 +91,7 @@ require 'chef/provider/user/pw'
require 'chef/provider/user/useradd'
require 'chef/provider/user/windows'
require 'chef/provider/user/solaris'
+require 'chef/provider/user/aix'
require 'chef/provider/group/aix'
require 'chef/provider/group/dscl'
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index 6c8e0434a0..6adf937f53 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -134,7 +134,6 @@ F
extend Chef::Mixin::ConvertToClassName
-
if Module.method(:const_defined?).arity == 1
def self.strict_const_defined?(const)
const_defined?(const)
@@ -277,7 +276,6 @@ F
end
end
-
def updated=(true_or_false)
Chef::Log.warn("Chef::Resource#updated=(true|false) is deprecated. Please call #updated_by_last_action(true|false) instead.")
Chef::Log.warn("Called from:")
@@ -544,7 +542,7 @@ F
# Serialize this object as a hash
def to_json(*a)
results = as_json
- results.to_json(*a)
+ Chef::JSONCompat.to_json(results, *a)
end
def to_hash
diff --git a/lib/chef/resource/paludis_package.rb b/lib/chef/resource/paludis_package.rb
index e456750b70..fde25e69b3 100644
--- a/lib/chef/resource/paludis_package.rb
+++ b/lib/chef/resource/paludis_package.rb
@@ -27,6 +27,7 @@ class Chef
@resource_name = :paludis_package
@provider = Chef::Provider::Package::Paludis
@allowed_actions = [ :install, :remove, :upgrade ]
+ @timeout = 3600
end
end
end
diff --git a/lib/chef/resource/user.rb b/lib/chef/resource/user.rb
index 357d6d12ea..05c076229f 100644
--- a/lib/chef/resource/user.rb
+++ b/lib/chef/resource/user.rb
@@ -38,6 +38,7 @@ class Chef
@password = nil
@system = false
@manage_home = false
+ @force = false
@non_unique = false
@action = :create
@supports = {
@@ -121,6 +122,14 @@ class Chef
)
end
+ def force(arg=nil)
+ set_or_return(
+ :force,
+ arg,
+ :kind_of => [ TrueClass, FalseClass ]
+ )
+ end
+
def non_unique(arg=nil)
set_or_return(
:non_unique,
diff --git a/lib/chef/resource_collection.rb b/lib/chef/resource_collection.rb
index a528a18aed..2cbd61cb0c 100644
--- a/lib/chef/resource_collection.rb
+++ b/lib/chef/resource_collection.rb
@@ -158,7 +158,6 @@ class Chef
# compat.
alias_method :resources, :find
-
# Returns true if +query_object+ is a valid string for looking up a
# resource, or raises InvalidResourceSpecification if not.
# === Arguments
@@ -189,16 +188,19 @@ class Chef
end
# Serialize this object as a hash
- def to_json(*a)
+ def to_hash
instance_vars = Hash.new
self.instance_variables.each do |iv|
instance_vars[iv] = self.instance_variable_get(iv)
end
- results = {
+ {
'json_class' => self.class.name,
'instance_vars' => instance_vars
}
- results.to_json(*a)
+ end
+
+ def to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def self.json_create(o)
diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb
index 47bbd13741..a19f26125e 100644
--- a/lib/chef/resource_reporter.rb
+++ b/lib/chef/resource_reporter.rb
@@ -230,18 +230,18 @@ class Chef
resource_history_url = "reports/nodes/#{node_name}/runs/#{run_id}"
Chef::Log.info("Sending resource update report (run-id: #{run_id})")
Chef::Log.debug run_data.inspect
- compressed_data = encode_gzip(run_data.to_json)
+ compressed_data = encode_gzip(Chef::JSONCompat.to_json(run_data))
+ Chef::Log.debug("Sending compressed run data...")
+ # Since we're posting compressed data we can not directly call post_rest which expects JSON
+ reporting_url = @rest_client.create_url(resource_history_url)
begin
- Chef::Log.debug("Sending compressed run data...")
- # Since we're posting compressed data we can not directly call post_rest which expects JSON
- reporting_url = @rest_client.create_url(resource_history_url)
@rest_client.raw_http_request(:POST, reporting_url, headers({'Content-Encoding' => 'gzip'}), compressed_data)
- rescue Net::HTTPServerException => e
- if e.response.code.to_s == "400"
+ rescue StandardError => e
+ if e.respond_to? :response
Chef::FileCache.store("failed-reporting-data.json", Chef::JSONCompat.to_json_pretty(run_data), 0640)
- Chef::Log.error("Failed to post reporting data to server (HTTP 400), saving to #{Chef::FileCache.load("failed-reporting-data.json", false)}")
+ Chef::Log.error("Failed to post reporting data to server (HTTP #{e.response.code}), saving to #{Chef::FileCache.load("failed-reporting-data.json", false)}")
else
- Chef::Log.error("Failed to post reporting data to server (HTTP #{e.response.code.to_s})")
+ Chef::Log.error("Failed to post reporting data to server (#{e})")
end
end
else
@@ -273,7 +273,7 @@ class Chef
resource_record.for_json
end
run_data["status"] = @status
- run_data["run_list"] = @run_status.node.run_list.to_json
+ run_data["run_list"] = Chef::JSONCompat.to_json(@run_status.node.run_list)
run_data["total_res_count"] = @total_res_count.to_s
run_data["data"] = {}
run_data["start_time"] = start_time.to_s
@@ -283,7 +283,7 @@ class Chef
exception_data = {}
exception_data["class"] = exception.inspect
exception_data["message"] = exception.message
- exception_data["backtrace"] = exception.backtrace.to_json
+ exception_data["backtrace"] = Chef::JSONCompat.to_json(exception.backtrace)
exception_data["description"] = @error_descriptions
run_data["data"]["exception"] = exception_data
end
diff --git a/lib/chef/role.rb b/lib/chef/role.rb
index 64952c6ab2..aeea873051 100644
--- a/lib/chef/role.rb
+++ b/lib/chef/role.rb
@@ -143,7 +143,7 @@ class Chef
# Serialize this object as a hash
def to_json(*a)
- to_hash.to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def update_from!(o)
@@ -236,8 +236,8 @@ class Chef
paths = Array(Chef::Config[:role_path])
paths.each do |path|
roles_files = Dir.glob(File.join(path, "**", "**"))
- js_files = roles_files.select { |file| file.match /#{name}\.json$/ }
- rb_files = roles_files.select { |file| file.match /#{name}\.rb$/ }
+ js_files = roles_files.select { |file| file.match /\/#{name}\.json$/ }
+ rb_files = roles_files.select { |file| file.match /\/#{name}\.rb$/ }
if js_files.count > 1 or rb_files.count > 1
raise Chef::Exceptions::DuplicateRole, "Multiple roles of same type found named #{name}"
end
diff --git a/lib/chef/run_list.rb b/lib/chef/run_list.rb
index 684c5e19fc..01e32ffc98 100644
--- a/lib/chef/run_list.rb
+++ b/lib/chef/run_list.rb
@@ -85,8 +85,12 @@ class Chef
@run_list_items.join(", ")
end
- def to_json(*args)
- to_a.map { |item| item.to_s}.to_json(*args)
+ def for_json
+ to_a.map { |item| item.to_s }
+ end
+
+ def to_json(*a)
+ Chef::JSONCompat.to_json(for_json, *a)
end
def empty?
@@ -158,6 +162,5 @@ class Chef
end
end
-
end
end
diff --git a/lib/chef/user.rb b/lib/chef/user.rb
index e2ef45dc5c..6569a97f00 100644
--- a/lib/chef/user.rb
+++ b/lib/chef/user.rb
@@ -73,7 +73,7 @@ class Chef
end
def to_json(*a)
- to_hash.to_json(*a)
+ Chef::JSONCompat.to_json(to_hash, *a)
end
def destroy
diff --git a/lib/chef/util/diff.rb b/lib/chef/util/diff.rb
index 7bce52d874..3117484a71 100644
--- a/lib/chef/util/diff.rb
+++ b/lib/chef/util/diff.rb
@@ -82,7 +82,7 @@ class Chef
end
end
end
-
+
# produces a unified-output-format diff with 3 lines of context
# ChefFS uses udiff() directly
def udiff(old_file, new_file)
@@ -185,4 +185,3 @@ class Chef
end
end
end
-
diff --git a/lib/chef/version_constraint.rb b/lib/chef/version_constraint.rb
index 7bfde41e74..a78e32e94f 100644
--- a/lib/chef/version_constraint.rb
+++ b/lib/chef/version_constraint.rb
@@ -24,7 +24,7 @@ class Chef
PATTERN = /^(#{OPS.join('|')}) *([0-9].*)$/
VERSION_CLASS = Chef::Version
- attr_reader :op, :version, :raw_version
+ attr_reader :op, :version
def initialize(constraint_spec=DEFAULT_CONSTRAINT)
case constraint_spec
@@ -50,11 +50,11 @@ class Chef
end
def inspect
- "(#{@op} #{@version})"
+ "(#{to_s})"
end
def to_s
- "#{@op} #{@version}"
+ "#{@op} #{@raw_version}"
end
def eql?(o)
@@ -106,7 +106,7 @@ class Chef
@op = $1
@raw_version = $2
@version = self.class::VERSION_CLASS.new(@raw_version)
- if raw_version.split('.').size <= 2
+ if @raw_version.split('.').size <= 2
@missing_patch_level = true
end
else