File: EXAMPLES.md

package info (click to toggle)
ruby-sshkit 1.25.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 712 kB
  • sloc: ruby: 3,749; makefile: 2
file content (488 lines) | stat: -rw-r--r-- 11,124 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
# Usage Examples

## Run a command as a different user

```ruby
on hosts do |host|
  as 'www-data' do
    puts capture(:whoami)
  end
end
```

## Run with default environmental variables

```ruby
SSHKit.config.default_env = { path: '/usr/local/libexec/bin:$PATH' }
on hosts do |host|
  puts capture(:env)
end
```

## Run a command in a different directory

```ruby
on hosts do |host|
  within '/var/log' do
    puts capture(:head, '-n5', 'messages')
  end
end
```

## Run a command with specific environmental variables

```ruby
# Please see the documentation for caveats related to commands that do not use
# the command map [such as simple strings].
#
# https://github.com/capistrano/sshkit#the-command-map
on hosts do |host|
  with rack_env: :test do
    puts capture("env | grep RACK_ENV")
  end
end
```

## Print some arbitrary output with the logging methods

```ruby
on hosts do |host|
  f = '/some/file'
  if test("[ -f #{f} ]")
    info "#{f} already exists on #{host}!"
  else
    execute :touch, f
  end
end
```

The `debug()`, `info()`, `warn()`, `error()` and `fatal()` honor the current
log level of `SSHKit.config.output_verbosity`

## Run a command in a different directory as a different user

```ruby
on hosts do |host|
  as 'www-data' do
    within '/var/log' do
      puts capture(:whoami)
      puts capture(:pwd)
    end
  end
end
```

This will output:

    www-data
    /var/log

**Note:** This example is a bit misleading, as the `www-data` user doesn't
have a shell defined, one cannot switch to that user.

## Run a command which requires interaction between the client and the server

```ruby
on hosts do |host|
  execute(:passwd, interaction_handler: {
    '(current) UNIX password: ' => "old_pw\n",
    'Enter new UNIX password: ' => "new_pw\n",
    'Retype new UNIX password: ' => "new_pw\n",
    'passwd: password updated successfully' => nil # For stdout/stderr which can be ignored, map a nil input
  })
end
```

## Download a file from disk

```ruby
on roles(:all) do
  puts 'Downloading DB Backup File'
  date_path = Date.today.strftime("%Y/%m/%d")
  download! "/var/mysql-backup/#{date_path}/my-awesome-db.sql.gz", "my-awesome-db.sql.gz"
end
```

## Upload a file from disk

```ruby
on hosts do |host|
  upload! '/config/database.yml', '/opt/my_project/shared/database.yml'
end
```

Upload and download will respect the `within()` directories:

```ruby
on hosts do |host|
  within 'my/app/directory' do
    upload! 'database.yml', 'config/database.yml'
  end
end
```

## Upload a file from a stream

```ruby
on hosts do |host|
  file = File.open('/config/database.yml')
  io   = StringIO.new(....)
  upload! file, '/opt/my_project/shared/database.yml'
  upload! io,   '/opt/my_project/shared/io.io.io'
end
```

The IO streaming is useful for uploading something rather than "cat"ing it,
for example

```ruby
on hosts do |host|
  contents = StringIO.new('ALL ALL = (ALL) NOPASSWD: ALL')
  upload! contents, '/etc/sudoers.d/yolo'
end
```

This spares one from having to figure out the correct escaping sequences for
something like "echo(:cat, '...?...', '> /etc/sudoers.d/yolo')".

## Upload a directory of files

```ruby
on hosts do |host|
  upload! '.', '/tmp/mypwd', recursive: true
end
```

