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,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