leap_cli 1.5.6 → 1.6.2
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.
- 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
|