corl 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +4 -0
  3. data/Gemfile +24 -0
  4. data/Gemfile.lock +123 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.rdoc +27 -0
  7. data/Rakefile +78 -0
  8. data/VERSION +1 -0
  9. data/bin/corl +55 -0
  10. data/corl.gemspec +228 -0
  11. data/lib/corl/action/add.rb +69 -0
  12. data/lib/corl/action/bootstrap.rb +83 -0
  13. data/lib/corl/action/clone.rb +40 -0
  14. data/lib/corl/action/create.rb +55 -0
  15. data/lib/corl/action/exec.rb +41 -0
  16. data/lib/corl/action/extract.rb +49 -0
  17. data/lib/corl/action/image.rb +30 -0
  18. data/lib/corl/action/images.rb +55 -0
  19. data/lib/corl/action/lookup.rb +35 -0
  20. data/lib/corl/action/machines.rb +51 -0
  21. data/lib/corl/action/provision.rb +37 -0
  22. data/lib/corl/action/remove.rb +51 -0
  23. data/lib/corl/action/save.rb +53 -0
  24. data/lib/corl/action/seed.rb +115 -0
  25. data/lib/corl/action/spawn.rb +75 -0
  26. data/lib/corl/action/start.rb +37 -0
  27. data/lib/corl/action/stop.rb +30 -0
  28. data/lib/corl/action/update.rb +37 -0
  29. data/lib/corl/command/shell.rb +164 -0
  30. data/lib/corl/configuration/file.rb +386 -0
  31. data/lib/corl/event/puppet.rb +90 -0
  32. data/lib/corl/event/regex.rb +52 -0
  33. data/lib/corl/extension/puppetloader.rb +24 -0
  34. data/lib/corl/machine/fog.rb +310 -0
  35. data/lib/corl/machine/physical.rb +161 -0
  36. data/lib/corl/network/default.rb +26 -0
  37. data/lib/corl/node/aws.rb +90 -0
  38. data/lib/corl/node/fog.rb +198 -0
  39. data/lib/corl/node/google.rb +115 -0
  40. data/lib/corl/node/local.rb +26 -0
  41. data/lib/corl/node/rackspace.rb +89 -0
  42. data/lib/corl/project/git.rb +465 -0
  43. data/lib/corl/project/github.rb +108 -0
  44. data/lib/corl/provisioner/puppetnode/resource.rb +245 -0
  45. data/lib/corl/provisioner/puppetnode/resource_group.rb +205 -0
  46. data/lib/corl/provisioner/puppetnode.rb +407 -0
  47. data/lib/corl/template/environment.rb +73 -0
  48. data/lib/corl/template/json.rb +16 -0
  49. data/lib/corl/template/wrapper.rb +16 -0
  50. data/lib/corl/template/yaml.rb +16 -0
  51. data/lib/corl/translator/json.rb +27 -0
  52. data/lib/corl/translator/yaml.rb +27 -0
  53. data/lib/corl.rb +173 -0
  54. data/lib/corl_core/codes.rb +107 -0
  55. data/lib/corl_core/config/collection.rb +57 -0
  56. data/lib/corl_core/config/options.rb +70 -0
  57. data/lib/corl_core/config.rb +337 -0
  58. data/lib/corl_core/core.rb +59 -0
  59. data/lib/corl_core/corl.rb +254 -0
  60. data/lib/corl_core/errors.rb +84 -0
  61. data/lib/corl_core/facade.rb +126 -0
  62. data/lib/corl_core/gems.rb +72 -0
  63. data/lib/corl_core/manager.rb +425 -0
  64. data/lib/corl_core/mixin/action/commit.rb +58 -0
  65. data/lib/corl_core/mixin/action/keypair.rb +105 -0
  66. data/lib/corl_core/mixin/action/node.rb +129 -0
  67. data/lib/corl_core/mixin/action/project.rb +53 -0
  68. data/lib/corl_core/mixin/action/push.rb +52 -0
  69. data/lib/corl_core/mixin/config/collection.rb +53 -0
  70. data/lib/corl_core/mixin/config/ops.rb +53 -0
  71. data/lib/corl_core/mixin/config/options.rb +39 -0
  72. data/lib/corl_core/mixin/lookup.rb +196 -0
  73. data/lib/corl_core/mixin/macro/object_interface.rb +361 -0
  74. data/lib/corl_core/mixin/macro/plugin_interface.rb +380 -0
  75. data/lib/corl_core/mixin/settings.rb +46 -0
  76. data/lib/corl_core/mixin/sub_config.rb +148 -0
  77. data/lib/corl_core/mod/hash.rb +29 -0
  78. data/lib/corl_core/mod/hiera_backend.rb +63 -0
  79. data/lib/corl_core/plugin/action.rb +381 -0
  80. data/lib/corl_core/plugin/base.rb +374 -0
  81. data/lib/corl_core/plugin/command.rb +98 -0
  82. data/lib/corl_core/plugin/configuration.rb +177 -0
  83. data/lib/corl_core/plugin/event.rb +53 -0
  84. data/lib/corl_core/plugin/extension.rb +12 -0
  85. data/lib/corl_core/plugin/machine.rb +266 -0
  86. data/lib/corl_core/plugin/network.rb +359 -0
  87. data/lib/corl_core/plugin/node.rb +904 -0
  88. data/lib/corl_core/plugin/project.rb +927 -0
  89. data/lib/corl_core/plugin/provisioner.rb +51 -0
  90. data/lib/corl_core/plugin/template.rb +80 -0
  91. data/lib/corl_core/plugin/translator.rb +38 -0
  92. data/lib/corl_core/util/cli.rb +352 -0
  93. data/lib/corl_core/util/data.rb +404 -0
  94. data/lib/corl_core/util/disk.rb +114 -0
  95. data/lib/corl_core/util/git.rb +47 -0
  96. data/lib/corl_core/util/interface.rb +319 -0
  97. data/lib/corl_core/util/liquid.rb +17 -0
  98. data/lib/corl_core/util/package.rb +93 -0
  99. data/lib/corl_core/util/shell.rb +239 -0
  100. data/lib/corl_core/util/ssh.rb +286 -0
  101. data/lib/facter/corl_config_ready.rb +13 -0
  102. data/lib/facter/corl_exists.rb +15 -0
  103. data/lib/facter/corl_network.rb +17 -0
  104. data/lib/hiera/corl_logger.rb +18 -0
  105. data/lib/puppet/indirector/corl.rb +27 -0
  106. data/lib/puppet/indirector/data_binding/corl.rb +6 -0
  107. data/lib/puppet/parser/functions/config_initialized.rb +26 -0
  108. data/lib/puppet/parser/functions/corl_include.rb +44 -0
  109. data/lib/puppet/parser/functions/corl_resources.rb +58 -0
  110. data/lib/puppet/parser/functions/deep_merge.rb +21 -0
  111. data/lib/puppet/parser/functions/ensure.rb +29 -0
  112. data/lib/puppet/parser/functions/file_exists.rb +19 -0
  113. data/lib/puppet/parser/functions/global_array.rb +35 -0
  114. data/lib/puppet/parser/functions/global_hash.rb +35 -0
  115. data/lib/puppet/parser/functions/global_options.rb +23 -0
  116. data/lib/puppet/parser/functions/global_param.rb +43 -0
  117. data/lib/puppet/parser/functions/interpolate.rb +26 -0
  118. data/lib/puppet/parser/functions/is_false.rb +21 -0
  119. data/lib/puppet/parser/functions/is_true.rb +21 -0
  120. data/lib/puppet/parser/functions/module_array.rb +38 -0
  121. data/lib/puppet/parser/functions/module_hash.rb +38 -0
  122. data/lib/puppet/parser/functions/module_options.rb +23 -0
  123. data/lib/puppet/parser/functions/module_param.rb +48 -0
  124. data/lib/puppet/parser/functions/name.rb +21 -0
  125. data/lib/puppet/parser/functions/render.rb +33 -0
  126. data/lib/puppet/parser/functions/value.rb +21 -0
  127. data/locales/en.yml +232 -0
  128. data/spec/corl_core/interface_spec.rb +489 -0
  129. data/spec/corl_mock_input.rb +29 -0
  130. data/spec/corl_test_kernel.rb +22 -0
  131. data/spec/spec_helper.rb +15 -0
  132. metadata +406 -0
