coral_core 0.1.10 → 0.2.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.
@@ -0,0 +1,82 @@
1
+
2
+ module Coral
3
+ module Template
4
+
5
+ #-----------------------------------------------------------------------------
6
+ # General template utilities
7
+
8
+ def self.instance(class_name, options = {}, defaults = {}, force = true)
9
+ return Template::const_get(class_name).new(options, defaults, force)
10
+ end
11
+
12
+ #---
13
+
14
+ def self.process(value)
15
+ case value
16
+ when String
17
+ return '' if Util::Data.undef?(value)
18
+ return 'false' if value == false
19
+ return 'true' if value == true
20
+ return value.to_s if value.is_a?(Symbol)
21
+
22
+ when Hash
23
+ value.each do |key, item|
24
+ value[key] = process(item)
25
+ end
26
+
27
+ when Array
28
+ value.each_with_index do |item, index|
29
+ value[index] = process(item)
30
+ end
31
+ end
32
+ return value
33
+ end
34
+
35
+ #---
36
+
37
+ def self.render(class_name, data, options = {})
38
+ config = Config.ensure(options)
39
+
40
+ normalize = config.get(:normalize_template, true)
41
+ interpolate = config.get(:interpolate_template, true)
42
+
43
+ #dbg(class_name, 'template engine name')
44
+ #dbg(data, 'template data -> init')
45
+
46
+ if normalize
47
+ data = Util::Data.normalize(data, config)
48
+ #dbg(data, 'template data -> post normalization')
49
+ end
50
+
51
+ if normalize && interpolate
52
+ data = Util::Data.interpolate(data, data, config)
53
+ #dbg(data, 'template data -> post interpolation')
54
+ end
55
+
56
+ engine = instance(class_name, config, {}, config.get(:force, true))
57
+ #dbg(engine, 'template engine')
58
+
59
+ output = engine.render(process(data))
60
+ #dbg(output, 'template output')
61
+
62
+ return output
63
+ end
64
+
65
+ #-----------------------------------------------------------------------------
66
+ # Base template
67
+
68
+ class Base < Config
69
+ # All template classes should extend Base
70
+
71
+ def intialize(options = {}, defaults = {}, force = true)
72
+ super(options, defaults, force)
73
+ end
74
+
75
+ #---
76
+
77
+ def render(input)
78
+ return '' # Implement in sub classes!!
79
+ end
80
+ end
81
+ end
82
+ end
@@ -2,58 +2,253 @@
2
2
  module Coral
3
3
  module Util
4
4
  class Data < Core
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Type checking
8
+
9
+ def self.undef?(value)
10
+ if value.nil? ||
11
+ (value.is_a?(Symbol) && value == :undef || value == :undefined) ||
12
+ (value.is_a?(String) && value.match(/^\s*(undef|UNDEF|Undef|nil|NIL|Nil)\s*$/))
13
+ return true
14
+ end
15
+ return false
16
+ end
17
+
18
+ #---
19
+
20
+ def self.true?(value)
21
+ if value == true ||
22
+ (value.is_a?(String) && value.match(/^\s*(true|TRUE|True)\s*$/))
23
+ return true
24
+ end
25
+ return false
26
+ end
27
+
28
+ #---
29
+
30
+ def self.false?(value)
31
+ if value == false ||
32
+ (value.is_a?(String) && value.match(/^\s*(false|FALSE|False)\s*$/))
33
+ return true
34
+ end
35
+ return false
36
+ end
37
+
38
+ #---
39
+
40
+ def self.empty?(value)
41
+ if undef?(value) || false?(value) || (value.respond_to?('empty?') && value.empty?)
42
+ return false
43
+ end
44
+ return true
45
+ end
5
46
 
6
47
  #-----------------------------------------------------------------------------
7
- # Utilities
48
+ # Translation
49
+
50
+ def self.to_json(data)
51
+ output = ''
52
+ begin
53
+ require 'json'
54
+ output = data.to_json
55
+ rescue LoadError
56
+ end
57
+ return output
58
+ end
59
+
60
+ #---
61
+
62
+ def self.to_yaml(data)
63
+ output = ''
64
+ begin
65
+ require 'yaml'
66
+ output = YAML.dump(data)
67
+ rescue LoadError
68
+ end
69
+ return output
70
+ end
71
+
72
+ #---
73
+
74
+ def self.value(value)
75
+ case value
76
+ when String
77
+ if undef?(value)
78
+ value = nil
79
+ elsif true?(value)
80
+ value = true
81
+ elsif false?(value)
82
+ value = false
83
+ end
8
84
 
