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.
- 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
|