dop_common 0.13.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 (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