9
- def self.merge(objects, merge_arrays = false)
10
- value = nil
11
- return value unless objects
85
+ when Array
86
+ value.each_with_index do |item, index|
87
+ value[index] = value(item)
88
+ end
12
89
 
13
- unless objects.is_a?(Array)
14
- objects = [ objects ]
90
+ when Hash
91
+ value.each do |key, data|
92
+ value[key] = value(data)
93
+ end
15
94
  end
16
-
17
- objects.each do |object|
18
- value = recursive_merge(value, object, merge_arrays)
95
+ return value
96
+ end
97
+
98
+ #-----------------------------------------------------------------------------
99
+ # Operations
100
+
101
+ def self.merge(data, force = true)
102
+ value = data
103
+
104
+ # Special case because this method is called from within Config.new so we
105
+ # can not use Config.ensure, as that would cause an infinite loop.
106
+ force = force.is_a?(Coral::Config) ? force.get(:force, force) : force
107
+
108
+ #dbg(data, 'data')
109
+
110
+ if data.is_a?(Array)
111
+ value = data.shift
112
+ data.each do |item|
113
+ #dbg(item, 'item')
114
+ case value
115
+ when Hash
116
+ begin
117
+ require 'deep_merge'
118
+ value = force ? value.deep_merge!(item) : value.deep_merge(item)
119
+ rescue LoadError
120
+ if item.is_a?(Hash) # Non recursive top level by default.
121
+ value = value.merge(item)
122
+ elsif force
123
+ value = item
124
+ end
125
+ end
126
+ when Array
127
+ if item.is_a?(Array)
128
+ value = value.concat(item).uniq
129
+ elsif force
130
+ value = item
131
+ end
132
+ end
133
+ end
19
134
  end
135
+
136
+ #dbg(value, 'value')
20
137
  return value
21
138
  end
139
+
140
+ #---
141
+
142
+ def self.normalize(data, override = nil, options = {})
143
+ config = Config.ensure(options)
144
+ results = {}
145
+
146
+ unless undef?(override)
147
+ case data
148
+ when String, Symbol
149
+ data = [ data, override ] if data != override
150
+ when Array
151
+ data << override unless data.include?(override)
152
+ when Hash
153
+ data = [ data, override ]
154
+ end
155
+ end
22
156
 
157
+ case data
158
+ when String, Symbol
159
+ results = Config.lookup(data.to_s, {}, config)
160
+
161
+ when Array
162
+ data.each do |item|
163
+ if item.is_a?(String) || item.is_a?(Symbol)
164
+ item = Config.lookup(item.to_s, {}, config)
165
+ end
166
+ unless undef?(item)
167
+ results = merge([ results, item ], config)
168
+ end
169
+ end
170
+
171
+ when Hash
172
+ results = data
173
+ end
174
+
175
+ return results
176
+ end
177
+
23
178
  #---
179
+
180
+ def self.interpolate(value, scope, options = {})
181
+ config = Config.ensure(options)
182
+
183
+ pattern = config.get(:pattern, '\$(\{)?([a-zA-Z0-9\_\-]+)(\})?')
184
+ group = config.get(:var_group, 2)
185
+ flags = config.get(:flags, '')
24
186
 
25
- def self.recursive_merge(overrides, data, merge_arrays = false)
26
- return data unless overrides
27
- return overrides unless data
28
-
29
- if overrides.is_a?(Hash)
30
- overrides.each do |name, override|
31
- if data.is_a?(Hash)
32
- if data[name]
33
- data[name] = recursive_merge(override, data[name])
34
- else
35
- begin
36
- item = override.dup
37
- rescue TypeError
38
- item = override
39
- end
40
- data[name] = recursive_merge(override, item)
41
- end
42
- else
43
- data = overrides
187
+ if scope.is_a?(Hash)
188
+ regexp = Regexp.new(pattern, flags.split(''))
189
+
190
+ replace = lambda do |item|
191
+ matches = item.match(regexp)
192
+ result = nil
193
+
194
+ #dbg(item, 'item')
195
+ #dbg(matches, 'matches')
196
+
197
+ unless matches.nil?
198
+ replacement = scope.search(matches[group], config)
199
+ result = item.gsub(matches[0], replacement) unless replacement.nil?
44
200
  end
201
+ return result
45
202
  end