In this case the `recursive: true` option mirrors the same options which are
available to [`Net::SCP`](https://github.com/net-ssh/net-scp) and
[`Net::SFTP`](https://github.com/net-ssh/net-sftp).

## Set the upload/download method (SCP or SFTP).

SSHKit can use SCP or SFTP for file transfers. The default is SCP, but this can be changed to SFTP per host:

```ruby
host = SSHKit::Host.new('user@example.com')
host.transfer_method = :sftp
```

or globally:

```ruby
SSHKit::Backend::Netssh.configure do |ssh|
  ssh.transfer_method = :sftp
end
```

## Setting global SSH options

Setting global SSH options, these will be overwritten by options set on the
individual hosts:

```ruby
SSHKit::Backend::Netssh.configure do |ssh|
  ssh.connection_timeout = 30
  ssh.ssh_options = {
    user: 'adifferentuser',
    keys: %w(/home/user/.ssh/id_rsa),
    forward_agent: false,
    auth_methods: %w(publickey password)
  }
end
```

## Run a command with a different effective group ID

```ruby
on hosts do |host|
  as user: 'www-data', group: 'project-group' do
    within '/var/log' do
      execute :touch, 'somefile'
      execute :ls, '-l'
    end
  end
end
```

One will see that the created file is owned by the user `www-data` and the
group `project-group`.

When combined with the `umask` configuration option, it is easy to share
scripts for deployment between team members without sharing logins.

## Stack directory nestings

```ruby
on hosts do
  within "/var" do
    puts capture(:pwd)
    within :log do
      puts capture(:pwd)
    end
  end
end
```

This will output:

    /var/
    /var/log

The directory paths are joined using `File.join()`, which should correctly
join parts without forcing the user of the code to care about trailing or
leading slashes. It may be misleading as the `File.join()` is performed on the
machine running the code, if that's a Windows box, the paths may be incorrectly
joined according to the expectations of the machine receiving the commands.

## Do not care about the host block

```ruby
on hosts do
  # The |host| argument is optional, it will
  # be nil in the block if not passed
end
```

## Change the output formatter

```ruby
# The default format is pretty, which outputs colored text
SSHKit.config.use_format :pretty

# Text with no coloring
SSHKit.config.use_format :simpletext

# Red / Green dots for each completed step
SSHKit.config.use_format :dot

# No output
SSHKit.config.use_format :blackhole
```

## Implement a dirt-simple formatter class

```ruby
module SSHKit
  module Formatter
    class MyFormatter < SSHKit::Formatter::Abstract
      def write(obj)
        if obj.is_a? SSHKit::Command
          # Do something here, see the SSHKit::Command documentation
        end
      end
    end
  end
end

# If your formatter is defined in the SSHKit::Formatter module configure with the format option:
SSHKit.config.use_format :myformatter

# Or configure the output directly
SSHKit.config.output = MyFormatter.new($stdout)
SSHKit.config.output = MyFormatter.new(SSHKit.config.output)
SSHKit.config.output = MyFormatter.new(File.open('log/deploy.log', 'wb'))
```

## Set a password for a host.

```ruby
host = SSHKit::Host.new('user@example.com')
host.password = "hackme"

on host do |host|
  puts capture(:echo, "I don't care about security!")
end
```

## Execute and raise an error if something goes wrong

```ruby
on hosts do |host|
  execute(:echo, '"Example Message!" 1>&2; false')
end
```

This will raise `SSHKit::Command::Failed` with the `#message` "Example Message!"
which will cause the command to abort.

## Make a test, or run a command which may fail without raising an error:

```ruby
on hosts do |host|
  if test "[ -d /opt/sites ]"
    within "/opt/sites" do
      execute :git, :pull
    end
  else
    execute :git, :clone, 'some-repository', '/opt/sites'
  end
end
```

The `test()` command behaves exactly the same as execute however will return
false if the command exits with a non-zero exit (as `man 1 test` does). As it
returns boolean it can be used to direct the control flow within the block.

## Do something different on one host, or another depending on a host property

```ruby
host1 = SSHKit::Host.new 'user@example.com'
host2 = SSHKit::Host.new 'user@example.org'

on hosts do |host|
  target = "/var/www/sites/"
  if host.hostname =~ /org/
    target += "dotorg"
  else
    target += "dotcom"
  end
  execute! :git, :clone, "git@git.#{host.hostname}", target
end
```

## Connect to a host in the easiest possible way

```ruby
on 'example.com' do |host|
  execute :uptime
end
```

This will resolve the `example.com` hostname into a `SSHKit::Host` object, and
try to pull up the correct configuration for it.

## Connect to a host on a port different than 22

If your ssh server is running on a port different than 22, you can change this is
shown:

```ruby
on('example.com', {port: 1234}) do
end
```

## Run a command without it being command-mapped

If the command you attempt to call contains a space character it won't be
mapped:

```ruby
Command.new(:git, :push, :origin, :master).to_s
# => /usr/bin/env git push origin master
# (also: execute(:git, :push, :origin, :master)

Command.new("git push origin master").to_s
# => git push origin master
# (also: execute("git push origin master"))
```

This can be used to access shell builtins (such as `if` and `test`)


## Run a command with a heredoc

An extension of the behaviour above, if you write a command like this:

```ruby
c = Command.new <<-EOCOMMAND
  if test -d /var/log
  then echo "Directory Exists"
  fi
EOCOMMAND
c.to_s
# => if test -d /var/log; then echo "Directory Exists; fi
# (also: execute <<- EOCOMMAND........))
```

**Note:** The logic which reformats the script into a oneliner may be naïve, but in all
known test cases, it works. The key thing is that `if` is not mapped to
`/usr/bin/env if`, which would break with a syntax error.

## Using with Rake

Into the `Rakefile` simply put something like:

```ruby
require 'sshkit'

SSHKit.config.command_map[:rake] = "./bin/rake"

desc "Deploy the site, pulls from Git, migrate the db and precompile assets, then restart Passenger."
task :deploy do
  include SSHKit::DSL

  on "example.com" do |host|
    within "/opt/sites/example.com" do
      execute :git, :pull
      execute :bundle, :install, '--deployment'
      execute :rake, 'db:migrate'
      execute :rake, 'assets:precompile'
      execute :touch, 'tmp/restart.txt'
    end
  end
end
```

## Using without the DSL

The *Coordinator* will resolve all hosts into *Host* objects, you can mix and
match.

```ruby
Coordinator.new("one.example.com", SSHKit::Host.new('two.example.com')).each in: :sequence do
  puts capture :uptime
end
```

You might also look at `./lib/sshkit/dsl.rb` where you can see almost the
exact code as above, which implements the `on()` method.

## Use the Host properties attribute

Implemented since `v0.0.6`

```ruby
servers = %w{one.example.com two.example.com
             three.example.com four.example.com}.collect do |s|
  h = SSHKit::Host.new(s)
  if s.match /(one|two)/
    h.properties.roles = [:web]
  else
    h.properties.roles = [:app]
  end
end

on servers do |host|
  if host.properties.roles.include?(:web)
    # Do something pertinent to web servers
  elsif host.properties.roles.include?(:app)
    # Do something pertinent to application servers
  end
end
```

The `SSHKit::Host#properties` is an [`OpenStruct`](http://ruby-doc.org/stdlib-1.9.3/libdoc/ostruct/rdoc/OpenStruct.html)
which is not verified or validated in any way, it is up to you, or your
library to attach meanings or conventions to this mechanism.

## Running local commands

Replace `on` with `run_locally`

```ruby
run_locally do
  within '/tmp' do
    execute :whoami
  end
end
```

You can achieve the same thing with `on(:local)`

```ruby
on(:local) do
  within '/tmp' do
    execute :whoami
  end
end
```