leap_cli 1.8.1 → 1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/bin/leap +6 -12
  3. data/lib/leap_cli.rb +3 -23
  4. data/lib/leap_cli/bootstrap.rb +36 -12
  5. data/lib/leap_cli/commands/common.rb +88 -46
  6. data/lib/leap_cli/commands/new.rb +24 -17
  7. data/lib/leap_cli/commands/pre.rb +3 -1
  8. data/lib/leap_cli/core_ext/hash.rb +19 -0
  9. data/lib/leap_cli/leapfile.rb +47 -32
  10. data/lib/leap_cli/log.rb +196 -88
  11. data/lib/leap_cli/path.rb +5 -5
  12. data/lib/leap_cli/util.rb +28 -18
  13. data/lib/leap_cli/version.rb +8 -3
  14. data/vendor/acme-client/lib/acme-client.rb +1 -0
  15. data/vendor/acme-client/lib/acme/client.rb +122 -0
  16. data/vendor/acme-client/lib/acme/client/certificate.rb +30 -0
  17. data/vendor/acme-client/lib/acme/client/certificate_request.rb +111 -0
  18. data/vendor/acme-client/lib/acme/client/crypto.rb +98 -0
  19. data/vendor/acme-client/lib/acme/client/error.rb +16 -0
  20. data/vendor/acme-client/lib/acme/client/faraday_middleware.rb +123 -0
  21. data/vendor/acme-client/lib/acme/client/resources.rb +5 -0
  22. data/vendor/acme-client/lib/acme/client/resources/authorization.rb +44 -0
  23. data/vendor/acme-client/lib/acme/client/resources/challenges.rb +6 -0
  24. data/vendor/acme-client/lib/acme/client/resources/challenges/base.rb +43 -0
  25. data/vendor/acme-client/lib/acme/client/resources/challenges/dns01.rb +19 -0
  26. data/vendor/acme-client/lib/acme/client/resources/challenges/http01.rb +18 -0
  27. data/vendor/acme-client/lib/acme/client/resources/challenges/tls_sni01.rb +24 -0
  28. data/vendor/acme-client/lib/acme/client/resources/registration.rb +37 -0
  29. data/vendor/acme-client/lib/acme/client/self_sign_certificate.rb +60 -0
  30. data/vendor/acme-client/lib/acme/client/version.rb +7 -0
  31. data/vendor/base32/lib/base32.rb +67 -0
  32. data/vendor/certificate_authority/lib/certificate_authority.rb +2 -1
  33. data/vendor/certificate_authority/lib/certificate_authority/certificate.rb +4 -4
  34. data/vendor/certificate_authority/lib/certificate_authority/certificate_revocation_list.rb +7 -5
  35. data/vendor/certificate_authority/lib/certificate_authority/core_extensions.rb +46 -0
  36. data/vendor/certificate_authority/lib/certificate_authority/distinguished_name.rb +6 -2
  37. data/vendor/certificate_authority/lib/certificate_authority/extensions.rb +10 -3
  38. data/vendor/certificate_authority/lib/certificate_authority/key_material.rb +11 -9
  39. data/vendor/certificate_authority/lib/certificate_authority/ocsp_handler.rb +3 -3
  40. data/vendor/certificate_authority/lib/certificate_authority/pkcs11_key_material.rb +0 -2
  41. data/vendor/certificate_authority/lib/certificate_authority/serial_number.rb +8 -2
  42. data/vendor/certificate_authority/lib/certificate_authority/validations.rb +31 -0
  43. data/vendor/rsync_command/lib/rsync_command.rb +49 -12
  44. metadata +50 -91
  45. data/lib/leap/platform.rb +0 -90
  46. data/lib/leap_cli/config/environment.rb +0 -180
  47. data/lib/leap_cli/config/filter.rb +0 -178
  48. data/lib/leap_cli/config/manager.rb +0 -419
  49. data/lib/leap_cli/config/node.rb +0 -77
  50. data/lib/leap_cli/config/object.rb +0 -428
  51. data/lib/leap_cli/config/object_list.rb +0 -209
  52. data/lib/leap_cli/config/provider.rb +0 -22
  53. data/lib/leap_cli/config/secrets.rb +0 -87
  54. data/lib/leap_cli/config/sources.rb +0 -11
  55. data/lib/leap_cli/config/tag.rb +0 -25
  56. data/lib/leap_cli/lib_ext/capistrano_connections.rb +0 -16
  57. data/lib/leap_cli/logger.rb +0 -237
  58. data/lib/leap_cli/remote/leap_plugin.rb +0 -192
  59. data/lib/leap_cli/remote/puppet_plugin.rb +0 -26
  60. data/lib/leap_cli/remote/rsync_plugin.rb +0 -35
  61. data/lib/leap_cli/remote/tasks.rb +0 -51
  62. data/lib/leap_cli/ssh_key.rb +0 -195
  63. data/lib/leap_cli/util/remote_command.rb +0 -158
  64. data/lib/leap_cli/util/secret.rb +0 -55
  65. data/lib/leap_cli/util/x509.rb +0 -33