46
- elsif merge_arrays && overrides.is_a?(Array)
47
- if data.is_a?(Array)
48
- data = data | overrides
49
- else
50
- data = overrides
203
+
204
+ case value
205
+ when String
206
+ #dbg(value, 'interpolate (string) -> init')
207
+ while (temp = replace.call(value))
208
+ #dbg(temp, 'interpolate (string) -> replacement')
209
+ value = temp
210
+ end
211
+
212
+ when Hash
213
+ #dbg(value, 'interpolate (hash) -> init')
214
+ value.each do |key, data|
215
+ #dbg(data, "interpolate (#{key}) -> data")
216
+ value[key] = interpolate(data, scope, config)
217
+ end
51
218
  end
52
- else
53
- data = overrides
54
219
  end
55
- return data
220
+ #dbg(value, 'interpolate -> result')
221
+ return value
56
222
  end
57
223
  end
58
224
  end
59
- end
225
+ end
226
+
227
+ #-------------------------------------------------------------------------------
228
+ # Data type alterations
229
+
230
+ class Hash
231
+ def search(search_key, options = {})
232
+ config = Coral::Config.ensure(options)
233
+ value = nil
234
+
235
+ recurse = config.get(:recurse, false)
236
+ recurse_level = config.get(:recurse_level, -1)
237
+
238
+ self.each do |key, data|
239
+ if key == search_key
240
+ value = data
241
+
242
+ elsif data.is_a?(Hash) &&
243
+ recurse && (recurse_level == -1 || recurse_level > 0)
244
+
245
+ recurse_level -= 1 unless recurse_level == -1
246
+ value = value.search(search_key,
247
+ Coral::Config.new(config).set(:recurse_level, recurse_level)
248
+ )
249
+ end
250
+ break unless value.nil?
251
+ end
252
+ return value
253
+ end
254
+ end
@@ -27,30 +27,30 @@ class Shell < Core
27
27
  ui.info(">> running: #{command}")
28
28
 
29
29
  begin
30
- t1, output_new, output_orig = pipe_exec_stream!($stdout, conditions, {
30
+ t1, output_new, output_orig, output_reader = pipe_exec_stream!($stdout, conditions, {
31
31
  :prefix => info_prefix,
32
32
  :suffix => info_suffix,
33
- }) do |line|
33
+ }, 'output') do |line|
34
34
  block_given? ? yield(line) : true
35
35
  end
36
36
 
37
- t2, error_new, error_orig = pipe_exec_stream!($stderr, conditions, {
37
+ t2, error_new, error_orig, error_reader = pipe_exec_stream!($stderr, conditions, {
38
38
  :prefix => error_prefix,
39
39
  :suffix => error_suffix,
40
- }) do |line|
40
+ }, 'error') do |line|
41
41
  block_given? ? yield(line) : true
42
42
  end
43
43
 
44
44
  system_success = system(command)
45
45
 
46
46
  ensure
47
- output_success = close_exec_pipe(t1, $stdout, output_orig, output_new)
48
- error_success = close_exec_pipe(t2, $stderr, error_orig, error_new)
47
+ output_success = close_exec_pipe(t1, $stdout, output_orig, output_new, 'output')
48
+ error_success = close_exec_pipe(t2, $stderr, error_orig, error_new, 'error')
49
49
  end
50
50
  ui.info('')
51
51
 
52
52
  success = ( system_success && output_success && error_success )
53
-
53
+
54
54
  min -= 1
55
55
  break if success && min <= 0 && conditions.empty?
56
56
  end
@@ -69,13 +69,13 @@ class Shell < Core
69
69
 
70
70
  #---
71
71
 
72
- def self.pipe_exec_stream!(output, conditions, options)
72
+ def self.pipe_exec_stream!(output, conditions, options, label)
73
73
  original = output.dup
74
74
  read, write = IO.pipe
75
75
 
76
76
  match_prefix = ( options[:match_prefix] ? options[:match_prefix] : 'EXIT' )
77
77
 
78
- thread = process_stream!(read, original, options) do |line|
78
+ thread = process_stream!(read, original, options, label) do |line|
79
79
  check_conditions!(line, conditions, match_prefix) do
80
80
  block_given? ? yield(line) : true
81
81
  end
@@ -84,17 +84,16 @@ class Shell < Core
84
84
  thread.abort_on_exception = false
85
85
 
86
86
  output.reopen(write)
87
- return thread, write, original
87
+ return thread, write, original, read
88
88
  end
89
89
 
90
90
  #---
91
91
 
