dop_common 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +23 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/CHANGELOG.md +176 -0
  6. data/Gemfile +11 -0
  7. data/LICENSE.txt +177 -0
  8. data/README.md +48 -0
  9. data/Rakefile +49 -0
  10. data/Vagrantfile +25 -0
  11. data/bin/dop-puppet-autosign +56 -0
  12. data/doc/examples/example_deploment_plan_v0.0.1.yaml +302 -0
  13. data/doc/plan_format_v0.0.1.md +919 -0
  14. data/doc/plan_format_v0.0.2_snippets.md +56 -0
  15. data/dop_common.gemspec +44 -0
  16. data/lib/dop_common/affinity_group.rb +57 -0
  17. data/lib/dop_common/cli/global_options.rb +37 -0
  18. data/lib/dop_common/cli/log.rb +51 -0
  19. data/lib/dop_common/cli/node_selection.rb +62 -0
  20. data/lib/dop_common/command.rb +125 -0
  21. data/lib/dop_common/config/helper.rb +39 -0
  22. data/lib/dop_common/config.rb +66 -0
  23. data/lib/dop_common/configuration.rb +37 -0
  24. data/lib/dop_common/credential.rb +152 -0
  25. data/lib/dop_common/data_disk.rb +62 -0
  26. data/lib/dop_common/dns.rb +55 -0
  27. data/lib/dop_common/hash_parser.rb +241 -0
  28. data/lib/dop_common/hooks.rb +81 -0
  29. data/lib/dop_common/infrastructure.rb +160 -0
  30. data/lib/dop_common/infrastructure_properties.rb +185 -0
  31. data/lib/dop_common/interface.rb +113 -0
  32. data/lib/dop_common/log.rb +78 -0
  33. data/lib/dop_common/network.rb +85 -0
  34. data/lib/dop_common/node/config.rb +159 -0
  35. data/lib/dop_common/node.rb +442 -0
  36. data/lib/dop_common/node_filter.rb +74 -0
  37. data/lib/dop_common/plan.rb +188 -0
  38. data/lib/dop_common/plan_cache.rb +83 -0
  39. data/lib/dop_common/plan_store.rb +263 -0
  40. data/lib/dop_common/pre_processor.rb +73 -0
  41. data/lib/dop_common/run_options.rb +56 -0
  42. data/lib/dop_common/signal_handler.rb +58 -0
  43. data/lib/dop_common/state_store.rb +95 -0
  44. data/lib/dop_common/step.rb +200 -0
  45. data/lib/dop_common/step_set.rb +41 -0
  46. data/lib/dop_common/thread_context_logger.rb +77 -0
  47. data/lib/dop_common/utils.rb +106 -0
  48. data/lib/dop_common/validator.rb +53 -0
  49. data/lib/dop_common/version.rb +3 -0
  50. data/lib/dop_common.rb +32 -0
  51. data/lib/hiera/backend/dop_backend.rb +94 -0
  52. data/lib/hiera/dop_logger.rb +20 -0
  53. data/spec/data/fake_hook_file_invalid +1 -0
  54. data/spec/data/fake_hook_file_valid +5 -0
  55. data/spec/data/fake_keyfile +1 -0
  56. data/spec/dop-puppet-autosign_spec_disable.rb +33 -0
  57. data/spec/dop_common/affinity_group_spec.rb +41 -0
  58. data/spec/dop_common/command_spec.rb +83 -0
  59. data/spec/dop_common/credential_spec.rb +73 -0
  60. data/spec/dop_common/data_disk_spec.rb +165 -0
  61. data/spec/dop_common/dns_spec.rb +33 -0
  62. data/spec/dop_common/hash_parser_spec.rb +181 -0
  63. data/spec/dop_common/hooks_spec.rb +33 -0
  64. data/spec/dop_common/infrastructure_properties_spec.rb +224 -0
  65. data/spec/dop_common/infrastructure_spec.rb +77 -0
  66. data/spec/dop_common/interface_spec.rb +192 -0
  67. data/spec/dop_common/network_spec.rb +92 -0
  68. data/spec/dop_common/node_filter_spec.rb +70 -0
  69. data/spec/dop_common/node_spec.rb +623 -0
  70. data/spec/dop_common/plan_cache_spec.rb +46 -0
  71. data/spec/dop_common/plan_spec.rb +136 -0
  72. data/spec/dop_common/plan_store_spec.rb +194 -0
  73. data/spec/dop_common/pre_processor_spec.rb +27 -0
  74. data/spec/dop_common/run_options_spec.rb +65 -0
  75. data/spec/dop_common/signal_handler_spec.rb +31 -0
  76. data/spec/dop_common/step_set_spec.rb +21 -0
  77. data/spec/dop_common/step_spec.rb +175 -0
  78. data/spec/dop_common/utils_spec.rb +27 -0
  79. data/spec/dop_common/validator_spec.rb +47 -0
  80. data/spec/example_plans_spec.rb +16 -0
  81. data/spec/fixtures/example_ssh_key +27 -0
  82. data/spec/fixtures/example_ssh_key.pub +1 -0
  83. data/spec/fixtures/incl/root_part.yaml +1 -0
  84. data/spec/fixtures/incl/some_list.yaml +2 -0
  85. data/spec/fixtures/other_plan_same_nodes.yaml +19 -0
  86. data/spec/fixtures/simple_include.yaml +6 -0
  87. data/spec/fixtures/simple_include_with_errors.yaml +4 -0
  88. data/spec/fixtures/simple_plan.yaml +19 -0
  89. data/spec/fixtures/simple_plan_invalid.yaml +18 -0
  90. data/spec/fixtures/simple_plan_modified.yaml +21 -0
  91. data/spec/spec_helper.rb +106 -0
  92. metadata +381 -0
