leap_cli 1.5.6 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/leap +29 -6
- data/lib/leap/platform.rb +36 -1
- data/lib/leap_cli/commands/ca.rb +97 -20
- data/lib/leap_cli/commands/compile.rb +49 -8
- data/lib/leap_cli/commands/db.rb +13 -4
- data/lib/leap_cli/commands/deploy.rb +138 -29
- data/lib/leap_cli/commands/env.rb +76 -0
- data/lib/leap_cli/commands/facts.rb +10 -3
- data/lib/leap_cli/commands/inspect.rb +2 -2
- data/lib/leap_cli/commands/list.rb +10 -10
- data/lib/leap_cli/commands/node.rb +7 -132
- data/lib/leap_cli/commands/node_init.rb +169 -0
- data/lib/leap_cli/commands/pre.rb +4 -27
- data/lib/leap_cli/commands/ssh.rb +152 -0
- data/lib/leap_cli/commands/test.rb +22 -13
- data/lib/leap_cli/commands/user.rb +12 -4
- data/lib/leap_cli/commands/vagrant.rb +4 -4
- data/lib/leap_cli/config/filter.rb +175 -0
- data/lib/leap_cli/config/manager.rb +130 -61
- data/lib/leap_cli/config/node.rb +32 -0
- data/lib/leap_cli/config/object.rb +69 -44
- data/lib/leap_cli/config/object_list.rb +44 -39
- data/lib/leap_cli/config/secrets.rb +24 -12
- data/lib/leap_cli/config/tag.rb +7 -0
- data/lib/{core_ext → leap_cli/core_ext}/boolean.rb +0 -0
- data/lib/{core_ext → leap_cli/core_ext}/hash.rb +0 -0
- data/lib/{core_ext → leap_cli/core_ext}/json.rb +0 -0
- data/lib/{core_ext → leap_cli/core_ext}/nil.rb +0 -0
- data/lib/{core_ext → leap_cli/core_ext}/string.rb +0 -0
- data/lib/leap_cli/core_ext/yaml.rb +29 -0
- data/lib/leap_cli/exceptions.rb +24 -0
- data/lib/leap_cli/leapfile.rb +60 -10
- data/lib/{lib_ext → leap_cli/lib_ext}/capistrano_connections.rb +0 -0
- data/lib/{lib_ext → leap_cli/lib_ext}/gli.rb +0 -0
- data/lib/leap_cli/log.rb +1 -1
- data/lib/leap_cli/logger.rb +18 -1
- data/lib/leap_cli/markdown_document_listener.rb +1 -1
- data/lib/leap_cli/override/json.rb +11 -0
- data/lib/leap_cli/path.rb +20 -6
- data/lib/leap_cli/remote/leap_plugin.rb +2 -2
- data/lib/leap_cli/remote/puppet_plugin.rb +1 -1
- data/lib/leap_cli/remote/rsync_plugin.rb +1 -1
- data/lib/leap_cli/remote/tasks.rb +1 -1
- data/lib/leap_cli/ssh_key.rb +63 -1
- data/lib/leap_cli/util/remote_command.rb +19 -2
- data/lib/leap_cli/util/secret.rb +1 -1
- data/lib/leap_cli/util/x509.rb +3 -2
- data/lib/leap_cli/util.rb +11 -3
- data/lib/leap_cli/version.rb +2 -2
- data/lib/leap_cli.rb +24 -14
- data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +85 -29
- data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +5 -0
- data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +406 -41
- data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +0 -34
- data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +6 -0
- data/vendor/certificate_authority/lib/certificate_authority/signing_request.rb +36 -1
- metadata +25 -24
- data/lib/leap_cli/commands/shell.rb +0 -89
- data/lib/leap_cli/config/macros.rb +0 -430
- data/lib/leap_cli/constants.rb +0 -7
- data/lib/leap_cli/requirements.rb +0 -19
- data/lib/lib_ext/markdown_document_listener.rb +0 -122
@@ -20,6 +20,16 @@ module LeapCli
|
|
20
20
|
|
21
21
|
def initialize
|
22
22
|
@environments = {} # hash of `Environment` objects, keyed by name.
|
23
|
+
|
24
|
+
# load macros and other custom ruby in provider base
|
25
|
+
platform_ruby_files = Dir[Path.provider_base + '/lib/*.rb']
|
26
|
+
if platform_ruby_files.any?
|
27
|
+
$: << Path.provider_base + '/lib'
|
28
|
+
platform_ruby_files.each do |rb_file|
|
29
|
+
require rb_file
|
30
|
+
end
|
31
|
+
end
|
32
|
+
Config::Object.send(:include, LeapCli::Macro)
|
23
33
|
end
|
24
34
|
|
25
35
|
##
|
@@ -54,9 +64,24 @@ module LeapCli
|
|
54
64
|
e
|
55
65
|
end
|
56
66
|
|
57
|
-
|
58
|
-
|
59
|
-
|
67
|
+
#
|
68
|
+
# The default accessors for services, tags, and provider.
|
69
|
+
# For these defaults, use 'default' environment, or whatever
|
70
|
+
# environment is pinned.
|
71
|
+
#
|
72
|
+
def services
|
73
|
+
env(default_environment).services
|
74
|
+
end
|
75
|
+
def tags
|
76
|
+
env(default_environment).tags
|
77
|
+
end
|
78
|
+
def provider
|
79
|
+
env(default_environment).provider
|
80
|
+
end
|
81
|
+
|
82
|
+
def default_environment
|
83
|
+
LeapCli.leapfile.environment
|
84
|
+
end
|
60
85
|
|
61
86
|
##
|
62
87
|
## IMPORT EXPORT
|
@@ -80,8 +105,8 @@ module LeapCli
|
|
80
105
|
@secrets = load_json( Path.named_path(:secrets_config, @provider_dir), Config::Secrets)
|
81
106
|
@common.inherit_from! @base_common
|
82
107
|
|
83
|
-
# load provider services, tags, and provider.json
|
84
|
-
log 3, :loading, 'default environment
|
108
|
+
# For the default environment, load provider services, tags, and provider.json
|
109
|
+
log 3, :loading, 'default environment...'
|
85
110
|
env('default') do |e|
|
86
111
|
e.services = load_all_json(Path.named_path([:service_config, '*'], @provider_dir), Config::Tag, :no_dots => true)
|
87
112
|
e.tags = load_all_json(Path.named_path([:tag_config, '*'], @provider_dir), Config::Tag, :no_dots => true)
|
@@ -92,28 +117,54 @@ module LeapCli
|
|
92
117
|
validate_provider(e.provider)
|
93
118
|
end
|
94
119
|
|
95
|
-
#
|
120
|
+
# create a special '_all_' environment, used for tracking the union
|
121
|
+
# of all the environments
|
122
|
+
env('_all_') do |e|
|
123
|
+
e.services = Config::ObjectList.new
|
124
|
+
e.tags = Config::ObjectList.new
|
125
|
+
e.provider = Config::Provider.new
|
126
|
+
e.services.inherit_from! env('default').services
|
127
|
+
e.tags.inherit_from! env('default').tags
|
128
|
+
e.provider.inherit_from! env('default').provider
|
129
|
+
end
|
130
|
+
|
131
|
+
# For each defined environment, load provider services, tags, and provider.json.
|
96
132
|
environment_names.each do |ename|
|
97
133
|
next unless ename
|
98
|
-
log 3, :loading, '%s environment
|
134
|
+
log 3, :loading, '%s environment...' % ename
|
99
135
|
env(ename) do |e|
|
100
136
|
e.services = load_all_json(Path.named_path([:service_env_config, '*', ename], @provider_dir), Config::Tag)
|
101
137
|
e.tags = load_all_json(Path.named_path([:tag_env_config, '*', ename], @provider_dir), Config::Tag)
|
102
138
|
e.provider = load_json( Path.named_path([:provider_env_config, ename], @provider_dir), Config::Provider)
|
103
|
-
e.services.inherit_from! env.services
|
104
|
-
e.tags.inherit_from! env.tags
|
105
|
-
e.provider.inherit_from! env.provider
|
139
|
+
e.services.inherit_from! env('default').services
|
140
|
+
e.tags.inherit_from! env('default').tags
|
141
|
+
e.provider.inherit_from! env('default').provider
|
106
142
|
validate_provider(e.provider)
|
107
143
|
end
|
108
144
|
end
|
109
145
|
|
146
|
+
# apply inheritance
|
110
147
|
@nodes.each do |name, node|
|
111
148
|
Util::assert! name =~ /^[0-9a-z-]+$/, "Illegal character(s) used in node name '#{name}'"
|
112
149
|
@nodes[name] = apply_inheritance(node)
|
113
150
|
end
|
114
151
|
|
115
|
-
|
116
|
-
|
152
|
+
# do some node-list post-processing
|
153
|
+
cleanup_node_lists(options)
|
154
|
+
|
155
|
+
# apply control files
|
156
|
+
@nodes.each do |name, node|
|
157
|
+
control_files(node).each do |file|
|
158
|
+
begin
|
159
|
+
node.eval_file file
|
160
|
+
rescue ConfigError => exc
|
161
|
+
if options[:continue_on_error]
|
162
|
+
exc.log
|
163
|
+
else
|
164
|
+
raise exc
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
117
168
|
end
|
118
169
|
end
|
119
170
|
|
@@ -178,35 +229,19 @@ module LeapCli
|
|
178
229
|
# returns a node list consisting only of nodes that satisfy the filter criteria.
|
179
230
|
#
|
180
231
|
# filter: condition [condition] [condition] [+condition]
|
181
|
-
# condition: [node_name | service_name | tag_name]
|
232
|
+
# condition: [node_name | service_name | tag_name | environment_name]
|
182
233
|
#
|
183
234
|
# if conditions is prefixed with +, then it works like an AND. Otherwise, it works like an OR.
|
184
235
|
#
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
node_list = Config::ObjectList.new
|
195
|
-
filters.each do |filter|
|
196
|
-
if filter =~ /^\+/
|
197
|
-
keep_list = nodes_for_name(filter[1..-1])
|
198
|
-
node_list.delete_if do |name, node|
|
199
|
-
if keep_list[name]
|
200
|
-
false
|
201
|
-
else
|
202
|
-
true
|
203
|
-
end
|
204
|
-
end
|
205
|
-
else
|
206
|
-
node_list.merge!(nodes_for_name(filter))
|
207
|
-
end
|
208
|
-
end
|
209
|
-
return node_list
|
236
|
+
# args:
|
237
|
+
# filter -- array of filter terms, one per item
|
238
|
+
#
|
239
|
+
# options:
|
240
|
+
# :local -- if :local is false and the filter is empty, then local nodes are excluded.
|
241
|
+
# :nopin -- if true, ignore environment pinning
|
242
|
+
#
|
243
|
+
def filter(filters=nil, options={})
|
244
|
+
Filter.new(filters, options, self).nodes()
|
210
245
|
end
|
211
246
|
|
212
247
|
#
|
@@ -248,6 +283,28 @@ module LeapCli
|
|
248
283
|
@nodes[node.name] = apply_inheritance!(node)
|
249
284
|
end
|
250
285
|
|
286
|
+
#
|
287
|
+
# returns all the partial data for the specified partial path.
|
288
|
+
# partial path is always relative to provider root, but there must be multiple files
|
289
|
+
# that match because provider root might be the base provider or the local provider.
|
290
|
+
#
|
291
|
+
def partials(partial_path)
|
292
|
+
@partials ||= {}
|
293
|
+
if @partials[partial_path].nil?
|
294
|
+
[Path.provider_base, Path.provider].each do |provider_dir|
|
295
|
+
path = File.join(provider_dir, partial_path)
|
296
|
+
if File.exists?(path)
|
297
|
+
@partials[partial_path] ||= []
|
298
|
+
@partials[partial_path] << load_json(path, Config::Object)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
if @partials[partial_path].nil?
|
302
|
+
raise RuntimeError, 'no such partial path `%s`' % partial_path, caller
|
303
|
+
end
|
304
|
+
end
|
305
|
+
@partials[partial_path]
|
306
|
+
end
|
307
|
+
|
251
308
|
private
|
252
309
|
|
253
310
|
def load_all_json(pattern, object_class, options={})
|
@@ -358,7 +415,6 @@ module LeapCli
|
|
358
415
|
raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions
|
359
416
|
else
|
360
417
|
new_node.deep_merge!(service)
|
361
|
-
self.services[node_service].node_list.add(name, new_node)
|
362
418
|
end
|
363
419
|
end
|
364
420
|
end
|
@@ -376,7 +432,6 @@ module LeapCli
|
|
376
432
|
raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions
|
377
433
|
else
|
378
434
|
new_node.deep_merge!(tag)
|
379
|
-
self.tags[node_tag].node_list.add(name, new_node)
|
380
435
|
end
|
381
436
|
end
|
382
437
|
end
|
@@ -390,45 +445,59 @@ module LeapCli
|
|
390
445
|
apply_inheritance(node, true)
|
391
446
|
end
|
392
447
|
|
393
|
-
|
448
|
+
#
|
449
|
+
# does some final clean at the end of loading nodes.
|
450
|
+
# this includes removing disabled nodes, and populating
|
451
|
+
# the services[x].node_list and tags[x].node_list
|
452
|
+
#
|
453
|
+
def cleanup_node_lists(options)
|
394
454
|
@disabled_nodes = Config::ObjectList.new
|
395
455
|
@nodes.each do |name, node|
|
396
|
-
|
397
|
-
log 2, :skipping, "disabled node #{name}."
|
398
|
-
@nodes.delete(name)
|
399
|
-
@disabled_nodes[name] = node
|
456
|
+
if node.enabled || options[:include_disabled]
|
400
457
|
if node['services']
|
401
458
|
node['services'].to_a.each do |node_service|
|
402
|
-
|
459
|
+
env(node.environment).services[node_service].node_list.add(node.name, node)
|
460
|
+
env('_all_').services[node_service].node_list.add(node.name, node)
|
403
461
|
end
|
404
462
|
end
|
405
463
|
if node['tags']
|
406
464
|
node['tags'].to_a.each do |node_tag|
|
407
|
-
|
465
|
+
env(node.environment).tags[node_tag].node_list.add(node.name, node)
|
466
|
+
env('_all_').tags[node_tag].node_list.add(node.name, node)
|
408
467
|
end
|
409
468
|
end
|
469
|
+
elsif !options[:include_disabled]
|
470
|
+
log 2, :skipping, "disabled node #{name}."
|
471
|
+
@nodes.delete(name)
|
472
|
+
@disabled_nodes[name] = node
|
410
473
|
end
|
411
474
|
end
|
412
475
|
end
|
413
476
|
|
477
|
+
def validate_provider(provider)
|
478
|
+
# nothing yet.
|
479
|
+
end
|
414
480
|
|
415
481
|
#
|
416
|
-
# returns a
|
482
|
+
# returns a list of 'control' files for this node.
|
483
|
+
# a control file is like a service or a tag JSON file, but it contains
|
484
|
+
# raw ruby code that gets evaluated in the context of the node.
|
485
|
+
# Yes, this entirely breaks our functional programming model
|
486
|
+
# for JSON generation.
|
417
487
|
#
|
418
|
-
def
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
488
|
+
def control_files(node)
|
489
|
+
files = []
|
490
|
+
[Path.provider_base, @provider_dir].each do |provider_dir|
|
491
|
+
[['services', :service_config], ['tags', :tag_config]].each do |attribute, path_sym|
|
492
|
+
node[attribute].each do |attr_value|
|
493
|
+
path = Path.named_path([path_sym, "#{attr_value}.rb"], provider_dir).sub(/\.json$/,'')
|
494
|
+
if File.exists?(path)
|
495
|
+
files << path
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|
427
499
|
end
|
428
|
-
|
429
|
-
|
430
|
-
def validate_provider(provider)
|
431
|
-
# nothing yet.
|
500
|
+
return files
|
432
501
|
end
|
433
502
|
|
434
503
|
end
|
data/lib/leap_cli/config/node.rb
CHANGED
@@ -33,6 +33,29 @@ module LeapCli; module Config
|
|
33
33
|
return vagrant_range.include?(ip_address)
|
34
34
|
end
|
35
35
|
|
36
|
+
#
|
37
|
+
# Return a hash table representation of ourselves, with the key equal to the @node.name,
|
38
|
+
# and the value equal to the fields specified in *keys.
|
39
|
+
#
|
40
|
+
# Also, the result is flattened to a single hash, so a key of 'a.b' becomes 'a_b'
|
41
|
+
#
|
42
|
+
# compare to Object#pick(*keys). This method is the sames as Config::ObjectList#pick_fields,
|
43
|
+
# but works on a single node.
|
44
|
+
#
|
45
|
+
# Example:
|
46
|
+
#
|
47
|
+
# node.pick('domain.internal') =>
|
48
|
+
#
|
49
|
+
# {
|
50
|
+
# 'node1': {
|
51
|
+
# 'domain_internal': 'node1.example.i'
|
52
|
+
# }
|
53
|
+
# }
|
54
|
+
#
|
55
|
+
def pick_fields(*keys)
|
56
|
+
{@node.name => self.pick(*keys)}
|
57
|
+
end
|
58
|
+
|
36
59
|
#
|
37
60
|
# can be overridden by the platform.
|
38
61
|
# returns a list of node names that should be tested before this node
|
@@ -40,6 +63,15 @@ module LeapCli; module Config
|
|
40
63
|
def test_dependencies
|
41
64
|
[]
|
42
65
|
end
|
66
|
+
|
67
|
+
# returns a string list of supported ssh host key algorithms for this node.
|
68
|
+
# or an empty string if it could not be determined
|
69
|
+
def supported_ssh_host_key_algorithms
|
70
|
+
@host_key_algo ||= SshKey.supported_host_key_algorithms(
|
71
|
+
Util.read_file([:node_ssh_pub_key, @node.name])
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
43
75
|
end
|
44
76
|
|
45
77
|
end; end
|
@@ -8,8 +8,6 @@ if $ruby_version < [1,9]
|
|
8
8
|
end
|
9
9
|
require 'ya2yaml' # pure ruby yaml
|
10
10
|
|
11
|
-
require 'leap_cli/config/macros'
|
12
|
-
|
13
11
|
module LeapCli
|
14
12
|
module Config
|
15
13
|
#
|
@@ -20,8 +18,6 @@ module LeapCli
|
|
20
18
|
#
|
21
19
|
class Object < Hash
|
22
20
|
|
23
|
-
include Config::Macros
|
24
|
-
|
25
21
|
attr_reader :node
|
26
22
|
attr_reader :manager
|
27
23
|
alias :global :manager
|
@@ -44,7 +40,7 @@ module LeapCli
|
|
44
40
|
#
|
45
41
|
def dump_yaml
|
46
42
|
evaluate(@node)
|
47
|
-
|
43
|
+
sorted_ya2yaml(:syck_compatible => true)
|
48
44
|
end
|
49
45
|
|
50
46
|
#
|
@@ -68,6 +64,11 @@ module LeapCli
|
|
68
64
|
get(key)
|
69
65
|
end
|
70
66
|
|
67
|
+
# Overrride some default methods in Hash that are likely to
|
68
|
+
# be used as attributes.
|
69
|
+
alias_method :hkey, :key
|
70
|
+
def key; get('key'); end
|
71
|
+
|
71
72
|
#
|
72
73
|
# make hash addressable like an object (e.g. obj['name'] available as obj.name)
|
73
74
|
#
|
@@ -134,7 +135,18 @@ module LeapCli
|
|
134
135
|
#
|
135
136
|
def deep_merge!(object, prefer_self=false)
|
136
137
|
object.each do |key,new_value|
|
137
|
-
|
138
|
+
if self.has_key?('+'+key)
|
139
|
+
mode = :add
|
140
|
+
old_value = self.fetch '+'+key, nil
|
141
|
+
self.delete('+'+key)
|
142
|
+
elsif self.has_key?('-'+key)
|
143
|
+
mode = :subtract
|
144
|
+
old_value = self.fetch '-'+key, nil
|
145
|
+
self.delete('-'+key)
|
146
|
+
else
|
147
|
+
mode = :normal
|
148
|
+
old_value = self.fetch key, nil
|
149
|
+
end
|
138
150
|
|
139
151
|
# clean up boolean
|
140
152
|
new_value = true if new_value == "true"
|
@@ -160,6 +172,18 @@ module LeapCli
|
|
160
172
|
elsif new_value.is_a?(Array) && !old_value.is_a?(Array)
|
161
173
|
(value = (new_value.dup << old_value).compact.uniq).delete('REQUIRED')
|
162
174
|
|
175
|
+
# merge two arrays
|
176
|
+
elsif old_value.is_a?(Array) && new_value.is_a?(Array)
|
177
|
+
if mode == :add
|
178
|
+
value = (old_value + new_value).sort.uniq
|
179
|
+
elsif mode == :subtract
|
180
|
+
value = new_value - old_value
|
181
|
+
elsif prefer_self
|
182
|
+
value = old_value
|
183
|
+
else
|
184
|
+
value = new_value
|
185
|
+
end
|
186
|
+
|
163
187
|
# catch errors
|
164
188
|
elsif type_mismatch?(old_value, new_value)
|
165
189
|
raise 'Type mismatch. Cannot merge %s (%s) with %s (%s). Key is "%s", name is "%s".' % [
|
@@ -168,7 +192,7 @@ module LeapCli
|
|
168
192
|
key, self.class
|
169
193
|
]
|
170
194
|
|
171
|
-
# merge strings
|
195
|
+
# merge simple strings & numbers
|
172
196
|
else
|
173
197
|
if prefer_self
|
174
198
|
value = old_value
|
@@ -206,6 +230,10 @@ module LeapCli
|
|
206
230
|
end
|
207
231
|
end
|
208
232
|
|
233
|
+
def eval_file(filename)
|
234
|
+
evaluate_ruby(filename, File.read(filename))
|
235
|
+
end
|
236
|
+
|
209
237
|
protected
|
210
238
|
|
211
239
|
#
|
@@ -246,45 +274,42 @@ module LeapCli
|
|
246
274
|
# (`key` is just passed for debugging purposes)
|
247
275
|
#
|
248
276
|
def evaluate_ruby(key, value)
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
Util::log "offending key: #{key}", :indent => 1
|
268
|
-
Util::log "offending string: #{value}", :indent => 1
|
269
|
-
Util::log "error message: no file '#{exc}'", :indent => 1
|
270
|
-
end
|
271
|
-
end
|
272
|
-
rescue AssertionFailed => exc
|
273
|
-
Util.bail! do
|
274
|
-
Util::log :failed, "assertion while evaluating node '#{self.name}'"
|
275
|
-
Util::log 'assertion: %s' % exc.assertion, :indent => 1
|
276
|
-
Util::log "offending key: #{key}", :indent => 1
|
277
|
-
end
|
278
|
-
rescue SyntaxError, StandardError => exc
|
279
|
-
Util::bail! do
|
280
|
-
Util::log :error, "while evaluating node '#{self.name}'"
|
281
|
-
Util::log "offending key: #{key}", :indent => 1
|
282
|
-
Util::log "offending string: #{value}", :indent => 1
|
283
|
-
Util::log "error message: #{exc.inspect}", :indent => 1
|
284
|
-
end
|
277
|
+
self.instance_eval(value, key, 1)
|
278
|
+
rescue ConfigError => exc
|
279
|
+
raise exc # pass through
|
280
|
+
rescue SystemStackError => exc
|
281
|
+
Util::log 0, :error, "while evaluating node '#{self.name}'"
|
282
|
+
Util::log 0, "offending key: #{key}", :indent => 1
|
283
|
+
Util::log 0, "offending string: #{value}", :indent => 1
|
284
|
+
Util::log 0, "STACK OVERFLOW, BAILING OUT. There must be an eval loop of death (variables with circular dependencies).", :indent => 1
|
285
|
+
raise SystemExit.new(1)
|
286
|
+
rescue FileMissing => exc
|
287
|
+
Util::bail! do
|
288
|
+
if exc.options[:missing]
|
289
|
+
Util::log :missing, exc.options[:missing].gsub('$node', self.name).gsub('$file', exc.path)
|
290
|
+
else
|
291
|
+
Util::log :error, "while evaluating node '#{self.name}'"
|
292
|
+
Util::log "offending key: #{key}", :indent => 1
|
293
|
+
Util::log "offending string: #{value}", :indent => 1
|
294
|
+
Util::log "error message: no file '#{exc}'", :indent => 1
|
285
295
|
end
|
296
|
+
raise exc if DEBUG
|
297
|
+
end
|
298
|
+
rescue AssertionFailed => exc
|
299
|
+
Util.bail! do
|
300
|
+
Util::log :failed, "assertion while evaluating node '#{self.name}'"
|
301
|
+
Util::log 'assertion: %s' % exc.assertion, :indent => 1
|
302
|
+
Util::log "offending key: #{key}", :indent => 1
|
303
|
+
raise exc if DEBUG
|
304
|
+
end
|
305
|
+
rescue SyntaxError, StandardError => exc
|
306
|
+
Util::bail! do
|
307
|
+
Util::log :error, "while evaluating node '#{self.name}'"
|
308
|
+
Util::log "offending key: #{key}", :indent => 1
|
309
|
+
Util::log "offending string: #{value}", :indent => 1
|
310
|
+
Util::log "error message: #{exc.inspect}", :indent => 1
|
311
|
+
raise exc if DEBUG
|
286
312
|
end
|
287
|
-
return result
|
288
313
|
end
|
289
314
|
|
290
315
|
private
|
@@ -28,42 +28,22 @@ module LeapCli
|
|
28
28
|
# nodes[:public_dns => true]
|
29
29
|
# all nodes with public dns
|
30
30
|
#
|
31
|
-
# nodes[:services => 'openvpn',
|
31
|
+
# nodes[:services => 'openvpn', 'location.country_code' => 'US']
|
32
|
+
# all nodes with services containing 'openvpn' OR country code of US
|
33
|
+
#
|
34
|
+
# Sometimes, you want to do an OR condition with multiple conditions
|
35
|
+
# for the same field. Since hash keys must be unique, you can use
|
36
|
+
# an array representation instead:
|
37
|
+
#
|
38
|
+
# nodes[[:services, 'openvpn'], [:services, 'tor']]
|
32
39
|
# nodes with openvpn OR tor service
|
33
40
|
#
|
34
41
|
# nodes[:services => 'openvpn'][:tags => 'production']
|
35
42
|
# nodes with openvpn AND are production
|
36
43
|
#
|
37
44
|
def [](key)
|
38
|
-
if key.is_a?
|
39
|
-
|
40
|
-
key.each do |field, match_value|
|
41
|
-
field = field.is_a?(Symbol) ? field.to_s : field
|
42
|
-
match_value = match_value.is_a?(Symbol) ? match_value.to_s : match_value
|
43
|
-
if match_value.is_a?(String) && match_value =~ /^!/
|
44
|
-
operator = :not_equal
|
45
|
-
match_value = match_value.sub(/^!/, '')
|
46
|
-
else
|
47
|
-
operator = :equal
|
48
|
-
end
|
49
|
-
each do |name, config|
|
50
|
-
value = config[field]
|
51
|
-
if value.is_a? Array
|
52
|
-
if operator == :equal && value.include?(match_value)
|
53
|
-
results[name] = config
|
54
|
-
elsif operator == :not_equal && !value.include?(match_value)
|
55
|
-
results[name] = config
|
56
|
-
end
|
57
|
-
else
|
58
|
-
if operator == :equal && value == match_value
|
59
|
-
results[name] = config
|
60
|
-
elsif operator == :not_equal && value != match_value
|
61
|
-
results[name] = config
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
results
|
45
|
+
if key.is_a?(Hash) || key.is_a?(Array)
|
46
|
+
filter(key)
|
67
47
|
else
|
68
48
|
super key.to_s
|
69
49
|
end
|
@@ -81,15 +61,40 @@ module LeapCli
|
|
81
61
|
end
|
82
62
|
end
|
83
63
|
|
84
|
-
#
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
64
|
+
#
|
65
|
+
# filters this object list, producing a new list.
|
66
|
+
# filter is an array or a hash. see []
|
67
|
+
#
|
68
|
+
def filter(filter)
|
69
|
+
results = Config::ObjectList.new
|
70
|
+
filter.each do |field, match_value|
|
71
|
+
field = field.is_a?(Symbol) ? field.to_s : field
|
72
|
+
match_value = match_value.is_a?(Symbol) ? match_value.to_s : match_value
|
73
|
+
if match_value.is_a?(String) && match_value =~ /^!/
|
74
|
+
operator = :not_equal
|
75
|
+
match_value = match_value.sub(/^!/, '')
|
76
|
+
else
|
77
|
+
operator = :equal
|
78
|
+
end
|
79
|
+
each do |name, config|
|
80
|
+
value = config[field]
|
81
|
+
if value.is_a? Array
|
82
|
+
if operator == :equal && value.include?(match_value)
|
83
|
+
results[name] = config
|
84
|
+
elsif operator == :not_equal && !value.include?(match_value)
|
85
|
+
results[name] = config
|
86
|
+
end
|
87
|
+
else
|
88
|
+
if operator == :equal && value == match_value
|
89
|
+
results[name] = config
|
90
|
+
elsif operator == :not_equal && value != match_value
|
91
|
+
results[name] = config
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
results
|
97
|
+
end
|
93
98
|
|
94
99
|
def add(name, object)
|
95
100
|
self[name] = object
|
@@ -13,6 +13,11 @@ module LeapCli; module Config
|
|
13
13
|
@discovered_keys = {}
|
14
14
|
end
|
15
15
|
|
16
|
+
# we can't use fetch() or get(), since those already have special meanings
|
17
|
+
def retrieve(key, environment=nil)
|
18
|
+
self.fetch(environment||'default', {})[key.to_s]
|
19
|
+
end
|
20
|
+
|
16
21
|
def set(key, value, environment=nil)
|
17
22
|
environment ||= 'default'
|
18
23
|
key = key.to_s
|
@@ -23,22 +28,29 @@ module LeapCli; module Config
|
|
23
28
|
end
|
24
29
|
|
25
30
|
#
|
26
|
-
# if
|
27
|
-
#
|
31
|
+
# if clean is true, then only secrets that have been discovered
|
32
|
+
# during this run will be exported.
|
33
|
+
#
|
34
|
+
# if environment is also pinned, then we will clean those secrets
|
35
|
+
# just for that environment.
|
28
36
|
#
|
29
|
-
#
|
30
|
-
# secrets that are actually in use will
|
37
|
+
# the clean argument should only be used when all nodes have
|
38
|
+
# been processed, otherwise secrets that are actually in use will
|
39
|
+
# get mistakenly removed.
|
31
40
|
#
|
32
|
-
def dump_json(
|
33
|
-
|
41
|
+
def dump_json(clean=false)
|
42
|
+
pinned_env = LeapCli.leapfile.environment
|
43
|
+
if clean
|
34
44
|
self.each_key do |environment|
|
35
|
-
|
36
|
-
|
37
|
-
|
45
|
+
if pinned_env.nil? || pinned_env == environment
|
46
|
+
self[environment].each_key do |key|
|
47
|
+
unless @discovered_keys[environment] && @discovered_keys[environment][key]
|
48
|
+
self[environment].delete(key)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
if self[environment].empty?
|
52
|
+
self.delete(environment)
|
38
53
|
end
|
39
|
-
end
|
40
|
-
if self[environment].empty?
|
41
|
-
self.delete(environment)
|
42
54
|
end
|
43
55
|
end
|
44
56
|
end
|
data/lib/leap_cli/config/tag.rb
CHANGED
@@ -13,6 +13,13 @@ module LeapCli; module Config
|
|
13
13
|
super(manager)
|
14
14
|
@node_list = Config::ObjectList.new
|
15
15
|
end
|
16
|
+
|
17
|
+
# don't copy the node list pointer when this object is dup'ed.
|
18
|
+
def initialize_copy(orig)
|
19
|
+
super
|
20
|
+
@node_list = Config::ObjectList.new
|
21
|
+
end
|
22
|
+
|
16
23
|
end
|
17
24
|
|
18
25
|
end; end
|
File without changes
|