bosh_cli 0.16

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/README +4 -0
  2. data/Rakefile +55 -0
  3. data/bin/bosh +17 -0
  4. data/lib/cli.rb +76 -0
  5. data/lib/cli/cache.rb +44 -0
  6. data/lib/cli/changeset_helper.rb +142 -0
  7. data/lib/cli/command_definition.rb +52 -0
  8. data/lib/cli/commands/base.rb +245 -0
  9. data/lib/cli/commands/biff.rb +300 -0
  10. data/lib/cli/commands/blob.rb +125 -0
  11. data/lib/cli/commands/cloudcheck.rb +169 -0
  12. data/lib/cli/commands/deployment.rb +147 -0
  13. data/lib/cli/commands/job.rb +42 -0
  14. data/lib/cli/commands/job_management.rb +117 -0
  15. data/lib/cli/commands/log_management.rb +81 -0
  16. data/lib/cli/commands/maintenance.rb +131 -0
  17. data/lib/cli/commands/misc.rb +240 -0
  18. data/lib/cli/commands/package.rb +112 -0
  19. data/lib/cli/commands/property_management.rb +125 -0
  20. data/lib/cli/commands/release.rb +469 -0
  21. data/lib/cli/commands/ssh.rb +271 -0
  22. data/lib/cli/commands/stemcell.rb +184 -0
  23. data/lib/cli/commands/task.rb +213 -0
  24. data/lib/cli/commands/user.rb +28 -0
  25. data/lib/cli/commands/vms.rb +53 -0
  26. data/lib/cli/config.rb +154 -0
  27. data/lib/cli/core_ext.rb +145 -0
  28. data/lib/cli/dependency_helper.rb +62 -0
  29. data/lib/cli/deployment_helper.rb +263 -0
  30. data/lib/cli/deployment_manifest_compiler.rb +28 -0
  31. data/lib/cli/director.rb +633 -0
  32. data/lib/cli/director_task.rb +64 -0
  33. data/lib/cli/errors.rb +48 -0
  34. data/lib/cli/event_log_renderer.rb +351 -0
  35. data/lib/cli/job_builder.rb +226 -0
  36. data/lib/cli/package_builder.rb +254 -0
  37. data/lib/cli/packaging_helper.rb +248 -0
  38. data/lib/cli/release.rb +176 -0
  39. data/lib/cli/release_builder.rb +215 -0
  40. data/lib/cli/release_compiler.rb +178 -0
  41. data/lib/cli/release_tarball.rb +272 -0
  42. data/lib/cli/runner.rb +771 -0
  43. data/lib/cli/stemcell.rb +83 -0
  44. data/lib/cli/task_log_renderer.rb +40 -0
  45. data/lib/cli/templates/help_message.erb +75 -0
  46. data/lib/cli/validation.rb +42 -0
  47. data/lib/cli/version.rb +7 -0
  48. data/lib/cli/version_calc.rb +48 -0
  49. data/lib/cli/versions_index.rb +126 -0
  50. data/lib/cli/yaml_helper.rb +62 -0
  51. data/spec/assets/biff/bad_gateway_config.yml +28 -0
  52. data/spec/assets/biff/good_simple_config.yml +63 -0
  53. data/spec/assets/biff/good_simple_golden_config.yml +63 -0
  54. data/spec/assets/biff/good_simple_template.erb +69 -0
  55. data/spec/assets/biff/multiple_subnets_config.yml +40 -0
  56. data/spec/assets/biff/network_only_template.erb +34 -0
  57. data/spec/assets/biff/no_cc_config.yml +27 -0
  58. data/spec/assets/biff/no_range_config.yml +27 -0
  59. data/spec/assets/biff/no_subnet_config.yml +16 -0
  60. data/spec/assets/biff/ok_network_config.yml +30 -0
  61. data/spec/assets/biff/properties_template.erb +6 -0
  62. data/spec/assets/deployment.MF +0 -0
  63. data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
  64. data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
  65. data/spec/assets/release/jobs/cacher.tgz +0 -0
  66. data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
  67. data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
  68. data/spec/assets/release/jobs/cacher/job.MF +6 -0
  69. data/spec/assets/release/jobs/cacher/monit +1 -0
  70. data/spec/assets/release/jobs/cleaner.tgz +0 -0
  71. data/spec/assets/release/jobs/cleaner/job.MF +4 -0
  72. data/spec/assets/release/jobs/cleaner/monit +1 -0
  73. data/spec/assets/release/jobs/sweeper.tgz +0 -0
  74. data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
  75. data/spec/assets/release/jobs/sweeper/job.MF +5 -0
  76. data/spec/assets/release/jobs/sweeper/monit +1 -0
  77. data/spec/assets/release/packages/mutator.tar.gz +0 -0
  78. data/spec/assets/release/packages/stuff.tgz +0 -0
  79. data/spec/assets/release/release.MF +17 -0
  80. data/spec/assets/release_invalid_checksum.tgz +0 -0
  81. data/spec/assets/release_invalid_jobs.tgz +0 -0
  82. data/spec/assets/release_no_name.tgz +0 -0
  83. data/spec/assets/release_no_version.tgz +0 -0
  84. data/spec/assets/stemcell/image +1 -0
  85. data/spec/assets/stemcell/stemcell.MF +6 -0
  86. data/spec/assets/stemcell_invalid_mf.tgz +0 -0
  87. data/spec/assets/stemcell_no_image.tgz +0 -0
  88. data/spec/assets/valid_release.tgz +0 -0
  89. data/spec/assets/valid_stemcell.tgz +0 -0
  90. data/spec/spec_helper.rb +25 -0
  91. data/spec/unit/base_command_spec.rb +66 -0
  92. data/spec/unit/biff_spec.rb +135 -0
  93. data/spec/unit/cache_spec.rb +36 -0
  94. data/spec/unit/cli_commands_spec.rb +481 -0
  95. data/spec/unit/config_spec.rb +139 -0
  96. data/spec/unit/core_ext_spec.rb +77 -0
  97. data/spec/unit/dependency_helper_spec.rb +52 -0
  98. data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
  99. data/spec/unit/director_spec.rb +511 -0
  100. data/spec/unit/director_task_spec.rb +48 -0
  101. data/spec/unit/event_log_renderer_spec.rb +171 -0
  102. data/spec/unit/hash_changeset_spec.rb +73 -0
  103. data/spec/unit/job_builder_spec.rb +454 -0
  104. data/spec/unit/package_builder_spec.rb +567 -0
  105. data/spec/unit/release_builder_spec.rb +65 -0
  106. data/spec/unit/release_spec.rb +66 -0
  107. data/spec/unit/release_tarball_spec.rb +33 -0
  108. data/spec/unit/runner_spec.rb +140 -0
  109. data/spec/unit/ssh_spec.rb +78 -0
  110. data/spec/unit/stemcell_spec.rb +17 -0
  111. data/spec/unit/version_calc_spec.rb +27 -0
  112. data/spec/unit/versions_index_spec.rb +132 -0
  113. metadata +338 -0
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::ReleaseBuilder do
6
+
7
+ before(:each) do
8
+ @release_dir = Dir.mktmpdir
9
+ FileUtils.mkdir_p(File.join(@release_dir, "config"))
10
+ @release = Bosh::Cli::Release.new(@release_dir)
11
+ end
12
+
13
+ def new_builder
14
+ Bosh::Cli::ReleaseBuilder.new(@release, [], [])
15
+ end
16
+
17
+ it "uses version 1 if no previous releases have been created" do
18
+ new_builder.version.should == 1
19
+ end
20
+
21
+ it "builds a release" do
22
+ builder = new_builder
23
+ builder.build
24
+
25
+ expected_tarball_path = File.join(@release_dir,
26
+ "dev_releases", "bosh_release-1.tgz")
27
+
28
+ builder.tarball_path.should == expected_tarball_path
29
+ File.file?(expected_tarball_path).should be_true
30
+ end
31
+
32
+ it "doesn't build a new release if nothing has changed" do
33
+ builder = new_builder
34
+ builder.build
35
+ builder.build
36
+
37
+ File.file?(File.join(@release_dir, "dev_releases", "bosh_release-1.tgz")).
38
+ should be_true
39
+ File.file?(File.join(@release_dir, "dev_releases", "bosh_release-2.tgz")).
40
+ should be_false
41
+ end
42
+
43
+ it "has a list of jobs affected by building this release" do
44
+ job1 = mock(:job, :new_version? => true,
45
+ :packages => ["bar", "baz"], :name => "job1")
46
+ job2 = mock(:job, :new_version? => false,
47
+ :packages => ["foo", "baz"], :name => "job2")
48
+ job3 = mock(:job, :new_version? => false,
49
+ :packages => ["baz", "zb"], :name => "job3")
50
+ job4 = mock(:job, :new_version? => false,
51
+ :packages => ["bar", "baz"], :name => "job4")
52
+
53
+ package1 = mock(:package, :name => "foo", :new_version? => true)
54
+ package2 = mock(:package, :name => "bar", :new_version? => false)
55
+ package3 = mock(:package, :name => "baz", :new_version? => false)
56
+ package4 = mock(:package, :name => "zb", :new_version? => true)
57
+
58
+ builder = Bosh::Cli::ReleaseBuilder.new(@release,
59
+ [package1, package2,
60
+ package3, package4],
61
+ [job1, job2, job3, job4])
62
+ builder.affected_jobs.should =~ [job1, job2, job3]
63
+ end
64
+
65
+ end
@@ -0,0 +1,66 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::Release do
6
+
7
+ before :each do
8
+ @release_dir = Dir.mktmpdir
9
+ FileUtils.mkdir_p(File.join(@release_dir, "config"))
10
+ end
11
+
12
+ def new_release(dir)
13
+ Bosh::Cli::Release.new(@release_dir)
14
+ end
15
+
16
+ it "persists release attributes" do
17
+ r = new_release(@dir)
18
+
19
+ r.dev_name.should be_nil
20
+ r.final_name.should be_nil
21
+ r.min_cli_version.should be_nil
22
+ r.latest_release_filename.should be_nil
23
+
24
+ r.dev_name = "dev-release"
25
+ r.final_name = "prod-release"
26
+ r.min_cli_version = "0.5.2"
27
+ r.latest_release_filename = "foobar"
28
+ r.save_config
29
+
30
+ r2 = new_release(@release_dir)
31
+ r.dev_name.should == "dev-release"
32
+ r.final_name.should == "prod-release"
33
+ r.min_cli_version.should == "0.5.2"
34
+ r.latest_release_filename.should == "foobar"
35
+ end
36
+
37
+ it "has attributes persisted in bosh user config" do
38
+ r = new_release(@release_dir)
39
+ r.dev_name = "dev-release"
40
+ r.final_name = "prod-release"
41
+ r.min_cli_version = "0.5.2"
42
+ r.save_config
43
+
44
+ FileUtils.rm_rf(File.join(@release_dir, "config", "dev.yml"))
45
+
46
+ r = new_release(@release_dir)
47
+ r.dev_name.should be_nil
48
+ r.final_name.should == "prod-release"
49
+ r.min_cli_version.should == "0.5.2"
50
+ end
51
+
52
+ it "has attributes persisted in public release config" do
53
+ r = new_release(@release_dir)
54
+ r.dev_name = "dev-release"
55
+ r.final_name = "prod-release"
56
+ r.min_cli_version = "0.5.2"
57
+ r.save_config
58
+
59
+ FileUtils.rm_rf(File.join(@release_dir, "config", "final.yml"))
60
+
61
+ r = new_release(@release_dir)
62
+ r.dev_name.should == "dev-release"
63
+ r.final_name.should be_nil
64
+ r.min_cli_version.should be_nil
65
+ end
66
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::ReleaseTarball do
6
+
7
+ describe "verifying a release" do
8
+ it "verifies and reports a valid release" do
9
+ tarball = Bosh::Cli::ReleaseTarball.new(spec_asset("valid_release.tgz"))
10
+ tarball.should be_valid
11
+ end
12
+
13
+ it "verifies repacked release if appropriate option is set" do
14
+ tarball = Bosh::Cli::ReleaseTarball.new(spec_asset("valid_release.tgz"))
15
+ remote_release = {
16
+ "packages" => [{ "name" => "mutator", "version" => "2.99.7" }],
17
+ "jobs" => [{ "name" => "cacher", "version" => "1" },
18
+ { "name" => "sweeper", "version" => "1" }]
19
+ }
20
+ repacked_tarball_path = tarball.repack(remote_release)
21
+
22
+ tarball.skipped.should == 2
23
+
24
+ repacked_tarball = Bosh::Cli::ReleaseTarball.new(repacked_tarball_path)
25
+ repacked_tarball.valid?.should be_false
26
+ repacked_tarball.reset_validation
27
+ repacked_tarball.valid?(:allow_sparse => true).should be_true
28
+ end
29
+ end
30
+
31
+ # TODO: add whining on potential errors
32
+
33
+ end
@@ -0,0 +1,140 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::Runner do
6
+
7
+ before(:all) do
8
+ @out = StringIO.new
9
+ Bosh::Cli::Config.output = @out
10
+ end
11
+
12
+ def test_cmd(args, namespace, action, cmd_args = [])
13
+ runner = Bosh::Cli::Runner.new(args)
14
+ runner.prepare
15
+ runner.dispatch
16
+
17
+ runner.namespace.should == namespace
18
+ runner.action.should == action
19
+ runner.args.should == cmd_args
20
+ end
21
+
22
+ it "has a set of default global options" do
23
+ runner = Bosh::Cli::Runner.new(["do", "some", "stuff"])
24
+ runner.parse_options!
25
+ runner.options[:verbose].should == nil
26
+ runner.options[:colorize].should == true
27
+ runner.options[:director_checks].should == true
28
+ runner.options[:quiet].should == nil
29
+ runner.options[:non_interactive].should == nil
30
+ end
31
+
32
+ it "allows overriding global options" do
33
+ runner = Bosh::Cli::Runner.new(["--verbose", "--no-color",
34
+ "--skip-director-checks", "--quiet",
35
+ "--non-interactive", "release",
36
+ "upload", "/path"])
37
+ runner.parse_options!
38
+ runner.options[:verbose].should == true
39
+ runner.options[:colorize].should == false
40
+ runner.options[:director_checks].should == false
41
+ runner.options[:quiet].should == true
42
+ runner.options[:non_interactive].should == true
43
+ end
44
+
45
+ it "dispatches commands to appropriate methods (nu school)" do
46
+ test_cmd(["version"], :misc, :version)
47
+ test_cmd(["status"], :misc, :status)
48
+ test_cmd(["target"], :misc, :show_target)
49
+ test_cmd(["target", "test"], :misc, :set_target, ["test"])
50
+ test_cmd(["target", "test", "alias"], :misc, :set_target, ["test", "alias"])
51
+ test_cmd(["deploy"], :deployment, :perform)
52
+ test_cmd(["deployment"], :deployment, :show_current)
53
+ test_cmd(["deployment", "test"], :deployment, :set_current, ["test"])
54
+
55
+ test_cmd(["delete", "deployment", "foo"], :deployment, :delete, ["foo"])
56
+ test_cmd(["delete", "stemcell", "a", "1"], :stemcell, :delete, ["a", "1"])
57
+ test_cmd(["delete", "release", "a"], :release, :delete, ["a"])
58
+ test_cmd(["delete", "release", "a", "--force"],
59
+ :release, :delete, ["a", "--force"])
60
+ test_cmd(["delete", "release", "a", "2.2.1", "--force"],
61
+ :release, :delete, ["a", "2.2.1", "--force"])
62
+
63
+ test_cmd(["create", "user", "admin"], :user, :create, ["admin"])
64
+ test_cmd(["create", "user", "admin", "12321"],
65
+ :user, :create, ["admin", "12321"])
66
+ test_cmd(["create", "release"], :release, :create)
67
+ test_cmd(["reset", "release"], :release, :reset)
68
+ test_cmd(["create", "package", "bla"], :package, :create, ["bla"])
69
+
70
+ test_cmd(["login", "admin", "12321"], :misc, :login, ["admin", "12321"])
71
+ test_cmd(["logout"], :misc, :logout)
72
+ test_cmd(["purge"], :misc, :purge_cache)
73
+
74
+ test_cmd(["init", "release"], :release, :init)
75
+ test_cmd(["init", "release", "/path"], :release, :init, ["/path"])
76
+
77
+ test_cmd(["upload", "release", "/path"], :release, :upload, ["/path"])
78
+ test_cmd(["upload", "release"], :release, :upload)
79
+ test_cmd(["upload", "stemcell", "/path"], :stemcell, :upload, ["/path"])
80
+
81
+ test_cmd(["generate", "package", "foo"], :package, :generate, ["foo"])
82
+ test_cmd(["generate", "job", "baz"], :job, :generate, ["baz"])
83
+
84
+ test_cmd(["verify", "release", "/path"], :release, :verify, ["/path"])
85
+ test_cmd(["verify", "stemcell", "/path"], :stemcell, :verify, ["/path"])
86
+
87
+ test_cmd(["stemcells"], :stemcell, :list)
88
+ test_cmd(["releases"], :release, :list)
89
+ test_cmd(["deployments"], :deployment, :list)
90
+
91
+ test_cmd(["tasks"], :task, :list_running)
92
+ test_cmd(["task", "500"], :task, :track, ["500"])
93
+ test_cmd(["tasks"], :task, :list_running)
94
+ test_cmd(["tasks", "recent"], :task, :list_recent)
95
+ test_cmd(["tasks", "recent", "42"], :task, :list_recent, ["42"])
96
+ end
97
+
98
+ it "cancels running task and quits when ctrl-c is issued " +
99
+ "and user agrees to quit" do
100
+ runner = Bosh::Cli::Runner.new(["any", "command"])
101
+ runner.runner = {}
102
+ runner.runner.stub(:task_running?).and_return(true)
103
+ runner.stub(:kill_current_task?).and_return(true)
104
+ runner.runner.should_receive(:cancel_current_task).once
105
+ lambda { runner.handle_ctrl_c }.should raise_error SystemExit
106
+ end
107
+
108
+ it "quits when ctrl-c is issued and there is no task running" do
109
+ runner = Bosh::Cli::Runner.new(["any", "command"])
110
+ runner.runner = {}
111
+ runner.runner.stub(:task_running?).and_return(false)
112
+ lambda { runner.handle_ctrl_c }.should raise_error SystemExit
113
+ end
114
+
115
+ it "doesn't quit when user issues ctrl-c but does not want to quit" do
116
+ runner = Bosh::Cli::Runner.new(["any", "command"])
117
+ runner.runner = {}
118
+ runner.runner.stub(:task_running?).and_return(true)
119
+ runner.stub(:kill_current_task?).and_return(false)
120
+ lambda { runner.handle_ctrl_c }.should_not raise_error SystemExit
121
+ end
122
+
123
+ it "loads custom plugins" do
124
+ plugin_path = spec_asset("plugins")
125
+ $:.unshift(plugin_path)
126
+
127
+ begin
128
+ test_cmd(["banner", "foo"], :echo, :banner, ["foo"])
129
+ test_cmd(["say", "bar"], :echo, :say_color, ["bar"])
130
+ test_cmd(["say", "baz", "--color", "red"],
131
+ :echo, :say_color, ["baz", "--color", "red"])
132
+
133
+ test_cmd(["ruby", "version"], :ruby, :ruby_version)
134
+ test_cmd(["ruby", "config", "arch"], :ruby, :ruby_config, ["arch"])
135
+ ensure
136
+ $:.shift.should == plugin_path
137
+ end
138
+ end
139
+
140
+ end
@@ -0,0 +1,78 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::Command::Base do
6
+
7
+ before :all do
8
+ @public_key = File.join(Dir.mktmpdir, "public_key")
9
+ File.open(@public_key, "w+") do |f|
10
+ f.write("PUBLIC_KEY")
11
+ end
12
+ end
13
+
14
+ describe Bosh::Cli::Command::Ssh do
15
+ it "should get the public key" do
16
+ ssh = Bosh::Cli::Command::Ssh.new
17
+ public_key = ssh.get_public_key("public_key" => @public_key)
18
+ public_key.should == "PUBLIC_KEY"
19
+ end
20
+
21
+ it "should get the public key from users home directory " +
22
+ "or raise exception" do
23
+ ssh = Bosh::Cli::Command::Ssh.new
24
+ public_key = nil
25
+ begin
26
+ public_key = ssh.get_public_key({})
27
+ rescue Bosh::Cli::CliExit
28
+ public_key = "SOMETHING"
29
+ end
30
+ public_key.should_not be_nil
31
+ end
32
+
33
+ it "should contact director to setup ssh on the job" do
34
+ mock_director = mock(Object)
35
+ mock_director.stub(:setup_ssh).and_return([{ "status" => "success",
36
+ "ip" => "127.0.0.1" }])
37
+ mock_director.stub(:cleanup_ssh)
38
+ Bosh::Cli::Director.should_receive(:new).and_return(mock_director)
39
+ ssh = Bosh::Cli::Command::Ssh.new
40
+ ssh.stub(:prepare_deployment_manifest).and_return("test")
41
+ ssh.stub(:cleanup_ssh)
42
+ ssh.setup_ssh("dea", 0, "temp_pass",
43
+ { "public_key" => @public_key }) do |results, user|
44
+ results.each do |result|
45
+ result["status"].should == "success"
46
+ result["ip"].should == "127.0.0.1"
47
+ end
48
+ end
49
+ end
50
+
51
+ it "should try to setup interactive shell when a job index is given" do
52
+ ssh = Bosh::Cli::Command::Ssh.new
53
+ @interactive_shell = false
54
+ @execute_command = false
55
+ ssh.stub(:setup_interactive_shell) { @interactive_shell = true }
56
+ ssh.stub(:execute_command) { @execute_command = true }
57
+ ssh.shell("dea", "0")
58
+ @interactive_shell.should == true && @execute_command.should == false
59
+ end
60
+
61
+ it "should fail to setup interactive shell when a job index is not given" do
62
+ ssh = Bosh::Cli::Command::Ssh.new
63
+ lambda {
64
+ ssh.shell("dea")
65
+ }.should raise_error(Bosh::Cli::CliExit, "Please specify a job index")
66
+ end
67
+
68
+ it "should try to execute given command remotely" do
69
+ ssh = Bosh::Cli::Command::Ssh.new
70
+ @interactive_shell = false
71
+ @execute_command = false
72
+ ssh.stub(:setup_interactive_shell) { @interactive_shell = true }
73
+ ssh.stub(:execute_command) { @execute_command = true }
74
+ ssh.shell("dea", "ls -l")
75
+ @interactive_shell.should == false && @execute_command.should == true
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,17 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::Stemcell do
6
+
7
+ describe "verifying a stemcell" do
8
+ it "verifies and reports a valid stemcell" do
9
+ sc = Bosh::Cli::Stemcell.new(spec_asset("valid_stemcell.tgz"),
10
+ Bosh::Cli::Cache.new(Dir.mktmpdir))
11
+ sc.should be_valid
12
+ end
13
+ end
14
+
15
+ # TODO: add whining on potential errors
16
+
17
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::VersionCalc do
6
+
7
+ before :each do
8
+ @obj = Object.new.extend(Bosh::Cli::VersionCalc)
9
+ end
10
+
11
+ it "can compare versions" do
12
+ @obj.version_cmp("1.2", "1.2").should == 0
13
+ @obj.version_cmp("1.3", "1.2").should == 1
14
+ @obj.version_cmp("0.1.7", "0.9.2").should == -1
15
+ @obj.version_cmp("0.1.7.5", "0.1.7").should == 1
16
+ @obj.version_cmp("0.1.7.4.9.9", "0.1.7.5").should == -1
17
+
18
+ @obj.version_cmp(10, 11).should == -1
19
+ @obj.version_cmp(43, 42).should == 1
20
+ @obj.version_cmp(7, 7).should == 0
21
+
22
+ @obj.version_cmp("10.9-dev", "10.10-dev").should == -1
23
+ @obj.version_cmp("0.2.3", "0.2.3.0.8").should == -1
24
+ @obj.version_cmp("0.2.3-dev", "0.2.3.0.3-dev").should == -1
25
+ end
26
+
27
+ end
@@ -0,0 +1,132 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ require "spec_helper"
4
+
5
+ describe Bosh::Cli::VersionsIndex do
6
+
7
+ before :each do
8
+ @dir = Dir.mktmpdir
9
+ @index_file = File.join(@dir, "index.yml")
10
+ @index = Bosh::Cli::VersionsIndex.new(@dir)
11
+ end
12
+
13
+ after :each do
14
+ FileUtils.rm_rf(@dir)
15
+ end
16
+
17
+ it "only creates directory structure on writes to index" do
18
+ File.exists?(@index_file).should be_false
19
+ @index.version_exists?(1).should be_false
20
+ @index["deadbeef"].should be_nil
21
+ @index.latest_version.should be_nil
22
+ File.exists?(@index_file).should be_false
23
+
24
+ @index.add_version("deadcafe", { "version" => 2 }, "payload2")
25
+ File.exists?(@index_file).should be_true
26
+ end
27
+
28
+ it "chokes on malformed index file" do
29
+ File.open(@index_file, "w") { |f| f.write("deadbeef") }
30
+
31
+ lambda {
32
+ @index = Bosh::Cli::VersionsIndex.new(@dir)
33
+ }.should raise_error(Bosh::Cli::InvalidIndex,
34
+ "Invalid versions index data type, " +
35
+ "String given, Hash expected")
36
+ end
37
+
38
+ it "doesn't choke on empty index file" do
39
+ File.open(@index_file, "w") { |f| f.write("") }
40
+ @index = Bosh::Cli::VersionsIndex.new(@dir)
41
+ @index.latest_version.should be_nil
42
+ end
43
+
44
+ it "can be used to add versioned payloads to index" do
45
+ item1 = { "a" => 1, "b" => 2, "version" => 1 }
46
+ item2 = { "a" => 3, "b" => 4, "version" => 2 }
47
+ @index.add_version("deadbeef", item1, "payload1")
48
+ @index.add_version("deadcafe", item2, "payload2")
49
+
50
+ @index.latest_version.should == 2
51
+ @index["deadbeef"].should ==
52
+ item1.merge("sha1" => Digest::SHA1.hexdigest("payload1"))
53
+ @index["deadcafe"].should ==
54
+ item2.merge("sha1" => Digest::SHA1.hexdigest("payload2"))
55
+ @index.version_exists?(1).should be_true
56
+ @index.version_exists?(2).should be_true
57
+ @index.version_exists?(3).should be_false
58
+
59
+ @index.filename(1).should == File.join(@dir, "1.tgz")
60
+ @index.filename(2).should == File.join(@dir, "2.tgz")
61
+ end
62
+
63
+ it "you shall not pass without version" do
64
+ item_noversion = { "a" => 1, "b" => 2 }
65
+ lambda {
66
+ @index.add_version("deadbeef", item_noversion, "payload1")
67
+ }.should raise_error(Bosh::Cli::InvalidIndex,
68
+ "Cannot save index entry without knowing its version")
69
+ end
70
+
71
+ it "latest version only gets updated if it's greater than current latest" do
72
+ item1 = { "a" => 1, "b" => 2, "version" => 1 }
73
+ item2 = { "a" => 3, "b" => 4, "version" => 2 }
74
+ item3 = { "a" => 3, "b" => 4, "version" => 3 }
75
+
76
+ @index.add_version("deadbeef", item1, "payload1")
77
+ @index.add_version("deadcafe", item2, "payload2")
78
+ @index.latest_version.should == 2
79
+ @index.add_version("addedface", item3, "payload2")
80
+ @index.latest_version.should == 3
81
+ @index.add_version("facedbeef", item1.merge("version" => "1.5"), "payload3")
82
+ @index.latest_version.should == 3
83
+ end
84
+
85
+ it "supports dev versions and proper version comparison when updating latest version" do
86
+ item1 = { "a" => 1, "b" => 2, "version" => "1.9-dev" }
87
+ item2 = { "a" => 3, "b" => 4, "version" => "1.8-dev" }
88
+ item3 = { "a" => 3, "b" => 4, "version" => "1.10-dev" }
89
+
90
+ @index.add_version("deadbeef", item1, "payload1")
91
+ @index.add_version("deadcafe", item2, "payload2")
92
+ @index.latest_version.should == "1.9-dev"
93
+ @index.add_version("facedead", item3, "payload2")
94
+ @index.latest_version.should == "1.10-dev"
95
+ @index.add_version("badbed", item1.merge("version" => "2.15-dev"), "payload3")
96
+ @index.latest_version.should == "2.15-dev"
97
+ end
98
+
99
+ it "doesn't allow duplicate fingerprints or versions" do
100
+ item1 = { "a" => 1, "b" => 2, "version" => "1.9-dev" }
101
+ item2 = { "a" => 3, "b" => 4, "version" => "1.8-dev" }
102
+
103
+ @index.add_version("deadbeef", item1, "payload1")
104
+
105
+ lambda {
106
+ @index.add_version("deadcafe", item1, "payload3")
107
+ }.should raise_error("Trying to add duplicate version `1.9-dev' " +
108
+ "into index `#{File.join(@dir, "index.yml")}'")
109
+ end
110
+
111
+ it "supports finding entries by checksum" do
112
+ item1 = { "a" => 1, "b" => 2, "version" => 1 }
113
+ item2 = { "a" => 3, "b" => 4, "version" => 2 }
114
+
115
+ @index.add_version("deadbeef", item1, "payload1")
116
+ @index.add_version("deadcafe", item2, "payload2")
117
+
118
+ checksum1 = Digest::SHA1.hexdigest("payload1")
119
+ checksum2 = Digest::SHA1.hexdigest("payload2")
120
+
121
+ @index.find_by_checksum(checksum1).should == item1.merge("sha1" => checksum1)
122
+ @index.find_by_checksum(checksum2).should == item2.merge("sha1" => checksum2)
123
+ end
124
+
125
+ it "supports name prefix" do
126
+ item = { "a" => 1, "b" => 2, "version" => 1 }
127
+ @index = Bosh::Cli::VersionsIndex.new(@dir, "foobar")
128
+ @index.add_version("deadbeef", item, "payload1")
129
+ @index.filename(1).should == File.join(@dir, "foobar-1.tgz")
130
+ end
131
+
132
+ end