Skip to content

Commit

Permalink
(#14529) Add registry::value defined type
Browse files Browse the repository at this point in the history
Without this patch we don't have an easy to use way to abstract away the
management of a value and the key it's in.

This patch fixes the problem by creating a new defined resource type in
Puppet that builds on top of the native `registry_key` and
`registry_value` resource types.

This registry::value type allows the user to manage values without
worrying about also managing the key the value exists in.  For example:

    registry::value { 'MyAPP - Owner Email':
      key   => 'HKLM\Software\Puppet Labs',
      value => 'Owner Email',
      data  => 'jeff@puppetlabs.com',
    }

This resource will automatically manage the parent key if it does not
exist and manage the value.  The key and value will be created in the
native architecture of the hive.  That is to say, the 64 bit hive on a
64 bit system and the 32 bit hive on a 32 bit system.
  • Loading branch information
Jeff McCune committed May 16, 2012
1 parent 2e9e45e commit bf44208
Show file tree
Hide file tree
Showing 5 changed files with 192 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Modulefile
Expand Up @@ -8,4 +8,4 @@ description 'This module provides a native type and provider to manage keys and
project_page 'http://links.puppetlabs.com/registry-module'

## Add dependencies, if any:
# dependency 'username/name', '>= 1.2.0'
dependency 'puppetlabs/stdlib', '>= 2.3.0'
17 changes: 17 additions & 0 deletions README.markdown
Expand Up @@ -50,6 +50,23 @@ The `registry_key` and `registry_value` types are provided by this module.
data => "The Puppet Agent service periodically manages your configuration",
}

The `registry::value` defined resource type provides a convenient way to manage
values and the parent key:

registry::value { 'MyApp Setting1':
key => 'HKLM\Software\Vendor\PuppetLabs',
value => setting1,
data => 'Hello World!'
}

With this single resource declaration both the `registry_key` of
`HKLM\Software\Vendor\PuppetLabs` and the `registry_value` of
`HKLM\Software\Vendor\PuppetLabs\setting` will be managed.

The `registry::value` defined type only managed keys and values in the system
native architecture. That is to say, the 32 bit keys won't be managed by this
defined type on a 64 bit OS.

Purge Values Example
--------------------

Expand Down
2 changes: 1 addition & 1 deletion acceptance/lib/systest/util/registry.rb
Expand Up @@ -138,7 +138,7 @@ def setup_master(master_manifest_content="# Intentionally Blank\n")
masters.each do |host|
moddir = get_test_file_path(host, master_module_dir)
mkdirs(host, moddir)
on host, "ln -s /opt/puppet-git-repos/registry \"#{moddir}/registry\""
on host, "ln -s /opt/puppet-git-repos/stdlib \"#{moddir}/stdlib\"; ln -s /opt/puppet-git-repos/registry \"#{moddir}/registry\""
end
end
end
Expand Down
105 changes: 105 additions & 0 deletions acceptance/tests/resource/registry/should_have_defined_type.rb
@@ -0,0 +1,105 @@
require 'pathname'
require Pathname.new(__FILE__).dirname.dirname.dirname.dirname + 'lib/systest/util/registry'
# Include our utility methods in the singleton class of the test case instance.
class << self
include Systest::Util::Registry
end

test_name "registry::value defined type"

# Generate a unique key name
keyname = "PuppetLabsTest_MixedCase_#{randomstring(8)}"
# This is the keypath we'll use for this entire test. We will actually create this key and delete it.
vendor_path = "HKLM\\Software\\Vendor"
keypath = "#{vendor_path}\\#{keyname}"

master_manifest_content = <<HERE
notify { fact_phase: message => "fact_phase: $fact_phase" }
registry_key { '#{vendor_path}': ensure => present }
class phase1 {
registry::value { 'Setting1':
key => '#{keypath}',
value => 'Setting1',
data => "fact_phase=${fact_phase}",
}
registry::value { 'Setting2':
key => '#{keypath}',
data => "fact_phase=${fact_phase}",
}
registry::value { 'Setting3':
key => '#{keypath}',
value => 'Setting3',
data => "fact_phase=${fact_phase}",
}
registry::value { 'Setting0':
key => '#{keypath}',
value => '(default)',
data => "fact_phase=${fact_phase}",
}
}
case $fact_phase {
default: { include phase1 }
}
HERE

# Setup the master to use the modules specified in the --modules option
setup_master master_manifest_content