@@ -0,0 +1,105 @@
1
+
2
+ module CORL
3
+ module Mixin
4
+ module Action
5
+ module Keypair
6
+
7
+ #-----------------------------------------------------------------------------
8
+ # Options
9
+
10
+ def keypair_config
11
+ register :private_key, :str, nil, 'corl.core.mixins.keypair.options.private_key' do |value|
12
+ success = true
13
+ if value
14
+ file = File.expand_path(value)
15
+ if File.exists?(file)
16
+ unless Util::SSH.generate({ :private_key => file })
17
+ warn('corl.core.mixins.keypair.errors.private_key_parse_error', { :value => file })
18
+ success = false
19
+ end
20
+ else
21
+ warn('corl.core.mixins.keypair.errors.private_key_not_found', { :value => file })
22
+ success = false
23
+ end
24
+ end
25
+ success
26
+ end
27
+
28
+ register :require_password, :bool, false, 'corl.core.mixins.keypair.options.require_password'
29
+
30
+ register :key_type, :str, 'RSA', 'corl.core.mixins.keypair.options.key_type' do |value|
31
+ key_type_choices = [ 'RSA', 'DSA' ]
32
+ unless key_type_choices.include?(value.to_s.upcase)
33
+ warn('corl.core.mixins.keypair.errors.key_type', { :value => value, :choices => key_type_choices })
34
+ next false
35
+ end
36
+ true
37
+ end
38
+ register :key_bits, :int, 2048, 'corl.core.mixins.keypair.options.key_bits' do |value|
39
+ unless value >= 2048
40
+ warn('corl.core.mixins.keypair.errors.key_bits', { :value => value, :required => 2048 })
41
+ next false
42
+ end
43
+ true
44
+ end
45
+ register :key_comment, :str, '', 'corl.core.mixins.keypair.options.key_comment'
46
+
47
+ config_subset(keypair_ignore)
48
+ end
49
+
50
+ #---
51
+
52
+ def keypair_ignore
53
+ [ :require_password, :key_type, :key_bits, :key_comment ]
54
+ end
55
+
56
+ #---
57
+
58
+ def keypair_clean
59
+ remove(keypair_ignore)
60
+ end
61
+
62
+ #-----------------------------------------------------------------------------
63
+ # Properties
64
+
65
+ @keypair = nil
66
+
67
+ #---
68
+
69
+ def keypair=options
70
+ config = Config.ensure(options).defaults({
71
+ :type => settings[:key_type].to_s.upcase,
72
+ :bits => settings[:key_bits],
73
+ :comment => settings[:key_comment]
74
+ })
75
+ @keypair = Util::SSH.generate(options)
76
+ settings.import({ :keypair => @keypair })
77
+ end
78
+
79
+ #---
80
+
81
+ def keypair(reset = false)
82
+ if reset || ! @keypair
83
+ if settings[:private_key]
84
+ key_options = { :private_key => private_key }
85
+ else
86
+ key_options = {}
87
+ if settings[:require_password]
88
+ password = ui.password('SSH')
89
+ if password
90
+ key_options[:passphrase] = password
91
+ else
92
+ warn('corl.core.mixins.keypair.errors.no_password')
93
+ return nil
94
+ end
95
+ end
96
+ end
97
+ myself.keypair = key_options
98
+ end
99
+ @keypair
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
105
+
@@ -0,0 +1,129 @@
1
+
2
+ module CORL
3
+ module Mixin
4
+ module Action
5
+ module Node
6
+
7
+ #-----------------------------------------------------------------------------
8
+ # Settings
9
+
10
+ def node_config
11
+ node_plugins = Manager.connection.loaded_plugins(:node)
12
+
13
+ register :parallel, :bool, true, 'corl.core.mixins.node.options.parallel'
14
+ register :net_provider, :str, :default, 'corl.core.mixins.node.options.net_provider' do |value|
15
+ value = value.to_sym
16
+ network_plugins = Manager.connection.loaded_plugins(:network)
17
+
18
+ unless network_plugins.keys.include?(value)
19
+ warn('corl.core.mixins.node.errors.network_provider', { :value => value, :choices => network_plugins.keys.join(", ") })
20
+ next false
21
+ end
22
+ true
23
+ end
24
+ register :node_provider, :str, :local, 'corl.core.mixins.node.options.node_provider' do |value|
25
+ value = value.to_sym
26
+
27
+ unless node_plugins.keys.include?(value)
28
+ warn('corl.core.mixins.node.errors.node_provider', { :value => value, :choices => node_plugins.keys.join(", ") })
29
+ next false
30
+ end
31
+ true
32
+ end
33
+ register :nodes, :array, [], 'corl.core.mixins.node.options.nodes' do |values|
34
+ success = true
35
+ values.each do |value|
36
+ if info = Plugin::Node.translate_reference(value)
37
+ if ! node_plugins.keys.include?(info[:provider].to_sym) || info[:name].empty?
38
+ warn('corl.core.mixins.node.errors.nodes', { :value => value, :provider => info[:provider], :name => info[:name] })
39
+ success = false
40
+ end
41
+ end
42
+ end
43
+ success
44
+ end
45
+ end
46
+
47
+ #---
48
+
49
+ def node_ignore
50
+ [ :parallel, :net_provider, :node_provider, :nodes ]
51
+ end
52
+
53
+ #-----------------------------------------------------------------------------
54
+ # Operations
55
+
56
+ def init_network(path = nil)
57
+ # Get network configuration path
58
+ if CORL.admin?
59
+ network_path = lookup(:corl_network)
60
+ Dir.mkdir(network_path) unless File.directory?(network_path)
61
+ else
62
+ network_path = ( path.nil? ? Dir.pwd : File.expand_path(path) )
63
+ end
64
+
65
+ # Load network if it exists
66
+ network_config = extended_config(:network, { :directory => network_path })
67
+
68
+ network = CORL.network(
69
+ CORL.sha1(network_config),
70
+ network_config,
71
+ settings[:net_provider]
72
+ )
73
+ network
74
+ end
75
+
76
+ #---
77
+
78
+ def node_exec
79
+ network = init_network
80
+
81
+ #
82
+ # A fork in the road...
83
+ #
84
+ if network.has_nodes? && ! settings[:nodes].empty?
85
+ # Execute action on remote nodes
86
+ success = network.batch(settings[:nodes], settings[:node_provider], settings[:parallel]) do |node|
87
+ exec_config = Config.new(settings)
88
+ exec_config.delete(:parallel)
89
+ exec_config.delete(:nodes)
90
+ exec_config.delete(:node_provider)
91
+
92
+ result = node.action(plugin_provider, exec_config) do |op, data|
93
+ ui_group(node.plugin_name) do
94
+ case op
95
+ when :config # Modify seed execution configurations
96
+ render("Starting remote execution of #{plugin_provider} action")
97
+ when :process # Process final result
98
+ render("Successfully finished remote execution of #{plugin_provider} action")
99
+ end
100
+ data = execute_remote(node, network, op, data)
101
+ end
102
+ data
103
+ end
104
+ result.status == code.success
105
+ end
106
+ myself.status = code.batch_error unless success
107
+ else
108
+ # Execute statement locally
109
+ node = network.local_node
110
+
111
+ if validate(node, network)
112
+ yield(node, network) if block_given?
113
+ else
114
+ puts "\n" + I18n.t('corl.core.exec.help.usage') + ': ' + help + "\n" unless quiet?
115
+ myself.status = code.validation_failed
116
+ end
117
+ end
118
+ end
119
+
120
+ #---
121
+
122
+ def execute_remote(node, network, op, data)
123
+ # Implement in sub classes if needed
124
+ data
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,53 @@
1
+
2
+ module CORL
3
+ module Mixin
4
+ module Action
5
+ module Project
6
+
7
+ #-----------------------------------------------------------------------------
8
+ # Options
9
+
10
+ def project_options(parser, ref_override = false, rev_override = false)
11
+ parser.option_str(:project_provider, 'git',
12
+ '--proj-provider PROVIDER',
13
+ 'corl.core.mixins.project.options.provider'
14
+ )
15
+ if ref_override
16
+ parser.option_str(:reference, nil,
17
+ '--reference PROJECT_REF',
18
+ 'corl.core.mixins.project.options.reference'
19
+ )
20
+ end
21
+ if rev_override
22
+ parser.option_str(:revision, nil,
23
+ '--revision PROJECT_REV',
24
+ 'corl.core.mixins.project.options.revision'
25
+ )
26
+ end
27
+ end
28
+
29
+ #-----------------------------------------------------------------------------
30
+ # Operations
31
+
32
+ def project_load(root_dir, update = false)
33
+
34
+ # 1. Set a default project provider (reference can override)
35
+ # 2. Get project from root directory
36
+ # 3. Initialize project if not yet initialized
37
+ # 4. Set remote if needed
38
+ # 5. Checkout revision if needed
39
+ # 6. Pull down updates if requested
40
+
41
+ return CORL.project(extended_config(:project, {
42
+ :provider => settings[:project_provider],
43
+ :directory => root_dir,
44
+ :url => settings[:reference],
45
+ :revision => settings[:revision],
46
+ :pull => update
47
+ }))
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
@@ -0,0 +1,52 @@
1
+
2
+ module CORL
3
+ module Mixin
4
+ module Action
5
+ module Push
6
+
7
+ #-----------------------------------------------------------------------------
8
+ # Options
9
+
10
+ def push_options(parser, optional = true)
11
+ if optional
12
+ parser.option_bool(:push, false,
13
+ '--push',
14
+ 'corl.core.mixins.push.options.push'
15
+ )
16
+ else
17
+ parser.options[:push] = true
18
+ end
19
+
20
+ parser.option_bool(:propogate, false,
21
+ '--propogate',
22
+ 'corl.core.mixins.push.options.propogate'
23
+ )
24
+ parser.option_str(:remote, :edit,
25
+ '--remote PROJECT_REMOTE',
26
+ 'corl.core.mixins.push.options.remote'
27
+ )
28
+ parser.option_str(:revision, :master,
29
+ '--revision PROJECT_REVISION',
30
+ 'corl.core.mixins.push.options.revision'
31
+ )
32
+ end
33
+
34
+ #-----------------------------------------------------------------------------
35
+ # Operations
36
+
37
+ def push(project, remote = :edit)
38
+ success = true
39
+
40
+ if project && settings[:push]
41
+ success = project.push(settings[:remote], extended_config(:push, {
42
+ :revision => settings[:revision],
43
+ :propogate => settings[:propogate]
44
+ }))
45
+ end
46
+ success
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,53 @@
1
+
2
+ # Should be included via extend
3
+ #
4
+ # extend Mixin::ConfigCollection
5
+ #
6
+
7
+ module CORL
8
+ module Mixin
9
+ module ConfigCollection
10
+
11
+ #-----------------------------------------------------------------------------
12
+ # Configuration collection interface
13
+
14
+ def all_properties
15
+ return Config::Collection.all
16
+ end
17
+
18
+ #---
19
+
20
+ def get_property(name)
21
+ return Config::Collection.get(name)
22
+ end
23
+
24
+ #---
25
+
26
+ def set_property(name, value)
27
+ Config::Collection.set(name, value)
28
+ return self
29
+ end
30
+
31
+ #---
32
+
33
+ def delete_property(name)
34
+ Config::Collection.delete(name)
35
+ return self
36
+ end
37
+
38
+ #---
39
+
40
+ def clear_properties
41
+ Config::Collection.clear
42
+ return self
43
+ end
44
+
45
+ #---
46
+
47
+ def save_properties
48
+ Config::Collection.save
49
+ return self
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,53 @@
1
+
2
+ # Should be included via extend
3
+ #
4
+ # extend Mixin::ConfigOps
5
+ #
6
+
7
+ module CORL
8
+ module Mixin
9
+ module ConfigOps
10
+
11
+ #-----------------------------------------------------------------------------
12
+ # Parsing
13
+
14
+ def parse(statement, options = {})
15
+ config = Config.ensure(options)
16
+
17
+ # statement = common->php::apache::memory_limit = 32M
18
+ # statement = identity -> test -> users::user[admin][shell]
19
+ # statement = nodes->development->dev.loc->facts[server_location]
20
+ # statement = corl->nodes[vagrant][dev.loc][vm][private_network]=172.5.5.10
21
+ # statement = corl->settings[debug][puppet][options] = ["--debug"]
22
+
23
+ reference, new_value = statement.split(/\=/)
24
+ new_value = new_value.join('=').strip if new_value && new_value.is_a?(Array)
25
+
26
+ config_elements = reference.gsub(/\s+/, '').split(/\-\>/)
27
+ property = config_elements.pop
28
+ config_file = config_elements.pop
29
+
30
+ if config_directory = config.get(:directory, nil)
31
+ config_path = File.join(project.directory, config_directory, *config_elements)
32
+ else
33
+ config_path = File.join(project.directory, *config_elements)
34
+ end
35
+
36
+ return nil unless property && config_file
37
+
38
+ config_file = "#{config_file}." + config.get(:ext, 'json')
39
+ property = property.gsub(/\]$/, '').split(/\]?\[/)
40
+ data = open(config_path, config_file, config)
41
+
42
+ return {
43
+ :path => config_path,
44
+ :file => config_file,
45
+ :property => property,
46
+ :conf => data,
47
+ :current_value => (data ? data.get(property) : nil),
48
+ :new_value => eval(new_value)
49
+ }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,39 @@
1
+
2
+ # Should be included via extend
3
+ #
4
+ # extend Mixin::ConfigOptions
5
+ #
6
+
7
+ module CORL
8
+ module Mixin
9
+ module ConfigOptions
10
+
11
+ #-----------------------------------------------------------------------------
12
+ # Configuration options interface
13
+
14
+ def contexts(contexts = [], hierarchy = [])
15
+ return Config::Options.contexts(contexts, hierarchy)
16
+ end
17
+
18
+ #---
19
+
20
+ def get_options(contexts, force = true)
21
+ return Config::Options.get(contexts, force)
22
+ end
23
+
24
+ #---
25
+
26
+ def set_options(contexts, options, force = true)
27
+ Config::Options.set(contexts, options, force)
28
+ return self
29
+ end
30
+
31
+ #---
32
+
33
+ def clear_options(contexts)
34
+ Config::Options.clear(contexts)
35
+ return self
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,196 @@
1
+
2
+ module CORL
3
+ module Mixin
4
+ module Lookup
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Facter lookup
8
+
9
+ def facts
10
+ fact_map = {}
11
+ Facter.list.each do |name|
12
+ fact_map[name] = Facter.value(name)
13
+ end
14
+ fact_map
15
+ end
16
+
17
+ def fact(name)
18
+ Facter.value(name)
19
+ end
20
+
21
+ #-----------------------------------------------------------------------------
22
+ # Hiera configuration
23
+
24
+ @@hiera = nil
25
+
26
+ #---
27
+
28
+ def hiera_config
29
+ config_file = CORL.value(:hiera_config_file, nil)
30
+ config = {}
31
+
32
+ if config_file && File.exist?(config_file)
33
+ config = Hiera::Config.load(config_file)
34
+ end
35
+ config[:logger] = :corl
36
+
37
+ results = CORL.config(:hiera_config, { :config => config })
38
+ results[:config]
39
+ end
40
+
41
+ #---
42
+
43
+ def hiera
44
+ @@hiera = Hiera.new(:config => hiera_config) if @@hiera.nil?
45
+ @@hiera
46
+ end
47
+
48
+ #-----------------------------------------------------------------------------
49
+ # Configuration lookup interface
50
+
51
+ def config_initialized?
52
+ ready = false
53
+ if CORL.admin? && hiera && network_path = fact(:corl_network)
54
+ ready = File.directory?(network_path) && File.directory?(File.join(network_path, 'config')) ? true : false
55
+ end
56
+ ready
57
+ end
58
+
59
+ #---
60
+
61
+ def lookup(properties, default = nil, options = {})
62
+ config = Config.ensure(options)
63
+ value = nil
64
+
65
+ provisioner = config.get(:provisioner, nil)
66
+
67
+ hiera_scope = config.get(:hiera_scope, {})
68
+ override = config.get(:override, nil)
69
+ context = config.get(:context, :priority)
70
+
71
+
72
+ return_property = config.get(:return_property, false)
73
+
74
+ unless properties.is_a?(Array)
75
+ properties = [ properties ].flatten
76
+ end
77
+
78
+ first_property = nil
79
+ properties.each do |property|
80
+ property = property.to_sym
81
+ first_property = property unless first_property
82
+
83
+ # Try to load facts first (these can not be overridden)
84
+ unless value = fact(property)
85
+ if CORL.admin?
86
+ if config_initialized?
87
+ # Try to find in Hiera data store (these might be security sensitive)
88
+ unless hiera_scope.respond_to?('[]')
89
+ hiera_scope = Hiera::Scope.new(hiera_scope)
90
+ end
91
+ value = hiera.lookup(property, nil, hiera_scope, override, context)
92
+ end
93
+
94
+ if provisioner && Util::Data.undef?(value)
95
+ # Search the provisioner scope (only admins can provision a machine)
96
+ value = CORL.provisioner(provisioner).lookup(property, default, config)
97
+ end
98
+ end
99
+ end
100
+ end
101
+ value = default if Util::Data.undef?(value) # Resort to default
102
+ value = Util::Data.value(value)
103
+
104
+ if ! Config.get_property(first_property) || ! Util::Data.undef?(value)
105
+ Config.set_property(first_property, value)
106
+ end
107
+ return value, first_property if return_property
108
+ value
109
+ end
110
+
111
+ #---
112
+
113
+ def lookup_array(properties, default = [], options = {})
114
+ config = Config.ensure(options)
115
+ value, property = lookup(properties, nil, config.import({ :return_property => true }))
116
+
117
+ if Util::Data.undef?(value)
118
+ value = default
119
+
120
+ elsif ! Util::Data.empty?(default)
121
+ if config.get(:merge, false)
122
+ value = Util::Data.merge([default, value], config)
123
+ end
124
+ end
125
+
126
+ unless value.is_a?(Array)
127
+ value = ( Util::Data.empty?(value) ? [] : [ value ] )
128
+ end
129
+
130
+ Config.set_property(property, value)
131
+ value
132
+ end
133
+
134
+ #---
135
+
136
+ def lookup_hash(properties, default = {}, options = {})
137
+ config = Config.ensure(options)
138
+ value, property = lookup(properties, nil, config.import({ :return_property => true }))
139
+
140
+ if Util::Data.undef?(value)
141
+ value = default
142
+
143
+ elsif ! Util::Data.empty?(default)
144
+ if config.get(:merge, false)
145
+ value = Util::Data.merge([default, value], config)
146
+ end
147
+ end
148
+
149
+ unless value.is_a?(Hash)
150
+ value = ( Util::Data.empty?(value) ? {} : { :value => value } )
151
+ end
152
+
153
+ Config.set_property(property, value)
154
+ value
155
+ end
156
+
157
+ #---
158
+
159
+ def normalize(data, override = nil, options = {})
160
+ config = Config.ensure(options)
161
+ results = {}
162
+
163
+ unless Util::Data.undef?(override)
164
+ case data
165
+ when String, Symbol
166
+ data = [ data, override ] if data != override
167
+ when Array
168
+ data << override unless data.include?(override)
169
+ when Hash
170
+ data = [ data, override ]
171
+ end
172
+ end
173
+
174
+ case data
175
+ when String, Symbol
176
+ results = lookup(data.to_s, {}, config)
177
+
178
+ when Array
179
+ data.each do |item|
180
+ if item.is_a?(String) || item.is_a?(Symbol)
181
+ item = lookup(item.to_s, {}, config)
182
+ end
183
+ unless Util::Data.undef?(item)
184
+ results = Util::Data.merge([ results, item ], config)
185
+ end
186
+ end
187
+
188
+ when Hash
189
+ results = data
190
+ end
191
+
192
+ results
193
+ end
194
+ end
195
+ end
196
+ end