bosh_cli 0.16
Sign up to get free protection for your applications and to get access to all the features.
- 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,24 @@
|
|
1
|
+
module Bosh::Cli::Command
|
2
|
+
class Ruby < Base
|
3
|
+
|
4
|
+
command :ruby_version do
|
5
|
+
usage "ruby version"
|
6
|
+
desc "Say ruby version"
|
7
|
+
route :ruby, :ruby_version
|
8
|
+
end
|
9
|
+
|
10
|
+
command :ruby_config do
|
11
|
+
usage "ruby config <string>"
|
12
|
+
desc "Query rbconfig"
|
13
|
+
route :ruby, :ruby_config
|
14
|
+
end
|
15
|
+
|
16
|
+
def ruby_version
|
17
|
+
say(RUBY_VERSION)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ruby_config(string)
|
21
|
+
say(::Config::CONFIG[string])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
Binary file
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
Monit conf goes here
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
Monit conf goes here
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
test
|
@@ -0,0 +1 @@
|
|
1
|
+
Monit conf goes here
|
Binary file
|
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
---
|
2
|
+
name: appcloud
|
3
|
+
version: 0.1
|
4
|
+
|
5
|
+
packages:
|
6
|
+
- name: stuff
|
7
|
+
version: 0.1.17
|
8
|
+
sha1: 6bbd4a12e3a59e10b96ecb9aac3d73ec4f819783
|
9
|
+
- name: mutator
|
10
|
+
version: 2.99.7
|
11
|
+
sha1: 86bd8b15562cde007f030a303fa64779af5fa4e7
|
12
|
+
|
13
|
+
jobs:
|
14
|
+
- cacher
|
15
|
+
- cleaner
|
16
|
+
- sweeper
|
17
|
+
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1 @@
|
|
1
|
+
STEMCELL
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require "rspec/core"
|
4
|
+
|
5
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
6
|
+
require "cli"
|
7
|
+
|
8
|
+
def spec_asset(filename)
|
9
|
+
File.expand_path(File.join(File.dirname(__FILE__), "assets", filename))
|
10
|
+
end
|
11
|
+
|
12
|
+
tmpdir = Dir.mktmpdir
|
13
|
+
ENV["TMPDIR"] = tmpdir
|
14
|
+
FileUtils.mkdir_p(tmpdir)
|
15
|
+
at_exit { FileUtils.rm_rf(tmpdir) }
|
16
|
+
|
17
|
+
RSpec.configure do |c|
|
18
|
+
c.before(:each) do
|
19
|
+
Bosh::Cli::Config.interactive = false
|
20
|
+
Bosh::Cli::Config.colorize = false
|
21
|
+
Bosh::Cli::Config.output = StringIO.new
|
22
|
+
end
|
23
|
+
|
24
|
+
c.color_enabled = true
|
25
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Bosh::Cli::Command::Base do
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@config = File.join(Dir.mktmpdir, "bosh_config")
|
9
|
+
@cache_dir = Dir.mktmpdir
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_config(object)
|
13
|
+
File.open(@config, "w") do |f|
|
14
|
+
f.write(YAML.dump(object))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def make_command(options = { })
|
19
|
+
Bosh::Cli::Command::Base.new({ :config => @config,
|
20
|
+
:cache_dir => @cache_dir }.merge(options))
|
21
|
+
end
|
22
|
+
|
23
|
+
it "can access configuration and respects options" do
|
24
|
+
add_config("target" => "localhost:8080", "deployment" => "test")
|
25
|
+
|
26
|
+
cmd = make_command(:verbose => true, :dry_run => true)
|
27
|
+
cmd.verbose?.should be_true
|
28
|
+
cmd.dry_run?.should be_true
|
29
|
+
cmd.target.should == "localhost:8080"
|
30
|
+
cmd.deployment.should == "test"
|
31
|
+
cmd.username.should == nil
|
32
|
+
cmd.password.should == nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "instantiates director when needed" do
|
36
|
+
add_config("target" => "localhost:8080", "deployment" => "test")
|
37
|
+
|
38
|
+
cmd = make_command()
|
39
|
+
cmd.director.director_uri.should == "localhost:8080"
|
40
|
+
end
|
41
|
+
|
42
|
+
it "can evaluate other commands" do
|
43
|
+
cmd = make_command
|
44
|
+
new_cmd = mock(Object)
|
45
|
+
|
46
|
+
Bosh::Cli::Command::Misc.should_receive(:new).and_return(new_cmd)
|
47
|
+
new_cmd.should_receive(:status).with(:arg1, :arg2)
|
48
|
+
|
49
|
+
cmd.run("misc", "status", :arg1, :arg2)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "can redirect to other commands " +
|
53
|
+
"(effectively exiting after running them)" do
|
54
|
+
cmd = make_command
|
55
|
+
new_cmd = mock(Object)
|
56
|
+
|
57
|
+
Bosh::Cli::Command::Misc.should_receive(:new).and_return(new_cmd)
|
58
|
+
new_cmd.should_receive(:status).with(:arg1, :arg2)
|
59
|
+
|
60
|
+
lambda {
|
61
|
+
cmd.redirect("misc", "status", :arg1, :arg2)
|
62
|
+
}.should raise_error(Bosh::Cli::GracefulExit,
|
63
|
+
"redirected to misc status arg1 arg2")
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Bosh::Cli::Command::Biff do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
# Let us test all private methods.
|
7
|
+
Bosh::Cli::Command::Biff.send(:public,
|
8
|
+
*Bosh::Cli::Command::Biff.private_instance_methods)
|
9
|
+
@biff = Bosh::Cli::Command::Biff.new
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
@biff.delete_temp_diff_files
|
14
|
+
end
|
15
|
+
|
16
|
+
it "throws an error when there is more than one subnet for default" do
|
17
|
+
config_file = spec_asset("biff/multiple_subnets_config.yml")
|
18
|
+
template_file = spec_asset("biff/network_only_template.erb")
|
19
|
+
@biff.stub!(:deployment).and_return(config_file)
|
20
|
+
lambda {
|
21
|
+
@biff.biff(template_file)
|
22
|
+
}.should raise_error(RuntimeError, "Biff doesn't know how to deal with " +
|
23
|
+
"anything other than one subnet in default")
|
24
|
+
end
|
25
|
+
|
26
|
+
it "throws an error when the gateway is anything other than the first ip" do
|
27
|
+
config_file = spec_asset("biff/bad_gateway_config.yml")
|
28
|
+
template_file = spec_asset("biff/network_only_template.erb")
|
29
|
+
@biff.stub!(:deployment).and_return(config_file)
|
30
|
+
lambda {
|
31
|
+
@biff.biff(template_file)
|
32
|
+
}.should raise_error(RuntimeError, "Biff only supports configurations " +
|
33
|
+
"where the gateway is the first IP (e.g. 172.31.196.1).")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "throws an error when the range is not specified in the config" do
|
37
|
+
config_file = spec_asset("biff/no_range_config.yml")
|
38
|
+
template_file = spec_asset("biff/network_only_template.erb")
|
39
|
+
@biff.stub!(:deployment).and_return(config_file)
|
40
|
+
lambda {
|
41
|
+
@biff.biff(template_file)
|
42
|
+
}.should raise_error(RuntimeError, "Biff requires each network to have " +
|
43
|
+
"range and dns entries.")
|
44
|
+
end
|
45
|
+
|
46
|
+
it "throws an error if there are no subnets" do
|
47
|
+
config_file = spec_asset("biff/no_subnet_config.yml")
|
48
|
+
template_file = spec_asset("biff/network_only_template.erb")
|
49
|
+
@biff.stub!(:deployment).and_return(config_file)
|
50
|
+
lambda {
|
51
|
+
@biff.biff(template_file)
|
52
|
+
}.should raise_error(RuntimeError, "You must have subnets in default")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "outputs the required yaml when the input does not contain it" do
|
56
|
+
config_file = spec_asset("biff/no_cc_config.yml")
|
57
|
+
template_file = spec_asset("biff/properties_template.erb")
|
58
|
+
@biff.stub!(:deployment).and_return(config_file)
|
59
|
+
|
60
|
+
@biff.should_receive(:say).with(
|
61
|
+
"Could not find properties.cc.srv_api_uri.").once
|
62
|
+
|
63
|
+
@biff.should_receive(:say).with("'#{template_file}' has it but " +
|
64
|
+
"'#{config_file}' does not.").once
|
65
|
+
|
66
|
+
# Cannot use this because 1.8.7 does not preserve Hash order, so this string
|
67
|
+
# can come back in any order.
|
68
|
+
@biff.should_receive(:say).with(/Add this to '':/).once
|
69
|
+
|
70
|
+
#@biff.should_receive(:say).once
|
71
|
+
|
72
|
+
@biff.should_receive(:say).with("There were 1 errors.").once
|
73
|
+
|
74
|
+
@biff.biff(template_file)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "correctly generates a file and reports when there are no differences" do
|
78
|
+
config_file = spec_asset("biff/good_simple_config.yml")
|
79
|
+
template_file = spec_asset("biff/good_simple_template.erb")
|
80
|
+
golden_file = spec_asset("biff/good_simple_golden_config.yml")
|
81
|
+
@biff.stub!(:deployment).and_return(config_file)
|
82
|
+
@biff.should_receive(:say).with("No differences.").once
|
83
|
+
|
84
|
+
@biff.biff(template_file)
|
85
|
+
@biff.template_output.should == File.read(golden_file)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "asks whether you would like to keep the new file" do
|
89
|
+
config_file = spec_asset("biff/ok_network_config.yml")
|
90
|
+
template_file = spec_asset("biff/network_only_template.erb")
|
91
|
+
@biff.stub!(:deployment).and_return(config_file)
|
92
|
+
@biff.should_receive(:agree).with(
|
93
|
+
"Would you like to keep the new version? [yn]").once.and_return(false)
|
94
|
+
|
95
|
+
@biff.biff(template_file)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "deletes temporary files" do
|
99
|
+
config_file = spec_asset("biff/ok_network_config.yml")
|
100
|
+
template_file = spec_asset("biff/network_only_template.erb")
|
101
|
+
@biff.stub!(:deployment).and_return(config_file)
|
102
|
+
@biff.should_receive(:agree).and_return(false)
|
103
|
+
# Twice because of after :each
|
104
|
+
@biff.should_receive(:delete_temp_diff_files).twice
|
105
|
+
@biff.biff(template_file)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "finds the object path" do
|
109
|
+
obj = { "path1" => {"path2" => {"path3" => 3}} }
|
110
|
+
@biff.find_in("path1.path2.path3", obj).should == 3
|
111
|
+
end
|
112
|
+
|
113
|
+
it "finds the object path in an array by the name key" do
|
114
|
+
obj = { "by_key" => 1, "arr" => [{"name" => "by_name"}]}
|
115
|
+
@biff.find_in("arr.by_name", obj).should == {"name" => "by_name"}
|
116
|
+
end
|
117
|
+
|
118
|
+
it "allows ip_range to take negative ranges" do
|
119
|
+
@biff.ip_helper = {
|
120
|
+
"default" => NetAddr::CIDR.create("192.168.1.0/22")
|
121
|
+
}
|
122
|
+
@biff.ip_range(-11..-2, "default").should == "192.168.3.245 - 192.168.3.254"
|
123
|
+
end
|
124
|
+
|
125
|
+
it "deletes all except one entry from a Hash" do
|
126
|
+
obj = { "by_key" => 1, "arr" => [{"name" => "by_name"}]}
|
127
|
+
@biff.delete_all_except(obj, "by_key").should == { "by_key" => 1 }
|
128
|
+
end
|
129
|
+
|
130
|
+
it "deletes all except one entry from a Array" do
|
131
|
+
obj = [ {"name" => "a"}, {"name" => "b"}, {"name" => "c"}]
|
132
|
+
@biff.delete_all_except(obj, "b").should == [ {"name" => "b"} ]
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
describe Bosh::Cli::Cache do
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@cache_dir = File.join(Dir.mktmpdir, "bosh_cache")
|
10
|
+
end
|
11
|
+
|
12
|
+
after :each do
|
13
|
+
FileUtils.rm_rf(@cache_dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "whines if cache directory turned out to be a file" do
|
17
|
+
FileUtils.touch(@cache_dir)
|
18
|
+
lambda {
|
19
|
+
Bosh::Cli::Cache.new(@cache_dir)
|
20
|
+
}.should raise_error(Bosh::Cli::CacheDirectoryError)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "creates cache directory" do
|
24
|
+
File.directory?(@cache_dir).should be_false
|
25
|
+
cache = Bosh::Cli::Cache.new(@cache_dir)
|
26
|
+
File.directory?(@cache_dir).should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "performs read/write in an expected way" do
|
30
|
+
cache = Bosh::Cli::Cache.new(@cache_dir)
|
31
|
+
cache.read("foo").should be_nil
|
32
|
+
cache.write("foo", "12321")
|
33
|
+
cache.read("foo").should == "12321"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,481 @@
|
|
1
|
+
# Copyright (c) 2009-2012 VMware, Inc.
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Bosh::Cli::Command::Base do
|
6
|
+
|
7
|
+
before :each do
|
8
|
+
@config = File.join(Dir.mktmpdir, "bosh_config")
|
9
|
+
@cache = File.join(Dir.mktmpdir, "bosh_cache")
|
10
|
+
@opts = { :config => @config, :cache_dir => @cache }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Bosh::Cli::Command::Misc do
|
14
|
+
|
15
|
+
before :each do
|
16
|
+
@cmd = Bosh::Cli::Command::Misc.new(@opts)
|
17
|
+
@cmd.stub!(:interactive?).and_return(false)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sets the target" do
|
21
|
+
@cmd.target.should == nil
|
22
|
+
@cmd.set_target("http://example.com:232")
|
23
|
+
@cmd.target.should == "http://example.com:232"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "normalizes target" do
|
27
|
+
@cmd.target.should == nil
|
28
|
+
@cmd.set_target("test")
|
29
|
+
@cmd.target.should == "http://test"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "respects director checks option when setting target" do
|
33
|
+
@cmd.options[:director_checks] = true
|
34
|
+
|
35
|
+
lambda {
|
36
|
+
mock_director = mock(Object)
|
37
|
+
mock_director.stub!(:get_status).and_raise(Bosh::Cli::DirectorError)
|
38
|
+
Bosh::Cli::Director.should_receive(:new).
|
39
|
+
with("http://test").and_return(mock_director)
|
40
|
+
@cmd.set_target("test")
|
41
|
+
}.should raise_error(Bosh::Cli::CliExit,
|
42
|
+
"Cannot talk to director at 'http://test', " +
|
43
|
+
"please set correct target")
|
44
|
+
|
45
|
+
@cmd.target.should == nil
|
46
|
+
|
47
|
+
mock_director = mock(Object)
|
48
|
+
mock_director.stub!(:get_status).and_return("name" => "ZB")
|
49
|
+
Bosh::Cli::Director.should_receive(:new).
|
50
|
+
with("http://test").and_return(mock_director)
|
51
|
+
@cmd.set_target("test")
|
52
|
+
@cmd.target.should == "http://test"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "supports named targets" do
|
56
|
+
@cmd.set_target("test", "mytarget")
|
57
|
+
@cmd.target.should == "http://test"
|
58
|
+
|
59
|
+
@cmd.set_target("foo", "myfoo")
|
60
|
+
|
61
|
+
@cmd.set_target("mytarget")
|
62
|
+
@cmd.target.should == "http://test"
|
63
|
+
|
64
|
+
@cmd.set_target("myfoo")
|
65
|
+
@cmd.target.should == "http://foo"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "logs user in" do
|
69
|
+
@cmd.set_target("test")
|
70
|
+
@cmd.login("user", "pass")
|
71
|
+
@cmd.logged_in?.should be_true
|
72
|
+
@cmd.username.should == "user"
|
73
|
+
@cmd.password.should == "pass"
|
74
|
+
end
|
75
|
+
|
76
|
+
it "logs user out" do
|
77
|
+
@cmd.set_target("test")
|
78
|
+
@cmd.login("user", "pass")
|
79
|
+
@cmd.logout
|
80
|
+
@cmd.logged_in?.should be_false
|
81
|
+
end
|
82
|
+
|
83
|
+
it "respects director checks option when logging in" do
|
84
|
+
@cmd.options[:director_checks] = true
|
85
|
+
|
86
|
+
mock_director = mock(Object)
|
87
|
+
mock_director.stub(:get_status).
|
88
|
+
and_return({ "user" => "user", "name" => "ZB" })
|
89
|
+
mock_director.stub(:authenticated?).and_return(true)
|
90
|
+
|
91
|
+
Bosh::Cli::Director.should_receive(:new).
|
92
|
+
with("http://test").and_return(mock_director)
|
93
|
+
@cmd.set_target("test")
|
94
|
+
|
95
|
+
Bosh::Cli::Director.should_receive(:new).
|
96
|
+
with("http://test", "user", "pass").and_return(mock_director)
|
97
|
+
|
98
|
+
@cmd.login("user", "pass")
|
99
|
+
@cmd.logged_in?.should be_true
|
100
|
+
@cmd.username.should == "user"
|
101
|
+
@cmd.password.should == "pass"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe Bosh::Cli::Command::Stemcell do
|
106
|
+
before :each do
|
107
|
+
@cmd = Bosh::Cli::Command::Stemcell.new(@opts)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "allows deleting the stemcell" do
|
111
|
+
mock_director = mock(Bosh::Cli::Director)
|
112
|
+
mock_director.should_receive(:delete_stemcell).with("foo", "123")
|
113
|
+
|
114
|
+
@cmd.stub!(:interactive?).and_return(false)
|
115
|
+
@cmd.stub!(:target).and_return("test")
|
116
|
+
@cmd.stub!(:username).and_return("user")
|
117
|
+
@cmd.stub!(:password).and_return("pass")
|
118
|
+
@cmd.stub!(:director).and_return(mock_director)
|
119
|
+
@cmd.delete("foo", "123")
|
120
|
+
end
|
121
|
+
|
122
|
+
it "needs confirmation to delete stemcell" do
|
123
|
+
mock_director = mock(Bosh::Cli::Director)
|
124
|
+
mock_director.should_not_receive(:delete_stemcell)
|
125
|
+
|
126
|
+
@cmd.stub!(:target).and_return("test")
|
127
|
+
@cmd.stub!(:username).and_return("user")
|
128
|
+
@cmd.stub!(:password).and_return("pass")
|
129
|
+
@cmd.stub!(:director).and_return(mock_director)
|
130
|
+
@cmd.stub!(:ask).and_return("")
|
131
|
+
@cmd.delete("foo", "123")
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe Bosh::Cli::Command::Deployment do
|
136
|
+
before :each do
|
137
|
+
@cmd = Bosh::Cli::Command::Deployment.new(@opts)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "allows deleting the deployment" do
|
141
|
+
mock_director = mock(Bosh::Cli::Director)
|
142
|
+
mock_director.should_receive(:delete_deployment).
|
143
|
+
with("foo", :force => false)
|
144
|
+
|
145
|
+
@cmd.stub!(:interactive?).and_return(false)
|
146
|
+
@cmd.stub!(:target).and_return("test")
|
147
|
+
@cmd.stub!(:username).and_return("user")
|
148
|
+
@cmd.stub!(:password).and_return("pass")
|
149
|
+
@cmd.stub!(:director).and_return(mock_director)
|
150
|
+
@cmd.delete("foo")
|
151
|
+
end
|
152
|
+
|
153
|
+
it "needs confirmation to delete deployment" do
|
154
|
+
mock_director = mock(Bosh::Cli::Director)
|
155
|
+
mock_director.should_not_receive(:delete_deployment)
|
156
|
+
|
157
|
+
@cmd.stub!(:target).and_return("test")
|
158
|
+
@cmd.stub!(:username).and_return("user")
|
159
|
+
@cmd.stub!(:password).and_return("pass")
|
160
|
+
@cmd.stub!(:director).and_return(mock_director)
|
161
|
+
@cmd.stub!(:ask).and_return("")
|
162
|
+
@cmd.delete("foo")
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
describe Bosh::Cli::Command::Release do
|
167
|
+
before :each do
|
168
|
+
@cmd = Bosh::Cli::Command::Release.new(@opts)
|
169
|
+
@cmd.stub!(:target).and_return("test")
|
170
|
+
@cmd.stub!(:username).and_return("user")
|
171
|
+
@cmd.stub!(:password).and_return("pass")
|
172
|
+
end
|
173
|
+
|
174
|
+
it "allows deleting the release (non-force)" do
|
175
|
+
mock_director = mock(Bosh::Cli::Director)
|
176
|
+
mock_director.should_receive(:delete_release).
|
177
|
+
with("foo", :force => false, :version => nil)
|
178
|
+
|
179
|
+
@cmd.stub!(:interactive?).and_return(false)
|
180
|
+
@cmd.stub!(:director).and_return(mock_director)
|
181
|
+
@cmd.delete("foo")
|
182
|
+
end
|
183
|
+
|
184
|
+
it "allows deleting the release (non-force)" do
|
185
|
+
mock_director = mock(Bosh::Cli::Director)
|
186
|
+
mock_director.should_receive(:delete_release).
|
187
|
+
with("foo", :force => true, :version => nil)
|
188
|
+
|
189
|
+
@cmd.stub!(:ask).and_return("yes")
|
190
|
+
@cmd.stub!(:director).and_return(mock_director)
|
191
|
+
@cmd.delete("foo", "--force")
|
192
|
+
end
|
193
|
+
|
194
|
+
it "allows deleting a particular release version (non-force)" do
|
195
|
+
mock_director = mock(Bosh::Cli::Director)
|
196
|
+
mock_director.should_receive(:delete_release).
|
197
|
+
with("foo", :force => false, :version => "42")
|
198
|
+
|
199
|
+
@cmd.stub!(:ask).and_return("yes")
|
200
|
+
@cmd.stub!(:director).and_return(mock_director)
|
201
|
+
@cmd.delete("foo", "42")
|
202
|
+
end
|
203
|
+
|
204
|
+
it "allows deleting a particular release version (non-force)" do
|
205
|
+
mock_director = mock(Bosh::Cli::Director)
|
206
|
+
mock_director.should_receive(:delete_release).
|
207
|
+
with("foo", :force => true, :version => "42")
|
208
|
+
|
209
|
+
@cmd.stub!(:ask).and_return("yes")
|
210
|
+
@cmd.stub!(:director).and_return(mock_director)
|
211
|
+
@cmd.delete("foo", "42", "--force")
|
212
|
+
end
|
213
|
+
|
214
|
+
it "requires confirmation on deleting release" do
|
215
|
+
mock_director = mock(Bosh::Cli::Director)
|
216
|
+
mock_director.should_not_receive(:delete_release)
|
217
|
+
@cmd.stub!(:director).and_return(mock_director)
|
218
|
+
@cmd.stub!(:ask).and_return("")
|
219
|
+
@cmd.delete("foo")
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
223
|
+
|
224
|
+
describe Bosh::Cli::Command::JobManagement do
|
225
|
+
before :each do
|
226
|
+
@manifest_path = spec_asset("deployment.MF")
|
227
|
+
@manifest_yaml = YAML.dump({ "name" => "foo" })
|
228
|
+
@cmd = Bosh::Cli::Command::JobManagement.new(@opts)
|
229
|
+
@cmd.stub!(:prepare_deployment_manifest).
|
230
|
+
with(:yaml => true).and_return(@manifest_yaml)
|
231
|
+
@cmd.stub!(:interactive?).and_return(false)
|
232
|
+
@cmd.stub!(:deployment).and_return(@manifest_path)
|
233
|
+
@cmd.stub!(:target).and_return("test.com")
|
234
|
+
@cmd.stub!(:target_name).and_return("dev2")
|
235
|
+
@cmd.stub!(:username).and_return("user")
|
236
|
+
@cmd.stub!(:password).and_return("pass")
|
237
|
+
@director = mock(Bosh::Cli::Director)
|
238
|
+
@cmd.stub!(:director).and_return(@director)
|
239
|
+
end
|
240
|
+
|
241
|
+
it "allows starting jobs" do
|
242
|
+
@director.should_receive(:change_job_state).
|
243
|
+
with("foo", @manifest_yaml, "dea", nil, "started")
|
244
|
+
@cmd.start_job("dea")
|
245
|
+
end
|
246
|
+
|
247
|
+
it "allows starting job instances" do
|
248
|
+
@director.should_receive(:change_job_state).
|
249
|
+
with("foo", @manifest_yaml, "dea", 3, "started")
|
250
|
+
@cmd.start_job("dea", 3)
|
251
|
+
end
|
252
|
+
|
253
|
+
it "allows stopping jobs" do
|
254
|
+
@director.should_receive(:change_job_state).
|
255
|
+
with("foo", @manifest_yaml, "dea", nil, "stopped")
|
256
|
+
@cmd.stop_job("dea")
|
257
|
+
end
|
258
|
+
|
259
|
+
it "allows stopping job instances" do
|
260
|
+
@director.should_receive(:change_job_state).
|
261
|
+
with("foo", @manifest_yaml, "dea", 3, "stopped")
|
262
|
+
@cmd.stop_job("dea", 3)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "allows restarting jobs" do
|
266
|
+
@director.should_receive(:change_job_state).
|
267
|
+
with("foo", @manifest_yaml, "dea", nil, "restart")
|
268
|
+
@cmd.restart_job("dea")
|
269
|
+
end
|
270
|
+
|
271
|
+
it "allows restart job instances" do
|
272
|
+
@director.should_receive(:change_job_state).
|
273
|
+
with("foo", @manifest_yaml, "dea", 3, "restart")
|
274
|
+
@cmd.restart_job("dea", 3)
|
275
|
+
end
|
276
|
+
|
277
|
+
it "allows recreating jobs" do
|
278
|
+
@director.should_receive(:change_job_state).
|
279
|
+
with("foo", @manifest_yaml, "dea", nil, "recreate")
|
280
|
+
@cmd.recreate_job("dea")
|
281
|
+
end
|
282
|
+
|
283
|
+
it "allows recreating job instances" do
|
284
|
+
@director.should_receive(:change_job_state).
|
285
|
+
with("foo", @manifest_yaml, "dea", 3, "recreate")
|
286
|
+
@cmd.recreate_job("dea", 3)
|
287
|
+
end
|
288
|
+
|
289
|
+
it "allows hard stop" do
|
290
|
+
@director.should_receive(:change_job_state).
|
291
|
+
with("foo", @manifest_yaml, "dea", 3, "detached")
|
292
|
+
@cmd.stop_job("dea", 3, "--hard")
|
293
|
+
end
|
294
|
+
|
295
|
+
it "allows soft stop (= regular stop)" do
|
296
|
+
@director.should_receive(:change_job_state).
|
297
|
+
with("foo", @manifest_yaml, "dea", 3, "stopped")
|
298
|
+
@cmd.stop_job("dea", 3, "--soft")
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
302
|
+
|
303
|
+
describe Bosh::Cli::Command::Blob do
|
304
|
+
before :each do
|
305
|
+
@cmd = Bosh::Cli::Command::Blob.new(@opts)
|
306
|
+
@release_dir = Dir.mktmpdir
|
307
|
+
@blob_dir = File.join(@release_dir, "blobs/")
|
308
|
+
@cmd.stub!(:work_dir).and_return(@release_dir)
|
309
|
+
@blobstore = mock("blobstore")
|
310
|
+
@cmd.stub!(:blobstore).and_return(@blobstore)
|
311
|
+
FileUtils.mkdir(@blob_dir)
|
312
|
+
end
|
313
|
+
|
314
|
+
after :each do
|
315
|
+
FileUtils.rm_rf(@release_dir)
|
316
|
+
end
|
317
|
+
|
318
|
+
it "refuse to run outside of the release directory" do
|
319
|
+
lambda {
|
320
|
+
@cmd.upload_blob("foo")
|
321
|
+
}.should raise_error(Bosh::Cli::CliExit,
|
322
|
+
"Sorry, your current directory doesn't " +
|
323
|
+
"look like release directory".red)
|
324
|
+
|
325
|
+
lambda {
|
326
|
+
@cmd.sync_blobs
|
327
|
+
}.should raise_error(Bosh::Cli::CliExit,
|
328
|
+
"Sorry, your current directory doesn't " +
|
329
|
+
"look like release directory".red)
|
330
|
+
|
331
|
+
lambda {
|
332
|
+
@cmd.blobs_info
|
333
|
+
}.should raise_error(Bosh::Cli::CliExit,
|
334
|
+
"Sorry, your current directory doesn't " +
|
335
|
+
"look like release directory".red)
|
336
|
+
end
|
337
|
+
|
338
|
+
it "refuse to upload blob outside of release/blobs" do
|
339
|
+
Dir.chdir(@release_dir) do
|
340
|
+
FileUtils.touch("test.tgz")
|
341
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
342
|
+
blob_path = Pathname.new(@release_dir).realpath.to_s
|
343
|
+
blobs_dir = Pathname.new(@blob_dir).realpath.to_s
|
344
|
+
lambda {
|
345
|
+
@cmd.upload_blob("test.tgz")
|
346
|
+
}.should raise_error(Bosh::Cli::CliExit,
|
347
|
+
"#{File.join(blob_path, "test.tgz")} is " +
|
348
|
+
"NOT under #{blobs_dir}/")
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
it "upload new blob to blobstore" do
|
353
|
+
Dir.chdir(@release_dir) do
|
354
|
+
FileUtils.mkdir("./blobs/test")
|
355
|
+
blob = FileUtils.touch("./blobs/test/test.tgz")
|
356
|
+
@cmd.should_receive(:get_blobs_index).and_return({})
|
357
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
358
|
+
@blobstore.should_receive(:create).and_return(2)
|
359
|
+
@cmd.upload_blob("./blobs/test/test.tgz")
|
360
|
+
YAML.load_file("blob_index.yml").should == {
|
361
|
+
"test/test.tgz" => {
|
362
|
+
"object_id" => 2,
|
363
|
+
"sha" => Digest::SHA1.file(blob.first).hexdigest
|
364
|
+
}
|
365
|
+
}
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
it "should skip upload if blob already exists" do
|
370
|
+
Dir.chdir(@release_dir) do
|
371
|
+
FileUtils.mkdir("./blobs/test")
|
372
|
+
blob = FileUtils.touch("./blobs/test/test.tgz")
|
373
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
374
|
+
@cmd.should_receive(:get_blobs_index).and_return('test/test.tgz' => {
|
375
|
+
"sha" => Digest::SHA1.file(blob.first).hexdigest,
|
376
|
+
"object_id" => 2
|
377
|
+
})
|
378
|
+
@blobstore.should_not_receive(:create)
|
379
|
+
@cmd.upload_blob("./blobs/test/test.tgz")
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
it "should ask user if blob was modified" do
|
384
|
+
Dir.chdir(@release_dir) do
|
385
|
+
FileUtils.mkdir("./blobs/test")
|
386
|
+
blob = FileUtils.touch("./blobs/test/test.tgz")
|
387
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
388
|
+
@cmd.should_receive(:get_blobs_index).and_return('test/test.tgz' => {
|
389
|
+
"sha" => 1,
|
390
|
+
"object_id" => 2
|
391
|
+
})
|
392
|
+
@cmd.should_receive(:ask).and_return("no")
|
393
|
+
@blobstore.should_not_receive(:create)
|
394
|
+
@cmd.upload_blob("./blobs/test/test.tgz")
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
it "should sync if file is not present" do
|
399
|
+
Dir.chdir(@release_dir) do
|
400
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
401
|
+
@cmd.should_receive(:get_blobs_index).and_return('test/test.tgz' => {
|
402
|
+
"sha" => 1,
|
403
|
+
"object_id" => 2
|
404
|
+
})
|
405
|
+
@cmd.should_receive(:fetch_blob).
|
406
|
+
with(File.join(@release_dir, "blobs", "test/test.tgz"),
|
407
|
+
{ "sha" => 1, "object_id" => 2 })
|
408
|
+
@cmd.sync_blobs
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
it "should not sync if the same file is present" do
|
413
|
+
Dir.chdir(@release_dir) do
|
414
|
+
FileUtils.mkdir("./blobs/test")
|
415
|
+
blob = FileUtils.touch("./blobs/test/test.tgz")
|
416
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
417
|
+
@cmd.should_receive(:get_blobs_index).and_return('test/test.tgz' => {
|
418
|
+
"sha" => Digest::SHA1.file(blob.first).hexdigest,
|
419
|
+
"object_id" => 2
|
420
|
+
})
|
421
|
+
@cmd.should_not_receive(:fetch_blob)
|
422
|
+
@cmd.sync_blobs
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
it "should ask user if blob sha is different" do
|
427
|
+
Dir.chdir(@release_dir) do
|
428
|
+
FileUtils.mkdir("./blobs/test")
|
429
|
+
blob = FileUtils.touch("./blobs/test/test.tgz")
|
430
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
431
|
+
@cmd.should_receive(:get_blobs_index).and_return('test/test.tgz' => {
|
432
|
+
"sha" => 1,
|
433
|
+
"object_id" => 2
|
434
|
+
})
|
435
|
+
@cmd.should_receive(:ask).and_return("")
|
436
|
+
@cmd.should_not_receive(:fetch_blob)
|
437
|
+
@cmd.sync_blobs
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
it "reports untracked blobs" do
|
442
|
+
Dir.chdir(@release_dir) do
|
443
|
+
FileUtils.mkdir("./blobs/test")
|
444
|
+
FileUtils.touch("./blobs/test/test.tgz")
|
445
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
446
|
+
@cmd.should_receive(:get_blobs_index).and_return({})
|
447
|
+
@cmd.should_receive(:say).
|
448
|
+
with("\nNew blobs ('bosh upload blob' to add): ".green)
|
449
|
+
@cmd.should_receive(:say).with("test/test.tgz")
|
450
|
+
@cmd.blobs_info
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
it "reports modified blobs" do
|
455
|
+
Dir.chdir(@release_dir) do
|
456
|
+
FileUtils.mkdir("./blobs/test")
|
457
|
+
FileUtils.touch("./blobs/test/test.tgz")
|
458
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
459
|
+
@cmd.should_receive(:get_blobs_index).
|
460
|
+
and_return({ "test/test.tgz" => { "sha" => 1, "object_id" => 2}})
|
461
|
+
@cmd.should_receive(:say).
|
462
|
+
with("\nModified blobs ('bosh upload blob' to update): ".green)
|
463
|
+
@cmd.should_receive(:say).with("test/test.tgz")
|
464
|
+
@cmd.blobs_info
|
465
|
+
end
|
466
|
+
end
|
467
|
+
|
468
|
+
it "reports unsynced blobs" do
|
469
|
+
Dir.chdir(@release_dir) do
|
470
|
+
@cmd.should_receive(:check_if_blobs_supported).and_return(true)
|
471
|
+
@cmd.should_receive(:get_blobs_index).
|
472
|
+
and_return({ "test/test.tgz" => { "sha" => 1, "object_id" => 2}})
|
473
|
+
@cmd.should_receive(:say).
|
474
|
+
with("\nMissing blobs ('bosh sync blobs' to fetch) : ".green)
|
475
|
+
@cmd.should_receive(:say).with("test/test.tgz")
|
476
|
+
@cmd.blobs_info
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
|
481
|
+
end
|