92
- def self.close_exec_pipe(thread, output, original, write)
92
+ def self.close_exec_pipe(thread, output, original, write, label)
93
93
  output.reopen(original)
94
-
94
+
95
95
  write.close
96
96
  success = thread.value
97
- Thread.kill(thread) # Die bastard, die!
98
97
 
99
98
  original.close
100
99
  return success
@@ -132,7 +131,7 @@ class Shell < Core
132
131
 
133
132
  #---
134
133
 
135
- def self.process_stream!(input, output, options)
134
+ def self.process_stream!(input, output, options, label)
136
135
  return Thread.new do
137
136
  success = true
138
137
  default_prefix = ( options[:prefix] ? options[:prefix] : '' )
@@ -149,29 +148,31 @@ class Shell < Core
149
148
  prefix = default_prefix
150
149
  suffix = default_suffix
151
150
 
152
- if block_given?
153
- result = yield(line)
151
+ unless line.empty?
152
+ if block_given?
153
+ result = yield(line)
154
154
 
155
- if result && result.is_a?(Hash)
156
- prefix = result[:prefix]
157
- suffix = result[:suffix]
158
- result = result[:success]
155
+ if result && result.is_a?(Hash)
156
+ prefix = result[:prefix]
157
+ suffix = result[:suffix]
158
+ result = result[:success]
159
+ end
160
+ success = result if success
159
161
  end
160
- success = result if success
161
- end
162
162
 
163
- prefix = ( prefix && ! prefix.empty? ? "#{prefix}: " : '' )
164
- suffix = ( suffix && ! suffix.empty? ? suffix : '' )
165
- eol = ( index < lines.length - 1 || newline ? "\n" : ' ' )
163
+ prefix = ( prefix && ! prefix.empty? ? "#{prefix}: " : '' )
164
+ suffix = ( suffix && ! suffix.empty? ? suffix : '' )
165
+ eol = ( index < lines.length - 1 || newline ? "\n" : ' ' )
166
166
 
167
- output.write(prefix.lstrip + line + suffix.rstrip + eol)
167
+ output.write(prefix.lstrip + line + suffix.rstrip + eol)
168
+ end
168
169
  end
169
170
  end
170
171
  end
171
172
  rescue EOFError
172
173
  end
173
174
 
174
- input.close
175
+ input.close()
175
176
  success
176
177
  end
177
178
  end
data/lib/coral_core.rb CHANGED
@@ -7,6 +7,7 @@ $:.unshift(home) unless
7
7
  #-------------------------------------------------------------------------------
8
8
 
9
9
  require 'rubygems'
10
+ require 'hiera_backend.rb'
10
11
 
11
12
  #---
12
13
 
@@ -45,5 +46,74 @@ end
45
46
  module Coral
46
47
 
47
48
  VERSION = File.read(File.join(File.dirname(__FILE__), '..', 'VERSION'))