@@ -0,0 +1,152 @@
1
+ #
2
+ # DOP Common credential hash parser
3
+ #
4
+
5
+ module DopCommon
6
+ class Credential
7
+ include Validator
8
+ include HashParser
9
+
10
+ attr_reader :hash, :name
11
+
12
+ VALID_TYPES = [:username_password, :kerberos, :ssh_key]
13
+
14
+ def initialize(name, hash)
15
+ @name = name
16
+ @hash = deep_symbolize_keys(hash)
17
+ DopCommon.add_log_filter(Proc.new {|msg| filter_secrets(msg)})
18
+ end
19
+
20
+ def validate
21
+ log_validation_method('type_valid?')
22
+ end
23
+
24
+ # This method filters the secrets from a message
25
+ def filter_secrets(msg)
26
+ case type
27
+ when :username_password then msg.gsub(password, '****')
28
+ else msg
29
+ end
30
+ end
31
+
32
+ def type
33
+ @type ||= type_valid? ? @hash[:type].to_sym : nil
34
+ end
35
+
36
+ def username
37
+ @username ||= username_valid? ? @hash[:username] : nil
38
+ end
39
+
40
+ def password
41
+ @password ||= password_valid? ? load_content(@hash[:password]) : nil
42
+ end
43
+
44
+ def realm
45
+ @realm ||= realm_valid? ? @hash[:realm] : nil
46
+ end
47
+
48
+ def service
49
+ @service ||= service_valid? ? @hash[:service] : nil
50
+ end
51
+
52
+ def keytab
53
+ @keytab ||= keytab_valid? ? load_content(@hash[:keytab]) : nil
54
+ end
55
+
56
+ def private_key
57
+ @private_key ||= private_key_valid? ? load_content(@hash[:private_key]) : nil
58
+ end
59
+
60
+ def public_key
61
+ @public_key ||= public_key_valid? ? load_content(@hash[:public_key]) : nil
62
+ end
63
+
64
+ private
65
+
66
+ def type_valid?
67
+ @hash[:type] or
68
+ raise PlanParsingError, "You need to specify the 'type' of the credental in #{@name} which can be one of #{VALID_TYPES.join(', ')}"
69
+ case @hash[:type]
70
+ when :username_password, 'username_password' then username_password_valid?
71
+ when :kerberos, 'kerberos' then kerberos_valid?
72
+ when :ssh_key, 'ssh_key' then ssh_key_valid?
73
+ else raise PlanParsingError, "The 'type' of the credental in #{@name} has to be one of #{VALID_TYPES.join(', ')}"
74
+ end
75
+ true
76
+ end
77
+
78
+ # This are the type validation methods, they will check if all the mandatory elements are there and
79
+ # if every supported attribute is valid.
80
+
81
+ def username_password_valid?
82
+ username_valid? or
83
+ raise PlanParsingError, "A username is missing in the credential #{@name} which is of type #{@hash[:type]}"
84
+ password_valid? or
85
+ raise PlanParsingError, "A password is missing in the credential #{@name} which is of type #{@hash[:type]}"
86
+ true
87
+ end
88
+
89
+ def kerberos_valid?
90
+ realm_valid? or
91
+ raise PlanParsingError, "A realm is missing in the credential #{@name} which is of type #{@hash[:type]}"
92
+ service_valid?
93
+ keytab_valid?
94
+ true
95
+ end
96
+
97
+ def ssh_key_valid?
98
+ username_valid? or
99
+ raise PlanParsingError, "A username is missing in the credential #{@name} which is of type #{@hash[:type]}"
100
+ private_key_valid?
101
+ public_key_valid?
102
+ true
103
+ end
104
+
105
+ # Attribute verification, will return false if the attribute is not valid, otherwise raise a PlanParsingError
106
+
107
+ def username_valid?
108
+ return false if @hash[:username].nil?
109
+ @hash[:username].kind_of?(String) or @hash[:username].kind_of?(Fixnum) or
110
+ raise PlanParsingError, "The username has to be a string or number in the credential #{@name} which is of type #{@hash[:type]}"
111
+ true
112
+ end
113
+
114
+ def password_valid?
115
+ credential_load_content_valid?(:password)
116
+ end
117
+
118
+ def keytab_valid?
119
+ credential_load_content_valid?(:keytab)
120
+ end
121
+
122
+ def private_key_valid?
123
+ credential_load_content_valid?(:private_key)
124
+ end
125
+
126
+ def public_key_valid?
127
+ credential_load_content_valid?(:public_key)
128
+ end
129
+
130
+ def credential_load_content_valid?(key)
131
+ return false if @hash[key].nil?
132
+ load_content_valid?(@hash[key])
133
+ rescue PlanParsingError => e
134
+ raise PlanParsingError, "Error while parsing the value for #{key} in credential #{@name}: #{e.message}"
135
+ end
136
+
137
+ def realm_valid?
138
+ return false if @hash[:realm].nil?
139
+ @hash[:realm].kind_of?(String) or
140
+ raise PlanParsingError, "The realm has to be a string in the credential #{@name} which is of type #{@hash[:type]}"
141
+ true
142
+ end
143
+
144
+ def service_valid?
145
+ return false if @hash[:service].nil?
146
+ @hash[:service].kind_of?(String) or
147
+ raise PlanParsingError, "The service has to be a string in the credential #{@name} which is of type #{@hash[:type]}"
148
+ true
149
+ end
150
+
151
+ end
152
+ end
@@ -0,0 +1,62 @@
1
+ module DopCommon
2
+ class DataDisk
3
+ include Validator
4
+ include HashParser
5
+ include Utils
6
+
7
+ attr_reader :name
8
+
9
+ def initialize(name, hash, parent = {})
10
+ @name = name
11
+ @hash = symbolize_keys(hash)
12
+ @parsed_infrastructure = parent[:parsed_infrastructure]
13
+ @parsed_infrastructure_properties = parent[:parsed_infrastructure_properties]
14
+ end
15
+
16
+ def validate
17
+ log_validation_method(:pool_valid?)
18
+ log_validation_method(:thin_valid?)
19
+ log_validation_method(:size_valid?)
20
+ try_validate_obj("Can't validate the 'data_disk' #{@name} because of previous error"){size}
21
+ end
22
+
23
+ def pool
24
+ @pool ||= pool_valid? ? @hash[:pool] : @parsed_infrastructure_properties.default_pool
25
+ end
26
+
27
+ def size
28
+ @size ||= size_valid? ? DopCommon::Utils::DataSize.new(@hash[:size]) : nil
29
+ end
30
+
31
+ def thin?
32
+ @thin ||= thin_valid? ? @hash[:thin] : true
33
+ end
34
+
35
+ private
36
+
37
+ def pool_valid?
38
+ provider = @parsed_infrastructure.provider
39
+ default_pool = @parsed_infrastructure_properties.default_pool
40
+ raise PlanParsingError, "Data disk #{@name}: A 'pool' is required for #{provider} provider type" unless
41
+ @parsed_infrastructure.provides?(:openstack, :baremetal) || @hash.has_key?(:pool) || default_pool
42
+ return false unless @hash.has_key?(:pool)
43
+ raise PlanParsingError, "Data disk #{@name}: 'pool', if defined, must be a non-empty string" if
44
+ !@hash[:pool].kind_of?(String) || @hash[:pool].empty?
45
+ true
46
+ end
47
+
48
+ def size_valid?
49
+ raise PlanParsingError, "Data disk #{@name}: 'size' is required" if @hash[:size].nil?
50
+ raise PlanParsingError, "Data disk #{@name}: 'size' must be of string type" unless
51
+ @hash[:size].kind_of?(String)
52
+ true
53
+ end
54
+
55
+ def thin_valid?
56
+ return false unless @hash.has_key?(:thin)
57
+ raise PlanParsingError, "Data disk #{@name}: thin, if specified, must be boolean" unless
58
+ [TrueClass, FalseClass].include?(@hash[:thin].class)
59
+ true
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,55 @@
1
+ require 'ipaddr'
2
+
3
+ module DopCommon
4
+ class DNS
5
+ include Validator
6
+ include HashParser
7
+
8
+ def initialize(hash)
9
+ @hash = (symbolize_keys(hash) || {}) # DNS is optional.
10
+ end
11
+
12
+ def validate
13
+ log_validation_method(:name_servers_valid?)
14
+ log_validation_method(:search_domains_valid?)
15
+ end
16
+
17
+ def name_servers
18
+ @name_servers ||= name_servers_valid? ? @hash[:name_servers] : []
19
+ end
20
+
21
+ def search_domains
22
+ @search_domains ||= search_domains_valid? ? @hash[:search_domains] : []
23
+ end
24
+
25
+ private
26
+
27
+ def name_servers_valid?
28
+ return false unless @hash.has_key?(:name_servers)
29
+ raise PlanParsingError, "DNS: name_servers must be an array of IP addresses" if
30
+ !@hash[:name_servers].kind_of?(Array) || @hash[:name_servers].empty?
31
+ @hash[:name_servers].each do |n|
32
+ begin
33
+ IPAddr.new(n)
34
+ rescue
35
+ raise PlanParsingError, "DNS: name_servers entry '#{n}' is not a valid IP address"
36
+ end
37
+ end
38
+ true
39
+ end
40
+
41
+ def search_domains_valid?
42
+ regex = /((^[a-z0-9]+(-[a-z0-9]+)*){1,63}$)|(^((?=[a-z0-9-]{1,63}\.)[a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,63}$)/
43
+ return false unless @hash.has_key?(:search_domains)
44
+ raise PlanParsingError, "DNS: search_domains must be an array of search domains" if
45
+ !@hash[:search_domains].kind_of?(Array) || @hash[:search_domains].empty?
46
+ raise PlanParsingError, "DNS: search_domains entries must be strings" unless
47
+ @hash[:search_domains].all? { |d| d.kind_of?(String) }
48
+ @hash[:search_domains].each do |d|
49
+ raise PlanParsingError, "DNS: search_domain entry '#{d}' is not a valid domain name" unless
50
+ d =~ regex
51
+ end
52
+ true
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,241 @@
1
+ #
2
+ # DOP Common Hash Parser Helper Functions
3
+ #
4
+
5
+ require 'open3'
6
+
7
+ module DopCommon
8
+ module HashParser
9
+
10
+ # This method will set the key from a list of defined
11
+ # aliases or raise an error if multiple values are found
12
+ def key_aliases(hash, key, key_aliases)
13
+ value = hash[key]
14
+ key_aliases.each do |key_alias|
15
+ next if hash[key_alias].nil?
16
+ unless value.nil?
17
+ keys_with_values = key_aliases.select{|a| !hash[a].nil?}
18
+ keys_with_values << key if hash[key]
19
+ key_list = keys_with_values.map{|k| k.kind_of?(Symbol) ? ':' + k.to_s : k}.join(', ')
20
+ raise DopCommon::PlanParsingError,
21
+ "Two or more values found for the same thing. There can only be one of: #{key_list}"
22
+ else
23
+ value = hash[key] = hash.delete(key_alias)
24
+ key_s = key.kind_of?(Symbol) ? ':' + key.to_s : key
25
+ DopCommon.log.debug("Key alias found '#{key_alias}', mapping for key #{key_s}")
26
+ end
27
+ end
28
+ end
29
+ module_function :key_aliases
30
+
31
+ def symbolize_keys(hash)
32
+ if hash.kind_of?(Hash)
33
+ Hash[hash.map { |k, v| [k.to_sym, v] }] if hash.kind_of?(Hash)
34
+ else
35
+ hash
36
+ end
37
+ end
38
+ module_function :symbolize_keys
39
+
40
+ def deep_symbolize_keys(hash, stack = [])
41
+ # prevent loops in recursive function
42
+ return if stack.include?(hash.object_id)
43
+ stack << hash.object_id
44
+
45
+ if hash.kind_of?(Hash)
46
+ Hash[
47
+ hash.map do |k, v|
48
+ [k.respond_to?(:to_sym) ? k.to_sym : k, deep_symbolize_keys(v, stack)]
49
+ end
50
+ ]
51
+ elsif hash.kind_of?(Array)
52
+ hash.map { |v| deep_symbolize_keys(v, stack) }
53
+ else
54
+ hash
55
+ end
56
+ end
57
+ module_function :deep_symbolize_keys
58
+
59
+ # This method will retrun true if the String in 'value' starts and
60
+ # ends with /, which means it represents a regexp
61
+ def represents_regexp?(value)
62
+ if value.kind_of?(String)
63
+ value[/^\/(.*)\/$/, 1] ? true : false
64
+ else
65
+ false
66
+ end
67
+ end
68
+ module_function :represents_regexp?
69
+
70
+ # This method will return true if the string in 'value' represents
71
+ # a regex and if it is possible to create a regexp object
72
+ def is_valid_regexp?(value)
73
+ return false unless represents_regexp?(value)
74
+ regexp = value[/^\/(.*)\/$/, 1]
75
+ Regexp.new(regexp)
76
+ true
77
+ rescue
78
+ false
79
+ end
80
+ module_function :is_valid_regexp?
81
+
82
+ # This method takes a hash and a key. It will then validate the
83
+ # pattern list in the value of that key.
84
+ #
85
+ # Examples (Simple String/Regexp):
86
+ #
87
+ # hash = { :key => 'my_node'}
88
+ # hash = { :key => '/my_node/'}
89
+ #
90
+ # Examples (Array of Strings, Regexps):
91
+ #
92
+ # hash = { :key => [ 'my_node', '/my_node/' ] }
93
+ #
94
+ def pattern_list_valid?(hash, key, optional = true)
95
+ return false if hash[key].nil? && optional
96
+ [Array, String, Symbol].include? hash[key].class or
97
+ raise PlanParsingError, "The value for '#{key}' has to be a string, an array or a symbol."
98
+ [hash[key]].flatten.each do |pattern|
99
+ [String, Symbol].include? pattern.class or
100
+ raise PlanParsingError, "The pattern #{pattern} in '#{key}' is not a symbol or string."
101
+ if HashParser.represents_regexp?(pattern)
102
+ HashParser.is_valid_regexp?(pattern) or
103
+ raise PlanParsingError, "The pattern #{pattern} in '#{key}' is not a valid regular expression."
104
+ end
105
+ end
106
+ true
107
+ end
108
+ module_function :pattern_list_valid?
109
+
110
+ # This method takes a hash where all the values are pattern lists
111
+ # and checks if they are valid.
112
+ #
113
+ # Example:
114
+ #
115
+ # hash = {
116
+ # :key => {
117
+ # 'list_1' => [ 'my_node', '/my_node/' ],
118
+ # 'list_2' => '/my_node'/
119
+ # }
120
+ # }
121
+ #
122
+ def hash_of_pattern_lists_valid?(hash, key, optional = true )
123
+ return false if hash[key].nil? && optional
124
+ hash[key].kind_of?(Hash) or
125
+ raise PlanParsingError, "The value for '#{key}' has to be a Hash"
126
+ hash[key].each_key do |list_name|
127
+ list_name.kind_of?(String) or
128
+ raise PlanParsingError, "The key '#{list_name.to_s}' in '#{key}' has to be a String"
129
+ HashParser.pattern_list_valid?(hash[key], list_name)
130
+ end
131
+ true
132
+ end
133
+ module_function :hash_of_pattern_lists_valid?
134
+
135
+ # This method will parse a valid pattern list and replace regexp
136
+ # strings with Regexp objects.
137
+ def parse_pattern_list(hash, key)
138
+ case hash[key]
139
+ when 'all', 'All', 'ALL', :all then :all
140
+ else
141
+ patterns = [hash[key]].flatten.compact
142
+ patterns.map do |pattern|
143
+ HashParser.represents_regexp?(pattern) ? Regexp.new(pattern[/^\/(.*)\/$/, 1]) : pattern
144
+ end
145
+ end
146
+ end
147
+ module_function :parse_pattern_list
148
+
149
+ # This method will parse a hash of pattern lists and replace the
150
+ # regexp strings with Regexp objects
151
+ def parse_hash_of_pattern_lists(hash, key)
152
+ Hash[hash[key].map do |list_name, pattern_list|
153
+ [list_name, parse_pattern_list(hash[key], list_name)]
154
+ end]
155
+ end
156
+ module_function :parse_hash_of_pattern_lists
157
+
158
+ # Load string content from different sources
159
+ #
160
+ # the content can be directly a string in which case it will
161
+ # immediatly return.
162
+ # load_content('hello world')
163
+ #
164
+ # it may also be a hash with the following content:
165
+ # load_content({ file => '/path/to/some/file' })
166
+ # This will check if the file exists and load it
167
+ #
168
+ # you can also specify a script it will execute to get the
169
+ # content
170
+ # load_content({ exec => '/path/to/some/executable_file' })
171
+ def load_content(value)
172
+ if value.kind_of?(Hash)
173
+ method, params = symbolize_keys(value).first
174
+ file = case params
175
+ when Array then params.join(' ')
176
+ when String then params
177
+ end
178
+ case method
179
+ when :file then File.read(file).chomp
180
+ when :exec
181
+ prog = params.first
182
+ args = params[1, params.length].join(' ')
183
+ o, e, s = Open3.capture3(Utils::sanitize_env, "#{prog} #{args}", :unsetenv_others => true)
184
+
185
+ unless s.success?
186
+ DopCommon.log.error("Standard error output of '#{prog} #{args}':\n#{e.chomp}")
187
+ DopCommon.log.error("Standard output of '#{prog} #{args}':\n#{o.chomp}")
188
+ raise PlanParsingError, "Program '#{prog}' returned non-zero exit status #{s.exitstatus}"
189
+ end
190
+
191
+ o.chomp
192
+ end
193
+ else
194
+ value
195
+ end
196
+ end
197
+ module_function :load_content
198
+
199
+ # This is the validation method for the load_content
200
+ # method and will check if the value is a correctly
201
+ # specified content source
202
+ def load_content_valid?(value)
203
+ case value
204
+ when String then true
205
+ when Hash
206
+ value.count == 1 or
207
+ raise PlanParsingError, "You can only specify one content type"
208
+ method, params = symbolize_keys(value).first
209
+ [:file, :exec].include?(method) or
210
+ raise PlanParsingError, "#{method} is not a valid content method. valid methods are :exec and :file"
211
+ file = case params
212
+ when Array
213
+ params.count >= 1 or
214
+ raise PlanParsingError, "The array for method #{method} has to have at least one entry"
215
+ params.all?{|e| e.kind_of?(String)} or
216
+ raise PlanParsingError, "The array for method #{method} can only contain strings"
217
+ method != :file or
218
+ raise PlanParsingError, "The method :file does not support arrays as an argument"
219
+ params.first
220
+ when String
221
+ params
222
+ else
223
+ raise PlanParsingError, "The value for method #{method} has to be an array or string"
224
+ end
225
+ File.exists?(file) or
226
+ raise PlanParsingError, "The file #{file} does not exist."
227
+ File.readable?(file) or
228
+ raise PlanParsingError, "The file #{file} is not readable."
229
+ if method == :exec
230
+ File.executable?(file) or
231
+ raise PlanParsingError, "The file #{file} is not executable."
232
+ end
233
+ else
234
+ raise PlanParsingError, 'The content source has to be a string or a hash with a content lookup method'
235
+ end
236
+ true
237
+ end
238
+ module_function :load_content_valid?
239
+
240
+ end
241
+ end
@@ -0,0 +1,81 @@
1
+ #
2
+ # DOP common hooks hash parser
3
+ #
4
+ module DopCommon
5
+ class Hooks
6
+ include Validator
7
+ include HashParser
8
+
9
+ def initialize(hash)
10
+ @hash = symbolize_keys(hash)
11
+ end
12
+
13
+ def validate
14
+ log_validation_method(:pre_create_vm_valid?)
15
+ log_validation_method(:post_create_vm_valid?)
16
+ log_validation_method(:pre_update_vm_valid?)
17
+ log_validation_method(:post_update_vm_valid?)
18
+ log_validation_method(:pre_destroy_vm_valid?)
19
+ log_validation_method(:post_destroy_vm_valid?)
20
+ end
21
+
22
+ def pre_create_vm
23
+ @pre_create_vm ||= pre_create_vm_valid? ? @hash[:pre_create_vm] : []
24
+ end
25
+
26
+ def post_create_vm
27
+ @post_create_vm ||= post_create_vm_valid? ? @hash[:post_create_vm] : []
28
+ end
29
+
30
+ def pre_update_vm
31
+ @pre_update_vm ||= pre_update_vm_valid? ? @hash[:pre_update_vm] : []
32
+ end
33
+
34
+ def post_update_vm
35
+ @post_update_vm ||= post_update_vm_valid? ? @hash[:post_update_vm] : []
36
+ end
37
+
38
+ def pre_destroy_vm
39
+ @pre_destroy_vm ||= pre_destroy_vm_valid? ? @hash[:pre_destroy_vm] : []
40
+ end
41
+
42
+ def post_destroy_vm
43
+ @post_destroy_vm ||= post_destroy_vm_valid? ? @hash[:post_destroy_vm] : []
44
+ end
45
+
46
+ private
47
+
48
+ def hook_valid?(hook_name)
49
+ return false unless @hash.has_key?(hook_name)
50
+ raise PlanParsingError, "Hook #{hook_name}: hooks must be a non-empty array of strings" if
51
+ !@hash[hook_name].kind_of?(Array) || @hash[hook_name].empty? || !@hash[hook_name].all? { |h| h.kind_of?(String) }
52
+ raise PlanParsingError, "Hook #{hook_name}: a hook must be an executable file" unless
53
+ @hash[hook_name].all? { |h| File.file?(h) && File.executable?(h) }
54
+ true
55
+ end
56
+
57
+ def pre_create_vm_valid?
58
+ hook_valid?(:pre_create_vm)
59
+ end
60
+
61
+ def post_create_vm_valid?
62
+ hook_valid?(:post_create_vm)
63
+ end
64
+
65
+ def pre_update_vm_valid?
66
+ hook_valid?(:pre_update_vm)
67
+ end
68
+
69
+ def post_update_vm_valid?
70
+ hook_valid?(:post_update_vm)
71
+ end
72
+
73
+ def pre_destroy_vm_valid?
74
+ hook_valid?(:pre_destroy_vm)
75
+ end
76
+
77
+ def post_destroy_vm_valid?
78
+ hook_valid?(:post_destroy_vm)
79
+ end
80
+ end
81
+ end