bosh_cli 0.16
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/README +4 -0
- data/Rakefile +55 -0
- data/bin/bosh +17 -0
- data/lib/cli.rb +76 -0
- data/lib/cli/cache.rb +44 -0
- data/lib/cli/changeset_helper.rb +142 -0
- data/lib/cli/command_definition.rb +52 -0
- data/lib/cli/commands/base.rb +245 -0
- data/lib/cli/commands/biff.rb +300 -0
- data/lib/cli/commands/blob.rb +125 -0
- data/lib/cli/commands/cloudcheck.rb +169 -0
- data/lib/cli/commands/deployment.rb +147 -0
- data/lib/cli/commands/job.rb +42 -0
- data/lib/cli/commands/job_management.rb +117 -0
- data/lib/cli/commands/log_management.rb +81 -0
- data/lib/cli/commands/maintenance.rb +131 -0
- data/lib/cli/commands/misc.rb +240 -0
- data/lib/cli/commands/package.rb +112 -0
- data/lib/cli/commands/property_management.rb +125 -0
- data/lib/cli/commands/release.rb +469 -0
- data/lib/cli/commands/ssh.rb +271 -0
- data/lib/cli/commands/stemcell.rb +184 -0
- data/lib/cli/commands/task.rb +213 -0
- data/lib/cli/commands/user.rb +28 -0
- data/lib/cli/commands/vms.rb +53 -0
- data/lib/cli/config.rb +154 -0
- data/lib/cli/core_ext.rb +145 -0
- data/lib/cli/dependency_helper.rb +62 -0
- data/lib/cli/deployment_helper.rb +263 -0
- data/lib/cli/deployment_manifest_compiler.rb +28 -0
- data/lib/cli/director.rb +633 -0
- data/lib/cli/director_task.rb +64 -0
- data/lib/cli/errors.rb +48 -0
- data/lib/cli/event_log_renderer.rb +351 -0
- data/lib/cli/job_builder.rb +226 -0
- data/lib/cli/package_builder.rb +254 -0
- data/lib/cli/packaging_helper.rb +248 -0
- data/lib/cli/release.rb +176 -0
- data/lib/cli/release_builder.rb +215 -0
- data/lib/cli/release_compiler.rb +178 -0
- data/lib/cli/release_tarball.rb +272 -0
- data/lib/cli/runner.rb +771 -0
- data/lib/cli/stemcell.rb +83 -0
- data/lib/cli/task_log_renderer.rb +40 -0
- data/lib/cli/templates/help_message.erb +75 -0
- data/lib/cli/validation.rb +42 -0
- data/lib/cli/version.rb +7 -0
- data/lib/cli/version_calc.rb +48 -0
- data/lib/cli/versions_index.rb +126 -0
- data/lib/cli/yaml_helper.rb +62 -0
- data/spec/assets/biff/bad_gateway_config.yml +28 -0
- data/spec/assets/biff/good_simple_config.yml +63 -0
- data/spec/assets/biff/good_simple_golden_config.yml +63 -0
- data/spec/assets/biff/good_simple_template.erb +69 -0
- data/spec/assets/biff/multiple_subnets_config.yml +40 -0
- data/spec/assets/biff/network_only_template.erb +34 -0
- data/spec/assets/biff/no_cc_config.yml +27 -0
- data/spec/assets/biff/no_range_config.yml +27 -0
- data/spec/assets/biff/no_subnet_config.yml +16 -0
- data/spec/assets/biff/ok_network_config.yml +30 -0
- data/spec/assets/biff/properties_template.erb +6 -0
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
- 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 +6 -0
- data/spec/assets/release/jobs/cacher/monit +1 -0
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +4 -0
- data/spec/assets/release/jobs/cleaner/monit +1 -0
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
- data/spec/assets/release/jobs/sweeper/job.MF +5 -0
- data/spec/assets/release/jobs/sweeper/monit +1 -0
- 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 +17 -0
- 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 +1 -0
- data/spec/assets/stemcell/stemcell.MF +6 -0
- 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 +25 -0
- data/spec/unit/base_command_spec.rb +66 -0
- data/spec/unit/biff_spec.rb +135 -0
- data/spec/unit/cache_spec.rb +36 -0
- data/spec/unit/cli_commands_spec.rb +481 -0
- data/spec/unit/config_spec.rb +139 -0
- data/spec/unit/core_ext_spec.rb +77 -0
- data/spec/unit/dependency_helper_spec.rb +52 -0
- data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
- data/spec/unit/director_spec.rb +511 -0
- data/spec/unit/director_task_spec.rb +48 -0
- data/spec/unit/event_log_renderer_spec.rb +171 -0
- data/spec/unit/hash_changeset_spec.rb +73 -0
- data/spec/unit/job_builder_spec.rb +454 -0
- data/spec/unit/package_builder_spec.rb +567 -0
- data/spec/unit/release_builder_spec.rb +65 -0
- data/spec/unit/release_spec.rb +66 -0
- data/spec/unit/release_tarball_spec.rb +33 -0
- data/spec/unit/runner_spec.rb +140 -0
- data/spec/unit/ssh_spec.rb +78 -0
- data/spec/unit/stemcell_spec.rb +17 -0
- data/spec/unit/version_calc_spec.rb +27 -0
- data/spec/unit/versions_index_spec.rb +132 -0
- metadata +338 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli::Command
|
|
4
|
+
class Deployment < Base
|
|
5
|
+
include Bosh::Cli::DeploymentHelper
|
|
6
|
+
|
|
7
|
+
def show_current
|
|
8
|
+
say(deployment ?
|
|
9
|
+
"Current deployment is '#{deployment.green}'" :
|
|
10
|
+
"Deployment not set")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def set_current(name)
|
|
14
|
+
manifest_filename = find_deployment(name)
|
|
15
|
+
|
|
16
|
+
unless File.exists?(manifest_filename)
|
|
17
|
+
err("Missing manifest for #{name} (tried '#{manifest_filename}')")
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
manifest = load_yaml_file(manifest_filename)
|
|
21
|
+
|
|
22
|
+
unless manifest.is_a?(Hash)
|
|
23
|
+
err("Invalid manifest format")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
unless manifest["target"].blank?
|
|
27
|
+
err(manifest_target_upgrade_notice)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
if manifest["director_uuid"].blank?
|
|
31
|
+
err("Director UUID is not defined in deployment manifest")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if target
|
|
35
|
+
old_director = Bosh::Cli::Director.new(target, username, password)
|
|
36
|
+
old_director_uuid = old_director.get_status["uuid"] rescue nil
|
|
37
|
+
else
|
|
38
|
+
old_director_uuid = nil
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if old_director_uuid != manifest["director_uuid"]
|
|
42
|
+
new_target_url = config.resolve_alias(:target,
|
|
43
|
+
manifest["director_uuid"])
|
|
44
|
+
if new_target_url.blank?
|
|
45
|
+
err("Cannot find director url for " +
|
|
46
|
+
"UUID '#{manifest["director_uuid"]}'")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
new_director = Bosh::Cli::Director.new(new_target_url,
|
|
50
|
+
username, password)
|
|
51
|
+
status = new_director.get_status
|
|
52
|
+
|
|
53
|
+
config.target = new_target_url
|
|
54
|
+
config.target_name = status["name"]
|
|
55
|
+
config.target_version = status["version"]
|
|
56
|
+
config.target_uuid = status["uuid"]
|
|
57
|
+
say("#{"WARNING!".red} Your target has been" +
|
|
58
|
+
"changed to `#{target.red}'!")
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
say("Deployment set to '#{manifest_filename.green}'")
|
|
62
|
+
config.set_deployment(manifest_filename)
|
|
63
|
+
config.save
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def perform(*options)
|
|
67
|
+
auth_required
|
|
68
|
+
recreate = options.include?("--recreate")
|
|
69
|
+
|
|
70
|
+
manifest_yaml = prepare_deployment_manifest(:yaml => true,
|
|
71
|
+
:resolve_properties => true)
|
|
72
|
+
|
|
73
|
+
if interactive?
|
|
74
|
+
inspect_deployment_changes(YAML.load(manifest_yaml))
|
|
75
|
+
say("Please review all changes carefully".yellow)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
desc = "`#{File.basename(deployment).green}' to `#{target_name.green}'"
|
|
79
|
+
|
|
80
|
+
unless confirmed?("Deploying #{desc}")
|
|
81
|
+
cancel_deployment
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
status, body = director.deploy(manifest_yaml, :recreate => recreate)
|
|
85
|
+
|
|
86
|
+
responses = {
|
|
87
|
+
:done => "Deployed #{desc}",
|
|
88
|
+
:non_trackable => "Started deployment but director at `#{target}' " +
|
|
89
|
+
"doesn't support deployment tracking",
|
|
90
|
+
:track_timeout => "Started deployment but timed out out " +
|
|
91
|
+
"while tracking status",
|
|
92
|
+
:error => "Started deployment but received an error " +
|
|
93
|
+
"while tracking status",
|
|
94
|
+
:invalid => "Deployment is invalid, please fix it and deploy again"
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
say(responses[status] || "Cannot deploy: #{body}")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def delete(name, *options)
|
|
101
|
+
auth_required
|
|
102
|
+
force = options.include?("--force")
|
|
103
|
+
|
|
104
|
+
say("\nYou are going to delete deployment `#{name}'.\n\n")
|
|
105
|
+
say("THIS IS A VERY DESTRUCTIVE OPERATION AND IT CANNOT BE UNDONE!\n".red)
|
|
106
|
+
|
|
107
|
+
unless confirmed?
|
|
108
|
+
say("Canceled deleting deployment".green)
|
|
109
|
+
return
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
status, message = director.delete_deployment(name, :force => force)
|
|
113
|
+
|
|
114
|
+
responses = {
|
|
115
|
+
:done => "Deleted deployment '#{name}'",
|
|
116
|
+
:non_trackable => "Deployment delete in progress but director " +
|
|
117
|
+
"at '#{target}' doesn't support task tracking",
|
|
118
|
+
:track_timeout => "Timed out out while tracking deployment " +
|
|
119
|
+
"deletion progress",
|
|
120
|
+
:error => "Attempted to delete deployment but received " +
|
|
121
|
+
"an error while tracking status",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
say(responses[status] || "Cannot delete deployment: #{message}")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def list
|
|
128
|
+
auth_required
|
|
129
|
+
|
|
130
|
+
deployments = director.list_deployments
|
|
131
|
+
|
|
132
|
+
err("No deployments") if deployments.size == 0
|
|
133
|
+
|
|
134
|
+
deployments_table = table do |t|
|
|
135
|
+
t.headings = ["Name"]
|
|
136
|
+
deployments.each do |r|
|
|
137
|
+
t << [r["name"]]
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
say("\n")
|
|
142
|
+
say(deployments_table)
|
|
143
|
+
say("\n")
|
|
144
|
+
say("Deployments total: %d" % deployments.size)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli::Command
|
|
4
|
+
class Job < Base
|
|
5
|
+
|
|
6
|
+
def generate(name)
|
|
7
|
+
check_if_release_dir
|
|
8
|
+
|
|
9
|
+
unless name.bosh_valid_id?
|
|
10
|
+
err("`#{name}' is not a vaild BOSH id")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
job_dir = File.join("jobs", name)
|
|
14
|
+
|
|
15
|
+
if File.exists?(job_dir)
|
|
16
|
+
err("Job `#{name}' already exists, please pick another name")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
say("create\t#{job_dir}")
|
|
20
|
+
FileUtils.mkdir_p(job_dir)
|
|
21
|
+
|
|
22
|
+
templates_dir = File.join(job_dir, "templates")
|
|
23
|
+
say("create\t#{templates_dir}")
|
|
24
|
+
FileUtils.mkdir_p(templates_dir)
|
|
25
|
+
|
|
26
|
+
spec_file = File.join(job_dir, "spec")
|
|
27
|
+
say("create\t#{spec_file}")
|
|
28
|
+
FileUtils.touch(spec_file)
|
|
29
|
+
|
|
30
|
+
monit_file = File.join(job_dir, "monit")
|
|
31
|
+
say("create\t#{monit_file}")
|
|
32
|
+
FileUtils.touch(monit_file)
|
|
33
|
+
|
|
34
|
+
File.open(spec_file, "w") do |f|
|
|
35
|
+
f.write("---\nname: #{name}\ntemplates:\n\npackages:\n")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
say("\nGenerated skeleton for `#{name}' job in `#{job_dir}'")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli::Command
|
|
4
|
+
class JobManagement < Base
|
|
5
|
+
include Bosh::Cli::DeploymentHelper
|
|
6
|
+
|
|
7
|
+
def start_job(*args)
|
|
8
|
+
change_job_state(:start, *args)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def stop_job(*args)
|
|
12
|
+
change_job_state(:stop, *args)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def restart_job(*args)
|
|
16
|
+
change_job_state(:restart, *args)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def recreate_job(*args)
|
|
20
|
+
change_job_state(:recreate, *args)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def change_job_state(operation, *args)
|
|
24
|
+
auth_required
|
|
25
|
+
manifest_yaml = prepare_deployment_manifest(:yaml => true)
|
|
26
|
+
manifest = YAML.load(manifest_yaml)
|
|
27
|
+
|
|
28
|
+
unless [:start, :stop, :restart, :recreate].include?(operation)
|
|
29
|
+
err("Unknown operation `#{operation}': supported operations are " +
|
|
30
|
+
"`start', `stop', `restart', `recreate'")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
args = args.dup
|
|
34
|
+
hard = args.delete("--hard")
|
|
35
|
+
soft = args.delete("--soft")
|
|
36
|
+
force = args.delete("--force")
|
|
37
|
+
|
|
38
|
+
if hard && soft
|
|
39
|
+
err("Cannot handle both --hard and --soft options, please choose one")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
if operation != :stop && (hard || soft)
|
|
43
|
+
err("--hard and --soft options only make sense for `stop' operation")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
job = args.shift
|
|
47
|
+
index = args.shift
|
|
48
|
+
deployment_desc = "`#{deployment.green}' to `#{target_name.green}'"
|
|
49
|
+
job_desc = index ? "#{job}(#{index})" : "#{job}"
|
|
50
|
+
|
|
51
|
+
case operation
|
|
52
|
+
when :start
|
|
53
|
+
op_desc = "start #{job_desc}"
|
|
54
|
+
new_state = "started"
|
|
55
|
+
completion_desc = "#{job_desc.green} has been started"
|
|
56
|
+
when :stop
|
|
57
|
+
if hard
|
|
58
|
+
op_desc = "stop #{job_desc} and power off its VM(s)"
|
|
59
|
+
completion_desc = "#{job_desc.green} has been stopped, " +
|
|
60
|
+
"VM(s) powered off"
|
|
61
|
+
new_state = "detached"
|
|
62
|
+
else
|
|
63
|
+
op_desc = "stop #{job_desc}"
|
|
64
|
+
completion_desc = "#{job_desc.green} has been stopped, " +
|
|
65
|
+
"VM(s) still running"
|
|
66
|
+
new_state = "stopped"
|
|
67
|
+
end
|
|
68
|
+
when :restart
|
|
69
|
+
op_desc = "restart #{job_desc}"
|
|
70
|
+
new_state = "restart"
|
|
71
|
+
completion_desc = "#{job_desc.green} has been restarted"
|
|
72
|
+
when :recreate
|
|
73
|
+
op_desc = "recreate #{job_desc}"
|
|
74
|
+
new_state = "recreate"
|
|
75
|
+
completion_desc = "#{job_desc.green} has been recreated"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
say("You are about to #{op_desc.green}")
|
|
79
|
+
|
|
80
|
+
if interactive?
|
|
81
|
+
# TODO: refactor inspect_deployment_changes
|
|
82
|
+
# to decouple changeset structure and rendering
|
|
83
|
+
other_changes_present = inspect_deployment_changes(
|
|
84
|
+
manifest, :show_empty_changeset => false)
|
|
85
|
+
|
|
86
|
+
if other_changes_present && !force
|
|
87
|
+
err("Cannot perform job management when other deployment changes " +
|
|
88
|
+
"are present. Please use `--force' to override.")
|
|
89
|
+
end
|
|
90
|
+
unless confirmed?("#{op_desc.capitalize}?")
|
|
91
|
+
cancel_deployment
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
nl
|
|
95
|
+
|
|
96
|
+
say("Performing `#{op_desc}'...")
|
|
97
|
+
|
|
98
|
+
status, body = director.change_job_state(manifest["name"],
|
|
99
|
+
manifest_yaml,
|
|
100
|
+
job, index, new_state)
|
|
101
|
+
|
|
102
|
+
responses = {
|
|
103
|
+
:done => completion_desc,
|
|
104
|
+
:non_trackable => "Started deployment but director at '#{target}' " +
|
|
105
|
+
"doesn't support deployment tracking",
|
|
106
|
+
:track_timeout => "Started deployment but timed out out "+
|
|
107
|
+
"while tracking status",
|
|
108
|
+
:error => "Started deployment but received an error " +
|
|
109
|
+
"while tracking status",
|
|
110
|
+
:invalid => "Deployment is invalid, please fix it and deploy again"
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
say(responses[status] || "Cannot deploy: #{body}")
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli::Command
|
|
4
|
+
class LogManagement < Base
|
|
5
|
+
include Bosh::Cli::DeploymentHelper
|
|
6
|
+
|
|
7
|
+
def fetch_logs(*args)
|
|
8
|
+
auth_required
|
|
9
|
+
target_required
|
|
10
|
+
|
|
11
|
+
job = args.shift
|
|
12
|
+
index = args.shift
|
|
13
|
+
filters = nil
|
|
14
|
+
|
|
15
|
+
for_job = args.delete("--job")
|
|
16
|
+
for_agent = args.delete("--agent")
|
|
17
|
+
|
|
18
|
+
if for_job && for_agent
|
|
19
|
+
err("Please specify which logs you want, job or agent")
|
|
20
|
+
elsif for_agent
|
|
21
|
+
log_type = "agent"
|
|
22
|
+
else # default log type is 'job'
|
|
23
|
+
log_type = "job"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if args.include?("--only")
|
|
27
|
+
pos = args.index("--only")
|
|
28
|
+
filters = args[pos+1]
|
|
29
|
+
if filters.nil?
|
|
30
|
+
err("Please provide a list of filters separated by comma")
|
|
31
|
+
end
|
|
32
|
+
args.delete("--only")
|
|
33
|
+
args.delete(filters)
|
|
34
|
+
elsif args.include?("--all")
|
|
35
|
+
args.delete("--all")
|
|
36
|
+
filters = "all"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if for_agent && !filters.nil? && filters != "all"
|
|
40
|
+
err("Custom filtering is not supported for agent logs")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if index !~ /^\d+$/
|
|
44
|
+
err("Job index is expected to be a positive integer")
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if args.size > 0
|
|
48
|
+
err("Unknown arguments: #{args.join(", ")}")
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
manifest = prepare_deployment_manifest
|
|
52
|
+
|
|
53
|
+
resource_id = director.fetch_logs(manifest["name"], job, index,
|
|
54
|
+
log_type, filters)
|
|
55
|
+
|
|
56
|
+
if resource_id.nil?
|
|
57
|
+
err("Error retrieving logs")
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
nl
|
|
61
|
+
say("Downloading log bundle (#{resource_id.to_s.green})...")
|
|
62
|
+
|
|
63
|
+
begin
|
|
64
|
+
time = Time.now.strftime("%Y-%m-%d@%H-%M-%S")
|
|
65
|
+
log_file = File.join(Dir.pwd, "#{job}.#{index}.#{time}.tgz")
|
|
66
|
+
|
|
67
|
+
tmp_file = director.download_resource(resource_id)
|
|
68
|
+
|
|
69
|
+
FileUtils.mv(tmp_file, log_file)
|
|
70
|
+
say("Logs saved in `#{log_file.green}'")
|
|
71
|
+
rescue Bosh::Cli::DirectorError => e
|
|
72
|
+
err("Unable to download logs from director: #{e}")
|
|
73
|
+
ensure
|
|
74
|
+
FileUtils.rm_rf(tmp_file) if File.exists?(tmp_file)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
|
2
|
+
|
|
3
|
+
module Bosh::Cli::Command
|
|
4
|
+
class Maintenance < Base
|
|
5
|
+
include Bosh::Cli::VersionCalc
|
|
6
|
+
|
|
7
|
+
RELEASES_TO_KEEP = 2
|
|
8
|
+
STEMCELLS_TO_KEEP = 2
|
|
9
|
+
|
|
10
|
+
def cleanup
|
|
11
|
+
target_required
|
|
12
|
+
auth_required
|
|
13
|
+
|
|
14
|
+
releases_to_keep = RELEASES_TO_KEEP
|
|
15
|
+
stemcells_to_keep = STEMCELLS_TO_KEEP
|
|
16
|
+
|
|
17
|
+
release_wording = pluralize(releases_to_keep, "latest version")
|
|
18
|
+
stemcell_wording = pluralize(stemcells_to_keep, "latest version")
|
|
19
|
+
|
|
20
|
+
desc = <<-EOS.gsub(/^ */, "")
|
|
21
|
+
Cleanup command will attempt to delete old unused
|
|
22
|
+
release versions and stemcells from your currently
|
|
23
|
+
targeted director at #{target_name.green}.
|
|
24
|
+
|
|
25
|
+
Only #{release_wording.green} of each release
|
|
26
|
+
and #{stemcell_wording.green} of each stemcell will be kept.
|
|
27
|
+
|
|
28
|
+
Releases and stemcells that are in use will not be affected.
|
|
29
|
+
EOS
|
|
30
|
+
|
|
31
|
+
say("\n#{desc}\n")
|
|
32
|
+
|
|
33
|
+
err("Cleanup canceled") unless confirmed?
|
|
34
|
+
|
|
35
|
+
nl
|
|
36
|
+
cleanup_stemcells(stemcells_to_keep)
|
|
37
|
+
nl
|
|
38
|
+
cleanup_releases(releases_to_keep)
|
|
39
|
+
|
|
40
|
+
nl
|
|
41
|
+
say("Cleanup complete".green)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def cleanup_stemcells(n_to_keep)
|
|
47
|
+
stemcells_by_name = director.list_stemcells.inject({}) do |h, stemcell|
|
|
48
|
+
h[stemcell["name"]] ||= []
|
|
49
|
+
h[stemcell["name"]] << stemcell
|
|
50
|
+
h
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
delete_list = []
|
|
54
|
+
say("Deleting old stemcells")
|
|
55
|
+
|
|
56
|
+
stemcells_by_name.each_pair do |name, stemcells|
|
|
57
|
+
stemcells.sort! do |sc1, sc2|
|
|
58
|
+
version_cmp(sc1["version"], sc2["version"])
|
|
59
|
+
end
|
|
60
|
+
delete_list += stemcells[0...(-n_to_keep)]
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
if delete_list.size > 0
|
|
64
|
+
delete_list.each do |stemcell|
|
|
65
|
+
name, version = stemcell["name"], stemcell["version"]
|
|
66
|
+
desc = "#{name}/#{version}"
|
|
67
|
+
perform(desc) do
|
|
68
|
+
director.delete_stemcell(name, version, :quiet => true)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
else
|
|
72
|
+
say(" none found".yellow)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def cleanup_releases(n_to_keep)
|
|
77
|
+
delete_list = []
|
|
78
|
+
say("Deleting old release versions")
|
|
79
|
+
|
|
80
|
+
director.list_releases.each do |release|
|
|
81
|
+
name = release["name"]
|
|
82
|
+
versions = release["versions"].sort { |v1, v2| version_cmp(v1, v2) }
|
|
83
|
+
|
|
84
|
+
versions[0...(-n_to_keep)].each do |version|
|
|
85
|
+
delete_list << [name, version]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if delete_list.size > 0
|
|
90
|
+
delete_list.each do |name, version|
|
|
91
|
+
desc = "#{name}/#{version}"
|
|
92
|
+
perform(desc) do
|
|
93
|
+
director.delete_release(name, :force => false,
|
|
94
|
+
:version => version, :quiet => true)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
say(" none found".yellow)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def refresh(message)
|
|
103
|
+
say("\r", "")
|
|
104
|
+
say(" " * 80, "")
|
|
105
|
+
say("\r#{message}", "")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def perform(desc)
|
|
109
|
+
say(" #{desc.yellow.ljust(40)}", "")
|
|
110
|
+
say("IN PROGRESS...".yellow, "")
|
|
111
|
+
|
|
112
|
+
status, task_id = yield
|
|
113
|
+
responses = {
|
|
114
|
+
:done => "DELETED".green,
|
|
115
|
+
:non_trackable => "CANNOT TRACK".red,
|
|
116
|
+
:track_timeout => "TIMED OUT".red,
|
|
117
|
+
:error => "ERROR".red,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
refresh(" #{desc.yellow.ljust(40)}#{responses[status]}\n")
|
|
121
|
+
|
|
122
|
+
if status == :error
|
|
123
|
+
task = director.get_task(task_id)
|
|
124
|
+
say(" #{task["result"].red}")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
status == :done
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
end
|
|
131
|
+
end
|