@@ -1,178 +0,0 @@
1
- #
2
- # Many leap_cli commands accept a list of filters to select a subset of nodes for the command to
3
- # be applied to. This class is a helper for manager to run these filters.
4
- #
5
- # Classes other than Manager should not use this class.
6
- #
7
- # Filter rules:
8
- #
9
- # * A filter consists of a list of tokens
10
- # * A token may be a service name, tag name, environment name, or node name.
11
- # * Each token may be optionally prefixed with a plus sign.
12
- # * Multiple tokens with a plus are treated as an OR condition,
13
- # but treated as an AND condition with the plus sign.
14
- #
15
- # For example
16
- #
17
- # * openvpn +development => all nodes with service 'openvpn' AND environment 'development'
18
- # * openvpn seattle => all nodes with service 'openvpn' OR tag 'seattle'.
19
- #
20
- # There can only be one environment specified. Typically, there are also tags
21
- # for each environment name. These name are treated as environments, not tags.
22
- #
23
- module LeapCli
24
- module Config
25
- class Filter
26
-
27
- #
28
- # filter -- array of strings, each one a filter
29
- # options -- hash, possible keys include
30
- # :nopin -- disregard environment pinning
31
- # :local -- if false, disallow local nodes
32
- #
33
- # A nil value in the filters array indicates
34
- # the default environment. This is in order to support
35
- # calls like `manager.filter(environments)`
36
- #
37
- def initialize(filters, options, manager)
38
- @filters = filters.nil? ? [] : filters.dup
39
- @environments = []
40
- @options = options
41
- @manager = manager
42
-
43
- # split filters by pulling out items that happen
44
- # to be environment names.
45
- if LeapCli.leapfile.environment.nil? || @options[:nopin]
46
- @environments = []
47
- else
48
- @environments = [LeapCli.leapfile.environment]
49
- end
50
- @filters.select! do |filter|
51
- if filter.nil?
52
- @environments << nil unless @environments.include?(nil)
53
- false
54
- else
55
- filter_text = filter.sub(/^\+/,'')
56
- if is_environment?(filter_text)
57
- if filter_text == LeapCli.leapfile.environment
58
- # silently ignore already pinned environments
59
- elsif (filter =~ /^\+/ || @filters.first == filter) && !@environments.empty?
60
- LeapCli::Util.bail! do
61
- LeapCli::Util.log "Environments are exclusive: no node is in two environments." do
62
- LeapCli::Util.log "Tried to filter on '#{@environments.join('\' AND \'')}' AND '#{filter_text}'"
63
- end
64
- end
65
- else
66
- @environments << filter_text
67
- end
68
- false
69
- else
70
- true
71
- end
72
- end
73
- end
74
-
75
- # don't let the first filter have a + prefix
76
- if @filters[0] =~ /^\+/
77
- @filters[0] = @filters[0][1..-1]
78
- end
79
- end
80
-
81
- # actually run the filter, returns a filtered list of nodes
82
- def nodes()
83
- if @filters.empty?
84
- return nodes_for_empty_filter
85
- else
86
- return nodes_for_filter
87
- end
88
- end
89
-
90
- private
91
-
92
- def nodes_for_empty_filter
93
- node_list = @manager.nodes
94
- if @environments.any?
95
- node_list = node_list[ @environments.collect{|e|[:environment, env_to_filter(e)]} ]
96
- end
97
- if @options[:local] === false
98
- node_list = node_list[:environment => '!local']
99
- end
100
- if @options[:disabled] === false
101
- node_list = node_list[:environment => '!disabled']
102
- end
103
- node_list
104
- end
105
-
106
- def nodes_for_filter
107
- node_list = Config::ObjectList.new
108
- @filters.each do |filter|
109
- if filter =~ /^\+/
110
- keep_list = nodes_for_name(filter[1..-1])
111
- node_list.delete_if do |name, node|
112
- if keep_list[name]
113
- false
114
- else
115
- true
116
- end
117
- end
118
- else
119
- node_list.merge!(nodes_for_name(filter))
120
- end
121
- end
122
- node_list
123
- end
124
-
125
- private
126
-
127
- #
128
- # returns a set of nodes corresponding to a single name,
129
- # where name could be a node name, service name, or tag name.
130
- #
131
- # For services and tags, we only include nodes for the
132
- # environments that are active
133
- #
134
- def nodes_for_name(name)
135
- if node = @manager.nodes[name]
136
- return Config::ObjectList.new(node)
137
- elsif @environments.empty?
138
- if @manager.services[name]
139
- return @manager.env('_all_').services[name].node_list
140
- elsif @manager.tags[name]
141
- return @manager.env('_all_').tags[name].node_list
142
- else
143
- LeapCli::Util.log :warning, "filter '#{name}' does not match any node names, tags, services, or environments."
144
- return Config::ObjectList.new
145
- end
146
- else
147
- node_list = Config::ObjectList.new
148
- if @manager.services[name]
149
- @environments.each do |env|
150
- node_list.merge!(@manager.env(env).services[name].node_list)
151
- end
152
- elsif @manager.tags[name]
153
- @environments.each do |env|
154
- node_list.merge!(@manager.env(env).tags[name].node_list)
155
- end
156
- else
157
- LeapCli::Util.log :warning, "filter '#{name}' does not match any node names, tags, services, or environments."
158
- end
159
- return node_list
160
- end
161
- end
162
-
163
- #
164
- # when pinning, we use the name 'default' to specify nodes
165
- # without an environment set, but when filtering, we need to filter
166
- # on :environment => nil.
167
- #
168
- def env_to_filter(environment)
169
- environment == 'default' ? nil : environment
170
- end
171
-
172
- def is_environment?(text)
173
- text == 'default' || @manager.environment_names.include?(text)
174
- end
175
-
176
- end
177
- end
178
- end
@@ -1,419 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'json/pure'
4
-
5
- if $ruby_version < [1,9]
6
- require 'iconv'
7
- end
8
-
9
- module LeapCli
10
- module Config
11
-
12
- #
13
- # A class to manage all the objects in all the configuration files.
14
- #
15
- class Manager
16
-
17
- def initialize
18
- @environments = {} # hash of `Environment` objects, keyed by name.
19
- Config::Object.send(:include, LeapCli::Macro)
20
- end
21
-
22
- ##
23
- ## ATTRIBUTES
24
- ##
25
-
26
- #
27
- # returns the Hash of the contents of facts.json
28
- #
29
- def facts
30
- @facts ||= begin
31
- content = Util.read_file(:facts)
32
- if !content || content.empty?
33
- content = "{}"
34
- end
35
- JSON.parse(content)
36
- rescue SyntaxError, JSON::ParserError => exc
37
- Util::bail! "Could not parse facts.json -- #{exc}"
38
- end
39
- end
40
-
41
- #
42
- # returns an Array of all the environments defined for this provider.
43
- # the returned array includes nil (for the default environment)
44
- #
45
- def environment_names
46
- @environment_names ||= begin
47
- [nil] + (env.tags.field('environment') + env.nodes.field('environment')).compact.uniq
48
- end
49
- end
50
-
51
- #
52
- # Returns the appropriate environment variable
53
- #
54
- def env(env=nil)
55
- @environments[env || 'default']
56
- end
57
-
58
- #
59
- # The default accessors
60
- #
61
- # For these defaults, use 'default' environment, or whatever
62
- # environment is pinned.
63
- #
64
- # I think it might be an error that these are ever used
65
- # and I would like to get rid of them.
66
- #
67
- def services; env(default_environment).services; end
68
- def tags; env(default_environment).tags; end
69
- def partials; env(default_environment).partials; end
70
- def provider; env(default_environment).provider; end
71
- def common; env(default_environment).common; end
72
- def secrets; env(default_environment).secrets; end
73
- def nodes; env(default_environment).nodes; end
74
- def template(*args)
75
- self.env.template(*args)
76
- end
77
-
78
- def default_environment
79
- LeapCli.leapfile.environment
80
- end
81
-
82
- ##
83
- ## IMPORT EXPORT
84
- ##
85
-
86
- def add_environment(args)
87
- if args[:inherit]
88
- parent = @environments[args.delete(:inherit)]
89
- else
90
- parent = nil
91
- end
92
- @environments[args[:name]] = Environment.new(
93
- self,
94
- args.delete(:name),
95
- args.delete(:dir),
96
- parent,
97
- args
98
- )
99
- end
100
-
101
- #
102
- # load .json configuration files
103
- #
104
- def load(options = {})
105
- @provider_dir = Path.provider
106
-
107
- # load base
108
- add_environment(name: '_base_', dir: Path.provider_base)
109
-
110
- # load provider
111
- Util::assert_files_exist!(Path.named_path(:provider_config, @provider_dir))
112
- add_environment(name: 'default', dir: @provider_dir,
113
- inherit: '_base_', no_dots: true)
114
-
115
- # create a special '_all_' environment, used for tracking
116
- # the union of all the environments
117
- add_environment(name: '_all_', inherit: 'default')
118
-
119
- # load environments
120
- environment_names.each do |ename|
121
- if ename
122
- log 3, :loading, '%s environment...' % ename
123
- add_environment(name: ename, dir: @provider_dir,
124
- inherit: 'default', scope: ename)
125
- end
126
- end
127
-
128
- # apply inheritance
129
- env.nodes.each do |name, node|
130
- Util::assert! name =~ /^[0-9a-z-]+$/, "Illegal character(s) used in node name '#{name}'"
131
- env.nodes[name] = apply_inheritance(node)
132
- end
133
-
134
- # do some node-list post-processing
135
- cleanup_node_lists(options)
136
-
137
- # apply control files
138
- env.nodes.each do |name, node|
139
- control_files(node).each do |file|
140
- begin
141
- node.eval_file file
142
- rescue ConfigError => exc
143
- if options[:continue_on_error]
144
- exc.log
145
- else
146
- raise exc
147
- end
148
- end
149
- end
150
- end
151
- end
152
-
153
- #
154
- # save compiled hiera .yaml files
155
- #
156
- # if a node_list is specified, only update those .yaml files.
157
- # otherwise, update all files, destroying files that are no longer used.
158
- #
159
- def export_nodes(node_list=nil)
160
- updated_hiera = []
161
- updated_files = []
162
- existing_hiera = nil
163
- existing_files = nil
164
-
165
- unless node_list
166
- node_list = env.nodes
167
- existing_hiera = Dir.glob(Path.named_path([:hiera, '*'], @provider_dir))
168
- existing_files = Dir.glob(Path.named_path([:node_files_dir, '*'], @provider_dir))
169
- end
170
-
171
- node_list.each_node do |node|
172
- filepath = Path.named_path([:node_files_dir, node.name], @provider_dir)
173
- hierapath = Path.named_path([:hiera, node.name], @provider_dir)
174
- Util::write_file!(hierapath, node.dump_yaml)
175
- updated_files << filepath
176
- updated_hiera << hierapath
177
- end
178
-
179
- if @disabled_nodes
180
- # make disabled nodes appear as if they are still active
181
- @disabled_nodes.each_node do |node|
182
- updated_files << Path.named_path([:node_files_dir, node.name], @provider_dir)
183
- updated_hiera << Path.named_path([:hiera, node.name], @provider_dir)
184
- end
185
- end
186
-
187
- # remove files that are no longer needed
188
- if existing_hiera
189
- (existing_hiera - updated_hiera).each do |filepath|
190
- Util::remove_file!(filepath)
191
- end
192
- end
193
- if existing_files
194
- (existing_files - updated_files).each do |filepath|
195
- Util::remove_directory!(filepath)
196
- end
197
- end
198
- end
199
-
200
- def export_secrets(clean_unused_secrets = false)
201
- if env.secrets.any?
202
- Util.write_file!([:secrets_config, @provider_dir], env.secrets.dump_json(clean_unused_secrets) + "\n")
203
- end
204
- end
205
-
206
- ##
207
- ## FILTERING
208
- ##
209
-
210
- #
211
- # returns a node list consisting only of nodes that satisfy the filter criteria.
212
- #
213
- # filter: condition [condition] [condition] [+condition]
214
- # condition: [node_name | service_name | tag_name | environment_name]
215
- #
216
- # if conditions is prefixed with +, then it works like an AND. Otherwise, it works like an OR.
217
- #
218
- # args:
219
- # filter -- array of filter terms, one per item
220
- #
221
- # options:
222
- # :local -- if :local is false and the filter is empty, then local nodes are excluded.
223
- # :nopin -- if true, ignore environment pinning
224
- #
225
- def filter(filters=nil, options={})
226
- Filter.new(filters, options, self).nodes()
227
- end
228
-
229
- #
230
- # same as filter(), but exits if there is no matching nodes
231
- #
232
- def filter!(filters, options={})
233
- node_list = filter(filters, options)
234
- Util::assert! node_list.any?, "Could not match any nodes from '#{filters.join ' '}'"
235
- return node_list
236
- end
237
-
238
- #
239
- # returns a single Config::Object that corresponds to a Node.
240
- #
241
- def node(name)
242
- if name =~ /\./
243
- # probably got a fqdn, since periods are not allowed in node names.
244
- # so, take the part before the first period as the node name
245
- name = name.split('.').first
246
- end
247
- env.nodes[name]
248
- end
249
-
250
- #
251
- # returns a single node that is disabled
252
- #
253
- def disabled_node(name)
254
- @disabled_nodes[name]
255
- end
256
-
257
- #
258
- # yields each node, in sorted order
259
- #
260
- def each_node(&block)
261
- env.nodes.each_node &block
262
- end
263
-
264
- def reload_node!(node)
265
- env.nodes[node.name] = apply_inheritance!(node)
266
- end
267
-
268
- ##
269
- ## CONNECTIONS
270
- ##
271
-
272
- class ConnectionList < Array
273
- def add(data={})
274
- self << {
275
- "from" => data[:from],
276
- "to" => data[:to],
277
- "port" => data[:port]
278
- }
279
- end
280
- end
281
-
282
- def connections
283
- @connections ||= ConnectionList.new
284
- end
285
-
286
- ##
287
- ## PRIVATE
288
- ##
289
-
290
- private
291
-
292
- #
293
- # makes a node inherit options from appropriate the common, service, and tag json files.
294
- #
295
- def apply_inheritance(node, throw_exceptions=false)
296
- new_node = Config::Node.new(nil)
297
- name = node.name
298
- node_env = guess_node_env(node)
299
- new_node.set_environment(node_env, new_node)
300
-
301
- # inherit from common
302
- new_node.deep_merge!(node_env.common)
303
-
304
- # inherit from services
305
- if node['services']
306
- node['services'].to_a.each do |node_service|
307
- service = node_env.services[node_service]
308
- if service.nil?
309
- msg = 'in node "%s": the service "%s" does not exist.' % [node['name'], node_service]
310
- log 0, :error, msg
311
- raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions
312
- else
313
- new_node.deep_merge!(service)
314
- end
315
- end
316
- end
317
-
318
- # inherit from tags
319
- if node.vagrant?
320
- node['tags'] = (node['tags'] || []).to_a + ['local']
321
- end
322
- if node['tags']
323
- node['tags'].to_a.each do |node_tag|
324
- tag = node_env.tags[node_tag]
325
- if tag.nil?
326
- msg = 'in node "%s": the tag "%s" does not exist.' % [node['name'], node_tag]
327
- log 0, :error, msg
328
- raise LeapCli::ConfigError.new(node, "error " + msg) if throw_exceptions
329
- else
330
- new_node.deep_merge!(tag)
331
- end
332
- end
333
- end
334
-
335
- # inherit from node
336
- new_node.deep_merge!(node)
337
- return new_node
338
- end
339
-
340
- def apply_inheritance!(node)
341
- apply_inheritance(node, true)
342
- end
343
-
344
- #
345
- # Guess the environment of the node from the tag names.
346
- #
347
- # Technically, this is wrong: a tag that sets the environment might not be
348
- # named the same as the environment. This code assumes that it is.
349
- #
350
- # Unfortunately, it is a chicken and egg problem. We need to know the nodes
351
- # likely environment in order to apply the inheritance that will actually
352
- # determine the node's properties.
353
- #
354
- def guess_node_env(node)
355
- environment = self.env(default_environment)
356
- if node['tags']
357
- node['tags'].to_a.each do |tag|
358
- if self.environment_names.include?(tag)
359
- environment = self.env(tag)
360
- end
361
- end
362
- end
363
- return environment
364
- end
365
-
366
- #
367
- # does some final clean at the end of loading nodes.
368
- # this includes removing disabled nodes, and populating
369
- # the services[x].node_list and tags[x].node_list
370
- #
371
- def cleanup_node_lists(options)
372
- @disabled_nodes = Config::ObjectList.new
373
- env.nodes.each do |name, node|
374
- if node.enabled || options[:include_disabled]
375
- if node['services']
376
- node['services'].to_a.each do |node_service|
377
- env(node.environment).services[node_service].node_list.add(node.name, node)
378
- env('_all_').services[node_service].node_list.add(node.name, node)
379
- end
380
- end
381
- if node['tags']
382
- node['tags'].to_a.each do |node_tag|
383
- env(node.environment).tags[node_tag].node_list.add(node.name, node)
384
- env('_all_').tags[node_tag].node_list.add(node.name, node)
385
- end
386
- end
387
- elsif !options[:include_disabled]
388
- log 2, :skipping, "disabled node #{name}."
389
- env.nodes.delete(name)
390
- @disabled_nodes[name] = node
391
- end
392
- end
393
- end
394
-
395
- #
396
- # returns a list of 'control' files for this node.
397
- # a control file is like a service or a tag JSON file, but it contains
398
- # raw ruby code that gets evaluated in the context of the node.
399
- # Yes, this entirely breaks our functional programming model
400
- # for JSON generation.
401
- #
402
- def control_files(node)
403
- files = []
404
- [Path.provider_base, @provider_dir].each do |provider_dir|
405
- [['services', :service_config], ['tags', :tag_config]].each do |attribute, path_sym|
406
- node[attribute].each do |attr_value|
407
- path = Path.named_path([path_sym, "#{attr_value}.rb"], provider_dir).sub(/\.json$/,'')
408
- if File.exists?(path)
409
- files << path
410
- end
411
- end
412
- end
413
- end
414
- return files
415
- end
416
-
417
- end
418
- end
419
- end