dop_common 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +176 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +177 -0
- data/README.md +48 -0
- data/Rakefile +49 -0
- data/Vagrantfile +25 -0
- data/bin/dop-puppet-autosign +56 -0
- data/doc/examples/example_deploment_plan_v0.0.1.yaml +302 -0
- data/doc/plan_format_v0.0.1.md +919 -0
- data/doc/plan_format_v0.0.2_snippets.md +56 -0
- data/dop_common.gemspec +44 -0
- data/lib/dop_common/affinity_group.rb +57 -0
- data/lib/dop_common/cli/global_options.rb +37 -0
- data/lib/dop_common/cli/log.rb +51 -0
- data/lib/dop_common/cli/node_selection.rb +62 -0
- data/lib/dop_common/command.rb +125 -0
- data/lib/dop_common/config/helper.rb +39 -0
- data/lib/dop_common/config.rb +66 -0
- data/lib/dop_common/configuration.rb +37 -0
- data/lib/dop_common/credential.rb +152 -0
- data/lib/dop_common/data_disk.rb +62 -0
- data/lib/dop_common/dns.rb +55 -0
- data/lib/dop_common/hash_parser.rb +241 -0
- data/lib/dop_common/hooks.rb +81 -0
- data/lib/dop_common/infrastructure.rb +160 -0
- data/lib/dop_common/infrastructure_properties.rb +185 -0
- data/lib/dop_common/interface.rb +113 -0
- data/lib/dop_common/log.rb +78 -0
- data/lib/dop_common/network.rb +85 -0
- data/lib/dop_common/node/config.rb +159 -0
- data/lib/dop_common/node.rb +442 -0
- data/lib/dop_common/node_filter.rb +74 -0
- data/lib/dop_common/plan.rb +188 -0
- data/lib/dop_common/plan_cache.rb +83 -0
- data/lib/dop_common/plan_store.rb +263 -0
- data/lib/dop_common/pre_processor.rb +73 -0
- data/lib/dop_common/run_options.rb +56 -0
- data/lib/dop_common/signal_handler.rb +58 -0
- data/lib/dop_common/state_store.rb +95 -0
- data/lib/dop_common/step.rb +200 -0
- data/lib/dop_common/step_set.rb +41 -0
- data/lib/dop_common/thread_context_logger.rb +77 -0
- data/lib/dop_common/utils.rb +106 -0
- data/lib/dop_common/validator.rb +53 -0
- data/lib/dop_common/version.rb +3 -0
- data/lib/dop_common.rb +32 -0
- data/lib/hiera/backend/dop_backend.rb +94 -0
- data/lib/hiera/dop_logger.rb +20 -0
- data/spec/data/fake_hook_file_invalid +1 -0
- data/spec/data/fake_hook_file_valid +5 -0
- data/spec/data/fake_keyfile +1 -0
- data/spec/dop-puppet-autosign_spec_disable.rb +33 -0
- data/spec/dop_common/affinity_group_spec.rb +41 -0
- data/spec/dop_common/command_spec.rb +83 -0
- data/spec/dop_common/credential_spec.rb +73 -0
- data/spec/dop_common/data_disk_spec.rb +165 -0
- data/spec/dop_common/dns_spec.rb +33 -0
- data/spec/dop_common/hash_parser_spec.rb +181 -0
- data/spec/dop_common/hooks_spec.rb +33 -0
- data/spec/dop_common/infrastructure_properties_spec.rb +224 -0
- data/spec/dop_common/infrastructure_spec.rb +77 -0
- data/spec/dop_common/interface_spec.rb +192 -0
- data/spec/dop_common/network_spec.rb +92 -0
- data/spec/dop_common/node_filter_spec.rb +70 -0
- data/spec/dop_common/node_spec.rb +623 -0
- data/spec/dop_common/plan_cache_spec.rb +46 -0
- data/spec/dop_common/plan_spec.rb +136 -0
- data/spec/dop_common/plan_store_spec.rb +194 -0
- data/spec/dop_common/pre_processor_spec.rb +27 -0
- data/spec/dop_common/run_options_spec.rb +65 -0
- data/spec/dop_common/signal_handler_spec.rb +31 -0
- data/spec/dop_common/step_set_spec.rb +21 -0
- data/spec/dop_common/step_spec.rb +175 -0
- data/spec/dop_common/utils_spec.rb +27 -0
- data/spec/dop_common/validator_spec.rb +47 -0
- data/spec/example_plans_spec.rb +16 -0
- data/spec/fixtures/example_ssh_key +27 -0
- data/spec/fixtures/example_ssh_key.pub +1 -0
- data/spec/fixtures/incl/root_part.yaml +1 -0
- data/spec/fixtures/incl/some_list.yaml +2 -0
- data/spec/fixtures/other_plan_same_nodes.yaml +19 -0
- data/spec/fixtures/simple_include.yaml +6 -0
- data/spec/fixtures/simple_include_with_errors.yaml +4 -0
- data/spec/fixtures/simple_plan.yaml +19 -0
- data/spec/fixtures/simple_plan_invalid.yaml +18 -0
- data/spec/fixtures/simple_plan_modified.yaml +21 -0
- data/spec/spec_helper.rb +106 -0
- 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
|