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,226 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ class JobBuilder
5
+ include PackagingHelper
6
+
7
+ attr_reader :name, :version, :packages, :templates,
8
+ :release_dir, :built_packages, :tarball_path
9
+
10
+ def self.run_prepare_script(script_path)
11
+ unless File.exists?(script_path)
12
+ raise InvalidJob, "Prepare script at `#{script_path}' doesn't exist"
13
+ end
14
+
15
+ unless File.executable?(script_path)
16
+ raise InvalidJob, "Prepare script at `#{script_path}' is not executable"
17
+ end
18
+
19
+ old_env = ENV
20
+
21
+ script_dir = File.dirname(script_path)
22
+ script_name = File.basename(script_path)
23
+
24
+ begin
25
+ # We need to temporarily delete some rubygems related artefacts
26
+ # because preparation scripts shouldn't share any assumptions
27
+ # with CLI itself
28
+ %w{ BUNDLE_GEMFILE RUBYOPT }.each { |key| ENV.delete(key) }
29
+
30
+ Dir.chdir(script_dir) do
31
+ cmd = "./#{script_name} 2>&1"
32
+ say("Running #{cmd}...")
33
+ script_output = `#{cmd}`
34
+ script_output.split("\n").each do |line|
35
+ say("> #{line}")
36
+ end
37
+ end
38
+
39
+ unless $?.exitstatus == 0
40
+ raise InvalidJob, "`#{script_path}' script failed"
41
+ end
42
+ ensure
43
+ ENV.each_pair { |k, v| ENV[k] = old_env[k] }
44
+ end
45
+ end
46
+
47
+ def initialize(spec, release_dir, final, blobstore, built_packages = [])
48
+ spec = load_yaml_file(spec) if spec.is_a?(String) && File.file?(spec)
49
+
50
+ @name = spec["name"]
51
+ @packages = spec["packages"].to_a
52
+ @built_packages = built_packages.to_a
53
+ @release_dir = release_dir
54
+ @templates_dir = File.join(job_dir, "templates")
55
+ @tarballs_dir = File.join(release_dir, "tmp", "jobs")
56
+ @final = final
57
+ @blobstore = blobstore
58
+ @artefact_type = "job"
59
+
60
+ case spec["templates"]
61
+ when Hash
62
+ @templates = spec["templates"].keys
63
+ else
64
+ raise InvalidJob, "Incorrect templates section in `#{@name}' " +
65
+ "job spec (should resolve to a hash)"
66
+ end
67
+
68
+ if @name.blank?
69
+ raise InvalidJob, "Job name is missing"
70
+ end
71
+
72
+ if @templates.nil?
73
+ raise InvalidJob, "Please include templates section with at least 1 " +
74
+ "(possibly dummy) file into `#{@name}' job spec"
75
+ end
76
+
77
+ unless @name.bosh_valid_id?
78
+ raise InvalidJob, "`#{@name}' is not a valid BOSH identifier"
79
+ end
80
+
81
+ unless File.exists?(File.join(job_dir, "spec"))
82
+ raise InvalidJob, "Cannot find spec file for '#{name}'"
83
+ end
84
+
85
+ if missing_packages.size > 0
86
+ raise InvalidJob, "Some packages required by '#{name}' job " +
87
+ "are missing: %s" % [missing_packages.join(", ")]
88
+ end
89
+
90
+ if missing_templates.size > 0
91
+ raise InvalidJob, "Some template files required by '#{name}' job " +
92
+ "are missing: %s" % [missing_templates.join(", ")]
93
+ end
94
+
95
+ if extra_templates.size > 0
96
+ raise InvalidJob, "There are unused template files for job " +
97
+ "'#{name}': %s" % [extra_templates.join(", ")]
98
+ end
99
+
100
+ unless monit_files.size > 0
101
+ raise InvalidJob, "Cannot find monit file for '#{name}'"
102
+ end
103
+
104
+ @dev_builds_dir = File.join(@release_dir, ".dev_builds", "jobs", @name)
105
+ @final_builds_dir = File.join(@release_dir, ".final_builds",
106
+ "jobs", @name)
107
+
108
+ FileUtils.mkdir_p(job_dir)
109
+ FileUtils.mkdir_p(@dev_builds_dir)
110
+ FileUtils.mkdir_p(@final_builds_dir)
111
+
112
+ at_exit { FileUtils.rm_rf(build_dir) }
113
+
114
+ init_indices
115
+ end
116
+
117
+ def copy_files
118
+ FileUtils.mkdir_p(File.join(build_dir, "templates"))
119
+ copied = 0
120
+
121
+ templates.each do |template|
122
+ src = File.join(@templates_dir, template)
123
+ dst = File.join(build_dir, "templates", template)
124
+ FileUtils.mkdir_p(File.dirname(dst))
125
+
126
+ FileUtils.cp(src, dst, :preserve => true)
127
+ copied += 1
128
+ end
129
+
130
+ monit_files.each do |file|
131
+ FileUtils.cp(file, build_dir, :preserve => true)
132
+ copied += 1
133
+ end
134
+
135
+ FileUtils.cp(File.join(job_dir, "spec"), File.join(build_dir, "job.MF"),
136
+ :preserve => true)
137
+ copied += 1
138
+ copied
139
+ end
140
+
141
+ def prepare_files
142
+ preparation_script = File.join(job_dir, "prepare")
143
+ end
144
+
145
+ def build_dir
146
+ @build_dir ||= Dir.mktmpdir
147
+ end
148
+
149
+ def job_dir
150
+ File.join(@release_dir, "jobs", @name)
151
+ end
152
+
153
+ def dev_builds_dir
154
+ File.join(@release_dir, ".dev_builds", "jobs", name)
155
+ end
156
+
157
+ def final_builds_dir
158
+ File.join(@release_dir, ".final_builds", "jobs", name)
159
+ end
160
+
161
+ def fingerprint
162
+ @fingerprint ||= make_fingerprint
163
+ end
164
+
165
+ def monit_files
166
+ glob = File.join(job_dir, '*.monit')
167
+ files = Dir.glob(glob)
168
+ monit = File.join(job_dir, "monit")
169
+ files << monit if File.exist?(monit)
170
+ files
171
+ end
172
+
173
+ def reload
174
+ @fingerprint = nil
175
+ @build_dir = nil
176
+ self
177
+ end
178
+
179
+ private
180
+
181
+ def make_fingerprint
182
+ contents = ""
183
+
184
+ # templates, monit, spec
185
+ files = templates.map do |template|
186
+ File.join(@templates_dir, template)
187
+ end.sort
188
+
189
+ files += monit_files
190
+ files << File.join(job_dir, "spec")
191
+
192
+ files.each do |filename|
193
+ path = File.basename(filename)
194
+ digest = Digest::SHA1.file(filename).hexdigest
195
+ contents << "%s%s%s" % [path, digest, tracked_permissions(filename)]
196
+ end
197
+
198
+ Digest::SHA1.hexdigest(contents)
199
+ end
200
+
201
+ def missing_packages
202
+ @missing_packages ||= packages - built_packages
203
+ end
204
+
205
+ def missing_templates
206
+ templates.select do |template|
207
+ !File.exists?(File.join(@templates_dir, template))
208
+ end
209
+ end
210
+
211
+ def extra_templates
212
+ return [] if !File.directory?(@templates_dir)
213
+
214
+ Dir.chdir(@templates_dir) do
215
+ Dir["**/*"].reject do |file|
216
+ File.directory?(file) || templates.include?(file)
217
+ end
218
+ end
219
+ end
220
+
221
+ def in_build_dir(&block)
222
+ Dir.chdir(build_dir) { yield }
223
+ end
224
+
225
+ end
226
+ end
@@ -0,0 +1,254 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ class PackageBuilder
5
+ include PackagingHelper
6
+
7
+ attr_reader :name, :globs, :version, :dependencies, :tarball_path
8
+ # We have two ways of getting/storing a package:
9
+ # development versions of packages, kept in release directory
10
+ # final versions of packages, kept in blobstore
11
+ # development packages and their metadata should always be gitignored
12
+ # final build tarballs should be ignored as well
13
+ # final builds metadata should be checked in
14
+
15
+ def initialize(spec, release_dir, final, blobstore,
16
+ sources_dir = nil, blobs_dir = nil)
17
+ spec = load_yaml_file(spec) if spec.is_a?(String) && File.file?(spec)
18
+
19
+ @name = spec["name"]
20
+ @globs = spec["files"]
21
+ @dependencies = Array(spec["dependencies"])
22
+ @release_dir = release_dir
23
+ @sources_dir = sources_dir || File.join(@release_dir, "src")
24
+ @blobs_dir = blobs_dir || File.join(@release_dir, "blobs")
25
+ @final = final
26
+ @blobstore = blobstore
27
+ @artefact_type = "package"
28
+
29
+ @metadata_files = %w(packaging pre_packaging)
30
+
31
+ if @name.blank?
32
+ raise InvalidPackage, "Package name is missing"
33
+ end
34
+
35
+ unless @name.bosh_valid_id?
36
+ raise InvalidPackage, "Package name should be a valid BOSH identifier"
37
+ end
38
+
39
+ unless @globs.is_a?(Array) && @globs.size > 0
40
+ raise InvalidPackage, "Package '#{@name}' doesn't include any files"
41
+ end
42
+
43
+ @dev_builds_dir = File.join(@release_dir, ".dev_builds",
44
+ "packages", @name)
45
+ @final_builds_dir = File.join(@release_dir, ".final_builds",
46
+ "packages", @name)
47
+
48
+ FileUtils.mkdir_p(package_dir)
49
+ FileUtils.mkdir_p(@dev_builds_dir)
50
+ FileUtils.mkdir_p(@final_builds_dir)
51
+
52
+ at_exit { FileUtils.rm_rf(build_dir) }
53
+
54
+ init_indices
55
+ end
56
+
57
+ def reload # Mostly for tests
58
+ @fingerprint = nil
59
+ @resolved_globs = nil
60
+ init_indices
61
+ self
62
+ end
63
+
64
+ def fingerprint
65
+ @fingerprint ||= make_fingerprint
66
+ end
67
+
68
+ def resolved_globs
69
+ @resolved_globs ||= resolve_globs
70
+ end
71
+
72
+ def source_files
73
+ resolved_globs[:source]
74
+ end
75
+
76
+ def blob_files
77
+ resolved_globs[:blob]
78
+ end
79
+
80
+ def files
81
+ (resolved_globs[:blob] + resolved_globs[:source]).sort
82
+ end
83
+
84
+ def build_dir
85
+ @build_dir ||= Dir.mktmpdir
86
+ end
87
+
88
+ def package_dir
89
+ File.join(@release_dir, "packages", name)
90
+ end
91
+
92
+ def copy_files
93
+ copied = 0
94
+
95
+ files.each do |filename|
96
+ file_path = get_file_path(filename)
97
+ destination = File.join(build_dir, filename)
98
+
99
+ if File.directory?(file_path)
100
+ FileUtils.mkdir_p(destination)
101
+ else
102
+ FileUtils.mkdir_p(File.dirname(destination))
103
+ FileUtils.cp(file_path, destination, :preserve => true)
104
+ copied += 1
105
+ end
106
+ end
107
+
108
+ in_package_dir do
109
+ @metadata_files.each do |filename|
110
+ destination = File.join(build_dir, filename)
111
+ next unless File.exists?(filename)
112
+ if File.exists?(destination)
113
+ raise InvalidPackage, "Package '#{name}' has '#{filename}' file " +
114
+ "which conflicts with BOSH packaging"
115
+ end
116
+ FileUtils.cp(filename, destination, :preserve => true)
117
+ copied += 1
118
+ end
119
+ end
120
+
121
+ pre_package
122
+ copied
123
+ end
124
+
125
+ def pre_package
126
+ pre_packaging_script = File.join(package_dir, "pre_packaging")
127
+
128
+ if File.exists?(pre_packaging_script)
129
+
130
+ say("Pre-packaging...")
131
+ FileUtils.cp(pre_packaging_script, build_dir, :preserve => true)
132
+
133
+ old_env = ENV
134
+
135
+ begin
136
+ ENV.delete_if { |key, _| key[0, 7] == "BUNDLE_" }
137
+ if ENV["RUBYOPT"]
138
+ ENV["RUBYOPT"] = ENV["RUBYOPT"].sub("-rbundler/setup", "")
139
+ end
140
+ ENV["BUILD_DIR"] = build_dir
141
+ in_build_dir do
142
+ pre_packaging_out = `bash -x pre_packaging 2>&1`
143
+ pre_packaging_out.split("\n").each do |line|
144
+ say("> #{line}")
145
+ end
146
+ unless $?.exitstatus == 0
147
+ raise InvalidPackage, "`#{name}' pre-packaging failed"
148
+ end
149
+ end
150
+
151
+ ensure
152
+ ENV.delete("BUILD_DIR")
153
+ old_env.each { |k, v| ENV[k] = old_env[k] }
154
+ end
155
+
156
+ FileUtils.rm(File.join(build_dir, "pre_packaging"))
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ def get_file_path(file)
163
+ file_path = File.join(@sources_dir, file)
164
+ if !File.exists?(file_path)
165
+ file_path = File.join(@blobs_dir, file)
166
+ unless File.exists?(file_path)
167
+ raise InvalidPackage, "#{file} cannot be found"
168
+ end
169
+ end
170
+ file_path
171
+ end
172
+
173
+ def make_fingerprint
174
+ contents = ""
175
+
176
+ signatures = files.map do |file|
177
+ path = get_file_path(file)
178
+ unless File.directory?(path)
179
+ file_digest = Digest::SHA1.file(path).hexdigest
180
+ end
181
+
182
+ "%s%s%s" % [file, file_digest, tracked_permissions(path)]
183
+ end
184
+ contents << signatures.join("")
185
+
186
+ in_package_dir do
187
+ @metadata_files.each do |file|
188
+ if File.file?(file)
189
+ file_digest = Digest::SHA1.file(file).hexdigest
190
+ contents << "%s%s" % [file, file_digest]
191
+ end
192
+ end
193
+ end
194
+
195
+ contents << @dependencies.sort.join(",")
196
+
197
+ Digest::SHA1.hexdigest(contents)
198
+ end
199
+
200
+ def resolve_globs
201
+ glob_map = { :blob => [], :source => [] }
202
+ blob_list = []
203
+ source_list = []
204
+
205
+ @globs.each do |glob|
206
+ matching_source = []
207
+ matching_blob = []
208
+
209
+ in_sources_dir do
210
+ matching_source = Dir.glob(glob, File::FNM_DOTMATCH).reject do |fn|
211
+ [".", ".."].include?(File.basename(fn))
212
+ end
213
+ end
214
+
215
+ in_blobs_dir do
216
+ matching_blob = Dir.glob(glob, File::FNM_DOTMATCH).reject do |fn|
217
+ [".", ".."].include?(File.basename(fn))
218
+ end
219
+ end
220
+
221
+ if matching_blob.size == 0 && matching_source.size ==0
222
+ raise InvalidPackage, "`#{name}' has a glob that " +
223
+ "resolves to an empty file list: #{glob}"
224
+ end
225
+
226
+ blob_list << matching_blob
227
+ source_list << matching_source
228
+ end
229
+ glob_map[:blob] = blob_list.flatten.sort
230
+ glob_map[:source] = source_list.flatten.sort
231
+ glob_map
232
+ end
233
+
234
+ def in_blobs_dir(&block)
235
+ # old release does not have 'blob'
236
+ if File.directory?(@blobs_dir)
237
+ Dir.chdir(@blobs_dir) { yield }
238
+ end
239
+ end
240
+
241
+ def in_sources_dir(&block)
242
+ Dir.chdir(@sources_dir) { yield }
243
+ end
244
+
245
+ def in_build_dir(&block)
246
+ Dir.chdir(build_dir) { yield }
247
+ end
248
+
249
+ def in_package_dir(&block)
250
+ Dir.chdir(package_dir) { yield }
251
+ end
252
+
253
+ end
254
+ end