leap_cli 1.7.4 → 1.8

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/bin/leap +6 -13
  3. data/lib/leap/platform.rb +2 -0
  4. data/lib/leap_cli.rb +2 -1
  5. data/lib/leap_cli/bootstrap.rb +197 -0
  6. data/lib/leap_cli/commands/common.rb +61 -0
  7. data/lib/leap_cli/commands/new.rb +5 -1
  8. data/lib/leap_cli/commands/pre.rb +1 -66
  9. data/lib/leap_cli/config/environment.rb +180 -0
  10. data/lib/leap_cli/config/manager.rb +100 -197
  11. data/lib/leap_cli/config/node.rb +2 -2
  12. data/lib/leap_cli/config/object.rb +56 -43
  13. data/lib/leap_cli/config/object_list.rb +6 -3
  14. data/lib/leap_cli/config/provider.rb +11 -0
  15. data/lib/leap_cli/config/secrets.rb +14 -1
  16. data/lib/leap_cli/config/tag.rb +2 -2
  17. data/lib/leap_cli/leapfile.rb +1 -0
  18. data/lib/leap_cli/log.rb +1 -0
  19. data/lib/leap_cli/logger.rb +16 -12
  20. data/lib/leap_cli/markdown_document_listener.rb +3 -1
  21. data/lib/leap_cli/path.rb +12 -0
  22. data/lib/leap_cli/remote/leap_plugin.rb +9 -34
  23. data/lib/leap_cli/remote/puppet_plugin.rb +0 -40
  24. data/lib/leap_cli/remote/tasks.rb +9 -34
  25. data/lib/leap_cli/ssh_key.rb +5 -2
  26. data/lib/leap_cli/version.rb +2 -2
  27. metadata +5 -18
  28. data/lib/leap_cli/commands/ca.rb +0 -518
  29. data/lib/leap_cli/commands/clean.rb +0 -16
  30. data/lib/leap_cli/commands/compile.rb +0 -340
  31. data/lib/leap_cli/commands/db.rb +0 -65
  32. data/lib/leap_cli/commands/deploy.rb +0 -368
  33. data/lib/leap_cli/commands/env.rb +0 -76
  34. data/lib/leap_cli/commands/facts.rb +0 -100
  35. data/lib/leap_cli/commands/inspect.rb +0 -144
  36. data/lib/leap_cli/commands/list.rb +0 -132
  37. data/lib/leap_cli/commands/node.rb +0 -165
  38. data/lib/leap_cli/commands/node_init.rb +0 -169
  39. data/lib/leap_cli/commands/ssh.rb +0 -220
  40. data/lib/leap_cli/commands/test.rb +0 -74
  41. data/lib/leap_cli/commands/user.rb +0 -136
  42. data/lib/leap_cli/commands/util.rb +0 -50
  43. data/lib/leap_cli/commands/vagrant.rb +0 -197
