summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorlamont-opscode <lamont@opscode.com>2012-12-21 10:52:42 -0800
committerlamont-opscode <lamont@opscode.com>2012-12-21 10:52:42 -0800
commiteed92c71356f44afc03c575ca5aadff8023cb334 (patch)
treece77d52b4c14ab85b558f120d481d0fa78dcc533
parentf70530ee2ba698ffa06f8cefd360fac3d4b6ef01 (diff)
parent217cf85876651185b33fb574de7256e5a499d499 (diff)
downloadchef-eed92c71356f44afc03c575ca5aadff8023cb334.tar.gz
Merge pull request #562 from lamont-opscode/lcg/OC-4660-registry
Lcg/oc 4660 registry
-rw-r--r--lib/chef/dsl.rb1
-rw-r--r--lib/chef/dsl/registry_helper.rb59
-rw-r--r--lib/chef/exceptions.rb16
-rw-r--r--lib/chef/platform.rb5
-rw-r--r--lib/chef/provider/registry_key.rb156
-rw-r--r--lib/chef/providers.rb1
-rw-r--r--lib/chef/recipe.rb2
-rw-r--r--lib/chef/resource.rb2
-rw-r--r--lib/chef/resource/registry_key.rb86
-rw-r--r--lib/chef/resources.rb1
-rw-r--r--lib/chef/win32/registry.rb371
-rw-r--r--spec/functional/dsl/registry_helper_spec.rb63
-rw-r--r--spec/functional/resource/registry_spec.rb576
-rw-r--r--spec/functional/win32/registry_helper_spec.rb632
-rw-r--r--spec/spec_helper.rb2
-rw-r--r--spec/support/platform_helpers.rb10
-rw-r--r--spec/unit/dsl/regsitry_helper_spec.rb55
-rw-r--r--spec/unit/provider/registry_key_spec.rb269
-rw-r--r--spec/unit/registry_helper_spec.rb374
-rw-r--r--spec/unit/resource/registry_key_spec.rb171
20 files changed, 2848 insertions, 4 deletions
diff --git a/lib/chef/dsl.rb b/lib/chef/dsl.rb
index 74244fafbb..7717d99113 100644
--- a/lib/chef/dsl.rb
+++ b/lib/chef/dsl.rb
@@ -3,3 +3,4 @@ require 'chef/dsl/platform_introspection'
require 'chef/dsl/data_query'
require 'chef/dsl/include_recipe'
require 'chef/dsl/include_attribute'
+require 'chef/dsl/registry_helper'
diff --git a/lib/chef/dsl/registry_helper.rb b/lib/chef/dsl/registry_helper.rb
new file mode 100644
index 0000000000..4dcd2f1eee
--- /dev/null
+++ b/lib/chef/dsl/registry_helper.rb
@@ -0,0 +1,59 @@
+#
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+# 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.
+#
+
+#
+# Helper functions to access the windows registry from within recipes and
+# the not_if/only_if blocks in resources. This only exposes the methods
+# in the chef/win32/registry class which are reasonably side-effect-free.
+# The actual modification of the registry should be done via the registry_key
+# resource in a more idempotent way.
+#
+#
+class Chef
+ module DSL
+ module RegistryHelper
+ # the registry instance is cheap to build and throwing it away ensures we
+ # don't carry any state (e.g. magic 32-bit/64-bit settings) between calls
+ def registry_key_exists?(key_path, architecture = :machine)
+ registry = Chef::Win32::Registry.new(run_context, architecture)
+ registry.key_exists?(key_path)
+ end
+ def registry_get_values(key_path, architecture = :machine)
+ registry = Chef::Win32::Registry.new(run_context, architecture)
+ registry.get_values(key_path)
+ end
+ def registry_has_subkeys?(key_path, architecture = :machine)
+ registry = Chef::Win32::Registry.new(run_context, architecture)
+ registry.has_subkeys?(key_path)
+ end
+ def registry_get_subkeys(key_path, architecture = :machine)
+ registry = Chef::Win32::Registry.new(run_context, architecture)
+ registry.get_subkeys(key_path)
+ end
+ def registry_value_exists?(key_path, value, architecture = :machine)
+ registry = Chef::Win32::Registry.new(run_context, architecture)
+ registry.value_exists?(key_path, value)
+ end
+ def registry_data_exists?(key_path, value, architecture = :machine)
+ registry = Chef::Win32::Registry.new(run_context, architecture)
+ registry.data_exists?(key_path, value)
+ end
+ end
+ end
+end
+
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb
index 87802639d3..6644554897 100644
--- a/lib/chef/exceptions.rb
+++ b/lib/chef/exceptions.rb
@@ -94,7 +94,8 @@ class Chef
# Thrown when Win32 API layer binds to non-existent Win32 function. Occurs
# when older versions of Windows don't support newer Win32 API functions.
class Win32APIFunctionNotImplemented < NotImplementedError; end
-
+ # Attempting to run windows code on a not-windows node
+ class Win32NotWindows < RuntimeError; end
class ObsoleteDependencySyntax < ArgumentError; end
class InvalidDataBagPath < ArgumentError; end
@@ -128,6 +129,19 @@ class Chef
# of merged attributes will trigger this error.
class StaleAttributeRead < StandardError; end
+ #Registry Helper throws the following errors
+ class Win32RegArchitectureIncorrect < RuntimeError; end
+ class Win32RegHiveMissing < ArgumentError; end
+ class Win32RegKeyMissing < RuntimeError; end
+ class Win32RegValueMissing < RuntimeError; end
+ class Win32RegDataMissing < RuntimeError; end
+ class Win32RegValueExists < RuntimeError; end
+ class Win32RegNoRecursive < ArgumentError; end
+ class Win32RegTypeDoesNotExist < ArgumentError; end
+ class Win32RegBadType < ArgumentError; end
+ class Win32RegBadValueSize < ArgumentError; end
+ class Win32RegTypesMismatch < ArgumentError; end
+
class MissingRole < RuntimeError
NULL = Object.new
diff --git a/lib/chef/platform.rb b/lib/chef/platform.rb
index 78cb00e96a..807eeafa6d 100644
--- a/lib/chef/platform.rb
+++ b/lib/chef/platform.rb
@@ -20,9 +20,8 @@ require 'chef/config'
require 'chef/log'
require 'chef/mixin/params_validate'
-# Actually, this file depends on nearly every provider in chef, but actually
-# requiring them causes circular requires resulting in uninitialized constant
-# errors.
+# This file depends on nearly every provider in chef, but requiring them
+# directly causes circular requires resulting in uninitialized constant errors.
require 'chef/provider'
require 'chef/provider/log'
require 'chef/provider/user'
diff --git a/lib/chef/provider/registry_key.rb b/lib/chef/provider/registry_key.rb
new file mode 100644
index 0000000000..b7bcdd908d
--- /dev/null
+++ b/lib/chef/provider/registry_key.rb
@@ -0,0 +1,156 @@
+#
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+#
+# Copyright:: 2011, Opscode, Inc.
+#
+# 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/config'
+require 'chef/log'
+require 'chef/resource/file'
+require 'chef/mixin/checksum'
+require 'chef/provider'
+require 'etc'
+require 'fileutils'
+require 'chef/scan_access_control'
+require 'chef/mixin/shell_out'
+require 'chef/win32/registry'
+
+class Chef
+
+ class Provider
+ class RegistryKey < Chef::Provider
+ include Chef::Mixin::Checksum
+ include Chef::Mixin::ShellOut
+
+ def whyrun_supported?
+ true
+ end
+
+ def running_on_windows!
+ unless Chef::Platform.windows?
+ raise Chef::Exceptions::Win32NotWindows, "Attempt to manipulate the windows registry on a non-windows node"
+ end
+ end
+
+ def load_current_resource
+ running_on_windows!
+ @current_resource ||= Chef::Resource::RegistryKey.new(@new_resource.key, run_context)
+ @current_resource.key(@new_resource.key)
+ @current_resource.architecture(@new_resource.architecture)
+ @current_resource.recursive(@new_resource.recursive)
+ if registry.key_exists?(@new_resource.key)
+ @current_resource.values(registry.get_values(@new_resource.key))
+ end
+ values_to_hash(@current_resource.values)
+ @current_resource
+ end
+
+ def registry
+ @registry ||= Chef::Win32::Registry.new(@run_context, @new_resource.architecture)
+ end
+
+ def values_to_hash(values)
+ if values
+ @name_hash = Hash[values.map { |val| [val[:name], val] }]
+ else
+ @name_hash = {}
+ end
+ end
+
+ def define_resource_requirements
+ requirements.assert(:create, :create_if_missing, :delete, :delete_key) do |a|
+ a.assertion{ registry.hive_exists?(@new_resource.key) }
+ a.failure_message(Chef::Exceptions::Win32RegHiveMissing, "Hive #{@new_resource.key.split("\\").shift} does not exist")
+ end
+ requirements.assert(:create) do |a|
+ a.assertion{ registry.key_exists?(@new_resource.key) }
+ a.whyrun("Key #{@new_resource.key} does not exist. Unless it would have been created before, attempt to modify its values would fail.")
+ end
+ requirements.assert(:create, :create_if_missing) do |a|
+ #If keys missing in the path and recursive == false
+ a.assertion{ !registry.keys_missing?(@current_resource.key) || @new_resource.recursive }
+ a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "Intermediate keys missing but recursive is set to false")
+ a.whyrun("Intermediate keys in #{@new_resource.key} go not exist. Unless they would have been created earlier, attempt to modify them would fail.")
+ end
+ requirements.assert(:delete_key) do |a|
+ #If key to be deleted has subkeys but recurssive == false
+ a.assertion{ !registry.key_exists?(@new_resource.key) || !registry.has_subkeys?(@new_resource.key) || @new_resource.recursive }
+ a.failure_message(Chef::Exceptions::Win32RegNoRecursive, "#{@new_resource.key} has subkeys but recursive is set to false.")
+ a.whyrun("#{@current_resource.key} has subkeys, but recursive is set to false. attempt to delete would fails unless subkeys were deleted prior to this action.")
+ end
+ end
+
+ def action_create
+ unless registry.key_exists?(@current_resource.key)
+ converge_by("create key #{@new_resource.key}") do
+ registry.create_key(@new_resource.key, @new_resource.recursive)
+ end
+ end
+ @new_resource.values.each do |value|
+ if @name_hash.has_key?(value[:name])
+ current_value = @name_hash[value[:name]]
+ unless current_value[:type] == value[:type] && current_value[:data] == value[:data]
+ converge_by("set value #{value}") do
+ registry.set_value(@new_resource.key, value)
+ end
+ end
+ else
+ converge_by("set value #{value}") do
+ registry.set_value(@new_resource.key, value)
+ end
+ end
+ end
+ end
+
+ def action_create_if_missing
+ unless registry.key_exists?(@new_resource.key)
+ converge_by("create key #{@new_resource.key}") do
+ registry.create_key(@new_resource.key, @new_resource.recursive)
+ end
+ end
+ @new_resource.values.each do |value|
+ unless @name_hash.has_key?(value[:name])
+ converge_by("create value #{value}") do
+ registry.set_value(@new_resource.key, value)
+ end
+ end
+ end
+ end
+
+ def action_delete
+ if registry.key_exists?(@new_resource.key)
+ @new_resource.values.each do |value|
+ if @name_hash.has_key?(value[:name])
+ converge_by("delete value #{value}") do
+ registry.delete_value(@new_resource.key, value)
+ end
+ end
+ end
+ end
+ end
+
+ def action_delete_key
+ if registry.key_exists?(@new_resource.key)
+ converge_by("delete key #{@new_resource.key}") do
+ registry.delete_key(@new_resource.key, @new_resource.recursive)
+ end
+ end
+ end
+
+ end
+ end
+end
+
diff --git a/lib/chef/providers.rb b/lib/chef/providers.rb
index 568e06e253..be3f487ca3 100644
--- a/lib/chef/providers.rb
+++ b/lib/chef/providers.rb
@@ -100,3 +100,4 @@ require 'chef/provider/deploy/revision'
require 'chef/provider/deploy/timestamped'
require "chef/provider/lwrp_base"
+require 'chef/provider/registry_key'
diff --git a/lib/chef/recipe.rb b/lib/chef/recipe.rb
index aca35db049..6ea69360b8 100644
--- a/lib/chef/recipe.rb
+++ b/lib/chef/recipe.rb
@@ -22,6 +22,7 @@ require 'chef/dsl/recipe'
require 'chef/dsl/data_query'
require 'chef/dsl/platform_introspection'
require 'chef/dsl/include_recipe'
+require 'chef/dsl/registry_helper'
require 'chef/mixin/from_file'
@@ -36,6 +37,7 @@ class Chef
include Chef::DSL::PlatformIntrospection
include Chef::DSL::IncludeRecipe
include Chef::DSL::Recipe
+ include Chef::DSL::RegistryHelper
include Chef::Mixin::FromFile
include Chef::Mixin::Deprecation
diff --git a/lib/chef/resource.rb b/lib/chef/resource.rb
index c49bb6684e..095a07c2e0 100644
--- a/lib/chef/resource.rb
+++ b/lib/chef/resource.rb
@@ -20,6 +20,7 @@
require 'chef/mixin/params_validate'
require 'chef/mixin/check_helper'
require 'chef/dsl/platform_introspection'
+require 'chef/dsl/registry_helper'
require 'chef/mixin/convert_to_class_name'
require 'chef/resource/conditional'
require 'chef/resource_collection'
@@ -122,6 +123,7 @@ F
include Chef::Mixin::CheckHelper
include Chef::Mixin::ParamsValidate
include Chef::DSL::PlatformIntrospection
+ include Chef::DSL::RegistryHelper
include Chef::Mixin::ConvertToClassName
include Chef::Mixin::Deprecation
diff --git a/lib/chef/resource/registry_key.rb b/lib/chef/resource/registry_key.rb
new file mode 100644
index 0000000000..2b5d077f4c
--- /dev/null
+++ b/lib/chef/resource/registry_key.rb
@@ -0,0 +1,86 @@
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+#
+# Copyright:: 2011, Opscode, Inc.
+#
+# 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/provider/registry_key'
+require 'chef/resource'
+
+class Chef
+ class Resource
+ class RegistryKey < Chef::Resource
+
+ identity_attr :key
+ state_attrs :values
+
+ def initialize(name, run_context=nil)
+ super
+ @resource_name = :registry_key
+ @action = :create
+ @architecture = :machine
+ @recursive = false
+ @key = name
+ @values = []
+ @allowed_actions.push(:create, :create_if_missing, :delete, :delete_key)
+ end
+
+ def key(arg=nil)
+ set_or_return(
+ :key,
+ arg,
+ :kind_of => String
+ )
+ end
+ def values(arg=nil)
+ if not arg.nil?
+ if arg.is_a?(Hash)
+ @values = [ arg ]
+ elsif arg.is_a?(Array)
+ @values = arg
+ else
+ raise ArgumentError, "Bad type for RegistryKey resource, use Hash or Array"
+ end
+ @values.each do |v|
+ raise ArgumentError, "Missing name key in RegistryKey values hash" unless v.has_key?(:name)
+ raise ArgumentError, "Missing type key in RegistryKey values hash" unless v.has_key?(:type)
+ raise ArgumentError, "Missing data key in RegistryKey values hash" unless v.has_key?(:data)
+ v.each_key do |key|
+ raise ArgumentError, "Bad key #{key} in RegistryKey values hash" unless [:name,:type,:data].include?(key)
+ end
+ raise ArgumentError, "Type of name => #{v[:name]} should be string" unless v[:name].is_a?(String)
+ raise Argument Error "Type of type => #{v[:name]} should be symbol" unless v[:type].is_a?(Symbol)
+ end
+ elsif self.instance_variable_defined?(:@values) == true
+ @values
+ end
+ end
+ def recursive(arg=nil)
+ set_or_return(
+ :recursive,
+ arg,
+ :kind_of => [TrueClass, FalseClass]
+ )
+ end
+ def architecture(arg=nil)
+ set_or_return(
+ :architecture,
+ arg,
+ :kind_of => Symbol
+ )
+ end
+
+ end
+ end
+end
diff --git a/lib/chef/resources.rb b/lib/chef/resources.rb
index f4212f1498..6dea46bfc9 100644
--- a/lib/chef/resources.rb
+++ b/lib/chef/resources.rb
@@ -50,6 +50,7 @@ require 'chef/resource/pacman_package'
require 'chef/resource/perl'
require 'chef/resource/portage_package'
require 'chef/resource/python'
+require 'chef/resource/registry_key'
require 'chef/resource/remote_directory'
require 'chef/resource/remote_file'
require 'chef/resource/rpm_package'
diff --git a/lib/chef/win32/registry.rb b/lib/chef/win32/registry.rb
new file mode 100644
index 0000000000..03d34040a9
--- /dev/null
+++ b/lib/chef/win32/registry.rb
@@ -0,0 +1,371 @@
+#
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+#
+# Copyright:: 2012, Opscode, Inc.
+#
+# 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/reserved_names'
+
+if RUBY_PLATFORM =~ /mswin|mingw32|windows/
+ require 'win32/registry'
+ require 'ruby-wmi'
+end
+
+class Chef
+ class Win32
+ class Registry
+
+ attr_accessor :run_context
+ attr_accessor :architecture
+
+ def initialize(run_context=nil, user_architecture=:machine)
+ @run_context = run_context
+ self.architecture = user_architecture
+ end
+
+ def architecture=(user_architecture)
+ @architecture = user_architecture.to_sym
+ assert_architecture!
+ end
+
+ def get_values(key_path)
+ hive, key = get_hive_and_key(key_path)
+ key_exists!(key_path)
+ values = hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
+ reg.map { |name, type, data| {:name=>name, :type=>get_name_from_type(type), :data=>data} }
+ end
+ end
+
+ def set_value(key_path, value)
+ Chef::Log.debug("Updating value #{value[:name]} in registry key #{key_path} with type #{value[:type]} and data #{value[:data]}")
+ key_exists!(key_path)
+ hive, key = get_hive_and_key(key_path)
+ if value_exists?(key_path, value)
+ if data_exists?(key_path, value)
+ Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} already had those values, not updated")
+ return false
+ else
+ hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg|
+ reg.write(value[:name], get_type_from_name(value[:type]), value[:data])
+ end
+ Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} updated")
+ end
+ else
+ hive.open(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | registry_system_architecture) do |reg|
+ reg.write(value[:name], get_type_from_name(value[:type]), value[:data])
+ end
+ Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} created")
+ end
+ true
+ end
+
+ def delete_value(key_path, value)
+ Chef::Log.debug("Deleting value #{value[:name]} from registry key #{key_path}")
+ if value_exists?(key_path, value)
+ begin
+ hive, key = get_hive_and_key(key_path)
+ rescue Chef::Exceptions::Win32RegKeyMissing
+ return true
+ end
+ hive.open(key, ::Win32::Registry::KEY_SET_VALUE | registry_system_architecture) do |reg|
+ reg.delete_value(value[:name])
+ Chef::Log.debug("Deleted value #{value[:name]} from registry key #{key_path}")
+ end
+ else
+ Chef::Log.debug("Value #{value[:name]} in registry key #{key_path} does not exist, not updated")
+ end
+ true
+ end
+
+ def create_key(key_path, recursive)
+ Chef::Log.debug("Creating registry key #{key_path}")
+ if keys_missing?(key_path)
+ if recursive == true
+ Chef::Log.debug("Registry key #{key_path} has missing subkeys, and recursive specified, creating them....")
+ create_missing(key_path)
+ else
+ raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has missing subkeys, and recursive not specified"
+ end
+ end
+ if key_exists?(key_path)
+ Chef::Log.debug("Registry key #{key_path} already exists, doing nothing")
+ else
+ hive, key = get_hive_and_key(key_path)
+ hive.create(key, ::Win32::Registry::KEY_WRITE | registry_system_architecture)
+ Chef::Log.debug("Registry key #{key_path} created")
+ end
+ true
+ end
+
+ def delete_key(key_path, recursive)
+ Chef::Log.debug("Deleting registry key #{key_path}")
+ unless key_exists?(key_path)
+ Chef::Log.debug("Registry key #{key_path}, does not exist, not deleting")
+ return true
+ end
+ hive, key = get_hive_and_key(key_path)
+ key_parent = key.split("\\")
+ key_to_delete = key_parent.pop
+ key_parent = key_parent.join("\\")
+ if has_subkeys?(key_path)
+ if recursive == true
+ hive.open(key_parent, ::Win32::Registry::KEY_WRITE | registry_system_architecture) do |reg|
+ Chef::Log.debug("Deleting registry key #{key_path} recursively")
+ reg.delete_key(key_to_delete,recursive)
+ end
+ else
+ raise Chef::Exceptions::Win32RegNoRecursive, "Registry key #{key_path} has subkeys, and recursive not specified"
+ end
+ else
+ hive.open(key_parent, ::Win32::Registry::KEY_WRITE | registry_system_architecture) do |reg|
+ Chef::Log.debug("Deleting registry key #{key_path}")
+ reg.delete_key(key_to_delete)
+ end
+ end
+ true
+ end
+
+ def key_exists?(key_path)
+ hive, key = get_hive_and_key(key_path)
+ begin
+ hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |current_key|
+ return true
+ end
+ rescue ::Win32::Registry::Error => e
+ return false
+ end
+ end
+
+ def key_exists!(key_path)
+ unless key_exists?(key_path)
+ raise Chef::Exceptions::Win32RegKeyMissing, "Registry key #{key_path} does not exist"
+ end
+ true
+ end
+
+ def hive_exists?(key_path)
+ begin
+ hive, key = get_hive_and_key(key_path)
+ rescue Chef::Exceptions::Win32RegHiveMissing => e
+ return false
+ end
+ return true
+ end
+
+ def has_subkeys?(key_path)
+ key_exists!(key_path)
+ hive, key = get_hive_and_key(key_path)
+ hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
+ reg.each_key{ |key| return true }
+ end
+ return false
+ end
+
+ def get_subkeys(key_path)
+ subkeys = []
+ key_exists!(key_path)
+ hive, key = get_hive_and_key(key_path)
+ hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
+ reg.each_key{ |current_key| subkeys << current_key }
+ end
+ return subkeys
+ end
+
+ # 32-bit chef clients running on 64-bit machines will default to reading the 64-bit registry
+ def registry_system_architecture
+ applied_arch = ( architecture == :machine ) ? machine_architecture : architecture
+ ( applied_arch == :x86_64 ) ? 0x0100 : 0x0200
+ end
+
+ def value_exists?(key_path, value)
+ key_exists!(key_path)
+ hive, key = get_hive_and_key(key_path)
+ hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
+ return true if reg.any? {|val| val == value[:name] }
+ end
+ return false
+ end
+
+ def data_exists?(key_path, value)
+ key_exists!(key_path)
+ hive, key = get_hive_and_key(key_path)
+ hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
+ reg.each do |val_name, val_type, val_data|
+ if val_name == value[:name] &&
+ val_type == get_type_from_name(value[:type]) &&
+ val_data == value[:data]
+ return true
+ end
+ end
+ end
+ return false
+ end
+
+ def value_exists!(key_path, value)
+ unless value_exists?(key_path, value)
+ raise Chef::Exceptions::Win32RegValueMissing, "Registry key #{key_path} has no value named #{value[:name]}"
+ end
+ true
+ end
+
+ def data_exists!(key_path, value)
+ unless data_exists?(key_path, value)
+ raise Chef::Exceptions::Win32RegDataMissing, "Registry key #{key_path} has no value named #{value[:name]}, containing type #{value[:type]} and data #{value[:data]}"
+ end
+ true
+ end
+
+ def type_matches?(key_path, value)
+ value_exists!(key_path, value)
+ hive, key = get_hive_and_key(key_path)
+ hive.open(key, ::Win32::Registry::KEY_READ | registry_system_architecture) do |reg|
+ reg.each do |val_name, val_type|
+ if val_name == value[:name]
+ type_new = get_type_from_name(value[:type])
+ if val_type == type_new
+ return true
+ end
+ end
+ end
+ end
+ return false
+ end
+
+ def type_matches!(key_path, value)
+ unless type_matches?(key_path, value)
+ raise Chef::Exceptions::Win32RegTypesMismatch, "Registry key #{key_path} has a value #{value[:name]} with a type that is not #{value[:type]}"
+ end
+ end
+
+ def get_type_from_name(val_type)
+ value = {
+ :binary => ::Win32::Registry::REG_BINARY,
+ :string => ::Win32::Registry::REG_SZ,
+ :multi_string => ::Win32::Registry::REG_MULTI_SZ,
+ :expand_string => ::Win32::Registry::REG_EXPAND_SZ,
+ :dword => ::Win32::Registry::REG_DWORD,
+ :dword_big_endian => ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
+ :qword => ::Win32::Registry::REG_QWORD
+ }[val_type]
+ return value
+ end
+
+ def keys_missing?(key_path)
+ missing_key_arr = key_path.split("\\")
+ missing_key_arr.pop
+ key = missing_key_arr.join("\\")
+ !key_exists?(key)
+ end
+
+ def get_type_from_name(val_type)
+ _type_name_map[val_type]
+ end
+
+ def get_name_from_type(val_class)
+ _name_type_map[val_class]
+ end
+
+ private
+
+ def node
+ run_context && run_context.node
+ end
+
+ def machine_architecture
+ node[:kernel][:machine].to_sym
+ end
+
+ def assert_architecture!
+ if machine_architecture == :i386 && architecture == :x86_64
+ raise Chef::Exceptions::Win32RegArchitectureIncorrect, "cannot access 64-bit registry on a 32-bit windows instance"
+ end
+ end
+
+ def get_hive_and_key(path)
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+ key = reg_path.join("\\")
+
+ hive = {
+ "HKLM" => ::Win32::Registry::HKEY_LOCAL_MACHINE,
+ "HKEY_LOCAL_MACHINE" => ::Win32::Registry::HKEY_LOCAL_MACHINE,
+ "HKU" => ::Win32::Registry::HKEY_USERS,
+ "HKEY_USERS" => ::Win32::Registry::HKEY_USERS,
+ "HKCU" => ::Win32::Registry::HKEY_CURRENT_USER,
+ "HKEY_CURRENT_USER" => ::Win32::Registry::HKEY_CURRENT_USER,
+ "HKCR" => ::Win32::Registry::HKEY_CLASSES_ROOT,
+ "HKEY_CLASSES_ROOT" => ::Win32::Registry::HKEY_CLASSES_ROOT,
+ "HKCC" => ::Win32::Registry::HKEY_CURRENT_CONFIG,
+ "HKEY_CURRENT_CONFIG" => ::Win32::Registry::HKEY_CURRENT_CONFIG,
+ }[hive_name]
+
+ raise Chef::Exceptions::Win32RegHiveMissing, "Registry Hive #{hive_name} does not exist" unless hive
+
+ return hive, key
+ end
+
+ def _type_name_map
+ {
+ :binary => ::Win32::Registry::REG_BINARY,
+ :string => ::Win32::Registry::REG_SZ,
+ :multi_string => ::Win32::Registry::REG_MULTI_SZ,
+ :expand_string => ::Win32::Registry::REG_EXPAND_SZ,
+ :dword => ::Win32::Registry::REG_DWORD,
+ :dword_big_endian => ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
+ :qword => ::Win32::Registry::REG_QWORD
+ }
+ end
+
+ def _name_type_map
+ @_name_type_map ||= _type_name_map.invert
+ end
+
+ def get_type_from_num(val_type)
+ value = {
+ 3 => ::Win32::Registry::REG_BINARY,
+ 1 => ::Win32::Registry::REG_SZ,
+ 7 => ::Win32::Registry::REG_MULTI_SZ,
+ 2 => ::Win32::Registry::REG_EXPAND_SZ,
+ 4 => ::Win32::Registry::REG_DWORD,
+ 5 => ::Win32::Registry::REG_DWORD_BIG_ENDIAN,
+ 11 => ::Win32::Registry::REG_QWORD
+ }[val_type]
+ return value
+ end
+
+ def create_missing(key_path)
+ missing_key_arr = key_path.split("\\")
+ hivename = missing_key_arr.shift
+ missing_key_arr.pop
+ existing_key_path = hivename
+ hive, key = get_hive_and_key(key_path)
+ missing_key_arr.each do |intermediate_key|
+ existing_key_path = existing_key_path << "\\" << intermediate_key
+ if !key_exists?(existing_key_path)
+ Chef::Log.debug("Recursively creating registry key #{existing_key_path}")
+ hive.create(get_key(existing_key_path), ::Win32::Registry::KEY_ALL_ACCESS | registry_system_architecture)
+ end
+ end
+ end
+
+ def get_key(path)
+ reg_path = path.split("\\")
+ hive_name = reg_path.shift
+ key = reg_path.join("\\")
+ end
+
+ end
+ end
+end
diff --git a/spec/functional/dsl/registry_helper_spec.rb b/spec/functional/dsl/registry_helper_spec.rb
new file mode 100644
index 0000000000..452c4c2799
--- /dev/null
+++ b/spec/functional/dsl/registry_helper_spec.rb
@@ -0,0 +1,63 @@
+#
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Copyright:: Copyright (c) 2011 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.
+#
+
+require "chef/dsl/registry_helper"
+require "spec_helper"
+
+describe Chef::Resource::RegistryKey, :windows_only do
+
+ before (:all) do
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root"
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch"
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['RootType1', Win32::Registry::REG_SZ] = 'fibrous'
+ reg.write('Roots', Win32::Registry::REG_MULTI_SZ, ["strong roots", "healthy tree"])
+ end
+
+ events = Chef::EventDispatch::Dispatcher.new
+ node = Chef::Node.new
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ node.consume_external_attrs(ohai.data,{})
+ run_context = Chef::RunContext.new(node, {}, events)
+ @resource = Chef::Resource.new("foo", run_context)
+ end
+
+ context "tests registry dsl" do
+ it "returns true if registry_key_exists" do
+ @resource.registry_key_exists?("HKCU\\Software\\Root").should == true
+ end
+ it "returns true if registry has specified value" do
+ values = @resource.registry_get_values("HKCU\\Software\\Root")
+ values.include?({:name=>"RootType1",:type=>:string,:data=>"fibrous"}).should == true
+ end
+ it "returns true if specified registry_has_subkey" do
+ @resource.registry_has_subkeys?("HKCU\\Software\\Root").should == true
+ end
+ it "returns true if specified key has specified subkey" do
+ subkeys = @resource.registry_get_subkeys("HKCU\\Software\\Root")
+ subkeys.include?("Branch").should == true
+ end
+ it "returns true if registry_value_exists" do
+ @resource.registry_value_exists?("HKCU\\Software\\Root", {:name=>"RootType1", :type=>:string, :data=>"fibrous"}).should == true
+ end
+ it "returns true if data_value_exists" do
+ @resource.registry_data_exists?("HKCU\\Software\\Root", {:name=>"RootType1", :type=>:string, :data=>"fibrous"}).should == true
+ end
+ end
+end
diff --git a/spec/functional/resource/registry_spec.rb b/spec/functional/resource/registry_spec.rb
new file mode 100644
index 0000000000..2ef359273d
--- /dev/null
+++ b/spec/functional/resource/registry_spec.rb
@@ -0,0 +1,576 @@
+#
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+# Copyright:: Copyright (c) 2011 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.
+#
+
+require "chef/win32/registry"
+require "chef/resource_reporter"
+require "spec_helper"
+
+describe Chef::Resource::RegistryKey, :unix_only do
+ before(:all) do
+ events = Chef::EventDispatch::Dispatcher.new
+ node = Chef::Node.new
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ node.consume_external_attrs(ohai.data,{})
+ run_context = Chef::RunContext.new(node, {}, events)
+ @resource = Chef::Resource::RegistryKey.new("HKCU\\Software", run_context)
+ end
+ context "when load_current_resource is run on a non-windows node" do
+ it "throws an exception because you don't have a windows registry (derp)" do
+ @resource.key("HKCU\\Software\\Opscode")
+ @resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
+ lambda{@resource.run_action(:create)}.should raise_error(Chef::Exceptions::Win32NotWindows)
+ end
+ end
+end
+
+describe Chef::Resource::RegistryKey, :windows_only do
+
+ # parent and key must be single keys, not paths
+ let(:parent) { 'Opscode' }
+ let(:child) { 'Whatever' }
+ let(:key_parent) { "SOFTWARE\\" + parent }
+ let(:key_child) { "SOFTWARE\\" + parent + "\\" + child }
+ # must be under HKLM\SOFTWARE for WOW64 redirection to work
+ let(:reg_parent) { "HKLM\\" + key_parent }
+ let(:reg_child) { "HKLM\\" + key_child }
+ let(:hive_class) { ::Win32::Registry::HKEY_LOCAL_MACHINE }
+ let(:resource_name) { "This is the name of my Resource" }
+
+ def clean_registry
+ # clean 64-bit space on WOW64
+ begin
+ hive_class.open(key_parent, Win32::Registry::KEY_WRITE | 0x0100) do |reg|
+ reg.delete_key(child, true)
+ end
+ rescue
+ end
+ # clean 32-bit space on WOW64
+ begin
+ hive_class.open(key_parent, Win32::Registry::KEY_WRITE | 0x0200) do |reg|
+ reg.delete_key(child, true)
+ end
+ rescue
+ end
+ end
+
+ def reset_registry
+ clean_registry
+ hive_class.create(key_parent, Win32::Registry::KEY_WRITE | 0x0100)
+ hive_class.create(key_parent, Win32::Registry::KEY_WRITE | 0x0200)
+ end
+
+ def create_deletable_keys
+ # create them both 32-bit and 64-bit
+ [ 0x0100, 0x0200 ].each do |flag|
+ hive_class.create(key_parent + '\Opscode', Win32::Registry::KEY_WRITE | flag)
+ hive_class.open(key_parent + '\Opscode', Win32::Registry::KEY_ALL_ACCESS | flag) do |reg|
+ reg["Color", Win32::Registry::REG_SZ] = "Orange"
+ reg.write("Opscode", Win32::Registry::REG_MULTI_SZ, ["Seattle", "Washington"])
+ reg["AKA", Win32::Registry::REG_SZ] = "OC"
+ end
+ hive_class.create(key_parent + '\ReportKey', Win32::Registry::KEY_WRITE | flag)
+ hive_class.open(key_parent + '\ReportKey', Win32::Registry::KEY_ALL_ACCESS | flag) do |reg|
+ reg["ReportVal4", Win32::Registry::REG_SZ] = "report4"
+ reg["ReportVal5", Win32::Registry::REG_SZ] = "report5"
+ end
+ hive_class.create(key_parent + '\OpscodeWhyRun', Win32::Registry::KEY_WRITE | flag)
+ hive_class.open(key_parent + '\OpscodeWhyRun', Win32::Registry::KEY_ALL_ACCESS | flag) do |reg|
+ reg["BriskWalk", Win32::Registry::REG_SZ] = "is good for health"
+ end
+ end
+ end
+
+ before(:all) do
+ @events = Chef::EventDispatch::Dispatcher.new
+ @node = Chef::Node.new
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ @node.consume_external_attrs(ohai.data,{})
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+
+ @new_resource = Chef::Resource::RegistryKey.new(resource_name, @run_context)
+ @registry = Chef::Win32::Registry.new(@run_context)
+
+ @current_whyrun = Chef::Config[:why_run]
+
+ reset_registry
+ end
+
+ #Reporting setup
+ before do
+ @node.name("windowsbox")
+ @rest_client = mock("Chef::REST (mock)")
+ @rest_client.stub!(:create_url).and_return("reports/nodes/windowsbox/runs/ABC123");
+ @rest_client.stub!(:raw_http_request).and_return({"result"=>"ok"});
+ @rest_client.stub!(:post_rest).and_return({"uri"=>"https://example.com/reports/nodes/windowsbox/runs/ABC123"});
+
+ @resource_reporter = Chef::ResourceReporter.new(@rest_client)
+ @events.register(@resource_reporter)
+ @resource_reporter.node_load_completed(@node, :expanded_run_list, :config)
+
+ @new_resource.cookbook_name = "monkey"
+ @cookbook_version = mock("Cookbook::Version", :version => "1.2.3")
+ @new_resource.stub!(:cookbook_version).and_return(@cookbook_version)
+ end
+
+ after (:all) do
+ clean_registry
+ end
+
+ context "when action is create" do
+ before (:all) do
+ reset_registry
+ end
+ it "creates registry key, value if the key is missing" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
+ @new_resource.run_action(:create)
+
+ @registry.key_exists?(reg_child).should == true
+ @registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+ end
+
+ it "does not create the key if it already exists with same value, type and data" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
+ @new_resource.run_action(:create)
+
+ @registry.key_exists?(reg_child).should == true
+ @registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+ end
+
+ it "creates a value if it does not exist" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Mango", :type=>:string, :data=>"Yellow"}])
+ @new_resource.run_action(:create)
+
+ @registry.data_exists?(reg_child, {:name=>"Mango", :type=>:string, :data=>"Yellow"}).should == true
+ end
+
+ it "modifies the data if the key and value exist and type matches" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Not just Orange - OpscodeOrange!"}])
+ @new_resource.run_action(:create)
+
+ @registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Not just Orange - OpscodeOrange!"}).should == true
+ end
+
+ it "modifys the type if the key and value exist and the type does not match" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Color", :type=>:multi_string, :data=>["Not just Orange - OpscodeOrange!"]}])
+ @new_resource.run_action(:create)
+
+ @registry.data_exists?(reg_child, {:name=>"Color", :type=>:multi_string, :data=>["Not just Orange - OpscodeOrange!"]}).should == true
+ end
+
+ it "creates subkey if parent exists" do
+ @new_resource.key(reg_child + '\OpscodeTest')
+ @new_resource.values([{:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:create)
+
+ @registry.key_exists?(reg_child + '\OpscodeTest').should == true
+ @registry.value_exists?(reg_child + '\OpscodeTest', {:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]}).should == true
+ end
+
+ it "gives error if action create and parent does not exist and recursive is set to false" do
+ @new_resource.key(reg_child + '\Missing1\Missing2')
+ @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}])
+ @new_resource.recursive(false)
+ lambda{@new_resource.run_action(:create)}.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "creates missing keys if action create and parent does not exist and recursive is set to true" do
+ @new_resource.key(reg_child + '\Missing1\Missing2')
+ @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:create)
+
+ @registry.key_exists?(reg_child + '\Missing1\Missing2').should == true
+ @registry.value_exists?(reg_child + '\Missing1\Missing2', {:name=>"OC", :type=>:string, :data=>"MissingData"}).should == true
+ end
+
+ it "creates key with multiple value as specified" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"one", :type=>:string, :data=>"1"},{:name=>"two", :type=>:string, :data=>"2"},{:name=>"three", :type=>:string, :data=>"3"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:create)
+
+ @new_resource.values.each do |value|
+ @registry.value_exists?(reg_child, value).should == true
+ end
+ end
+
+ context "when running on 64-bit server", :windows64_only do
+ before(:all) do
+ reset_registry
+ end
+ after(:all) do
+ @new_resource.architecture(:machine)
+ @registry.architecture = :machine
+ end
+ it "creates a key in a 32-bit registry that is not viewable in 64-bit" do
+ @new_resource.key(reg_child + '\Atraxi' )
+ @new_resource.values([{:name=>"OC", :type=>:string, :data=>"Data"}])
+ @new_resource.recursive(true)
+ @new_resource.architecture(:i386)
+ @new_resource.run_action(:create)
+ @registry.architecture = :i386
+ @registry.data_exists?(reg_child + '\Atraxi', {:name=>"OC", :type=>:string, :data=>"Data"}).should == true
+ @registry.architecture = :x86_64
+ @registry.key_exists?(reg_child + '\Atraxi').should == false
+ end
+ end
+
+ it "prepares the reporting data for action :create" do
+ @new_resource.key(reg_child + '\Ood')
+ @new_resource.values([{:name=>"ReportingVal1", :type=>:string, :data=>"report1"},{:name=>"ReportingVal2", :type=>:string, :data=>"report2"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:create)
+ @report = @resource_reporter.prepare_run_data
+
+ @report["action"].should == "end"
+ @report["resources"][0]["type"].should == "registry_key"
+ @report["resources"][0]["name"].should == resource_name
+ @report["resources"][0]["id"].should == reg_child + '\Ood'
+ @report["resources"][0]["after"][:values].should == [{:name=>"ReportingVal1", :type=>:string, :data=>"report1"},
+ {:name=>"ReportingVal2", :type=>:string, :data=>"report2"}]
+ @report["resources"][0]["before"][:values].should == []
+ @report["resources"][0]["result"].should == "create"
+ @report["status"].should == "success"
+ @report["total_res_count"].should == "1"
+ end
+
+ context "while running in whyrun mode" do
+ before (:all) do
+ Chef::Config[:why_run] = true
+ end
+ after (:all) do
+ Chef::Config[:why_run] = @current_whyrun
+ end
+
+ it "does not throw an exception if the keys do not exist but recursive is set to false" do
+ @new_resource.key(reg_child + '\Slitheen\Raxicoricofallapatorius')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ lambda{@new_resource.run_action(:create)}.should_not raise_error
+ @registry.key_exists?(reg_child + '\Slitheen').should == false
+ @registry.key_exists?(reg_child + '\Slitheen\Raxicoricofallapatorius').should == false
+ end
+ it "does not create key if the action is create" do
+ @new_resource.key(reg_child + '\Slitheen')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:create)
+ @registry.key_exists?(reg_child + '\Slitheen').should == false
+ end
+ end
+ end
+
+ context "when action is create_if_missing" do
+ before (:all) do
+ reset_registry
+ end
+
+ it "creates registry key, value if the key is missing" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
+ @new_resource.run_action(:create_if_missing)
+
+ @registry.key_exists?(reg_parent).should == true
+ @registry.key_exists?(reg_child).should == true
+ @registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+ end
+
+ it "does not create the key if it already exists with same value, type and data" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
+ @new_resource.run_action(:create_if_missing)
+
+ @registry.key_exists?(reg_child).should == true
+ @registry.data_exists?(reg_child, {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+ end
+
+ it "creates a value if it does not exist" do
+ @new_resource.key(reg_child)
+ @new_resource.values([{:name=>"Mango", :type=>:string, :data=>"Yellow"}])
+ @new_resource.run_action(:create_if_missing)
+
+ @registry.data_exists?(reg_child, {:name=>"Mango", :type=>:string, :data=>"Yellow"}).should == true
+ end
+
+ it "creates subkey if parent exists" do
+ @new_resource.key(reg_child + '\Pyrovile')
+ @new_resource.values([{:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:create_if_missing)
+
+ @registry.key_exists?(reg_child + '\Pyrovile').should == true
+ @registry.value_exists?(reg_child + '\Pyrovile', {:name=>"Chef", :type=>:multi_string, :data=>["OpscodeOrange", "Rules"]}).should == true
+ end
+
+ it "gives error if action create and parent does not exist and recursive is set to false" do
+ @new_resource.key(reg_child + '\Sontaran\Sontar')
+ @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}])
+ @new_resource.recursive(false)
+ lambda{@new_resource.run_action(:create_if_missing)}.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "creates missing keys if action create and parent does not exist and recursive is set to true" do
+ @new_resource.key(reg_child + '\Sontaran\Sontar')
+ @new_resource.values([{:name=>"OC", :type=>:string, :data=>"MissingData"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:create_if_missing)
+
+ @registry.key_exists?(reg_child + '\Sontaran\Sontar').should == true
+ @registry.value_exists?(reg_child + '\Sontaran\Sontar', {:name=>"OC", :type=>:string, :data=>"MissingData"}).should == true
+ end
+
+ it "creates key with multiple value as specified" do
+ @new_resource.key(reg_child + '\Adipose')
+ @new_resource.values([{:name=>"one", :type=>:string, :data=>"1"},{:name=>"two", :type=>:string, :data=>"2"},{:name=>"three", :type=>:string, :data=>"3"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:create_if_missing)
+
+ @new_resource.values.each do |value|
+ @registry.value_exists?(reg_child + '\Adipose', value).should == true
+ end
+ end
+
+ it "prepares the reporting data for :create_if_missing" do
+ @new_resource.key(reg_child + '\Judoon')
+ @new_resource.values([{:name=>"ReportingVal3", :type=>:string, :data=>"report3"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:create_if_missing)
+ @report = @resource_reporter.prepare_run_data
+
+ @report["action"].should == "end"
+ @report["resources"][0]["type"].should == "registry_key"
+ @report["resources"][0]["name"].should == resource_name
+ @report["resources"][0]["id"].should == reg_child + '\Judoon'
+ @report["resources"][0]["after"][:values].should == [{:name=>"ReportingVal3", :type=>:string, :data=>"report3"}]
+ @report["resources"][0]["before"][:values].should == []
+ @report["resources"][0]["result"].should == "create_if_missing"
+ @report["status"].should == "success"
+ @report["total_res_count"].should == "1"
+ end
+
+ context "while running in whyrun mode" do
+ before (:all) do
+ Chef::Config[:why_run] = true
+ end
+ after (:all) do
+ Chef::Config[:why_run] = @current_whyrun
+ end
+
+ it "does not throw an exception if the keys do not exist but recursive is set to false" do
+ @new_resource.key(reg_child + '\Zygons\Zygor')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ lambda{@new_resource.run_action(:create_if_missing)}.should_not raise_error
+ @registry.key_exists?(reg_child + '\Zygons').should == false
+ @registry.key_exists?(reg_child + '\Zygons\Zygor').should == false
+ end
+ it "does nothing if the action is create_if_missing" do
+ @new_resource.key(reg_child + '\Zygons')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:create_if_missing)
+ @registry.key_exists?(reg_child + '\Zygons').should == false
+ end
+ end
+ end
+
+ context "when the action is delete" do
+ before(:all) do
+ reset_registry
+ create_deletable_keys
+ end
+
+ it "takes no action if the specified key path does not exist in the system" do
+ @registry.key_exists?(reg_parent + '\Osirian').should == false
+
+ @new_resource.key(reg_parent+ '\Osirian')
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete)
+
+ @registry.key_exists?(reg_parent + '\Osirian').should == false
+ end
+
+ it "takes no action if the key exists but the value does not" do
+ @registry.data_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+
+ @new_resource.key(reg_parent + '\Opscode')
+ @new_resource.values([{:name=>"LooksLike", :type=>:multi_string, :data=>["SeattleGrey", "OCOrange"]}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete)
+
+ @registry.data_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+ end
+
+ it "deletes only specified values under a key path" do
+ @new_resource.key(reg_parent + '\Opscode')
+ @new_resource.values([{:name=>"Opscode", :type=>:multi_string, :data=>["Seattle", "Washington"]}, {:name=>"AKA", :type=>:string, :data=>"OC"}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete)
+
+ @registry.data_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"}).should == true
+ @registry.value_exists?(reg_parent + '\Opscode', {:name=>"AKA", :type=>:string, :data=>"OC"}).should == false
+ @registry.value_exists?(reg_parent + '\Opscode', {:name=>"Opscode", :type=>:multi_string, :data=>["Seattle", "Washington"]}).should == false
+ end
+
+ it "it deletes the values with the same name irrespective of it type and data" do
+ @new_resource.key(reg_parent + '\Opscode')
+ @new_resource.values([{:name=>"Color", :type=>:multi_string, :data=>["Black", "Orange"]}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete)
+
+ @registry.value_exists?(reg_parent + '\Opscode', {:name=>"Color", :type=>:string, :data=>"Orange"}).should == false
+ end
+
+ it "prepares the reporting data for action :delete" do
+ @new_resource.key(reg_parent + '\ReportKey')
+ @new_resource.values([{:name=>"ReportVal4", :type=>:string, :data=>"report4"},{:name=>"ReportVal5", :type=>:string, :data=>"report5"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:delete)
+
+ @report = @resource_reporter.prepare_run_data
+
+ @registry.value_exists?(reg_parent + '\ReportKey', [{:name=>"ReportVal4", :type=>:string, :data=>"report4"},{:name=>"ReportVal5", :type=>:string, :data=>"report5"}]).should == false
+
+ @report["action"].should == "end"
+ @report["resources"].count.should == 1
+ @report["resources"][0]["type"].should == "registry_key"
+ @report["resources"][0]["name"].should == resource_name
+ @report["resources"][0]["id"].should == reg_parent + '\ReportKey'
+ @report["resources"][0]["before"][:values].should == [{:name=>"ReportVal4", :type=>:string, :data=>"report4"},
+ {:name=>"ReportVal5", :type=>:string, :data=>"report5"}]
+ #Not testing for after values to match since after -> new_resource values.
+ @report["resources"][0]["result"].should == "delete"
+ @report["status"].should == "success"
+ @report["total_res_count"].should == "1"
+ end
+
+ context "while running in whyrun mode" do
+ before (:all) do
+ Chef::Config[:why_run] = true
+ end
+ after (:all) do
+ Chef::Config[:why_run] = @current_whyrun
+ end
+ it "does nothing if the action is delete" do
+ @new_resource.key(reg_parent + '\OpscodeWhyRun')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete)
+
+ @registry.key_exists?(reg_parent + '\OpscodeWhyRun').should == true
+ end
+ end
+ end
+
+ context "when the action is delete_key" do
+ before (:all) do
+ reset_registry
+ create_deletable_keys
+ end
+
+ it "takes no action if the specified key path does not exist in the system" do
+ @registry.key_exists?(reg_parent + '\Osirian').should == false
+
+ @new_resource.key(reg_parent + '\Osirian')
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete_key)
+
+ @registry.key_exists?(reg_parent + '\Osirian').should == false
+ end
+
+ it "deletes key if it has no subkeys and recursive == false" do
+ @new_resource.key(reg_parent + '\OpscodeTest')
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete_key)
+
+ @registry.key_exists?(reg_parent + '\OpscodeTest').should == false
+ end
+
+ it "raises an exception if the the key has subkeys and recursive == false" do
+ @new_resource.key(reg_parent)
+ @new_resource.recursive(false)
+ lambda{@new_resource.run_action(:delete_key)}.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "ignores the values under a key" do
+ @new_resource.key(reg_parent + '\OpscodeIgnoredValues')
+ #@new_resource.values([{:name=>"DontExist", :type=>:string, :data=>"These will be ignored anyways"}])
+ @new_resource.recursive(true)
+ @new_resource.run_action(:delete_key)
+ end
+
+ it "deletes the key if it has subkeys and recursive == true" do
+ @new_resource.key(reg_parent + '\Opscode')
+ @new_resource.recursive(true)
+ @new_resource.run_action(:delete_key)
+
+ @registry.key_exists?(reg_parent + '\Opscode').should == false
+ end
+
+ it "prepares the reporting data for action :delete_key" do
+ @new_resource.key(reg_parent + '\ReportKey')
+ @new_resource.recursive(true)
+ @new_resource.run_action(:delete_key)
+
+ @report = @resource_reporter.prepare_run_data
+ @report["action"].should == "end"
+ @report["resources"][0]["type"].should == "registry_key"
+ @report["resources"][0]["name"].should == resource_name
+ @report["resources"][0]["id"].should == reg_parent + '\ReportKey'
+ #Not testing for before or after values to match since
+ #after -> new_resource.values and
+ #before -> current_resource.values
+ @report["resources"][0]["result"].should == "delete_key"
+ @report["status"].should == "success"
+ @report["total_res_count"].should == "1"
+ end
+ context "while running in whyrun mode" do
+ before (:all) do
+ Chef::Config[:why_run] = true
+ end
+ after (:all) do
+ Chef::Config[:why_run] = @current_whyrun
+ end
+
+ it "does not throw an exception if the key has subkeys but recursive is set to false" do
+ @new_resource.key(reg_parent + '\OpscodeWhyRun')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete_key)
+ @new_resource.should_not raise_error(ArgumentError)
+ end
+ it "does nothing if the action is delete_key" do
+ @new_resource.key(reg_parent + '\OpscodeWhyRun')
+ @new_resource.values([{:name=>"BriskWalk",:type=>:string,:data=>"is good for health"}])
+ @new_resource.recursive(false)
+ @new_resource.run_action(:delete_key)
+
+ @registry.key_exists?(reg_parent + '\OpscodeWhyRun').should == true
+ end
+ end
+ end
+end
diff --git a/spec/functional/win32/registry_helper_spec.rb b/spec/functional/win32/registry_helper_spec.rb
new file mode 100644
index 0000000000..830d6f4777
--- /dev/null
+++ b/spec/functional/win32/registry_helper_spec.rb
@@ -0,0 +1,632 @@
+#
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+# 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.
+#
+
+require 'spec_helper'
+require 'chef/win32/registry'
+
+describe Chef::Resource::RegistryKey, :unix_only do
+ before(:all) do
+ events = Chef::EventDispatch::Dispatcher.new
+ node = Chef::Node.new
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ node.consume_external_attrs(ohai.data,{})
+ run_context = Chef::RunContext.new(node, {}, events)
+ @resource = Chef::Resource::RegistryKey.new("HKCU\\Software", run_context)
+ end
+ context "when load_current_resource is run on a non-windows node" do
+ it "throws an exception because you don't have a windows registry (derp)" do
+ @resource.key("HKCU\\Software\\Opscode")
+ @resource.values([{:name=>"Color", :type=>:string, :data=>"Orange"}])
+ lambda{@resource.run_action(:create)}.should raise_error(Chef::Exceptions::Win32NotWindows)
+ end
+ end
+end
+
+describe 'Chef::Win32::Registry', :windows_only do
+
+ before(:all) do
+ #Create a registry item
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root"
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch"
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Flower"
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['RootType1', Win32::Registry::REG_SZ] = 'fibrous'
+ reg.write('Roots', Win32::Registry::REG_MULTI_SZ, ["strong roots", "healthy tree"])
+ end
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['Strong', Win32::Registry::REG_SZ] = 'bird nest'
+ end
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch\\Flower', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['Petals', Win32::Registry::REG_MULTI_SZ] = ["Pink", "Delicate"]
+ end
+
+ #Create the node with ohai data
+ events = Chef::EventDispatch::Dispatcher.new
+ @node = Chef::Node.new
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ @node.consume_external_attrs(ohai.data,{})
+ @run_context = Chef::RunContext.new(@node, {}, events)
+
+ #Create a registry object that has access ot the node previously created
+ @registry = Chef::Win32::Registry.new(@run_context)
+ end
+
+ #Delete what is left of the registry key-values previously created
+ after(:all) do
+ ::Win32::Registry::HKEY_CURRENT_USER.open("Software") do |reg|
+ reg.delete_key("Root", true)
+ end
+ end
+
+ # Server Versions
+ # it "succeeds if server versiion is 2003R2, 2008, 2008R2, 2012" do
+ # end
+ # it "falis if the server versions are anything else" do
+ # end
+
+ describe "hive_exists?" do
+ it "returns true if the hive exists" do
+ @registry.hive_exists?("HKCU\\Software\\Root").should == true
+ end
+
+ it "returns false if the hive does not exist" do
+ hive = @registry.hive_exists?("LYRU\\Software\\Root").should == false
+ end
+ end
+
+ describe "key_exists?" do
+ it "returns true if the key path exists" do
+ @registry.key_exists?("HKCU\\Software\\Root\\Branch\\Flower").should == true
+ end
+
+ it "returns false if the key path does not exist" do
+ @registry.key_exists?("HKCU\\Software\\Branch\\Flower").should == false
+ end
+
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.key_exists?("JKLM\\Software\\Branch\\Flower")}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ end
+
+ describe "key_exists!" do
+ it "returns true if the key path exists" do
+ @registry.key_exists!("HKCU\\Software\\Root\\Branch\\Flower").should == true
+ end
+
+ it "throws an exception if the key path does not exist" do
+ lambda {@registry.key_exists!("HKCU\\Software\\Branch\\Flower")}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.key_exists!("JKLM\\Software\\Branch\\Flower")}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ end
+
+ describe "value_exists?" do
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.value_exists?("JKLM\\Software\\Branch\\Flower", {:name=>"Petals"})}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ it "throws an exception if the key does not exist" do
+ lambda {@registry.value_exists?("HKCU\\Software\\Branch\\Flower", {:name=>"Petals"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "returns true if the value exists" do
+ @registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"}).should == true
+ end
+ it "returns false if the value does not exist" do
+ @registry.value_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"}).should == false
+ end
+ end
+
+ describe "value_exists!" do
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.value_exists!("JKLM\\Software\\Branch\\Flower", {:name=>"Petals"})}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ it "throws an exception if the key does not exist" do
+ lambda {@registry.value_exists!("HKCU\\Software\\Branch\\Flower", {:name=>"Petals"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "returns true if the value exists" do
+ @registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals"}).should == true
+ end
+ it "throws an exception if the value does not exist" do
+ lambda {@registry.value_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"FOOBAR"})}.should raise_error(Chef::Exceptions::Win32RegValueMissing)
+ end
+ end
+
+ describe "data_exists?" do
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.data_exists?("JKLM\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ it "throws an exception if the key does not exist" do
+ lambda {@registry.data_exists?("HKCU\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "returns true if all the data matches" do
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]}).should == true
+ end
+ it "returns false if the name does not exist" do
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]}).should == false
+ end
+ it "returns false if the types do not match" do
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Pink"}).should == false
+ end
+ it "returns false if the data does not match" do
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Mauve", "Delicate"]}).should == false
+ end
+ end
+
+ describe "data_exists!" do
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.data_exists!("JKLM\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ it "throws an exception if the key does not exist" do
+ lambda {@registry.data_exists!("HKCU\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "returns true if all the data matches" do
+ @registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Pink", "Delicate"]}).should == true
+ end
+ it "throws an exception if the name does not exist" do
+ lambda {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"slateP", :type=>:multi_string, :data=>["Pink", "Delicate"]})}.should raise_error(Chef::Exceptions::Win32RegDataMissing)
+ end
+ it "throws an exception if the types do not match" do
+ lambda {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Pink"})}.should raise_error(Chef::Exceptions::Win32RegDataMissing)
+ end
+ it "throws an exception if the data does not match" do
+ lambda {@registry.data_exists!("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Mauve", "Delicate"]})}.should raise_error(Chef::Exceptions::Win32RegDataMissing)
+ end
+ end
+
+ describe "get_values" do
+ it "returns all values for a key if it exists" do
+ values = @registry.get_values("HKCU\\Software\\Root")
+ values.should be_an_instance_of Array
+ values.should == [{:name=>"RootType1", :type=>:string, :data=>"fibrous"},
+ {:name=>"Roots", :type=>:multi_string, :data=>["strong roots", "healthy tree"]}]
+ end
+
+ it "throws an exception if the key does not exist" do
+ lambda {@registry.get_values("HKCU\\Software\\Branch\\Flower")}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.get_values("JKLM\\Software\\Branch\\Flower")}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ end
+
+ describe "set_value" do
+ it "updates a value if the key, value exist and type matches and value different" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == true
+ end
+
+ it "updates a value if the type does match and the values are different" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Yellow"}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:string, :data=>"Yellow"}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == false
+ end
+
+ it "creates a value if key exists and value does not" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == true
+ end
+
+ it "does nothing if data,type and name parameters for the value are same" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == false
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"Stamen", :type=>:multi_string, :data=>["Yellow", "Changed Color"]}).should == true
+ end
+
+ it "throws an exception if the key does not exist" do
+ lambda {@registry.set_value("HKCU\\Software\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.set_value("JKLM\\Software\\Root\\Branch\\Flower", {:name=>"Petals", :type=>:multi_string, :data=>["Yellow", "Changed Color"]})}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+
+ # we are validating that the data gets .to_i called on it when type is a :dword
+
+ it "casts an integer string given as a dword into an integer" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe32767", :type=>:dword, :data=>"32767"}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe32767", :type=>:dword, :data=>32767}).should == true
+ end
+
+ it "casts a nonsense string given as a dword into zero" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBeZero", :type=>:dword, :data=>"whatdoesthisdo"}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBeZero", :type=>:dword, :data=>0}).should == true
+ end
+
+ it "throws an exception when trying to cast an array to an int for a dword" do
+ lambda {@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldThrow", :type=>:dword, :data=>["one","two"]})}.should raise_error
+ end
+
+ # we are validating that the data gets .to_s called on it when type is a :string
+
+ it "stores the string representation of an array into a string if you pass it an array" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBePainful", :type=>:string, :data=>["one","two"]}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBePainful", :type=>:string, :data=>'["one", "two"]'}).should == true
+ end
+
+ it "stores the string representation of a number into a string if you pass it an number" do
+ @registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe65535", :type=>:string, :data=>65535}).should == true
+ @registry.data_exists?("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBe65535", :type=>:string, :data=>"65535"}).should == true
+ end
+
+ # we are validating that the data gets .to_a called on it when type is a :multi_string
+
+ it "throws an exception when a multi-string is passed a number" do
+ lambda {@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldThrow", :type=>:multi_string, :data=>65535})}.should raise_error
+ end
+
+ it "throws an exception when a multi-string is passed a string" do
+ lambda {@registry.set_value("HKCU\\Software\\Root\\Branch\\Flower", {:name=>"ShouldBeWat", :type=>:multi_string, :data=>"foo"})}.should raise_error
+ end
+ end
+
+ describe "create_key" do
+ before(:all) do
+ ::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root") do |reg|
+ begin
+ reg.delete_key("Trunk", true)
+ rescue
+ end
+ end
+ end
+
+ it "throws an exception if the path has missing keys but recursive set to false" do
+ lambda {@registry.create_key("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", false)}.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ @registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker").should == false
+ end
+
+ it "creates the key_path if the keys were missing but recursive was set to true" do
+ @registry.create_key("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", true).should == true
+ @registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker").should == true
+ end
+
+ it "does nothing if the key already exists" do
+ @registry.create_key("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", false).should == true
+ @registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker").should == true
+ end
+
+ it "throws an exception of the hive does not exist" do
+ lambda {@registry.create_key("JKLM\\Software\\Root\\Trunk\\Peck\\Woodpecker", false)}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ end
+
+ describe "delete_value" do
+ before(:all) do
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk\\Peck\\Woodpecker"
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Trunk\\Peck\\Woodpecker', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['Peter', Win32::Registry::REG_SZ] = 'Tiny'
+ end
+ end
+
+ it "deletes values if the value exists" do
+ @registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"}).should == true
+ @registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"}).should == false
+ end
+
+ it "does nothing if value does not exist" do
+ @registry.delete_value("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"}).should == true
+ @registry.value_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"}).should == false
+ end
+
+ it "throws an exception if the key does not exist?" do
+ lambda {@registry.delete_value("HKCU\\Software\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.delete_value("JKLM\\Software\\Root\\Trunk\\Peck\\Woodpecker", {:name=>"Peter", :type=>:string, :data=>"Tiny"})}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ end
+
+ describe "delete_key" do
+ before (:all) do
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Branch\\Fruit"
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Branch\\Fruit', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['Apple', Win32::Registry::REG_MULTI_SZ] = ["Red", "Juicy"]
+ end
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk\\Peck\\Woodpecker"
+ ::Win32::Registry::HKEY_CURRENT_USER.open('Software\\Root\\Trunk\\Peck\\Woodpecker', Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg['Peter', Win32::Registry::REG_SZ] = 'Tiny'
+ end
+ end
+
+ it "deletes a key if it has no subkeys" do
+ @registry.delete_key("HKCU\\Software\\Root\\Branch\\Fruit", false).should == true
+ @registry.key_exists?("HKCU\\Software\\Root\\Branch\\Fruit").should == false
+ end
+
+ it "throws an exception if key to delete has subkeys and recursive is false" do
+ lambda { @registry.delete_key("HKCU\\Software\\Root\\Trunk", false) }.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ @registry.key_exists?("HKCU\\Software\\Root\\Trunk\\Peck\\Woodpecker").should == true
+ end
+
+ it "deletes a key if it has subkeys and recursive true" do
+ @registry.delete_key("HKCU\\Software\\Root\\Trunk", true).should == true
+ @registry.key_exists?("HKCU\\Software\\Root\\Trunk").should == false
+ end
+
+ it "does nothing if the key does not exist" do
+ @registry.delete_key("HKCU\\Software\\Root\\Trunk", true).should == true
+ @registry.key_exists?("HKCU\\Software\\Root\\Trunk").should == false
+ end
+
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.delete_key("JKLM\\Software\\Root\\Branch\\Flower", false)}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ end
+
+ describe "has_subkeys?" do
+ before(:all) do
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk"
+ ::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root\\Trunk") do |reg|
+ begin
+ reg.delete_key("Red", true)
+ rescue
+ end
+ end
+ end
+
+ it "throws an exception if the hive was missing" do
+ lambda {@registry.has_subkeys?("LMNO\\Software\\Root")}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+
+ it "throws an exception if the key is missing" do
+ lambda {@registry.has_subkeys?("HKCU\\Software\\Root\\Trunk\\Red")}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "returns true if the key has subkeys" do
+ @registry.has_subkeys?("HKCU\\Software\\Root").should == true
+ end
+
+ it "returns false if the key has no subkeys" do
+ ::Win32::Registry::HKEY_CURRENT_USER.create "Software\\Root\\Trunk\\Red"
+ @registry.has_subkeys?("HKCU\\Software\\Root\\Trunk\\Red").should == false
+ end
+ end
+
+ describe "get_subkeys" do
+ it "throws an exception if the key is missing" do
+ lambda {@registry.get_subkeys("HKCU\\Software\\Trunk\\Red")}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "throws an exception if the hive does not exist" do
+ lambda {@registry.get_subkeys("JKLM\\Software\\Root")}.should raise_error(Chef::Exceptions::Win32RegHiveMissing)
+ end
+ it "returns the array of subkeys for a given key" do
+ subkeys = @registry.get_subkeys("HKCU\\Software\\Root")
+ reg_subkeys = []
+ ::Win32::Registry::HKEY_CURRENT_USER.open("Software\\Root", Win32::Registry::KEY_ALL_ACCESS) do |reg|
+ reg.each_key{|name| reg_subkeys << name}
+ end
+ reg_subkeys.should == subkeys
+ end
+ end
+
+ describe "architecture" do
+ describe "on 32-bit" do
+ before(:all) do
+ @saved_kernel_machine = @node.automatic_attrs[:kernel][:machine]
+ @node.automatic_attrs[:kernel][:machine] = :i386
+ end
+
+ after(:all) do
+ @node.automatic_attrs[:kernel][:machine] = @saved_kernel_machine
+ end
+
+ context "registry constructor" do
+ it "throws an exception if requested architecture is 64bit but running on 32bit" do
+ lambda {Chef::Win32::Registry.new(@run_context, :x86_64)}.should raise_error(Chef::Exceptions::Win32RegArchitectureIncorrect)
+ end
+
+ it "can correctly set the requested architecture to 32-bit" do
+ @r = Chef::Win32::Registry.new(@run_context, :i386)
+ @r.architecture.should == :i386
+ @r.registry_system_architecture.should == 0x0200
+ end
+
+ it "can correctly set the requested architecture to :machine" do
+ @r = Chef::Win32::Registry.new(@run_context, :machine)
+ @r.architecture.should == :machine
+ @r.registry_system_architecture.should == 0x0200
+ end
+ end
+
+ context "architecture setter" do
+ it "throws an exception if requested architecture is 64bit but running on 32bit" do
+ lambda {@registry.architecture = :x86_64}.should raise_error(Chef::Exceptions::Win32RegArchitectureIncorrect)
+ end
+
+ it "sets the requested architecture to :machine if passed :machine" do
+ @registry.architecture = :machine
+ @registry.architecture.should == :machine
+ @registry.registry_system_architecture.should == 0x0200
+ end
+
+ it "sets the requested architecture to 32-bit if passed i386 as a string" do
+ @registry.architecture = :i386
+ @registry.architecture.should == :i386
+ @registry.registry_system_architecture.should == 0x0200
+ end
+ end
+ end
+
+ describe "on 64-bit" do
+ before(:all) do
+ @saved_kernel_machine = @node.automatic_attrs[:kernel][:machine]
+ @node.automatic_attrs[:kernel][:machine] = :x86_64
+ end
+
+ after(:all) do
+ @node.automatic_attrs[:kernel][:machine] = @saved_kernel_machine
+ end
+
+ context "registry constructor" do
+ it "can correctly set the requested architecture to 32-bit" do
+ @r = Chef::Win32::Registry.new(@run_context, :i386)
+ @r.architecture.should == :i386
+ @r.registry_system_architecture.should == 0x0200
+ end
+
+ it "can correctly set the requested architecture to 64-bit" do
+ @r = Chef::Win32::Registry.new(@run_context, :x86_64)
+ @r.architecture.should == :x86_64
+ @r.registry_system_architecture.should == 0x0100
+ end
+
+ it "can correctly set the requested architecture to :machine" do
+ @r = Chef::Win32::Registry.new(@run_context, :machine)
+ @r.architecture.should == :machine
+ @r.registry_system_architecture.should == 0x0100
+ end
+ end
+
+ context "architecture setter" do
+ it "sets the requested architecture to 64-bit if passed 64-bit" do
+ @registry.architecture = :x86_64
+ @registry.architecture.should == :x86_64
+ @registry.registry_system_architecture.should == 0x0100
+ end
+
+ it "sets the requested architecture to :machine if passed :machine" do
+ @registry.architecture = :machine
+ @registry.architecture.should == :machine
+ @registry.registry_system_architecture.should == 0x0100
+ end
+
+ it "sets the requested architecture to 32-bit if passed 32-bit" do
+ @registry.architecture = :i386
+ @registry.architecture.should == :i386
+ @registry.registry_system_architecture.should == 0x0200
+ end
+ end
+ end
+
+ describe "when running on an actual 64-bit server", :windows64_only do
+ before(:all) do
+ begin
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg|
+ reg.delete_key("Trunk", true)
+ end
+ rescue
+ end
+ begin
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg|
+ reg.delete_key("Trunk", true)
+ end
+ rescue
+ end
+ # 64-bit
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.create("Software\\Root\\Mauve", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100)
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open('Software\\Root\\Mauve', Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg|
+ reg['Alert', Win32::Registry::REG_SZ] = 'Universal'
+ end
+ # 32-bit
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.create("Software\\Root\\Poosh", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200)
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open('Software\\Root\\Poosh', Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg|
+ reg['Status', Win32::Registry::REG_SZ] = 'Lost'
+ end
+ end
+
+ after(:all) do
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0100) do |reg|
+ reg.delete_key("Trunk", true)
+ end
+ ::Win32::Registry::HKEY_LOCAL_MACHINE.open("Software\\Root", ::Win32::Registry::KEY_ALL_ACCESS | 0x0200) do |reg|
+ reg.delete_key("Trunk", true)
+ end
+ end
+
+ describe "key_exists?" do
+ it "does not find 64-bit keys in the 32-bit registry" do
+ @registry.architecture=:i386
+ @registry.key_exists?("HKLM\\Software\\Root\\Mauve").should == false
+ end
+ it "finds 32-bit keys in the 32-bit registry" do
+ @registry.architecture=:i386
+ @registry.key_exists?("HKLM\\Software\\Root\\Poosh").should == true
+ end
+ it "does not find 32-bit keys in the 64-bit registry" do
+ @registry.architecture=:x86_64
+ @registry.key_exists?("HKLM\\Software\\Root\\Mauve").should == true
+ end
+ it "finds 64-bit keys in the 64-bit registry" do
+ @registry.architecture=:x86_64
+ @registry.key_exists?("HKLM\\Software\\Root\\Poosh").should == false
+ end
+ end
+
+ describe "value_exists?" do
+ it "does not find 64-bit values in the 32-bit registry" do
+ @registry.architecture=:i386
+ lambda{@registry.value_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "finds 32-bit values in the 32-bit registry" do
+ @registry.architecture=:i386
+ @registry.value_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status"}).should == true
+ end
+ it "does not find 32-bit values in the 64-bit registry" do
+ @registry.architecture=:x86_64
+ @registry.value_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert"}).should == true
+ end
+ it "finds 64-bit values in the 64-bit registry" do
+ @registry.architecture=:x86_64
+ lambda{@registry.value_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "data_exists?" do
+ it "does not find 64-bit keys in the 32-bit registry" do
+ @registry.architecture=:i386
+ lambda{@registry.data_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert", :type=>:string, :data=>"Universal"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ it "finds 32-bit keys in the 32-bit registry" do
+ @registry.architecture=:i386
+ @registry.data_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status", :type=>:string, :data=>"Lost"}).should == true
+ end
+ it "does not find 32-bit keys in the 64-bit registry" do
+ @registry.architecture=:x86_64
+ @registry.data_exists?("HKLM\\Software\\Root\\Mauve", {:name=>"Alert", :type=>:string, :data=>"Universal"}).should == true
+ end
+ it "finds 64-bit keys in the 64-bit registry" do
+ @registry.architecture=:x86_64
+ lambda{@registry.data_exists?("HKLM\\Software\\Root\\Poosh", {:name=>"Status", :type=>:string, :data=>"Lost"})}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "create_key" do
+ it "can create a 32-bit only registry key" do
+ @registry.architecture = :i386
+ @registry.create_key("HKLM\\Software\\Root\\Trunk\\Red", true).should == true
+ @registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Red").should == true
+ @registry.architecture = :x86_64
+ @registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Red").should == false
+ end
+
+ it "can create a 64-bit only registry key" do
+ @registry.architecture = :x86_64
+ @registry.create_key("HKLM\\Software\\Root\\Trunk\\Blue", true).should == true
+ @registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Blue").should == true
+ @registry.architecture = :i386
+ @registry.key_exists?("HKLM\\Software\\Root\\Trunk\\Blue").should == false
+ end
+ end
+
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8d64997d50..3a5ca22868 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -71,6 +71,8 @@ RSpec.configure do |config|
# Add jruby filters here
config.filter_run_excluding :windows_only => true unless windows?
config.filter_run_excluding :not_supported_on_win2k3 => true if windows_win2k3?
+ config.filter_run_excluding :windows64_only => true unless windows64?
+ config.filter_run_excluding :windows32_only => true unless windows32?
config.filter_run_excluding :unix_only => true unless unix?
config.filter_run_excluding :ruby_18_only => true unless ruby_18?
config.filter_run_excluding :ruby_19_only => true unless ruby_19?
diff --git a/spec/support/platform_helpers.rb b/spec/support/platform_helpers.rb
index fcb825319c..df94d991cc 100644
--- a/spec/support/platform_helpers.rb
+++ b/spec/support/platform_helpers.rb
@@ -18,6 +18,16 @@ def windows_win2k3?
(host.version && host.version.start_with?("5.2"))
end
+# detects if the hardware is 64-bit (evaluates to true in "WOW64" mode in a 32-bit app on a 64-bit system)
+def windows64?
+ windows? && ( ENV['PROCESSOR_ARCHITECTURE'] == 'AMD64' || ENV['PROCESSOR_ARCHITEW6432'] == 'AMD64' )
+end
+
+# detects if the hardware is 32-bit
+def windows32?
+ windows? && !windows64?
+end
+
# def jruby?
def unix?
diff --git a/spec/unit/dsl/regsitry_helper_spec.rb b/spec/unit/dsl/regsitry_helper_spec.rb
new file mode 100644
index 0000000000..7fe08e310f
--- /dev/null
+++ b/spec/unit/dsl/regsitry_helper_spec.rb
@@ -0,0 +1,55 @@
+#
+# Author:: Prajakta Purohit (<prajakta@opscode.com>)
+# Copyright:: Copyright (c) 2011 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.
+#
+
+require "chef/dsl/registry_helper"
+require "spec_helper"
+
+describe Chef::Resource::RegistryKey do
+
+ before (:all) do
+ events = Chef::EventDispatch::Dispatcher.new
+ node = Chef::Node.new
+ ohai = Ohai::System.new
+ ohai.all_plugins
+ node.consume_external_attrs(ohai.data,{})
+ run_context = Chef::RunContext.new(node, {}, events)
+ @resource = Chef::Resource::new("foo", run_context)
+ end
+
+ context "tests registry dsl" do
+ it "resource can access registry_helper method registry_key_exists" do
+ @resource.respond_to?('registry_key_exists?').should == true
+ end
+ it "resource can access registry_helper method registry_get_values" do
+ @resource.respond_to?('registry_get_values').should == true
+ end
+ it "resource can access registry_helper method registry_has_subkey" do
+ @resource.respond_to?('registry_has_subkeys?').should == true
+ end
+ it "resource can access registry_helper method registry_get_subkeys" do
+ @resource.respond_to?('registry_get_subkeys').should == true
+ end
+ it "resource can access registry_helper method registry_value_exists" do
+ @resource.respond_to?('registry_value_exists?').should == true
+ end
+ it "resource can access registry_helper method data_value_exists" do
+ @resource.respond_to?('registry_data_exists?').should == true
+ end
+ end
+end
+
diff --git a/spec/unit/provider/registry_key_spec.rb b/spec/unit/provider/registry_key_spec.rb
new file mode 100644
index 0000000000..2f6f8179e6
--- /dev/null
+++ b/spec/unit/provider/registry_key_spec.rb
@@ -0,0 +1,269 @@
+#
+# Author:: Lamont Granquist (lamont@opscode.com)
+# 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.
+#
+
+require 'spec_helper'
+
+describe Chef::Provider::RegistryKey do
+
+ let(:testval1) { { :name => "one", :type => :string, :data => "1" } }
+ let(:testval1_wrong_type) { { :name => "one", :type => :multi_string, :data => "1" } }
+ let(:testval1_wrong_data) { { :name => "one", :type => :string, :data => "2" } }
+ let(:testval2) { { :name => "two", :type => :string, :data => "2" } }
+ let(:testkey1) { 'HKLM\Software\Opscode\Testing' }
+
+ before(:each) do
+ @node = Chef::Node.new
+ @events = Chef::EventDispatch::Dispatcher.new
+ @run_context = Chef::RunContext.new(@node, {}, @events)
+
+ @new_resource = Chef::Resource::RegistryKey.new("windows is fun", @run_context)
+ @new_resource.key testkey1
+ @new_resource.values( testval1 )
+ @new_resource.recursive false
+
+ @provider = Chef::Provider::RegistryKey.new(@new_resource, @run_context)
+
+ @provider.stub!(:running_on_windows!).and_return(true)
+ @double_registry = double(Chef::Win32::Registry)
+ @provider.stub!(:registry).and_return(@double_registry)
+ end
+
+ describe "when first created" do
+ end
+
+ describe "executing load_current_resource" do
+ describe "when the key exists" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).with(testkey1).and_return(true)
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval2 )
+ @provider.load_current_resource
+ end
+
+ it "should set the key of the current resource to the key of the new resource" do
+ @provider.current_resource.key.should == @new_resource.key
+ end
+
+ it "should set the architecture of the current resource to the architecture of the new resource" do
+ @provider.current_resource.architecture.should == @new_resource.architecture
+ end
+
+ it "should set the recursive flag of the current resource to the recursive flag of the new resource" do
+ @provider.current_resource.recursive.should == @new_resource.recursive
+ end
+
+ it "should set the values of the current resource to the values it got from the registry" do
+ @provider.current_resource.values.should == [ testval2 ]
+ end
+ end
+
+ describe "when the key does not exist" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).with(testkey1).and_return(false)
+ @provider.load_current_resource
+ end
+
+ it "should set the values in the current resource to empty array" do
+ @provider.current_resource.values.should == []
+ end
+ end
+ end
+
+ describe "action_create" do
+ context "when the key exists" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(true)
+ end
+ it "should do nothing if the key and the value both exist" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1 )
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ it "should create the value if the key exists but the value does not" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval2 )
+ @double_registry.should_receive(:set_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ it "should set the value if the key exists but the data does not match" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1_wrong_data )
+ @double_registry.should_receive(:set_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ it "should set the value if the key exists but the type does not match" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1_wrong_type )
+ @double_registry.should_receive(:set_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ context "when the key exists and the values in the new resource are empty" do
+ it "when a value is in the key, it should do nothing" do
+ @provider.new_resource.values([])
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(true)
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1 )
+ @double_registry.should_not_receive(:create_key)
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ it "when no value is in the key, it should do nothing" do
+ @provider.new_resource.values([])
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(true)
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( nil )
+ @double_registry.should_not_receive(:create_key)
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ context "when the key does not exist" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(false)
+ end
+ it "should create the key and the value" do
+ @double_registry.should_receive(:create_key).with(testkey1, false)
+ @double_registry.should_receive(:set_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ context "when the key does not exist and the values in the new resource are empty" do
+ it "should create the key" do
+ @new_resource.values([])
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(false)
+ @double_registry.should_receive(:create_key).with(testkey1, false)
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create
+ end
+ end
+ end
+
+ describe "action_create_if_missing" do
+ context "when the key exists" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(true)
+ end
+ it "should do nothing if the key and the value both exist" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1 )
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create_if_missing
+ end
+ it "should create the value if the key exists but the value does not" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval2 )
+ @double_registry.should_receive(:set_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_create_if_missing
+ end
+ it "should not set the value if the key exists but the data does not match" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1_wrong_data )
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create_if_missing
+ end
+ it "should not set the value if the key exists but the type does not match" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1_wrong_type )
+ @double_registry.should_not_receive(:set_value)
+ @provider.load_current_resource
+ @provider.action_create_if_missing
+ end
+ end
+ context "when the key does not exist" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(false)
+ end
+ it "should create the key and the value" do
+ @double_registry.should_receive(:create_key).with(testkey1, false)
+ @double_registry.should_receive(:set_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_create_if_missing
+ end
+ end
+ end
+
+ describe "action_delete" do
+ context "when the key exists" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(true)
+ end
+ it "deletes the value when the value exists" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1 )
+ @double_registry.should_receive(:delete_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_delete
+ end
+ it "deletes the value when the value exists, but the type is wrong" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1_wrong_type )
+ @double_registry.should_receive(:delete_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_delete
+ end
+ it "deletes the value when the value exists, but the data is wrong" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1_wrong_data )
+ @double_registry.should_receive(:delete_value).with(testkey1, testval1)
+ @provider.load_current_resource
+ @provider.action_delete
+ end
+ it "does not delete the value when the value does not exist" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval2 )
+ @double_registry.should_not_receive(:delete_value)
+ @provider.load_current_resource
+ @provider.action_delete
+ end
+ end
+ context "when the key does not exist" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(false)
+ end
+ it "does nothing" do
+ @double_registry.should_not_receive(:delete_value)
+ @provider.load_current_resource
+ @provider.action_delete
+ end
+ end
+ end
+
+ describe "action_delete_key" do
+ context "when the key exists" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(true)
+ end
+ it "deletes the key" do
+ @double_registry.should_receive(:get_values).with(testkey1).and_return( testval1 )
+ @double_registry.should_receive(:delete_key).with(testkey1, false)
+ @provider.load_current_resource
+ @provider.action_delete_key
+ end
+ end
+ context "when the key does not exist" do
+ before(:each) do
+ @double_registry.should_receive(:key_exists?).twice.with(testkey1).and_return(false)
+ end
+ it "does nothing" do
+ @double_registry.should_not_receive(:delete_key)
+ @provider.load_current_resource
+ @provider.action_delete_key
+ end
+ end
+ end
+
+end
+
diff --git a/spec/unit/registry_helper_spec.rb b/spec/unit/registry_helper_spec.rb
new file mode 100644
index 0000000000..a83895ba20
--- /dev/null
+++ b/spec/unit/registry_helper_spec.rb
@@ -0,0 +1,374 @@
+#
+# Author:: Prajakta Purohit (prajakta@opscode.com)
+# 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.
+#
+
+require 'spec_helper'
+
+describe Chef::Provider::RegistryKey do
+
+ let(:value1) { { :name => "one", :type => :string, :data => "1" } }
+ let(:key_path) { 'HKCU\Software\OpscodeNumbers' }
+ let(:key) { 'Software\OpscodeNumbers' }
+ let(:key_parent) { 'Software' }
+ let(:key_to_delete) { 'OpscodeNumbers' }
+ let(:sub_key) {'OpscodePrimes'}
+ let(:missing_key_path) {'HKCU\Software'}
+
+ before(:each) do
+ Chef::Win32::Registry.any_instance.stub(:machine_architecture).and_return(:x86_64)
+ @registry = Chef::Win32::Registry.new()
+
+ #Making the values for registry constants available on unix
+ Object.send(:remove_const, 'Win32') if defined?(Win32)
+ Win32 = Module.new
+ Win32::Registry = Class.new
+ Win32::Registry::KEY_SET_VALUE = 0x0002
+ Win32::Registry::KEY_QUERY_VALUE = 0x0001
+ Win32::Registry::KEY_WRITE = 0x00020000 | 0x0002 | 0x0004
+ Win32::Registry::KEY_READ = 0x00020000 | 0x0001 | 0x0008 | 0x0010
+
+ Win32::Registry::Error = Class.new(RuntimeError)
+
+ @hive_mock = mock("::Win32::Registry::HKEY_CURRENT_USER")
+ @reg_mock = mock("reg")
+ end
+
+ describe "get_values" do
+ it "gets all values for a key if the key exists" do
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:map)
+ @registry.get_values(key_path)
+ end
+
+ it "throws an exception if key does not exist" do
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ lambda{@registry.get_values(key_path)}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "set_value" do
+ it "does nothing if key and hive and value exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(true)
+ @registry.should_receive(:data_exists?).with(key_path, value1).and_return(true)
+ @registry.set_value(key_path, value1)
+ end
+
+ it "updates value if key and hive and value exist, but data is different" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(true)
+ @registry.should_receive(:data_exists?).with(key_path, value1).and_return(false)
+ @hive_mock.should_receive(:open).with(key, Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @registry.should_receive(:get_type_from_name).with(:string).and_return(1)
+ @reg_mock.should_receive(:write).with("one", 1, "1")
+ @registry.set_value(key_path, value1)
+ end
+
+ it "creates value if the key exists and the value does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(false)
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | ::Win32::Registry::KEY_QUERY_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @registry.should_receive(:get_type_from_name).with(:string).and_return(1)
+ @reg_mock.should_receive(:write).with("one", 1, "1")
+ @registry.set_value(key_path, value1)
+ end
+
+ it "should raise an exception if the key does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ lambda {@registry.set_value(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "delete_value" do
+ it "deletes value if value exists" do
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_SET_VALUE | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:delete_value).with("one").and_return(true)
+ @registry.delete_value(key_path, value1)
+ end
+
+ it "raises an exception if the key does not exist" do
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ @registry.delete_value(key_path, value1)
+ end
+
+ it "does nothing if the value does not exist" do
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(false)
+ @registry.delete_value(key_path, value1)
+ end
+ end
+
+ describe "create_key" do
+ it "creates key if intermediate keys are missing and recursive is set to true" do
+ @registry.should_receive(:keys_missing?).with(key_path).and_return(true)
+ @registry.should_receive(:create_missing).with(key_path)
+ @registry.should_receive(:key_exists?).with(key_path).and_return(false)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture)
+ @registry.create_key(key_path, true)
+ end
+
+ it "raises an exception if intermediate keys are missing and recursive is set to false" do
+ @registry.should_receive(:keys_missing?).with(key_path).and_return(true)
+ lambda{@registry.create_key(key_path, false)}.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "does nothing if the key exists" do
+ @registry.should_receive(:keys_missing?).with(key_path).and_return(true)
+ @registry.should_receive(:create_missing).with(key_path)
+ @registry.should_receive(:key_exists?).with(key_path).and_return(true)
+ @registry.create_key(key_path, true)
+ end
+
+ it "create key if intermediate keys not missing and recursive is set to false" do
+ @registry.should_receive(:keys_missing?).with(key_path).and_return(false)
+ @registry.should_receive(:key_exists?).with(key_path).and_return(false)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture)
+ @registry.create_key(key_path, false)
+ end
+
+ it "create key if intermediate keys not missing and recursive is set to true" do
+ @registry.should_receive(:keys_missing?).with(key_path).and_return(false)
+ @registry.should_receive(:key_exists?).with(key_path).and_return(false)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:create).with(key, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture)
+ @registry.create_key(key_path, true)
+ end
+ end
+
+ describe "delete_key" do
+ it "deletes key if it has subkeys and recursive is set to true" do
+ @registry.should_receive(:key_exists?).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:has_subkeys?).with(key_path).and_return(true)
+ @hive_mock.should_receive(:open).with(key_parent, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:delete_key).with(key_to_delete, true)
+ @registry.delete_key(key_path, true)
+ end
+
+ it "raises an exception if it has subkeys but recursive is set to false" do
+ @registry.should_receive(:key_exists?).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:has_subkeys?).with(key_path).and_return(true)
+ lambda{@registry.delete_key(key_path, false)}.should raise_error(Chef::Exceptions::Win32RegNoRecursive)
+ end
+
+ it "deletes key if the key exists and has no subkeys" do
+ @registry.should_receive(:key_exists?).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:has_subkeys?).with(key_path).and_return(false)
+ @hive_mock.should_receive(:open).with(key_parent, ::Win32::Registry::KEY_WRITE | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:delete_key).with(key_to_delete)
+ @registry.delete_key(key_path, true)
+ end
+ end
+
+ describe "key_exists?" do
+ it "returns true if key_exists" do
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @registry.key_exists?(key_path).should == true
+ end
+
+ it "returns false if key does not exist" do
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_raise(::Win32::Registry::Error)
+ @registry.key_exists?(key_path).should == false
+ end
+ end
+
+ describe "key_exists!" do
+ it "throws an exception if the key_parent does not exist" do
+ @registry.should_receive(:key_exists?).with(key_path).and_return(false)
+ lambda{@registry.key_exists!(key_path)}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "hive_exists?" do
+ it "returns true if the hive exists" do
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.hive_exists?(key_path) == true
+ end
+
+ it "returns false if the hive does not exist" do
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_raise(Chef::Exceptions::Win32RegHiveMissing)
+ @registry.hive_exists?(key_path) == false
+ end
+ end
+
+ describe "has_subkeys?" do
+ it "returns true if the key has subkeys" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:each_key).and_yield(key)
+ @registry.has_subkeys?(key_path) == true
+ end
+
+ it "returns false if the key does not have subkeys" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:each_key).and_return(no_args())
+ @registry.has_subkeys?(key_path).should == false
+ end
+
+ it "throws an exception if the key does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ lambda {@registry.set_value(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+ end
+
+ describe "get_subkeys" do
+ it "returns the subkeys if they exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:each_key).and_yield(sub_key)
+ @registry.get_subkeys(key_path)
+ end
+ end
+
+ describe "value_exists?" do
+ it "throws an exception if the key does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ lambda {@registry.value_exists?(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "returns true if the value exists" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:any?).and_yield("one")
+ @registry.value_exists?(key_path, value1) == true
+ end
+
+ it "returns false if the value does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:any?).and_yield(no_args())
+ @registry.value_exists?(key_path, value1) == false
+ end
+ end
+
+ describe "data_exists?" do
+ it "throws an exception if the key does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_raise(Chef::Exceptions::Win32RegKeyMissing)
+ lambda {@registry.data_exists?(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegKeyMissing)
+ end
+
+ it "returns true if the data exists" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @registry.should_receive(:get_type_from_name).with(:string).and_return(1)
+ @reg_mock.should_receive(:each).with(no_args()).and_yield("one", 1, "1")
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @registry.data_exists?(key_path, value1).should == true
+ end
+
+ it "returns false if the data does not exist" do
+ @registry.should_receive(:key_exists!).with(key_path).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @registry.should_receive(:get_type_from_name).with(:string).and_return(1)
+ @reg_mock.should_receive(:each).with(no_args()).and_yield("one", 1, "2")
+ @registry.data_exists?(key_path, value1).should == false
+ end
+ end
+
+ describe "value_exists!" do
+ it "does nothing if the value exists" do
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(true)
+ @registry.value_exists!(key_path, value1)
+ end
+
+ it "throws an exception if the value does not exist" do
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(false)
+ lambda{@registry.value_exists!(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegValueMissing)
+ end
+ end
+
+ describe "data_exists!" do
+ it "does nothing if the data exists" do
+ @registry.should_receive(:data_exists?).with(key_path, value1).and_return(true)
+ @registry.data_exists!(key_path, value1)
+ end
+
+ it "throws an exception if the data does not exist" do
+ @registry.should_receive(:data_exists?).with(key_path, value1).and_return(false)
+ lambda{@registry.data_exists!(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegDataMissing)
+ end
+ end
+
+ describe "type_matches?" do
+ it "returns true if type matches" do
+ @registry.should_receive(:value_exists!).with(key_path, value1).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @registry.should_receive(:get_type_from_name).with(:string).and_return(1)
+ @reg_mock.should_receive(:each).and_yield("one", 1)
+ @registry.type_matches?(key_path, value1).should == true
+ end
+
+ it "returns false if type does not match" do
+ @registry.should_receive(:value_exists!).with(key_path, value1).and_return(true)
+ @registry.should_receive(:get_hive_and_key).with(key_path).and_return([@hive_mock, key])
+ @hive_mock.should_receive(:open).with(key, ::Win32::Registry::KEY_READ | @registry.registry_system_architecture).and_yield(@reg_mock)
+ @reg_mock.should_receive(:each).and_yield("two", 2)
+ @registry.type_matches?(key_path, value1).should == false
+ end
+
+ it "throws an exception if value does not exist" do
+ @registry.should_receive(:value_exists?).with(key_path, value1).and_return(false)
+ lambda{@registry.type_matches?(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegValueMissing)
+ end
+ end
+
+ describe "type_matches!" do
+ it "does nothing if the type_matches" do
+ @registry.should_receive(:type_matches?).with(key_path, value1).and_return(true)
+ @registry.type_matches!(key_path, value1)
+ end
+
+ it "throws an exception if the type does not match" do
+ @registry.should_receive(:type_matches?).with(key_path, value1).and_return(false)
+ lambda{@registry.type_matches!(key_path, value1)}.should raise_error(Chef::Exceptions::Win32RegTypesMismatch)
+ end
+ end
+
+ describe "keys_missing?" do
+ it "returns true if the keys are missing" do
+ @registry.should_receive(:key_exists?).with(missing_key_path).and_return(false)
+ @registry.keys_missing?(key_path).should == true
+ end
+
+ it "returns false if no keys in the path are missing" do
+ @registry.should_receive(:key_exists?).with(missing_key_path).and_return(true)
+ @registry.keys_missing?(key_path).should == false
+ end
+ end
+end
diff --git a/spec/unit/resource/registry_key_spec.rb b/spec/unit/resource/registry_key_spec.rb
new file mode 100644
index 0000000000..9d7680de0c
--- /dev/null
+++ b/spec/unit/resource/registry_key_spec.rb
@@ -0,0 +1,171 @@
+#
+# Author:: Lamont Granquist (<lamont@opscode.com>)
+# 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.
+#
+
+require 'spec_helper'
+
+describe Chef::Resource::RegistryKey, "initialize" do
+ before(:each) do
+ @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ end
+
+ it "should create a new Chef::Resource::RegistryKey" do
+ @resource.should be_a_kind_of(Chef::Resource)
+ @resource.should be_a_kind_of(Chef::Resource::RegistryKey)
+ end
+
+ it "should set the resource_name to :registry_key" do
+ @resource.resource_name.should eql(:registry_key)
+ end
+
+ it "should set the key equal to the argument to initialize" do
+ @resource.key.should eql('HKCU\Software\Raxicoricofallapatorius')
+ end
+
+ it "should default recursive to false" do
+ @resource.recursive.should eql(false)
+ end
+
+ it "should default architecture to :machine" do
+ @resource.architecture.should eql(:machine)
+ end
+
+ it "should set action to :create" do
+ @resource.action.should eql(:create)
+ end
+
+ %w{create create_if_missing delete delete_key}.each do |action|
+ it "should allow action #{action}" do
+ @resource.allowed_actions.detect { |a| a == action.to_sym }.should eql(action.to_sym)
+ end
+ end
+end
+
+describe Chef::Resource::RegistryKey, "key" do
+ before(:each) do
+ @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ end
+
+ it "should allow a string" do
+ @resource.key 'HKCU\Software\Poosh'
+ @resource.key.should eql('HKCU\Software\Poosh')
+ end
+
+ it "should not allow an integer" do
+ lambda { @resource.send(:key, 100) }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow a hash" do
+ lambda { @resource.send(:key, { :sonic => "screwdriver" }) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Chef::Resource::RegistryKey, "values" do
+ before(:each) do
+ @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ end
+
+ it "should allow a single proper hash of registry values" do
+ @resource.values( { :name => 'poosh', :type => :string, :data => 'carmen' } )
+ @resource.values.should eql([ { :name => 'poosh', :type => :string, :data => 'carmen' } ])
+ end
+
+ it "should allow an array of proper hashes of registry values" do
+ @resource.values [ { :name => 'poosh', :type => :string, :data => 'carmen' } ]
+ @resource.values.should eql([ { :name => 'poosh', :type => :string, :data => 'carmen' } ])
+ end
+
+ it "should throw an exception if the name field is missing" do
+ lambda { @resource.values [ { :type => :string, :data => 'carmen' } ] }.should raise_error(ArgumentError)
+ end
+
+ it "should throw an exception if the type field is missing" do
+ lambda { @resource.values [ { :name => 'poosh', :data => 'carmen' } ] }.should raise_error(ArgumentError)
+ end
+
+ it "should throw an exception if the data field is missing" do
+ lambda { @resource.values [ { :name => 'poosh', :type => :string } ] }.should raise_error(ArgumentError)
+ end
+
+ it "should throw an exception if extra fields are present" do
+ lambda { @resource.values [ { :name => 'poosh', :type => :string, :data => 'carmen', :screwdriver => 'sonic' } ] }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow a string" do
+ lambda { @resource.send(:values, 'souffle') }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow an integer" do
+ lambda { @resource.send(:values, 100) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Chef::Resource::RegistryKey, "recursive" do
+ before(:each) do
+ @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ end
+
+ it "should allow a boolean" do
+ @resource.recursive(true)
+ @resource.recursive.should eql(true)
+ end
+
+ it "should not allow a hash" do
+ lambda { @resource.recursive({:sonic => :screwdriver}) }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow an array" do
+ lambda { @resource.recursive([:nose, :chin]) }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow a string" do
+ lambda { @resource.recursive('souffle') }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow an integer" do
+ lambda { @resource.recursive(100) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Chef::Resource::RegistryKey, "architecture" do
+ before(:each) do
+ @resource = Chef::Resource::RegistryKey.new('HKCU\Software\Raxicoricofallapatorius')
+ end
+
+ [ :i386, :x86_64, :machine ].each do |arch|
+ it "should allow #{arch} as a symbol" do
+ @resource.architecture(arch)
+ @resource.architecture.should eql(arch)
+ end
+ end
+
+ it "should not allow a hash" do
+ lambda { @resource.architecture({:sonic => :screwdriver}) }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow an array" do
+ lambda { @resource.architecture([:nose, :chin]) }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow a string" do
+ lambda { @resource.architecture('souffle') }.should raise_error(ArgumentError)
+ end
+
+ it "should not allow an integer" do
+ lambda { @resource.architecture(100) }.should raise_error(ArgumentError)
+ end
+end