48
-
49
- end
49
+
50
+ #---
51
+
52
+ @@ui = Coral::Core.ui
53
+
54
+ #-----------------------------------------------------------------------------
55
+ # Initialization
56
+
57
+ def self.load(base_path)
58
+ if File.exists?(base_path)
59
+ Dir.glob(File.join(base_path, '*.rb')).each do |file|
60
+ require file
61
+ end
62
+ Dir.glob(File.join(base_path, 'event', '*.rb')).each do |file|
63
+ require file
64
+ end
65
+ Dir.glob(File.join(base_path, 'template', '*.rb')).each do |file|
66
+ require file
67
+ end
68
+ end
69
+ end
70
+
71
+ #---
72
+
73
+ @@initialized = false
74
+
75
+ def self.initialize
76
+ unless @@initialized
77
+ Config.set_property('time', Time.now.to_i)
78
+
79
+ # Include Coral extensions
80
+ Puppet::Node::Environment.new.modules.each do |mod|
81
+ load(File.join(mod.path, 'lib', 'coral'))
82
+ end
83
+
84
+ @@initialized = true
85
+ end
86
+ end
87
+
88
+ #-----------------------------------------------------------------------------
89
+ # External execution
90
+
91
+ def self.run
92
+ begin
93
+ initialize
94
+ yield
95
+
96
+ rescue Exception => error
97
+ ui.warning(error.inspect)
98
+ ui.warning(Util::Data.to_yaml(error.backtrace))
99
+ raise
100
+ end
101
+ end
102
+ end
103
+
104
+ #-------------------------------------------------------------------------------
105
+ # Global namespace
106
+
107
+ module Kernel
108
+ def dbg(data, label = '')
109
+ require 'pp'
110
+
111
+ puts '>>----------------------'
112
+ unless label.empty?
113
+ puts label
114
+ puts '---'
115
+ end
116
+ pp data
117
+ puts '<<'
118
+ end
119
+ end
@@ -0,0 +1,63 @@
1
+ begin
2
+ require 'hiera/backend'
3
+
4
+ class Hiera
5
+ module Backend
6
+ #
7
+ # NOTE: This function is overridden so we can collect accumulated hiera
8
+ # parameters and their values on a particular puppet run for reporting
9
+ # purposes.
10
+ #
11
+ # Calls out to all configured backends in the order they
12
+ # were specified. The first one to answer will win.
13
+ #
14
+ # This lets you declare multiple backends, a possible
15
+ # use case might be in Puppet where a Puppet module declares
16
+ # default data using in-module data while users can override
17
+ # using JSON/YAML etc. By layering the backends and putting
18
+ # the Puppet one last you can override module author data
19
+ # easily.
20
+ #
21
+ # Backend instances are cached so if you need to connect to any
22
+ # databases then do so in your constructor, future calls to your
23
+ # backend will not create new instances
24
+ def lookup(key, default, scope, order_override, resolution_type)
25
+ @backends ||= {}
26
+ answer = nil
27
+
28
+ Config[:backends].each do |backend|
29
+ if constants.include?("#{backend.capitalize}_backend") || constants.include?("#{backend.capitalize}_backend".to_sym)
30
+ @backends[backend] ||= Backend.const_get("#{backend.capitalize}_backend").new
31
+ new_answer = @backends[backend].lookup(key, scope, order_override, resolution_type)
32
+
33
+ if not new_answer.nil?
34
+ case resolution_type
35
+ when :array
36
+ raise Exception, "Hiera type mismatch: expected Array and got #{new_answer.class}" unless new_answer.kind_of? Array or new_answer.kind_of? String
37
+ answer ||= []
38
+ answer << new_answer
39
+ when :hash
40
+ raise Exception, "Hiera type mismatch: expected Hash and got #{new_answer.class}" unless new_answer.kind_of? Hash
41
+ answer ||= {}
42
+ answer = merge_answer(new_answer,answer)
43
+ else
44
+ answer = new_answer
45
+ break
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ answer = resolve_answer(answer, resolution_type) unless answer.nil?
52
+ answer = parse_string(default, scope) if answer.nil? and default.is_a?(String)
53
+
54
+ answer = default if answer.nil?
55
+
56
+ Coral::Config.set_property(key, answer) # This is why we override this function!!
57
+ return answer
58
+ end
59
+ end
60
+ end
61
+
62
+ rescue LoadError
63
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coral_core
3
3
  version: !ruby/object:Gem::Version
4
- hash: 15
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 10
10
- version: 0.1.10
8
+ - 2
9
+ - 0
10
+ version: 0.2.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Adrian Webb
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2013-03-15 00:00:00 Z
18
+ date: 2013-05-09 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  name: git
@@ -191,12 +191,19 @@ files:
191
191
  - coral_core.gemspec
192
192
  - lib/coral_core.rb
193
193
  - lib/coral_core/command.rb
194
+ - lib/coral_core/config.rb
194
195
  - lib/coral_core/core.rb
195
196
  - lib/coral_core/event.rb
196
197
  - lib/coral_core/event/regexp_event.rb
197
198
  - lib/coral_core/interface.rb
198
199
  - lib/coral_core/memory.rb
199
200
  - lib/coral_core/repository.rb
201
+ - lib/coral_core/resource.rb
202
+ - lib/coral_core/template.rb
203
+ - lib/coral_core/template/environment.rb
204
+ - lib/coral_core/template/json.rb
205
+ - lib/coral_core/template/wrapper.rb
206
+ - lib/coral_core/template/yaml.rb
200
207
  - lib/coral_core/util/data.rb
201
208
  - lib/coral_core/util/disk.rb
202
209
  - lib/coral_core/util/git.rb
@@ -204,6 +211,7 @@ files:
204
211
  - lib/coral_core/util/git/lib.rb
205
212
  - lib/coral_core/util/git/remote.rb
206
213
  - lib/coral_core/util/shell.rb
214
+ - lib/hiera_backend.rb
207
215
  - spec/coral_core/interface_spec.rb
208
216
  - spec/coral_mock_input.rb
209
217
  - spec/coral_test_kernel.rb