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.
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,240 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli::Command
4
+ class Misc < Base
5
+ DEFAULT_STATUS_TIMEOUT = 3 # seconds
6
+
7
+ def version
8
+ say("BOSH %s" % [Bosh::Cli::VERSION])
9
+ end
10
+
11
+ def status
12
+ if config.target && options[:director_checks]
13
+ say("Updating director data...", " ")
14
+
15
+ begin
16
+ timeout(config.status_timeout || DEFAULT_STATUS_TIMEOUT) do
17
+ director = Bosh::Cli::Director.new(config.target)
18
+ status = director.get_status
19
+
20
+ config.target_name = status["name"]
21
+ config.target_version = status["version"]
22
+ config.target_uuid = status["uuid"]
23
+ config.save
24
+ say("done".green)
25
+ end
26
+ rescue TimeoutError
27
+ say("timed out".red)
28
+ rescue => e
29
+ say("error".red)
30
+ end
31
+ nl
32
+ end
33
+
34
+ target_name = full_target_name ? full_target_name.green : "not set".red
35
+ target_uuid = config.target_uuid ? config.target_uuid.green : "n/a".red
36
+ user = logged_in? ? username.green : "not set".red
37
+ deployment = config.deployment ? config.deployment.green : "not set".red
38
+
39
+ say("Target".ljust(15) + target_name)
40
+ say("UUID".ljust(15) + target_uuid)
41
+ say("User".ljust(15) + user)
42
+ say("Deployment".ljust(15) + deployment)
43
+
44
+ if in_release_dir?
45
+ header("You are in release directory")
46
+
47
+ dev_name = release.dev_name
48
+ dev_version = Bosh::Cli::VersionsIndex.new(
49
+ File.join(work_dir, "dev_releases")).latest_version
50
+
51
+ final_name = release.final_name
52
+ final_version = Bosh::Cli::VersionsIndex.new(
53
+ File.join(work_dir, "releases")).latest_version
54
+
55
+ say("Dev name: %s" % [dev_name ? dev_name.green : "not set".red])
56
+ say("Dev version: %s" % [dev_version && dev_version > 0 ?
57
+ dev_version.to_s.green : "
58
+ no versions yet".red])
59
+ say("\n")
60
+ say("Final name: %s" % [final_name ?
61
+ final_name.green :
62
+ "not set".red])
63
+ say("Final version: %s" % [final_version && final_version > 0 ?
64
+ final_version.to_s.green :
65
+ "no versions yet".red])
66
+
67
+ say("\n")
68
+ say("Packages")
69
+ print_specs("package", "packages")
70
+
71
+ say("\n")
72
+ say("Jobs")
73
+ print_specs("job", "jobs")
74
+ end
75
+ end
76
+
77
+ def login(username = nil, password = nil)
78
+ target_required
79
+
80
+ unless options[:non_interactive]
81
+ username = ask("Your username: ").to_s if username.blank?
82
+
83
+ password_retries = 0
84
+ while password.blank? && password_retries < 3
85
+ password = ask("Enter password: ") { |q| q.echo = "*" }.to_s
86
+ password_retries += 1
87
+ end
88
+ end
89
+
90
+ if username.blank? || password.blank?
91
+ err("Please provide username and password")
92
+ end
93
+ logged_in = false
94
+
95
+ if options[:director_checks]
96
+ director = Bosh::Cli::Director.new(target, username, password)
97
+
98
+ if director.authenticated?
99
+ say("Logged in as '#{username}'")
100
+ logged_in = true
101
+ else
102
+ say("Cannot log in as '#{username}', please try again")
103
+ unless options[:non_interactive]
104
+ redirect(:misc, :login, username, nil)
105
+ end
106
+ end
107
+ end
108
+
109
+ if logged_in || !options[:director_checks]
110
+ config.set_credentials(target, username, password)
111
+ config.save
112
+ end
113
+ end
114
+
115
+ def logout
116
+ target_required
117
+ config.set_credentials(target, nil, nil)
118
+ config.save
119
+ say("You are no longer logged in to '#{target}'")
120
+ end
121
+
122
+ def purge_cache
123
+ if cache.cache_dir != Bosh::Cli::DEFAULT_CACHE_DIR
124
+ say("Cache directory '#{@cache.cache_dir}' differs from default, " +
125
+ "please remove manually")
126
+ else
127
+ FileUtils.rm_rf(cache.cache_dir)
128
+ say("Purged cache")
129
+ end
130
+ end
131
+
132
+ def show_target
133
+ say(target ?
134
+ "Current target is '#{full_target_name.green}'" :
135
+ "Target not set")
136
+ end
137
+
138
+ def set_target(director_url, name = nil)
139
+ if name.nil?
140
+ director_url = config.resolve_alias(:target, director_url) ||
141
+ director_url
142
+ end
143
+
144
+ if director_url.blank?
145
+ err("Target name cannot be blank")
146
+ end
147
+
148
+ director_url = normalize_url(director_url)
149
+ if director_url == target
150
+ say("Target already set to '#{full_target_name.green}'")
151
+ return
152
+ end
153
+
154
+ director = Bosh::Cli::Director.new(director_url)
155
+
156
+ if options[:director_checks]
157
+ begin
158
+ status = director.get_status
159
+ rescue Bosh::Cli::AuthError
160
+ status = {}
161
+ rescue Bosh::Cli::DirectorError
162
+ err("Cannot talk to director at '#{director_url}', " +
163
+ "please set correct target")
164
+ end
165
+ else
166
+ status = { "name" => "Unknown Director", "version" => "n/a" }
167
+ end
168
+
169
+ config.target = director_url
170
+ config.target_name = status["name"]
171
+ config.target_version = status["version"]
172
+ config.target_uuid = status["uuid"]
173
+
174
+ unless name.blank?
175
+ config.set_alias(:target, name, director_url)
176
+ end
177
+
178
+ unless status["uuid"].blank?
179
+ config.set_alias(:target, status["uuid"], director_url)
180
+ end
181
+
182
+ config.save
183
+ say("Target set to '#{full_target_name.green}'")
184
+
185
+ if interactive? && (config.username.blank? || config.password.blank?)
186
+ redirect(:misc, :login)
187
+ end
188
+ end
189
+
190
+ def set_alias(name, value)
191
+ config.set_alias(:cli, name, value.to_s.strip)
192
+ config.save
193
+ say("Alias `#{name.green}' created for command `#{value.green}'")
194
+ end
195
+
196
+ private
197
+
198
+ def print_specs(entity, dir)
199
+ specs = Dir[File.join(work_dir, dir, "*", "spec")]
200
+
201
+ if specs.empty?
202
+ say("No #{entity} specs found")
203
+ end
204
+
205
+ t = table ["Name", "Dev", "Final"]
206
+
207
+ specs.each do |spec_file|
208
+ if spec_file.is_a?(String) && File.file?(spec_file)
209
+ spec = load_yaml_file(spec_file)
210
+ name = spec["name"]
211
+
212
+ unless name.bosh_valid_id?
213
+ err("`#{name}' is an invalid #{entity} name, " +
214
+ "please fix before proceeding")
215
+ end
216
+
217
+ begin
218
+ dev_index = Bosh::Cli::VersionsIndex.new(
219
+ File.join(work_dir, ".dev_builds", dir, name))
220
+ final_index = Bosh::Cli::VersionsIndex.new(
221
+ File.join(work_dir, ".final_builds", dir, name))
222
+
223
+ dev_version = dev_index.latest_version || "n/a"
224
+ final_version = final_index.latest_version || "n/a"
225
+
226
+ t << [name, dev_version.gsub(/\-dev$/, "").rjust(8),
227
+ final_version.to_s.rjust(8)]
228
+ rescue Bosh::Cli::InvalidIndex => e
229
+ say("Problem with #{entity} index for `#{name}': #{e.message}".red)
230
+ end
231
+ else
232
+ say("Spec file `#{spec_file}' is invalid")
233
+ end
234
+ end
235
+
236
+ say(t) unless t.rows.empty?
237
+ end
238
+
239
+ end
240
+ end
@@ -0,0 +1,112 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli::Command
4
+ class Package < Base
5
+
6
+ def create_all
7
+ specs_glob = File.join(work_dir, "packages", "*", "spec")
8
+
9
+ Dir[specs_glob].each do |spec|
10
+ create(spec)
11
+ end
12
+ end
13
+
14
+ def create(name_or_path)
15
+ if name_or_path == "--all"
16
+ redirect(:package, :create_all)
17
+ end
18
+
19
+ spec = read_spec(name_or_path)
20
+
21
+ unless spec.is_a?(Hash) && spec.has_key?("name") && spec.has_key?("files")
22
+ err("Sorry, '#{name_or_path}' doesn't look like a valid package spec")
23
+ end
24
+
25
+ package_name = spec["name"]
26
+ header("Found '#{package_name}' spec")
27
+ print_spec(spec)
28
+ header("Building package...")
29
+
30
+ builder = Bosh::Cli::PackageBuilder.new(spec, work_dir,
31
+ false, release.blobstore)
32
+ builder.build
33
+ builder
34
+ end
35
+
36
+ def generate(name)
37
+ check_if_release_dir
38
+
39
+ unless name.bosh_valid_id?
40
+ err("`#{name}' is not a vaild BOSH id")
41
+ end
42
+
43
+ package_dir = File.join("packages", name)
44
+
45
+ if File.exists?(package_dir)
46
+ err("Package `#{name}' already exists, please pick another name")
47
+ end
48
+
49
+ say("create\t#{package_dir}")
50
+ FileUtils.mkdir_p(package_dir)
51
+
52
+ generate_file(package_dir, "packaging") do
53
+ "# abort script on any command that exit with a non zero value\nset -e\n"
54
+ end
55
+
56
+ generate_file(package_dir, "pre_packaging") do
57
+ "# abort script on any command that exit with a non zero value\nset -e\n"
58
+ end
59
+
60
+ generate_file(package_dir, "spec") do
61
+ "---\nname: #{name}\n\ndependencies:\n\nfiles:\n"
62
+ end
63
+
64
+ say("\nGenerated skeleton for `#{name}' package in `#{package_dir}'")
65
+ end
66
+
67
+ private
68
+
69
+ def generate_file(dir, file)
70
+ path = File.join(dir, file)
71
+ say("create\t#{path}")
72
+ FileUtils.touch(path)
73
+ File.open(path, "w") do |f|
74
+ f.write(yield)
75
+ end
76
+ end
77
+
78
+ def print_spec(spec)
79
+ say("Package name: #{spec["name"]}")
80
+ say("Files:")
81
+ for file in spec["files"]
82
+ say(" - #{file}")
83
+ end
84
+ end
85
+
86
+ def read_spec(name)
87
+ load_yaml_file(find_spec(name))
88
+ end
89
+
90
+ def find_spec(name)
91
+ if File.directory?(name)
92
+ spec_path = File.join(name, "spec")
93
+ if File.exists?(spec_path)
94
+ spec_path
95
+ else
96
+ err("Cannot find spec file in '#{name}' directory")
97
+ end
98
+ elsif File.file?(name)
99
+ name
100
+ else
101
+ package_dir = File.join(work_dir, "packages", name)
102
+ if File.directory?(package_dir)
103
+ find_spec(package_dir)
104
+ else
105
+ err("Cannot find package '#{name}' (tried '#{package_dir}')")
106
+ end
107
+
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,125 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli::Command
4
+ class PropertyManagement < Base
5
+ include Bosh::Cli::DeploymentHelper
6
+
7
+ def set(name, value)
8
+ prepare
9
+ show_header
10
+
11
+ status, body = director.get_property(@deployment_name, name)
12
+ existing_property = status == 200
13
+
14
+ if existing_property
15
+ say("Current `#{name.green}' value is " +
16
+ "`#{format_property(body["value"]).green}'")
17
+ else
18
+ say("This will be a new property")
19
+ end
20
+
21
+ prompt = "Are you sure you want to set property" +
22
+ " `#{name.green}' to `#{format_property(value).green}'? " +
23
+ "(type yes to proceed): "
24
+
25
+ if interactive? && ask(prompt) != "yes"
26
+ err("Canceled")
27
+ end
28
+
29
+ if existing_property
30
+ status, body = director.update_property(@deployment_name, name, value)
31
+ else
32
+ status, body = director.create_property(@deployment_name, name, value)
33
+ end
34
+
35
+ if status == 204
36
+ say("Property `#{name.green}' set to `#{value.green}'")
37
+ else
38
+ err(director.parse_error_message(status, body))
39
+ end
40
+ end
41
+
42
+ def unset(name)
43
+ prepare
44
+ show_header
45
+
46
+ prompt = "Are you sure you want to unset property " +
47
+ "`#{name.green}'? (type yes to proceed): "
48
+
49
+ if interactive? && ask(prompt) != "yes"
50
+ err("Canceled")
51
+ end
52
+
53
+ status, body = director.delete_property(@deployment_name, name)
54
+
55
+ if status == 204
56
+ say("Property `#{name.green}' has been unset")
57
+ else
58
+ err(director.parse_error_message(status, body))
59
+ end
60
+ end
61
+
62
+ def get(name)
63
+ prepare
64
+ show_header
65
+
66
+ status, body = director.get_property(@deployment_name, name)
67
+ if status == 200
68
+ say("Property `#{name.green}' value is " +
69
+ "`#{format_property(body["value"]).green}'")
70
+ else
71
+ err(director.parse_error_message(status, body))
72
+ end
73
+ end
74
+
75
+ def list(*args)
76
+ prepare
77
+ terse = args.include?("--terse")
78
+ show_header unless terse
79
+
80
+ properties = director.list_properties(@deployment_name)
81
+ unless properties.kind_of?(Enumerable)
82
+ err("Invalid properties format, please check your director")
83
+ end
84
+
85
+ output = properties.sort do |p1, p2|
86
+ p1["name"] <=> p2["name"]
87
+ end.map do |property|
88
+ [property["name"], format_property(property["value"])]
89
+ end
90
+
91
+ if terse
92
+ output.each { |row| say("#{row[0]}\t#{row[1]}") }
93
+ else
94
+ if output.size > 0
95
+ properties_table = table do |t|
96
+ t.headings = ["Name", "Value"]
97
+ output.each { |row| t << [row[0], row[1].truncate(40)] }
98
+ end
99
+ say(properties_table)
100
+ else
101
+ say("No properties found")
102
+ end
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def prepare
109
+ auth_required
110
+ manifest = prepare_deployment_manifest
111
+ @deployment_name = manifest["name"]
112
+ end
113
+
114
+ def show_header
115
+ say("Target #{target_name.green}")
116
+ say("Deployment #{@deployment_name.green}")
117
+ nl
118
+ end
119
+
120
+ def format_property(value)
121
+ value.gsub("\n", "\\n")
122
+ end
123
+
124
+ end
125
+ end