@@ -1,16 +0,0 @@
1
- module LeapCli
2
- module Commands
3
-
4
- desc 'Removes all files generated with the "compile" command.'
5
- command :clean do |c|
6
- c.action do |global_options,options,args|
7
- Dir.glob(path([:hiera, '*'])).each do |file|
8
- remove_file! file
9
- end
10
- remove_file! path(:authorized_keys)
11
- remove_file! path(:known_hosts)
12
- end
13
- end
14
-
15
- end
16
- end
@@ -1,340 +0,0 @@
1
- require 'socket'
2
-
3
- module LeapCli
4
- module Commands
5
-
6
- desc "Compile generated files."
7
- command [:compile, :c] do |c|
8
- c.desc 'Compiles node configuration files into hiera files used for deployment.'
9
- c.arg_name 'ENVIRONMENT', :optional => true
10
- c.command :all do |all|
11
- all.action do |global_options,options,args|
12
- environment = args.first
13
- if !LeapCli.leapfile.environment.nil? && !environment.nil? && environment != LeapCli.leapfile.environment
14
- bail! "You cannot specify an ENVIRONMENT argument while the environment is pinned."
15
- end
16
- if environment
17
- if manager.environment_names.include?(environment)
18
- compile_hiera_files(manager.filter([environment]), false)
19
- else
20
- bail! "There is no environment named `#{environment}`."
21
- end
22
- else
23
- clean_export = LeapCli.leapfile.environment.nil?
24
- compile_hiera_files(manager.filter, clean_export)
25
- end
26
- if file_exists?(:static_web_readme)
27
- compile_provider_json(environment)
28
- end
29
- end
30
- end
31
-
32
- c.desc "Compile a DNS zone file for your provider."
33
- c.command :zone do |zone|
34
- zone.action do |global_options, options, args|
35
- compile_zone_file
36
- end
37
- end
38
-
39
- c.desc "Compile provider.json bootstrap files for your provider."
40
- c.command 'provider.json' do |provider|
41
- provider.action do |global_options, options, args|
42
- compile_provider_json
43
- end
44
- end
45
-
46
- c.default_command :all
47
- end
48
-
49
- protected
50
-
51
- #
52
- # a "clean" export of secrets will also remove keys that are no longer used,
53
- # but this should not be done if we are not examining all possible nodes.
54
- #
55
- def compile_hiera_files(nodes, clean_export)
56
- update_compiled_ssh_configs # must come first
57
- sanity_check(nodes)
58
- manager.export_nodes(nodes)
59
- manager.export_secrets(clean_export)
60
- end
61
-
62
- def update_compiled_ssh_configs
63
- generate_monitor_ssh_keys
64
- update_authorized_keys
65
- update_known_hosts
66
- end
67
-
68
- def sanity_check(nodes)
69
- # confirm that every node has a unique ip address
70
- ips = {}
71
- nodes.pick_fields('ip_address').each do |name, ip_address|
72
- if ips.key?(ip_address)
73
- bail! {
74
- log(:fatal_error, "Every node must have its own IP address.") {
75
- log "Nodes `#{name}` and `#{ips[ip_address]}` are both configured with `#{ip_address}`."
76
- }
77
- }
78
- else
79
- ips[ip_address] = name
80
- end
81
- end
82
- # confirm that the IP address of this machine is not also used for a node.
83
- Socket.ip_address_list.each do |addrinfo|
84
- if !addrinfo.ipv4_private? && ips.key?(addrinfo.ip_address)
85
- ip = addrinfo.ip_address
86
- name = ips[ip]
87
- bail! {
88
- log(:fatal_error, "Something is very wrong. The `leap` command must only be run on your sysadmin machine, not on a provider node.") {
89
- log "This machine has the same IP address (#{ip}) as node `#{name}`."
90
- }
91
- }
92
- end
93
- end
94
- end
95
-
96
- ##
97
- ## SSH
98
- ##
99
-
100
- #
101
- # generates a ssh key pair that is used only by remote monitors
102
- # to connect to nodes and run certain allowed commands.
103
- #
104
- # every node has the public monitor key added to their authorized
105
- # keys, and every monitor node has a copy of the private monitor key.
106
- #
107
- def generate_monitor_ssh_keys
108
- priv_key_file = path(:monitor_priv_key)
109
- pub_key_file = path(:monitor_pub_key)
110
- unless file_exists?(priv_key_file, pub_key_file)
111
- ensure_dir(File.dirname(priv_key_file))
112
- ensure_dir(File.dirname(pub_key_file))
113
- cmd = %(ssh-keygen -N '' -C 'monitor' -t rsa -b 4096 -f '%s') % priv_key_file
114
- assert_run! cmd
115
- if file_exists?(priv_key_file, pub_key_file)
116
- log :created, priv_key_file
117
- log :created, pub_key_file
118
- else
119
- log :failed, 'to create monitor ssh keys'
120
- end
121
- end
122
- end
123
-
124
- #
125
- # Compiles the authorized keys file, which gets installed on every during init.
126
- # Afterwards, puppet installs an authorized keys file that is generated differently
127
- # (see authorized_keys() in macros.rb)
128
- #
129
- def update_authorized_keys
130
- buffer = StringIO.new
131
- keys = Dir.glob(path([:user_ssh, '*']))
132
- if keys.empty?
133
- bail! "You must have at least one public SSH user key configured in order to proceed. See `leap help add-user`."
134
- end
135
- if file_exists?(path(:monitor_pub_key))
136
- keys << path(:monitor_pub_key)
137
- end
138
- keys.sort.each do |keyfile|
139
- ssh_type, ssh_key = File.read(keyfile).strip.split(" ")
140
- buffer << ssh_type
141
- buffer << " "
142
- buffer << ssh_key
143
- buffer << " "
144
- buffer << Path.relative_path(keyfile)
145
- buffer << "\n"
146
- end
147
- write_file!(:authorized_keys, buffer.string)
148
- end
149
-
150
- #
151
- # generates the known_hosts file.
152
- #
153
- # we do a 'late' binding on the hostnames and ip part of the ssh pub key record in order to allow
154
- # for the possibility that the hostnames or ip has changed in the node configuration.
155
- #
156
- def update_known_hosts
157
- buffer = StringIO.new
158
- buffer << "#\n"
159
- buffer << "# This file is automatically generated by the command `leap`. You should NOT modify this file.\n"
160
- buffer << "# Instead, rerun `leap node init` on whatever node is causing SSH problems.\n"
161
- buffer << "#\n"
162
- manager.nodes.keys.sort.each do |node_name|
163
- node = manager.nodes[node_name]
164
- hostnames = [node.name, node.domain.internal, node.domain.full, node.ip_address].join(',')
165
- pub_key = read_file([:node_ssh_pub_key,node.name])
166
- if pub_key
167
- buffer << [hostnames, pub_key].join(' ')
168
- buffer << "\n"
169
- end
170
- end
171
- write_file!(:known_hosts, buffer.string)
172
- end
173
-
174
- ##
175
- ## provider.json
176
- ##
177
-
178
- #
179
- # generates static provider.json files that can put into place
180
- # (e.g. https://domain/provider.json) for the cases where the
181
- # webapp domain does not match the provider's domain.
182
- #
183
- def compile_provider_json(environments=nil)
184
- webapp_nodes = manager.nodes[:services => 'webapp']
185
- write_file!(:static_web_readme, STATIC_WEB_README)
186
- environments ||= manager.environment_names
187
- environments.each do |env|
188
- node = webapp_nodes[:environment => env].values.first
189
- if node
190
- env ||= 'default'
191
- write_file!(
192
- [:static_web_provider_json, env],
193
- node['definition_files']['provider']
194
- )
195
- write_file!(
196
- [:static_web_htaccess, env],
197
- HTACCESS_FILE % {:min_version => manager.env(env).provider.client_version['min']}
198
- )
199
- end
200
- end
201
- end
202
-
203
- HTACCESS_FILE = %[
204
- <Location /provider.json>
205
- Header set X-Minimum-Client-Version %{min_version}
206
- </Location>
207
- ]
208
-
209
- STATIC_WEB_README = %[
210
- This directory contains statically rendered copies of the `provider.json` file
211
- used by the client to "bootstrap" configure itself for use with your service
212
- provider.
213
-
214
- There is a separate provider.json file for each environment, although you
215
- should only need 'production/provider.json' or, if you have no environments
216
- configured, 'default/provider.json'.
217
-
218
- To clarify, this is the public `provider.json` file used by the client, not the
219
- `provider.json` file that is used to configure the provider.
220
-
221
- The provider.json file must be available at `https://domain/provider.json`
222
- (unless this provider is included in the list of providers which are pre-
223
- seeded in client).
224
-
225
- This provider.json file can be served correctly in one of three ways:
226
-
227
- (1) If the property webapp.domain is not configured, then the web app will be
228
- installed at https://domain/ and it will handle serving the provider.json file.
229
-
230
- (2) If one or more nodes have the 'static' service configured for the provider's
231
- domain, then these 'static' nodes will correctly serve provider.json.
232
-
233
- (3) Otherwise, you must copy the provider.json file to your web
234
- server and make it available at '/provider.json'. The example htaccess
235
- file shows what header options should be sent by the web server
236
- with the response.
237
-
238
- This directory is needed for method (3), but not for methods (1) or (2).
239
-
240
- This directory has been created by the command `leap compile provider.json`.
241
- Once created, it will be kept up to date everytime you compile. You may safely
242
- remove this directory if you don't use it.
243
- ]
244
-
245
- ##
246
- ##
247
- ## ZONE FILE
248
- ##
249
-
250
- def relative_hostname(fqdn)
251
- @domain_regexp ||= /\.?#{Regexp.escape(provider.domain)}$/
252
- fqdn.sub(@domain_regexp, '')
253
- end
254
-
255
- #
256
- # serial is any number less than 2^32 (4294967296)
257
- #
258
- def compile_zone_file
259
- hosts_seen = {}
260
- f = $stdout
261
- f.puts ZONE_HEADER % {:domain => provider.domain, :ns => provider.domain, :contact => provider.contacts.default.first.sub('@','.')}
262
- max_width = manager.nodes.values.inject(0) {|max, node| [max, relative_hostname(node.domain.full).length].max }
263
- put_line = lambda do |host, line|
264
- host = '@' if host == ''
265
- f.puts("%-#{max_width}s %s" % [host, line])
266
- end
267
-
268
- f.puts ORIGIN_HEADER
269
- # 'A' records for primary domain
270
- manager.nodes[:environment => '!local'].each_node do |node|
271
- if node.dns['aliases'] && node.dns.aliases.include?(provider.domain)
272
- put_line.call "", "IN A #{node.ip_address}"
273
- end
274
- end
275
-
276
- # NS records
277
- if provider['dns'] && provider.dns['nameservers']
278
- provider.dns.nameservers.each do |ns|
279
- put_line.call "", "IN NS #{ns}."
280
- end
281
- end
282
-
283
- # all other records
284
- manager.environment_names.each do |env|
285
- next if env == 'local'
286
- nodes = manager.nodes[:environment => env]
287
- next unless nodes.any?
288
- f.puts ENV_HEADER % (env.nil? ? 'default' : env)
289
- nodes.each_node do |node|
290
- if node.dns.public
291
- hostname = relative_hostname(node.domain.full)
292
- put_line.call relative_hostname(node.domain.full), "IN A #{node.ip_address}"
293
- end
294
- if node.dns['aliases']
295
- node.dns.aliases.each do |host_alias|
296
- if host_alias != node.domain.full && host_alias != provider.domain
297
- put_line.call relative_hostname(host_alias), "IN A #{node.ip_address}"
298
- end
299
- end
300
- end
301
- if node.services.include? 'mx'
302
- put_line.call relative_hostname(node.domain.full_suffix), "IN MX 10 #{relative_hostname(node.domain.full)}"
303
- end
304
- end
305
- end
306
- end
307
-
308
- ENV_HEADER = %[
309
- ;;
310
- ;; ENVIRONMENT %s
311
- ;;
312
-
313
- ]
314
-
315
- ZONE_HEADER = %[
316
- ;;
317
- ;; BIND data file for %{domain}
318
- ;;
319
-
320
- $TTL 600
321
- $ORIGIN %{domain}.
322
-
323
- @ IN SOA %{ns}. %{contact}. (
324
- 0000 ; serial
325
- 7200 ; refresh ( 24 hours)
326
- 3600 ; retry ( 2 hours)
327
- 1209600 ; expire (1000 hours)
328
- 600 ) ; minimum ( 2 days)
329
- ;
330
- ]
331
-
332
- ORIGIN_HEADER = %[
333
- ;;
334
- ;; ZONE ORIGIN
335
- ;;
336
-
337
- ]
338
-
339
- end
340
- end
@@ -1,65 +0,0 @@
1
- module LeapCli; module Commands
2
-
3
- desc 'Database commands.'
4
- command :db do |db|
5
- db.desc 'Destroy one or more databases. If present, limit to FILTER nodes. For example `leap db destroy --db sessions,tokens testing`.'
6
- db.arg_name 'FILTER', :optional => true
7
- db.command :destroy do |destroy|
8
- destroy.flag :db, :arg_name => "DATABASES", :desc => 'Comma separated list of databases to destroy (no space). Use "--db all" to destroy all databases.', :optional => false
9
- destroy.action do |global_options,options,args|
10
- dbs = (options[:db]||"").split(',')
11
- bail!('No databases specified') if dbs.empty?
12
- nodes = manager.filter(args)
13
- if nodes.any?
14
- nodes = nodes[:services => 'couchdb']
15
- end
16
- if nodes.any?
17
- unless global_options[:yes]
18
- if dbs.include?('all')
19
- say 'You are about to permanently destroy all database data for nodes [%s].' % nodes.keys.join(', ')
20
- else
21
- say 'You are about to permanently destroy databases [%s] for nodes [%s].' % [dbs.join(', '), nodes.keys.join(', ')]
22
- end
23
- bail! unless agree("Continue? ")
24
- end
25
- if dbs.include?('all')
26
- destroy_all_dbs(nodes)
27
- else
28
- destroy_dbs(nodes, dbs)
29
- end
30
- say 'You must run `leap deploy` in order to create the databases again.'
31
- else
32
- say 'No nodes'
33
- end
34
- end
35
- end
36
- end
37
-
38
- private
39
-
40
- def destroy_all_dbs(nodes)
41
- ssh_connect(nodes) do |ssh|
42
- ssh.run('/etc/init.d/bigcouch stop && test ! -z "$(ls /opt/bigcouch/var/lib/ 2> /dev/null)" && rm -r /opt/bigcouch/var/lib/* && echo "db destroyed" || echo "db already destroyed"')
43
- ssh.run('grep ^seq_dir /etc/leap/tapicero.yaml | cut -f2 -d\" | xargs rm -rv')
44
- end
45
- end
46
-
47
- def destroy_dbs(nodes, dbs)
48
- nodes.each_node do |node|
49
- ssh_connect(node) do |ssh|
50
- dbs.each do |db|
51
- ssh.run(DESTROY_DB_COMMAND % {:db => db})
52
- end
53
- end
54
- end
55
- end
56
-
57
- DESTROY_DB_COMMAND = %{
58
- if [ 200 = `curl -ns -w "%%{http_code}" -X GET "127.0.0.1:5984/%{db}" -o /dev/null` ]; then
59
- echo "Result from DELETE /%{db}:" `curl -ns -X DELETE "127.0.0.1:5984/%{db}"`;
60
- else
61
- echo "Skipping db '%{db}': it does not exist or has already been deleted.";
62
- fi
63
- }
64
-
65
- end; end
@@ -1,368 +0,0 @@
1
- require 'etc'
2
-
3
- module LeapCli
4
- module Commands
5
-
6
- desc 'Apply recipes to a node or set of nodes.'
7
- long_desc 'The FILTER can be the name of a node, service, or tag.'
8
- arg_name 'FILTER'
9
- command [:deploy, :d] do |c|
10
-
11
- c.switch :fast, :desc => 'Makes the deploy command faster by skipping some slow steps. A "fast" deploy can be used safely if you recently completed a normal deploy.',
12
- :negatable => false
13
-
14
- c.switch :sync, :desc => "Sync files, but don't actually apply recipes.", :negatable => false
15
-
16
- c.switch :force, :desc => 'Deploy even if there is a lockfile.', :negatable => false
17
-
18
- c.switch :downgrade, :desc => 'Allows deploy to run with an older platform version.', :negatable => false
19
-
20
- c.switch :dev, :desc => "Development mode: don't run 'git submodule update' before deploy.", :negatable => false
21
-
22
- c.flag :tags, :desc => 'Specify tags to pass through to puppet (overriding the default).',
23
- :arg_name => 'TAG[,TAG]'
24
-
25
- c.flag :port, :desc => 'Override the default SSH port.',
26
- :arg_name => 'PORT'
27
-
28
- c.flag :ip, :desc => 'Override the default SSH IP address.',
29
- :arg_name => 'IPADDRESS'
30
-
31
- c.action do |global,options,args|
32
-
33
- if options[:dev] != true
34
- init_submodules
35
- end
36
-
37
- nodes = manager.filter!(args, :disabled => false)
38
- if nodes.size > 1
39
- say "Deploying to these nodes: #{nodes.keys.join(', ')}"
40
- if !global[:yes] && !agree("Continue? ")
41
- quit! "OK. Bye."
42
- end
43
- end
44
-
45
- environments = nodes.field('environment').uniq
46
- if environments.empty?
47
- environments = [nil]
48
- end
49
- environments.each do |env|
50
- check_platform_pinning(env, global)
51
- end
52
- # compile hiera files for all the nodes in every environment that is
53
- # being deployed and only those environments.
54
- compile_hiera_files(manager.filter(environments), false)
55
- # update server certificates if needed
56
- update_certificates(nodes)
57
-
58
- ssh_connect(nodes, connect_options(options)) do |ssh|
59
- ssh.leap.log :checking, 'node' do
60
- ssh.leap.check_for_no_deploy
61
- ssh.leap.assert_initialized
62
- end
63
- ssh.leap.log :synching, "configuration files" do
64
- sync_hiera_config(ssh)
65
- sync_support_files(ssh)
66
- end
67
- ssh.leap.log :synching, "puppet manifests" do
68
- sync_puppet_files(ssh)
69
- end
70
- unless options[:sync]
71
- ssh.leap.log :applying, "puppet" do
72
- ssh.puppet.apply(:verbosity => [LeapCli.log_level,5].min,
73
- :tags => tags(options),
74
- :force => options[:force],
75
- :info => deploy_info,
76
- :downgrade => options[:downgrade]
77
- )
78
- end
79
- end
80
- end
81
- if !Util.exit_status.nil? && Util.exit_status != 0
82
- log :warning, "puppet did not finish successfully."
83
- end
84
- end
85
- end
86
-
87
- desc 'Display recent deployment history for a set of nodes.'
88
- long_desc 'The FILTER can be the name of a node, service, or tag.'
89
- arg_name 'FILTER'
90
- command [:history, :h] do |c|
91
- c.flag :port, :desc => 'Override the default SSH port.',
92
- :arg_name => 'PORT'
93
- c.flag :ip, :desc => 'Override the default SSH IP address.',
94
- :arg_name => 'IPADDRESS'
95
- c.action do |global,options,args|
96
- nodes = manager.filter!(args)
97
- ssh_connect(nodes, connect_options(options)) do |ssh|
98
- ssh.leap.history
99
- end
100
- end
101
- end
102
-
103
- private
104
-
105
- def forcible_prompt(forced, msg, prompt)
106
- say(msg)
107
- if forced
108
- log :warning, "continuing anyway because of --force"
109
- else
110
- say "hint: use --force to skip this prompt."
111
- quit!("OK. Bye.") unless agree(prompt)
112
- end
113
- end
114
-
115
- #
116
- # The currently activated provider.json could have loaded some pinning
117
- # information for the platform. If this is the case, refuse to deploy
118
- # if there is a mismatch.
119
- #
120
- # For example:
121
- #
122
- # "platform": {
123
- # "branch": "develop"
124
- # "version": "1.0..99"
125
- # "commit": "e1d6280e0a8c565b7fb1a4ed3969ea6fea31a5e2..HEAD"
126
- # }
127
- #
128
- def check_platform_pinning(environment, global_options)
129
- provider = manager.env(environment).provider
130
- return unless provider['platform']
131
-
132
- if environment.nil? || environment == 'default'
133
- provider_json = 'provider.json'
134
- else
135
- provider_json = 'provider.' + environment + '.json'
136
- end
137
-
138
- # can we have json schema verification already?
139
- unless provider.platform.is_a? Hash
140
- bail!('`platform` attribute in #{provider_json} must be a hash (was %s).' % provider.platform.inspect)
141
- end
142
-
143
- # check version
144
- if provider.platform['version']
145
- if !Leap::Platform.version_in_range?(provider.platform.version)
146
- forcible_prompt(
147
- global_options[:force],
148
- "The platform is pinned to a version range of '#{provider.platform.version}' "+
149
- "by the `platform.version` property in #{provider_json}, but the platform "+
150
- "(#{Path.platform}) has version #{Leap::Platform.version}.",
151
- "Do you really want to deploy from the wrong version? "
152
- )
153
- end
154
- end
155
-
156
- # check branch
157
- if provider.platform['branch']
158
- if !is_git_directory?(Path.platform)
159
- forcible_prompt(
160
- global_options[:force],
161
- "The platform is pinned to a particular branch by the `platform.branch` property "+
162
- "in #{provider_json}, but the platform directory (#{Path.platform}) is not a git repository.",
163
- "Do you really want to deploy anyway? "
164
- )
165
- end
166
- unless provider.platform.branch == current_git_branch(Path.platform)
167
- forcible_prompt(
168
- global_options[:force],
169
- "The platform is pinned to branch '#{provider.platform.branch}' by the `platform.branch` property "+
170
- "in #{provider_json}, but the current branch is '#{current_git_branch(Path.platform)}' " +
171
- "(for directory '#{Path.platform}')",
172
- "Do you really want to deploy from the wrong branch? "
173
- )
174
- end
175
- end
176
-
177
- # check commit
178
- if provider.platform['commit']
179
- if !is_git_directory?(Path.platform)
180
- forcible_prompt(
181
- global_options[:force],
182
- "The platform is pinned to a particular commit range by the `platform.commit` property "+
183
- "in #{provider_json}, but the platform directory (#{Path.platform}) is not a git repository.",
184
- "Do you really want to deploy anyway? "
185
- )
186
- end
187
- current_commit = current_git_commit(Path.platform)
188
- Dir.chdir(Path.platform) do
189
- commit_range = assert_run!("git log --pretty='format:%H' '#{provider.platform.commit}'",
190
- "The platform is pinned to a particular commit range by the `platform.commit` property "+
191
- "in #{provider_json}, but git was not able to find commits in the range specified "+
192
- "(#{provider.platform.commit}).")
193
- commit_range = commit_range.split("\n")
194
- if !commit_range.include?(current_commit) &&
195
- provider.platform.commit.split('..').first != current_commit
196
- forcible_prompt(
197
- global_options[:force],
198
- "The platform is pinned via the `platform.commit` property in #{provider_json} " +
199
- "to a commit in the range #{provider.platform.commit}, but the current HEAD " +
200
- "(#{current_commit}) is not in that range.",
201
- "Do you really want to deploy from the wrong commit? "
202
- )
203
- end
204
- end
205
- end
206
- end
207
-
208
- def sync_hiera_config(ssh)
209
- ssh.rsync.update do |server|
210
- node = manager.node(server.host)
211
- hiera_file = Path.relative_path([:hiera, node.name])
212
- ssh.leap.log hiera_file + ' -> ' + node.name + ':' + Leap::Platform.hiera_path
213
- {
214
- :source => hiera_file,
215
- :dest => Leap::Platform.hiera_path,
216
- :flags => "-rltp --chmod=u+rX,go-rwx"
217
- }
218
- end
219
- end
220
-
221
- #
222
- # sync various support files.
223
- #
224
- def sync_support_files(ssh)
225
- dest_dir = Leap::Platform.files_dir
226
- custom_files = build_custom_file_list
227
- ssh.rsync.update do |server|
228
- node = manager.node(server.host)
229
- files_to_sync = node.file_paths.collect {|path| Path.relative_path(path, Path.provider) }
230
- files_to_sync += custom_files
231
- if files_to_sync.any?
232
- ssh.leap.log(files_to_sync.join(', ') + ' -> ' + node.name + ':' + dest_dir)
233
- {
234
- :chdir => Path.named_path(:files_dir),
235
- :source => ".",
236
- :dest => dest_dir,
237
- :excludes => "*",
238
- :includes => calculate_includes_from_files(files_to_sync, '/files'),
239
- :flags => "-rltp --chmod=u+rX,go-rwx --relative --delete --delete-excluded --copy-links"
240
- }
241
- else
242
- nil
243
- end
244
- end
245
- end
246
-
247
- def sync_puppet_files(ssh)
248
- ssh.rsync.update do |server|
249
- ssh.leap.log(Path.platform + '/[bin,tests,puppet] -> ' + server.host + ':' + Leap::Platform.leap_dir)
250
- {
251
- :dest => Leap::Platform.leap_dir,
252
- :source => '.',
253
- :chdir => Path.platform,
254
- :excludes => '*',
255
- :includes => ['/bin', '/bin/**', '/puppet', '/puppet/**', '/tests', '/tests/**'],
256
- :flags => "-rlt --relative --delete --copy-links"
257
- }
258
- end
259
- end
260
-
261
- #
262
- # ensure submodules are up to date, if the platform is a git
263
- # repository.
264
- #
265
- def init_submodules
266
- return unless is_git_directory?(Path.platform)
267
- Dir.chdir Path.platform do
268
- assert_run! "git submodule sync"
269
- statuses = assert_run! "git submodule status"
270
- statuses.strip.split("\n").each do |status_line|
271
- if status_line =~ /^[\+-]/
272
- submodule = status_line.split(' ')[1]
273
- log "Updating submodule #{submodule}"
274
- assert_run! "git submodule update --init #{submodule}"
275
- end
276
- end
277
- end
278
- end
279
-
280
- #
281
- # converts an array of file paths into an array
282
- # suitable for --include of rsync
283
- #
284
- # if set, `prefix` is stripped off.
285
- #
286
- def calculate_includes_from_files(files, prefix=nil)
287
- return nil unless files and files.any?
288
-
289
- # prepend '/' (kind of like ^ for rsync)
290
- includes = files.collect {|file| file =~ /^\// ? file : '/' + file }
291
-
292
- # include all sub files of specified directories
293
- includes.size.times do |i|
294
- if includes[i] =~ /\/$/
295
- includes << includes[i] + '**'
296
- end
297
- end
298
-
299
- # include all parent directories (required because of --exclude '*')
300
- includes.size.times do |i|
301
- path = File.dirname(includes[i])
302
- while(path != '/')
303
- includes << path unless includes.include?(path)
304
- path = File.dirname(path)
305
- end
306
- end
307
-
308
- if prefix
309
- includes.map! {|path| path.sub(/^#{Regexp.escape(prefix)}\//, '/')}
310
- end
311
-
312
- return includes
313
- end
314
-
315
- def tags(options)
316
- if options[:tags]
317
- tags = options[:tags].split(',')
318
- else
319
- tags = Leap::Platform.default_puppet_tags.dup
320
- end
321
- tags << 'leap_slow' unless options[:fast]
322
- tags.join(',')
323
- end
324
-
325
- #
326
- # a provider might have various customization files that should be sync'ed to the server.
327
- # this method builds that list of files to sync.
328
- #
329
- def build_custom_file_list
330
- custom_files = []
331
- Leap::Platform.paths.keys.grep(/^custom_/).each do |path|
332
- if file_exists?(path)
333
- relative_path = Path.relative_path(path, Path.provider)
334
- if dir_exists?(path)
335
- custom_files << relative_path + '/' # rsync needs trailing slash
336
- else
337
- custom_files << relative_path
338
- end
339
- end
340
- end
341
- return custom_files
342
- end
343
-
344
- def deploy_info
345
- info = []
346
- info << "user: %s" % Etc.getpwuid(Process.euid).name
347
- if is_git_directory?(Path.platform) && current_git_branch(Path.platform) != 'master'
348
- info << "platform: %s (%s %s)" % [
349
- Leap::Platform.version,
350
- current_git_branch(Path.platform),
351
- current_git_commit(Path.platform)[0..4]
352
- ]
353
- else
354
- info << "platform: %s" % Leap::Platform.version
355
- end
356
- if is_git_directory?(LEAP_CLI_BASE_DIR)
357
- info << "leap_cli: %s (%s %s)" % [
358
- LeapCli::VERSION,
359
- current_git_branch(LEAP_CLI_BASE_DIR),
360
- current_git_commit(LEAP_CLI_BASE_DIR)[0..4]
361
- ]
362
- else
363
- info << "leap_cli: %s" % LeapCli::VERSION
364
- end
365
- info.join(', ')
366
- end
367
- end
368
- end