leap_cli 1.7.4 → 1.8

Sign up to get free protection for your applications and to get access to all the features.
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