step "Start the master" do
with_master_running_on(master, master_options) do
# A set of keys we expect Puppet to create
phase1_resources_created = [
/Registry_key\[HKLM.Software.Vendor.PuppetLabsTest\w+\].ensure: created/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\\].ensure: created/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\Setting1\].ensure: created/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\Setting2\].ensure: created/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\Setting3\].ensure: created/,
]

phase2_resources_changed = [
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\\].data: data changed 'fact_phase=1' to 'fact_phase=2'/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\Setting1\].data: data changed 'fact_phase=1' to 'fact_phase=2'/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\Setting2\].data: data changed 'fact_phase=1' to 'fact_phase=2'/,
/Registry_value\[HKLM.Software.Vendor.PuppetLabsTest\w+\\Setting3\].data: data changed 'fact_phase=1' to 'fact_phase=2'/,
]

windows_agents.each do |agent|
this_agent_args = agent_args % get_test_file_path(agent, agent_lib_dir)

step "Phase 1.a - Create some values"
on agent, puppet_agent(this_agent_args, :environment => { 'FACTER_FACT_PHASE' => '1' }), :acceptable_exit_codes => agent_exit_codes do
phase1_resources_created.each do |val_re|
assert_match(val_re, result.stdout, "Expected output to contain #{val_re.inspect}.")
end
assert_no_match(/err:/, result.stdout, "Expected no error messages.")
end

step "Phase 1.b - Make sure Puppet is idempotent"
on agent, puppet_agent(this_agent_args, :environment => { 'FACTER_FACT_PHASE' => '1' }), :acceptable_exit_codes => agent_exit_codes do
phase1_resources_created.each do |val_re|
assert_no_match(val_re, result.stdout, "Expected output not to contain #{val_re.inspect}.")
end
assert_no_match(/err:/, result.stdout, "Expected no error messages.")
end

step "Phase 2.a - Change some values"
on agent, puppet_agent(this_agent_args, :environment => { 'FACTER_FACT_PHASE' => '2' }), :acceptable_exit_codes => agent_exit_codes do
phase2_resources_changed.each do |val_re|
assert_match(val_re, result.stdout, "Expected output to contain #{val_re.inspect}.")
end
assert_no_match(/err:/, result.stdout, "Expected no error messages.")
end

step "Phase 2.b - Make sure Puppet is idempotent"
on agent, puppet_agent(this_agent_args, :environment => { 'FACTER_FACT_PHASE' => '2' }), :acceptable_exit_codes => agent_exit_codes do
(phase1_resources_created + phase2_resources_changed).each do |val_re|
assert_no_match(val_re, result.stdout, "Expected output not to contain #{val_re.inspect}.")
end
assert_no_match(/err:/, result.stdout, "Expected no error messages.")
end
end
end
end
68 changes: 68 additions & 0 deletions manifests/value.pp
@@ -0,0 +1,68 @@
# = Define: registry::value
#
# This defined resource type provides a higher level of abstraction on top of
# the registry_key and registry_value resources. Using this defined resource
# type, you do not need to explicitly manage the parent key for a particular
# value. Puppet will automatically manage the parent key for you.
#
# == Parameters:
#
# key:: The path of key the value will placed inside.
#
# value:: The name of the registry value to manage. This will be copied from
# the resource title if not specified. The special value of
# '(default)' may be used to manage the default value of the key.
#
# type:: The type the registry value. Defaults to 'string'. See the output of
# `puppet describe registry_value` for a list of supported types in the
# "type" parameter.
#
# data:: The data to place inside the registry value.
#
# == Actions:
# - Manage the parent key if not already managed.
# - Manage the value
#
# == Requires:
# - Registry Module
# - Stdlib Module
#
# == Sample Usage:
#
# This example will automatically manage the key. It will also create a value
# named 'puppetmaster' inside this key.
#
# class myapp {
# registry::value { 'puppetmaster':
# key => 'HKLM\Software\Vendor\PuppetLabs',
# data => 'puppet.puppetlabs.com',
# }
# }
#
define registry::value($key, $value=undef, $type='string', $data=undef) {
# validate our inputs.
validate_re($key, '^\w+', "key parameter must not be empty but it is key => '$key'")
validate_re($type, '^\w+', "type parameter must not be empty but it is type => '$type'")

$value_real = $value ? {
undef => $name,
'(default)' => '',
default => $value,
}

# Resource defaults.
Registry_key { ensure => present }
Registry_value { ensure => present }

if !defined(Registry_key["${key}"]) {
registry_key { "${key}": }
}

# If value_real is an empty string then the default value of the key will be
# managed.
registry_value { "${key}\\${value_real}":
type => $type,
data => $data,
}
}

0 comments on commit bf44208

Please sign in to comment.