bosh_cli 1.0.3 → 1.5.0.pre.1113
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/bosh +0 -9
- data/lib/cli.rb +69 -64
- data/lib/cli/backup_destination_path.rb +33 -0
- data/lib/cli/base_command.rb +57 -56
- data/lib/cli/blob_manager.rb +12 -12
- data/lib/cli/changeset_helper.rb +6 -7
- data/lib/cli/client/director.rb +724 -0
- data/lib/cli/command_handler.rb +6 -7
- data/lib/cli/commands/backup.rb +39 -0
- data/lib/cli/commands/biff.rb +42 -21
- data/lib/cli/commands/blob_management.rb +1 -1
- data/lib/cli/commands/cloudcheck.rb +11 -13
- data/lib/cli/commands/deployment.rb +53 -37
- data/lib/cli/commands/help.rb +3 -2
- data/lib/cli/commands/job_management.rb +67 -103
- data/lib/cli/commands/job_rename.rb +6 -8
- data/lib/cli/commands/log_management.rb +78 -55
- data/lib/cli/commands/maintenance.rb +36 -30
- data/lib/cli/commands/misc.rb +72 -51
- data/lib/cli/commands/package.rb +2 -2
- data/lib/cli/commands/property_management.rb +10 -12
- data/lib/cli/commands/release.rb +236 -133
- data/lib/cli/commands/snapshot.rb +93 -0
- data/lib/cli/commands/ssh.rb +216 -213
- data/lib/cli/commands/stemcell.rb +46 -34
- data/lib/cli/commands/task.rb +2 -2
- data/lib/cli/commands/user.rb +27 -3
- data/lib/cli/commands/vm.rb +28 -0
- data/lib/cli/commands/vms.rb +81 -23
- data/lib/cli/config.rb +6 -2
- data/lib/cli/core_ext.rb +31 -30
- data/lib/cli/deployment_helper.rb +134 -159
- data/lib/cli/deployment_manifest.rb +66 -0
- data/lib/cli/deployment_manifest_compiler.rb +0 -3
- data/lib/cli/event_log_renderer.rb +10 -10
- data/lib/cli/file_with_progress_bar.rb +52 -0
- data/lib/cli/job_builder.rb +1 -1
- data/lib/cli/job_command_args.rb +23 -0
- data/lib/cli/job_property_collection.rb +4 -7
- data/lib/cli/job_property_validator.rb +22 -12
- data/lib/cli/job_state.rb +54 -0
- data/lib/cli/line_wrap.rb +54 -0
- data/lib/cli/packaging_helper.rb +10 -10
- data/lib/cli/release.rb +18 -15
- data/lib/cli/release_builder.rb +9 -4
- data/lib/cli/release_compiler.rb +9 -9
- data/lib/cli/release_tarball.rb +3 -6
- data/lib/cli/resurrection.rb +31 -0
- data/lib/cli/runner.rb +56 -30
- data/lib/cli/stemcell.rb +25 -10
- data/lib/cli/task_log_renderer.rb +1 -1
- data/lib/cli/task_tracker.rb +10 -9
- data/lib/cli/validation.rb +3 -1
- data/lib/cli/version.rb +1 -1
- data/lib/cli/version_calc.rb +5 -18
- data/lib/cli/versions_index.rb +1 -1
- data/lib/cli/vm_state.rb +43 -0
- data/lib/cli/yaml_helper.rb +26 -35
- metadata +75 -208
- data/Rakefile +0 -56
- data/lib/cli/director.rb +0 -628
- data/spec/assets/biff/bad_gateway_config.yml +0 -28
- data/spec/assets/biff/good_simple_config.yml +0 -63
- data/spec/assets/biff/good_simple_golden_config.yml +0 -63
- data/spec/assets/biff/good_simple_template.erb +0 -69
- data/spec/assets/biff/ip_out_of_range.yml +0 -63
- data/spec/assets/biff/multiple_subnets_config.yml +0 -40
- data/spec/assets/biff/network_only_template.erb +0 -34
- data/spec/assets/biff/no_cc_config.yml +0 -27
- data/spec/assets/biff/no_range_config.yml +0 -27
- data/spec/assets/biff/no_subnet_config.yml +0 -16
- data/spec/assets/biff/ok_network_config.yml +0 -30
- data/spec/assets/biff/properties_template.erb +0 -6
- data/spec/assets/config/atmos/config/final.yml +0 -6
- data/spec/assets/config/atmos/config/private.yml +0 -4
- data/spec/assets/config/bad-providers/config/final.yml +0 -5
- data/spec/assets/config/bad-providers/config/private.yml +0 -4
- data/spec/assets/config/deprecation/config/final.yml +0 -5
- data/spec/assets/config/deprecation/config/private.yml +0 -2
- data/spec/assets/config/local/config/final.yml +0 -5
- data/spec/assets/config/local/config/private.yml +0 -1
- data/spec/assets/config/s3/config/final.yml +0 -5
- data/spec/assets/config/s3/config/private.yml +0 -5
- data/spec/assets/config/swift-hp/config/final.yml +0 -6
- data/spec/assets/config/swift-hp/config/private.yml +0 -7
- data/spec/assets/config/swift-rackspace/config/final.yml +0 -6
- data/spec/assets/config/swift-rackspace/config/private.yml +0 -6
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +0 -43
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +0 -24
- data/spec/assets/release/jobs/cacher.tgz +0 -0
- data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
- data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
- data/spec/assets/release/jobs/cacher/job.MF +0 -6
- data/spec/assets/release/jobs/cacher/monit +0 -1
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +0 -4
- data/spec/assets/release/jobs/cleaner/monit +0 -1
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +0 -1
- data/spec/assets/release/jobs/sweeper/job.MF +0 -5
- data/spec/assets/release/jobs/sweeper/monit +0 -1
- data/spec/assets/release/packages/mutator.tar.gz +0 -0
- data/spec/assets/release/packages/stuff.tgz +0 -0
- data/spec/assets/release/release.MF +0 -17
- data/spec/assets/release_invalid_checksum.tgz +0 -0
- data/spec/assets/release_invalid_jobs.tgz +0 -0
- data/spec/assets/release_no_name.tgz +0 -0
- data/spec/assets/release_no_version.tgz +0 -0
- data/spec/assets/stemcell/image +0 -1
- data/spec/assets/stemcell/stemcell.MF +0 -6
- data/spec/assets/stemcell_invalid_mf.tgz +0 -0
- data/spec/assets/stemcell_no_image.tgz +0 -0
- data/spec/assets/valid_release.tgz +0 -0
- data/spec/assets/valid_stemcell.tgz +0 -0
- data/spec/spec_helper.rb +0 -28
- data/spec/unit/base_command_spec.rb +0 -87
- data/spec/unit/biff_spec.rb +0 -172
- data/spec/unit/blob_manager_spec.rb +0 -288
- data/spec/unit/cache_spec.rb +0 -36
- data/spec/unit/cli_commands_spec.rb +0 -356
- data/spec/unit/config_spec.rb +0 -125
- data/spec/unit/core_ext_spec.rb +0 -81
- data/spec/unit/dependency_helper_spec.rb +0 -52
- data/spec/unit/deployment_manifest_compiler_spec.rb +0 -63
- data/spec/unit/deployment_manifest_spec.rb +0 -153
- data/spec/unit/director_spec.rb +0 -471
- data/spec/unit/director_task_spec.rb +0 -48
- data/spec/unit/event_log_renderer_spec.rb +0 -171
- data/spec/unit/hash_changeset_spec.rb +0 -73
- data/spec/unit/job_builder_spec.rb +0 -455
- data/spec/unit/job_property_collection_spec.rb +0 -111
- data/spec/unit/job_property_validator_spec.rb +0 -7
- data/spec/unit/job_rename_spec.rb +0 -200
- data/spec/unit/package_builder_spec.rb +0 -593
- data/spec/unit/release_builder_spec.rb +0 -120
- data/spec/unit/release_spec.rb +0 -173
- data/spec/unit/release_tarball_spec.rb +0 -29
- data/spec/unit/runner_spec.rb +0 -7
- data/spec/unit/ssh_spec.rb +0 -84
- data/spec/unit/stemcell_spec.rb +0 -17
- data/spec/unit/task_tracker_spec.rb +0 -131
- data/spec/unit/version_calc_spec.rb +0 -27
- data/spec/unit/versions_index_spec.rb +0 -144
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Copyright (c) 2009-2013 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli::Command
|
|
4
|
+
class Snapshot < Base
|
|
5
|
+
usage 'snapshots'
|
|
6
|
+
desc 'List all snapshots'
|
|
7
|
+
def list(job = nil, index = nil)
|
|
8
|
+
auth_required
|
|
9
|
+
|
|
10
|
+
deployment_name = prepare_deployment_manifest['name']
|
|
11
|
+
say("Deployment `#{deployment_name.make_green}'")
|
|
12
|
+
|
|
13
|
+
snapshots = director.list_snapshots(deployment_name, job, index)
|
|
14
|
+
|
|
15
|
+
sorted = snapshots.sort do |a, b|
|
|
16
|
+
s = a['job'].to_s <=> b['job'].to_s
|
|
17
|
+
s = a['index'].to_i <=> b['index'].to_i if s == 0
|
|
18
|
+
s = a['created_at'].to_s <=> b['created_at'].to_s if s == 0
|
|
19
|
+
s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
snapshots_table = table do |t|
|
|
23
|
+
t.headings = ['Job/index', 'Snapshot CID', 'Created at', 'Clean']
|
|
24
|
+
|
|
25
|
+
sorted.each do |snapshot|
|
|
26
|
+
job = "#{snapshot['job'] || 'unknown'}/#{snapshot['index'] || 'unknown'}"
|
|
27
|
+
t << [job, snapshot['snapshot_cid'], snapshot['created_at'], snapshot['clean']]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
nl
|
|
32
|
+
say(snapshots_table)
|
|
33
|
+
nl
|
|
34
|
+
say('Snapshots total: %d' % snapshots.size)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
usage 'take snapshot'
|
|
38
|
+
desc 'Takes a snapshot'
|
|
39
|
+
def take(job = nil, index = nil)
|
|
40
|
+
auth_required
|
|
41
|
+
|
|
42
|
+
deployment_name = prepare_deployment_manifest['name']
|
|
43
|
+
say("Deployment `#{deployment_name.make_green}'")
|
|
44
|
+
|
|
45
|
+
unless job && index
|
|
46
|
+
unless confirmed?("Are you sure you want to take a snapshot of all deployment `#{deployment_name}'?")
|
|
47
|
+
say('Canceled taking snapshot'.make_green)
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
status, task_id = director.take_snapshot(deployment_name, job, index)
|
|
53
|
+
|
|
54
|
+
task_report(status, task_id, 'Snapshot taken')
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
usage 'delete snapshot'
|
|
58
|
+
desc 'Deletes a snapshot'
|
|
59
|
+
def delete(snapshot_cid)
|
|
60
|
+
auth_required
|
|
61
|
+
|
|
62
|
+
deployment_name = prepare_deployment_manifest['name']
|
|
63
|
+
say("Deployment `#{deployment_name.make_green}'")
|
|
64
|
+
|
|
65
|
+
unless confirmed?("Are you sure you want to delete snapshot `#{snapshot_cid}'?")
|
|
66
|
+
say('Canceled deleting snapshot'.make_green)
|
|
67
|
+
return
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
status, task_id = director.delete_snapshot(deployment_name, snapshot_cid)
|
|
71
|
+
|
|
72
|
+
task_report(status, task_id, "Deleted Snapshot `#{snapshot_cid}'")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
usage 'delete snapshots'
|
|
76
|
+
desc 'Deletes all snapshots of a deployment'
|
|
77
|
+
def delete_all
|
|
78
|
+
auth_required
|
|
79
|
+
|
|
80
|
+
deployment_name = prepare_deployment_manifest['name']
|
|
81
|
+
say("Deployment `#{deployment_name.make_green}'")
|
|
82
|
+
|
|
83
|
+
unless confirmed?("Are you sure you want to delete all snapshots of deployment `#{deployment_name}'?")
|
|
84
|
+
say('Canceled deleting snapshots'.make_green)
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
status, task_id = director.delete_all_snapshots(deployment_name)
|
|
89
|
+
|
|
90
|
+
task_report(status, task_id, "Deleted all snapshots of deployment `#{deployment_name}'")
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/cli/commands/ssh.rb
CHANGED
|
@@ -1,264 +1,267 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
module Bosh::Cli
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
require 'cli/job_command_args'
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli
|
|
4
|
+
module Command
|
|
5
|
+
class Ssh < Base
|
|
6
|
+
SSH_USER_PREFIX = 'bosh_'
|
|
7
|
+
SSH_DSA_PUB = File.expand_path('~/.ssh/id_dsa.pub')
|
|
8
|
+
SSH_RSA_PUB = File.expand_path('~/.ssh/id_rsa.pub')
|
|
9
|
+
|
|
10
|
+
# bosh ssh
|
|
11
|
+
usage 'ssh'
|
|
12
|
+
desc 'Execute command or start an interactive session'
|
|
13
|
+
option '--public_key FILE', 'Public key'
|
|
14
|
+
option '--gateway_host HOST', 'Gateway host'
|
|
15
|
+
option '--gateway_user USER', 'Gateway user'
|
|
16
|
+
option '--gateway_identity_file FILE', 'Gateway identity file'
|
|
17
|
+
option '--default_password PASSWORD',
|
|
18
|
+
'Use default ssh password (NOT RECOMMENDED)'
|
|
19
|
+
|
|
20
|
+
def shell(*args)
|
|
21
|
+
if args.size > 0
|
|
22
|
+
job, index, command = JobCommandArgs.new(args).to_a
|
|
23
|
+
else
|
|
24
|
+
command = ''
|
|
25
|
+
job, index = prompt_for_job_and_index
|
|
25
26
|
end
|
|
26
|
-
setup_interactive_shell(job, index)
|
|
27
|
-
else
|
|
28
|
-
say("Executing `#{command.join(" ")}' on #{job}/#{index}")
|
|
29
|
-
perform_operation(:exec, job, index, command)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
27
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
desc "upload/download the source file to the given job. " +
|
|
36
|
-
"Note: for download /path/to/destination is a directory"
|
|
37
|
-
option "--download", "Download file"
|
|
38
|
-
option "--upload", "Upload file"
|
|
39
|
-
option "--public_key FILE", "Public key"
|
|
40
|
-
option "--gateway_host HOST", "Gateway host"
|
|
41
|
-
option "--gateway_user USER", "Gateway user"
|
|
42
|
-
def scp(*args)
|
|
43
|
-
job, index, args = parse_args(args)
|
|
44
|
-
upload = options[:upload]
|
|
45
|
-
download = options[:download]
|
|
46
|
-
if (upload && download) || (upload.nil? && download.nil?)
|
|
47
|
-
err("Please specify either --upload or --download")
|
|
48
|
-
end
|
|
28
|
+
job_must_exist_in_deployment(job)
|
|
29
|
+
index = valid_index_for(job, index, integer_index: true)
|
|
49
30
|
|
|
50
|
-
|
|
51
|
-
|
|
31
|
+
if command.empty?
|
|
32
|
+
setup_interactive_shell(job, index)
|
|
33
|
+
else
|
|
34
|
+
say("Executing `#{command.join(' ')}' on #{job}/#{index}")
|
|
35
|
+
perform_operation(:exec, job, index, command)
|
|
36
|
+
end
|
|
52
37
|
end
|
|
53
|
-
say("Executing file operations on job #{job}")
|
|
54
|
-
perform_operation(upload ? :upload : :download, job, index, args)
|
|
55
|
-
end
|
|
56
38
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
39
|
+
# bosh scp
|
|
40
|
+
usage 'scp'
|
|
41
|
+
desc 'upload/download the source file to the given job. ' +
|
|
42
|
+
'Note: for download /path/to/destination is a directory'
|
|
43
|
+
option '--download', 'Download file'
|
|
44
|
+
option '--upload', 'Upload file'
|
|
45
|
+
option '--public_key FILE', 'Public key'
|
|
46
|
+
option '--gateway_host HOST', 'Gateway host'
|
|
47
|
+
option '--gateway_user USER', 'Gateway user'
|
|
48
|
+
option '--gateway_identity_file FILE', 'Gateway identity file'
|
|
49
|
+
|
|
50
|
+
def scp(*args)
|
|
51
|
+
job, index, args = JobCommandArgs.new(args).to_a
|
|
52
|
+
upload = options[:upload]
|
|
53
|
+
download = options[:download]
|
|
54
|
+
if (upload && download) || (upload.nil? && download.nil?)
|
|
55
|
+
err('Please specify either --upload or --download')
|
|
56
|
+
end
|
|
64
57
|
|
|
65
|
-
|
|
58
|
+
job_must_exist_in_deployment(job)
|
|
66
59
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
60
|
+
if args.size != 2
|
|
61
|
+
err('Please enter valid source and destination paths')
|
|
62
|
+
end
|
|
63
|
+
say("Executing file operations on job #{job}")
|
|
64
|
+
perform_operation(upload ? :upload : :download, job, index, args)
|
|
65
|
+
end
|
|
70
66
|
|
|
71
|
-
|
|
67
|
+
usage 'cleanup ssh'
|
|
68
|
+
desc 'Cleanup SSH artifacts'
|
|
72
69
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
charset << "."
|
|
79
|
-
charset << "/"
|
|
80
|
-
charset
|
|
81
|
-
end
|
|
70
|
+
def cleanup(*args)
|
|
71
|
+
job, index, args = JobCommandArgs.new(args).to_a
|
|
72
|
+
if args.size > 0
|
|
73
|
+
err("SSH cleanup doesn't accept any extra args")
|
|
74
|
+
end
|
|
82
75
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
salt = ""
|
|
87
|
-
8.times do
|
|
88
|
-
salt << @salt_charset[rand(@salt_charset.size)]
|
|
89
|
-
end
|
|
90
|
-
plain_text.crypt(salt)
|
|
91
|
-
end
|
|
76
|
+
job_must_exist_in_deployment(job)
|
|
77
|
+
|
|
78
|
+
manifest_name = prepare_deployment_manifest['name']
|
|
92
79
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
# @param [optional,String] password
|
|
96
|
-
def setup_ssh(job, index, password = nil)
|
|
97
|
-
public_key = get_public_key
|
|
98
|
-
user = SSH_USER_PREFIX + rand(36**9).to_s(36)
|
|
99
|
-
deployment_name = prepare_deployment_manifest["name"]
|
|
100
|
-
|
|
101
|
-
say("Target deployment is `#{deployment_name}'")
|
|
102
|
-
status, task_id = director.setup_ssh(
|
|
103
|
-
deployment_name, job, index, user,
|
|
104
|
-
public_key, encrypt_password(password))
|
|
105
|
-
|
|
106
|
-
unless status == :done
|
|
107
|
-
err("Failed to set up SSH: see task #{task_id} log for details")
|
|
80
|
+
say("Cleaning up ssh artifacts from #{job}/#{index}")
|
|
81
|
+
director.cleanup_ssh(manifest_name, job, "^#{SSH_USER_PREFIX}", [index])
|
|
108
82
|
end
|
|
109
83
|
|
|
110
|
-
|
|
84
|
+
private
|
|
111
85
|
|
|
112
|
-
|
|
113
|
-
|
|
86
|
+
def get_salt_charset
|
|
87
|
+
charset = []
|
|
88
|
+
charset.concat(('a'..'z').to_a)
|
|
89
|
+
charset.concat(('A'..'Z').to_a)
|
|
90
|
+
charset.concat(('0'..'9').to_a)
|
|
91
|
+
charset << '.'
|
|
92
|
+
charset << '/'
|
|
93
|
+
charset
|
|
114
94
|
end
|
|
115
95
|
|
|
116
|
-
|
|
117
|
-
unless
|
|
118
|
-
|
|
119
|
-
|
|
96
|
+
def encrypt_password(plain_text)
|
|
97
|
+
return unless plain_text
|
|
98
|
+
@salt_charset ||= get_salt_charset
|
|
99
|
+
salt = ''
|
|
100
|
+
8.times do
|
|
101
|
+
salt << @salt_charset[rand(@salt_charset.size)]
|
|
120
102
|
end
|
|
103
|
+
plain_text.crypt(salt)
|
|
121
104
|
end
|
|
122
105
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
gateway = nil
|
|
130
|
-
end
|
|
106
|
+
# @param [String] job
|
|
107
|
+
# @param [Integer] index
|
|
108
|
+
# @param [optional,String] password
|
|
109
|
+
def setup_ssh(job, index, password = nil)
|
|
110
|
+
user = random_ssh_username
|
|
111
|
+
deployment_name = prepare_deployment_manifest['name']
|
|
131
112
|
|
|
132
|
-
|
|
133
|
-
yield sessions, user, gateway
|
|
134
|
-
ensure
|
|
113
|
+
say("Target deployment is `#{deployment_name}'")
|
|
135
114
|
nl
|
|
136
|
-
say(
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# @param [String] job Job name
|
|
144
|
-
# @param [Integer] index Job index
|
|
145
|
-
def setup_interactive_shell(job, index)
|
|
146
|
-
password = options[:default_password]
|
|
115
|
+
say('Setting up ssh artifacts')
|
|
116
|
+
status, task_id = director.setup_ssh(
|
|
117
|
+
deployment_name, job, index, user,
|
|
118
|
+
public_key, encrypt_password(password))
|
|
147
119
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"sudo on remote host): ") { |q| q.echo = "*" }
|
|
152
|
-
|
|
153
|
-
err("Please provide ssh password") if password.blank?
|
|
154
|
-
end
|
|
120
|
+
unless status == :done
|
|
121
|
+
err("Failed to set up SSH: see task #{task_id} log for details")
|
|
122
|
+
end
|
|
155
123
|
|
|
156
|
-
|
|
157
|
-
session = sessions.first
|
|
124
|
+
sessions = JSON.parse(director.get_task_result_log(task_id))
|
|
158
125
|
|
|
159
|
-
unless
|
|
160
|
-
err("
|
|
126
|
+
unless sessions && sessions.kind_of?(Array) && sessions.size > 0
|
|
127
|
+
err("Error setting up ssh, check task #{task_id} log for more details")
|
|
161
128
|
end
|
|
162
129
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
ssh_session = fork do
|
|
168
|
-
exec("ssh #{user}@localhost -p #{port}")
|
|
130
|
+
sessions.each do |session|
|
|
131
|
+
unless session.kind_of?(Hash)
|
|
132
|
+
err("Unexpected SSH session info: #{session.inspect}. " +
|
|
133
|
+
"Please check task #{task_id} log for more details")
|
|
169
134
|
end
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
begin
|
|
138
|
+
if options[:gateway_host]
|
|
139
|
+
require 'net/ssh/gateway'
|
|
140
|
+
gw_host = options[:gateway_host]
|
|
141
|
+
gw_user = options[:gateway_user] || ENV['USER']
|
|
142
|
+
gw_options = {}
|
|
143
|
+
gw_options[:keys] = [options[:gateway_identity_file]] if options[:gateway_identity_file]
|
|
144
|
+
begin
|
|
145
|
+
gateway = Net::SSH::Gateway.new(gw_host, gw_user, gw_options)
|
|
146
|
+
rescue Net::SSH::AuthenticationFailed
|
|
147
|
+
err("Authentication failed with gateway #{gw_host} and user #{gw_user}.")
|
|
148
|
+
end
|
|
149
|
+
else
|
|
150
|
+
gateway = nil
|
|
175
151
|
end
|
|
176
|
-
|
|
152
|
+
|
|
153
|
+
yield sessions, user, gateway
|
|
154
|
+
ensure
|
|
155
|
+
nl
|
|
156
|
+
say('Cleaning up ssh artifacts')
|
|
157
|
+
indices = sessions.map { |session| session['index'] }
|
|
158
|
+
director.cleanup_ssh(deployment_name, job, "^#{user}$", indices)
|
|
159
|
+
gateway.shutdown! if gateway
|
|
177
160
|
end
|
|
178
161
|
end
|
|
179
|
-
end
|
|
180
162
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
163
|
+
def random_ssh_username
|
|
164
|
+
SSH_USER_PREFIX + rand(36**9).to_s(36)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# @param [String] job Job name
|
|
168
|
+
# @param [Integer] index Job index
|
|
169
|
+
def setup_interactive_shell(job, index)
|
|
170
|
+
deployment_required
|
|
171
|
+
password = options[:default_password]
|
|
172
|
+
|
|
173
|
+
if password.nil?
|
|
174
|
+
password = ask(
|
|
175
|
+
'Enter password (use it to ' +
|
|
176
|
+
'sudo on remote host): ') { |q| q.echo = '*' }
|
|
177
|
+
|
|
178
|
+
err('Please provide ssh password') if password.blank?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
setup_ssh(job, index, password) do |sessions, user, gateway|
|
|
182
|
+
session = sessions.first
|
|
183
|
+
|
|
184
|
+
unless session['status'] == 'success' && session['ip']
|
|
185
185
|
err("Failed to set up SSH on #{job}/#{index}: #{session.inspect}")
|
|
186
186
|
end
|
|
187
187
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
path = "#{args[1]}/#{file}.#{job}.#{session["index"]}"
|
|
199
|
-
ssh.scp.download!(args[0], path)
|
|
200
|
-
say("Downloaded file to #{path}".green)
|
|
201
|
-
else
|
|
202
|
-
err("Unknown operation #{operation}")
|
|
203
|
-
end
|
|
188
|
+
say("Starting interactive shell on job #{job}/#{index}")
|
|
189
|
+
|
|
190
|
+
if gateway
|
|
191
|
+
port = gateway.open(session['ip'], 22)
|
|
192
|
+
ssh_session = Process.spawn('ssh', "#{user}@localhost", '-p', port.to_s)
|
|
193
|
+
Process.waitpid(ssh_session)
|
|
194
|
+
gateway.close(port)
|
|
195
|
+
else
|
|
196
|
+
ssh_session = Process.spawn('ssh', "#{user}@#{session['ip']}")
|
|
197
|
+
Process.waitpid(ssh_session)
|
|
204
198
|
end
|
|
205
199
|
end
|
|
206
200
|
end
|
|
207
|
-
end
|
|
208
201
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
202
|
+
def perform_operation(operation, job, index, args)
|
|
203
|
+
setup_ssh(job, index, nil) do |sessions, user, gateway|
|
|
204
|
+
sessions.each do |session|
|
|
205
|
+
unless session['status'] == 'success' && session['ip']
|
|
206
|
+
err("Failed to set up SSH on #{job}/#{index}: #{session.inspect}")
|
|
207
|
+
end
|
|
214
208
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
209
|
+
with_ssh(user, session['ip'], gateway) do |ssh|
|
|
210
|
+
case operation
|
|
211
|
+
when :exec
|
|
212
|
+
nl
|
|
213
|
+
say("#{job}/#{session['index']}")
|
|
214
|
+
say(ssh.exec!(args.join(' ')))
|
|
215
|
+
when :upload
|
|
216
|
+
ssh.scp.upload!(args[0], args[1])
|
|
217
|
+
when :download
|
|
218
|
+
file = File.basename(args[0])
|
|
219
|
+
path = "#{args[1]}/#{file}.#{job}.#{session['index']}"
|
|
220
|
+
ssh.scp.download!(args[0], path)
|
|
221
|
+
say("Downloaded file to #{path}".make_green)
|
|
222
|
+
else
|
|
223
|
+
err("Unknown operation #{operation}")
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
end
|
|
220
227
|
end
|
|
221
|
-
elsif args[0] =~ /^\d+$/
|
|
222
|
-
index = args.shift.to_i
|
|
223
228
|
end
|
|
224
229
|
|
|
225
|
-
[
|
|
226
|
-
|
|
230
|
+
# @return [String] Public key
|
|
231
|
+
def public_key
|
|
232
|
+
public_key_path = options[:public_key]
|
|
227
233
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
unless File.file?(public_key_path)
|
|
234
|
-
err("Can't find file `#{public_key_path}'")
|
|
235
|
-
end
|
|
236
|
-
return File.read(public_key_path)
|
|
237
|
-
else
|
|
238
|
-
%x[ssh-add -L 1>/dev/null 2>&1]
|
|
239
|
-
if $?.exitstatus == 0
|
|
240
|
-
return %x[ssh-add -L].split("\n").first
|
|
234
|
+
if public_key_path
|
|
235
|
+
unless File.file?(public_key_path)
|
|
236
|
+
err("Can't find file `#{public_key_path}'")
|
|
237
|
+
end
|
|
238
|
+
return File.read(public_key_path)
|
|
241
239
|
else
|
|
242
|
-
[
|
|
243
|
-
|
|
240
|
+
%x[ssh-add -L 1>/dev/null 2>&1]
|
|
241
|
+
if $?.exitstatus == 0
|
|
242
|
+
return %x[ssh-add -L].split("\n").first
|
|
243
|
+
else
|
|
244
|
+
[SSH_DSA_PUB, SSH_RSA_PUB].each do |key_file|
|
|
245
|
+
return File.read(key_file) if File.file?(key_file)
|
|
246
|
+
end
|
|
244
247
|
end
|
|
245
248
|
end
|
|
246
|
-
end
|
|
247
249
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
+
err('Please specify a public key file')
|
|
251
|
+
end
|
|
250
252
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
gateway
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
253
|
+
# @param [String] user
|
|
254
|
+
# @param [String] ip
|
|
255
|
+
# @param [optional, Net::SSH::Gateway] gateway
|
|
256
|
+
# @yield [Net::SSH]
|
|
257
|
+
def with_ssh(user, ip, gateway = nil)
|
|
258
|
+
require 'net/scp'
|
|
259
|
+
if gateway
|
|
260
|
+
gateway.ssh(ip, user) { |ssh| yield ssh }
|
|
261
|
+
else
|
|
262
|
+
require 'net/ssh'
|
|
263
|
+
Net::SSH.start(ip, user) { |ssh| yield ssh }
|
|
264
|
+
end
|
|
262
265
|
end
|
|
263
266
|
end
|
|
264
267
|
end
|