From 1607a876ea1d76a8bc17060c0020c9772829ca23 Mon Sep 17 00:00:00 2001 From: Corey Innis Date: Thu, 22 Jan 2015 15:36:11 -0800 Subject: [PATCH] Revert "Add code and tests to include master LICENSE file in root of release" This reverts several commits: 7ae35fe~..18c935b cb44ace 2e2c654~..7cda92d 7cda92d - Move release git ignored files from root .gitignore to release/.gitignore (25 hours ago) 2e2c654 - License tarballs added to release/.gitignore. [#84810956] (25 hours ago) cb44ace - Remove spec helper ReleaseDirectory#to_str and use ReleaseDirectory for other builder specs (2 days ago) 18c935b - Add ReleaseDirectory#has_file? and refactor LicenseBuilder and PackageBuilder specs to use helpers better (2 days ago) 0aa71fe - Refactor ReleaseDirectory spec helper to not subclass String and add cleanup (2 days ago) cbd5e2c - Refactor LicenseBuilder to remove a bit of unused/unwanted logic (2 days ago) 49806d2 - Rework #add_version spec helper to simplify/clarify specs (2 days ago) 29cb8f1 - Refactor LicenseBuilderSpec examples for dev/final build (2 days ago) d28f779 - Refactor: rename SpecPackage as ReleaseDirectory (2 days ago) dca5cf8 - Refactor some of LicenseBuilder specs and improve LICENSE/NOTICE logic (2 days ago) c5a82fa - Refactor: move file-related spec helpers into SpecPackage (2 days ago) 911fff2 - Refactor: simplify spec's #make_builder helper (2 days ago) f82306b - Refactor: move #add_version helper to FileHelpers (2 days ago) 83fa21f - Refactor: extract spec tmp files helpers to Support::FileHelpers (2 days ago) 7ae35fe - Add code and tests to include master LICENSE file in root of release (2 days ago) Signed-off-by: Christian Williams --- .gitignore | 17 + .../cli/commands/release/create_release.rb | 38 +- bosh_cli/lib/cli/license_builder.rb | 98 --- bosh_cli/lib/cli/packaging_helper.rb | 2 +- bosh_cli/lib/cli/release_builder.rb | 33 +- bosh_cli/spec/support/file_helpers.rb | 87 -- .../commands/release/create_release_spec.rb | 6 +- bosh_cli/spec/unit/job_builder_spec.rb | 816 ++++++++--------- bosh_cli/spec/unit/license_builder_spec.rb | 181 ---- bosh_cli/spec/unit/package_builder_spec.rb | 822 +++++++++--------- bosh_cli/spec/unit/release_builder_spec.rb | 386 ++++---- bosh_cli/spec/unit/release_spec.rb | 527 ++++++----- release/.gitignore | 20 +- 13 files changed, 1323 insertions(+), 1710 deletions(-) delete mode 100644 bosh_cli/lib/cli/license_builder.rb delete mode 100644 bosh_cli/spec/support/file_helpers.rb delete mode 100644 bosh_cli/spec/unit/license_builder_spec.rb diff --git a/.gitignore b/.gitignore index 3de037f105b..5545f4452a2 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,23 @@ micro/tmp pkg/ +/release/.blobs +/release/.dev_builds +/release/.final_builds/jobs/**/*.tgz +/release/.final_builds/packages/**/*.tgz +/release/.idea +/release/blobs +/release/blobs +/release/config/dev.yml +/release/config/private.yml +/release/dev-releases +/release/dev_releases +/release/jobs/micro_*/monit +/release/jobs/micro_*/spec +/release/jobs/micro_*/templates/ +/release/releases/*.tgz +/release/src/bosh/* + /vsphere-cpi-release/.blobs /vsphere-cpi-release/.dev_builds /vsphere-cpi-release/.final_builds/jobs/**/*.tgz diff --git a/bosh_cli/lib/cli/commands/release/create_release.rb b/bosh_cli/lib/cli/commands/release/create_release.rb index 9d6f0c312fc..512d67b686e 100644 --- a/bosh_cli/lib/cli/commands/release/create_release.rb +++ b/bosh_cli/lib/cli/commands/release/create_release.rb @@ -1,5 +1,3 @@ -require 'cli/license_builder' - module Bosh::Cli::Command module Release class CreateRelease < Base @@ -92,11 +90,8 @@ def create_from_spec(version) header('Building jobs') jobs = build_jobs(packages.map(&:name), dry_run, final) - header('Building license') - license_builders = build_licenses(dry_run, final) - header('Building release') - release_builder = build_release(dry_run, final, jobs, manifest_only, packages, license_builders, name, version) + release_builder = build_release(dry_run, final, jobs, manifest_only, packages, name, version) header('Release summary') show_summary(release_builder) @@ -138,23 +133,6 @@ def dirty_blob_check(force) end end - def build_licenses(dry_run, final) - licenses = Bosh::Cli::LicenseBuilder.discover( - work_dir, - :final => final, - :blobstore => release.blobstore, - :dry_run => dry_run - ) - - licenses.each do |license| - say("Building #{license.name.make_green}...") - license.build - nl - end - - licenses - end - def build_packages(dry_run, final) packages = Bosh::Cli::PackageBuilder.discover( work_dir, @@ -186,8 +164,8 @@ def build_packages(dry_run, final) packages end - def build_release(dry_run, final, jobs, manifest_only, packages, license_builders, name, version) - release_builder = Bosh::Cli::ReleaseBuilder.new(release, packages, jobs, license_builders, name, + def build_release(dry_run, final, jobs, manifest_only, packages, name, version) + release_builder = Bosh::Cli::ReleaseBuilder.new(release, packages, jobs, name, final: final, commit_hash: commit_hash, version: version, @@ -259,21 +237,11 @@ def show_summary(builder) end end - licenses_table = table do |t| - t.headings = %w(Name Version Notes) - builder.licenses.each do |license| - t << artifact_summary(license) - end - end - say('Packages') say(packages_table) nl say('Jobs') say(jobs_table) - nl - say('Licenses') - say(licenses_table) affected_jobs = builder.affected_jobs diff --git a/bosh_cli/lib/cli/license_builder.rb b/bosh_cli/lib/cli/license_builder.rb deleted file mode 100644 index d83ad804af9..00000000000 --- a/bosh_cli/lib/cli/license_builder.rb +++ /dev/null @@ -1,98 +0,0 @@ -module Bosh::Cli - class LicenseBuilder - include PackagingHelper - - attr_reader :name, :version, :release_dir, :tarball_path - - # @param [String] directory Release directory - # @param [Hash] options Build options - def self.discover(directory, options = {}) - final = options[:final] - blobstore = options[:blobstore] - builder = new(directory, final, blobstore) - - [builder] - end - - def initialize(release_dir, final = false, blobstore) - @name = "license" - @version = nil - @tarball_path = nil - @final = final - @release_dir = release_dir - @blobstore = blobstore - - @license_dir = @release_dir - @dev_builds_dir = File.join(@release_dir, ".dev_builds", @name) - @final_builds_dir = File.join(@release_dir, ".final_builds", @name) - - FileUtils.mkdir_p(@dev_builds_dir) - FileUtils.mkdir_p(@final_builds_dir) - - init_indices - end - - def fingerprint - @fingerprint ||= make_fingerprint - end - - def reload - @fingerprint = nil - @build_dir = nil - self - end - - def copy_files - expected_files = ['LICENSE', 'NOTICE'] - expected_paths = expected_files.map { |name| File.join(@release_dir, name) } - actual = [] - - expected_paths.each do |path| - next unless File.file?(path) - name = File.split(path).last - base = File.basename(path) - destination = File.join(build_dir, base) - - FileUtils.cp(path, destination, :preserve => true) - actual << name - end - - expected_files.each do |file| - warn("Does not contain #{file} within #{@release_dir}") unless actual.include?(file) - end - - actual.length - end - - def build_dir - @build_dir ||= Dir.mktmpdir - end - - private - - def make_fingerprint - versioning_scheme = 2 - contents = "v#{versioning_scheme}" - - files = [] - Dir[File.join(@license_dir, "*")].each do |package_dir| - next unless File.file?(package_dir) - files << File.absolute_path(package_dir) - end - - files.each do |filename| - path = File.basename(filename) - digest = Digest::SHA1.file(filename).hexdigest - contents << "%s%s" % [path, digest] - end - - Digest::SHA1.hexdigest(contents) - end - - def in_build_dir(&block) - Dir.chdir(build_dir) { yield } - end - - end -end - diff --git a/bosh_cli/lib/cli/packaging_helper.rb b/bosh_cli/lib/cli/packaging_helper.rb index 275db70b485..59781a7a1da 100644 --- a/bosh_cli/lib/cli/packaging_helper.rb +++ b/bosh_cli/lib/cli/packaging_helper.rb @@ -194,7 +194,7 @@ def checksum file_checksum(@tarball_path) else raise RuntimeError, - 'cannot read checksum for not yet generated package/job/license' + 'cannot read checksum for not yet generated package/job' end end diff --git a/bosh_cli/lib/cli/release_builder.rb b/bosh_cli/lib/cli/release_builder.rb index 871f0302e69..79cbad6640c 100644 --- a/bosh_cli/lib/cli/release_builder.rb +++ b/bosh_cli/lib/cli/release_builder.rb @@ -2,21 +2,19 @@ module Bosh::Cli class ReleaseBuilder include Bosh::Cli::DependencyHelper - attr_reader :release, :packages, :jobs, :licenses, :name, :version, :build_dir, :commit_hash, :uncommitted_changes + attr_reader :release, :packages, :jobs, :name, :version, :build_dir, :commit_hash, :uncommitted_changes # @param [Bosh::Cli::Release] release Current release # @param [Array] packages Built packages # @param [Array] jobs Built jobs - # @param [Array] licenses Built licenses # @param [Hash] options Release build options - def initialize(release, packages, jobs, licenses, name, options = { }) + def initialize(release, packages, jobs, name, options = { }) @release = release @final = options.has_key?(:final) ? !!options[:final] : false @commit_hash = options.fetch(:commit_hash, '00000000') @uncommitted_changes = options.fetch(:uncommitted_changes, true) @packages = packages @jobs = jobs - @licenses = licenses @name = name raise 'Release name is blank' if name.blank? @@ -29,7 +27,6 @@ def initialize(release, packages, jobs, licenses, name, options = { }) @index = @final ? @final_index : @dev_index @release_storage = Versions::LocalVersionStorage.new(@index.storage_dir, @name) - release_dir = @release.dir if @version && @release_storage.has_file?(@version) raise ReleaseVersionError.new('Release version already exists') end @@ -96,17 +93,6 @@ def copy_packages @packages_copied = true end - # Copies licenses into release (LICENSE on the root repo only now) - def copy_licenses - licenses.each do |license| - say("%-40s %s" % [license.name.make_green, - pretty_size(license.tarball_path)]) - `tar -xvf #{license.tarball_path} -C #{build_dir} ` - - end - @licenses_copied = true - end - # Copies jobs into release def copy_jobs jobs.each do |job| @@ -142,14 +128,6 @@ def generate_manifest } end - manifest["license"] = licenses.map do |license| - { - "version" => license.version, - "fingerprint" => license.fingerprint, - "sha1" => license.checksum - } - end - manifest["commit_hash"] = commit_hash manifest["uncommitted_changes"] = uncommitted_changes @@ -192,13 +170,6 @@ def generate_tarball nl end - unless @licenses_copied - header("Copying licenses...") - copy_licenses - nl - end - - FileUtils.mkdir_p(File.dirname(tarball_path)) in_build_dir do diff --git a/bosh_cli/spec/support/file_helpers.rb b/bosh_cli/spec/support/file_helpers.rb deleted file mode 100644 index ce3eaedb97d..00000000000 --- a/bosh_cli/spec/support/file_helpers.rb +++ /dev/null @@ -1,87 +0,0 @@ -require 'spec_helper' - -module Support - module FileHelpers - class << self - def included(base) - base.extend(ClassMethods) - end - end - - module ClassMethods - def with_release_directory - dir = ReleaseDirectory.new - yield dir - - after do - FileUtils.remove_entry dir.path if File.exists?(dir.path) - end - end - end - - # TODO: compose around String, rather than subclassing. - class ReleaseDirectory - attr_reader :path - - def initialize - @path = Dir.mktmpdir - end - - def add_dir(subdir) - FileUtils.mkdir_p(File.join(path, subdir)) - end - - def add_file(subdir, filepath, contents = nil) - full_path = File.join([path, subdir, filepath].compact) - FileUtils.mkdir_p(File.dirname(full_path)) - - if contents - File.open(full_path, 'w') { |f| f.write(contents) } - else - FileUtils.touch(full_path) - end - end - - def add_files(subdir, filepaths) - filepaths.each { |filepath| add_file(subdir, filepath) } - end - - def add_version(key, storage_dir, payload, build) - storage_path = self.join(storage_dir) - version_index = Bosh::Cli::Versions::VersionsIndex.new(storage_path) - version_store = Bosh::Cli::Versions::LocalVersionStorage.new(storage_path) - src_path = get_tmp_file_path(payload) - - version_index.add_version(key, build) - file = version_store.put_file(key, src_path) - - build['sha1'] = Digest::SHA1.file(file).hexdigest - version_index.update_version(key, build) - end - - def has_file?(filepath) - File.exists?(join(filepath)) - end - - def join(*args) - File.join(*([path] + args)) - end - - def remove_dir(subdir) - FileUtils.rm_rf(File.join(path, subdir)) - end - - def remove_file(subdir, filepath) - FileUtils.rm(File.join(path, [subdir, filepath].compact)) - end - - def remove_files(subdir, filepaths) - filepaths.each { |filepath| remove_file(subdir, filepath) } - end - end - end -end - -RSpec.configure do |config| - config.include(Support::FileHelpers) -end diff --git a/bosh_cli/spec/unit/commands/release/create_release_spec.rb b/bosh_cli/spec/unit/commands/release/create_release_spec.rb index d4fc16f495b..4d64216a326 100644 --- a/bosh_cli/spec/unit/commands/release/create_release_spec.rb +++ b/bosh_cli/spec/unit/commands/release/create_release_spec.rb @@ -30,7 +30,6 @@ module Bosh::Cli::Command::Release allow(command).to receive(:dirty_state?).and_return(false) allow(command).to receive(:build_packages).and_return([]) allow(command).to receive(:build_jobs) - allow(command).to receive(:build_licenses) allow(Bosh::Cli::ReleaseBuilder).to receive(:new).and_return(release_builder) allow(release_builder).to receive(:build) @@ -62,7 +61,6 @@ module Bosh::Cli::Command::Release expect(command).to receive(:header).with('Building DEV release').once.ordered expect(command).to receive(:header).with('Building packages').once.ordered expect(command).to receive(:header).with('Building jobs').once.ordered - expect(command).to receive(:header).with('Building license').once.ordered expect(command).to receive(:header).with('Building release').once.ordered expect(command).to receive(:header).with('Release summary').once.ordered @@ -124,7 +122,7 @@ module Bosh::Cli::Command::Release let(:provided_name) { 'c-release' } it 'builds release with the specified name' do - expect(command).to receive(:build_release).with(true, nil, nil, true, [], nil, provided_name, nil) + expect(command).to receive(:build_release).with(true, nil, nil, true, [], provided_name, nil) command.create end @@ -139,7 +137,7 @@ module Bosh::Cli::Command::Release context 'when a version is provided with --version' do it 'builds release with the specified version' do - expect(command).to receive(:build_release).with(true, nil, nil, true, [], nil, configured_dev_name, '1.0.1') + expect(command).to receive(:build_release).with(true, nil, nil, true, [], configured_dev_name, '1.0.1') command.options[:version] = '1.0.1' command.create end diff --git a/bosh_cli/spec/unit/job_builder_spec.rb b/bosh_cli/spec/unit/job_builder_spec.rb index 5f6e0614454..f08cc3d9973 100644 --- a/bosh_cli/spec/unit/job_builder_spec.rb +++ b/bosh_cli/spec/unit/job_builder_spec.rb @@ -1,419 +1,433 @@ require 'spec_helper' describe Bosh::Cli::JobBuilder do - with_release_directory do |dir| - let(:release_dir) { dir } - - def make_builder(name, packages = [], templates = { }, built_packages = [], - create_spec = true, final = false, blobstore = double('blobstore')) - # Workaround for Hash requirement - if templates.is_a?(Array) - templates = templates.inject({ }) { |h, e| h[e] = e; h } - end - - spec = { - 'name' => name, - 'packages' => packages, - 'templates' => templates - } - add_spec(name) if create_spec - - Bosh::Cli::JobBuilder.new(spec, release_dir.path, - final, blobstore, built_packages) - end - - def add_job(job_name, file, contents = nil) - release_dir.add_file("jobs/#{job_name}", file, contents) - end - - def add_spec(job_name) - add_job(job_name, 'spec') - end - - def add_monit(job_name, file='monit') - add_job(job_name, file) - end - - def add_templates(job_name, *files) - job_template_path = release_dir.join('jobs', job_name, 'templates') - FileUtils.mkdir_p(job_template_path) - - files.each do |file| - add_job(job_name, "templates/#{file}") - end - end - - def remove_templates(job_name, *files) - job_template_path = release_dir.join('jobs', job_name, 'templates') - - files.each do |file| - FileUtils.rm_rf(File.join(job_template_path, file)) - end - end - def add_version(index, storage, key, build, src_file_path) - index.add_version(key, build) - file_path = storage.put_file(key, src_file_path) - build['sha1'] = Digest::SHA1.file(file_path).hexdigest - index.update_version(key, build) - end + before { @release_dir = Dir.mktmpdir } - it 'creates a new builder' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - builder = make_builder('foo', ['foo', 'bar', 'baz'], - ['a.conf', 'b.yml'], ['foo', 'bar', 'baz']) - expect(builder.packages).to eq(['foo', 'bar', 'baz']) - expect(builder.templates).to match_array(['a.conf', 'b.yml']) - expect(builder.release_dir).to eq(release_dir.path) + def new_builder(name, packages = [], templates = { }, built_packages = [], + create_spec = true, final = false, blobstore = double('blobstore')) + # Workaround for Hash requirement + if templates.is_a?(Array) + templates = templates.inject({ }) { |h, e| h[e] = e; h } end - - it 'has a fingerprint' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - builder = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - expect(builder.fingerprint).to eq('962d57a4f8bc4f48fd6282d8c4d94e4a744f155b') - end - - it 'has a stable portable fingerprint' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - b1 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - f1 = b1.fingerprint - expect(b1.reload.fingerprint).to eq(f1) - - b2 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - expect(b2.fingerprint).to eq(f1) - end - - it 'changes fingerprint when new template file is added' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - - b1 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - f1 = b1.fingerprint - - add_templates('foo', 'baz') - b2 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml', 'baz'], ['foo', 'bar']) - expect(b2.fingerprint).not_to eq(f1) + + spec = { + 'name' => name, + 'packages' => packages, + 'templates' => templates + } + add_spec(name) if create_spec + + Bosh::Cli::JobBuilder.new(spec, @release_dir, + final, blobstore, built_packages) + end + + def add_file(job_name, file, contents = nil) + job_dir = File.join(@release_dir, 'jobs', job_name) + file_path = File.join(job_dir, file) + FileUtils.mkdir_p(File.dirname(file_path)) + FileUtils.touch(file_path) + if contents + File.open(file_path, 'w') { |f| f.write(contents) } end - - it 'changes fingerprint when template files is changed' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - - b1 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - f1 = b1.fingerprint - - add_job('foo', 'templates/a.conf', 'bzz') - expect(b1.reload.fingerprint).not_to eq(f1) + end + + def add_spec(job_name) + add_file(job_name, 'spec') + end + + def add_monit(job_name, file='monit') + add_file(job_name, file) + end + + def add_templates(job_name, *files) + job_template_path = File.join(@release_dir, 'jobs', job_name, 'templates') + FileUtils.mkdir_p(job_template_path) + + files.each do |file| + add_file(job_name, "templates/#{file}") end - - it 'changes fingerprint when new monit file is added' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo', 'foo.monit') - - b1 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - f1 = b1.fingerprint - - add_monit('foo', 'bar.monit') - b2 = make_builder('foo', ['foo', 'bar'], - ['a.conf', 'b.yml'], ['foo', 'bar']) - expect(b2.fingerprint).not_to eq(f1) + end + + def remove_templates(job_name, *files) + job_template_path = File.join(@release_dir, 'jobs', job_name, 'templates') + + files.each do |file| + FileUtils.rm_rf(File.join(job_template_path, file)) end - - it 'can read template file names from hash' do + end + + it 'creates a new builder' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + builder = new_builder('foo', ['foo', 'bar', 'baz'], + ['a.conf', 'b.yml'], ['foo', 'bar', 'baz']) + expect(builder.packages).to eq(['foo', 'bar', 'baz']) + expect(builder.templates).to match_array(['a.conf', 'b.yml']) + expect(builder.release_dir).to eq(@release_dir) + end + + it 'has a fingerprint' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + builder = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + expect(builder.fingerprint).to eq('962d57a4f8bc4f48fd6282d8c4d94e4a744f155b') + end + + it 'has a stable portable fingerprint' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + b1 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + f1 = b1.fingerprint + expect(b1.reload.fingerprint).to eq(f1) + + b2 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + expect(b2.fingerprint).to eq(f1) + end + + it 'changes fingerprint when new template file is added' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + + b1 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + f1 = b1.fingerprint + + add_templates('foo', 'baz') + b2 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml', 'baz'], ['foo', 'bar']) + expect(b2.fingerprint).not_to eq(f1) + end + + it 'changes fingerprint when template files is changed' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + + b1 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + f1 = b1.fingerprint + + add_file('foo', 'templates/a.conf', 'bzz') + expect(b1.reload.fingerprint).not_to eq(f1) + end + + it 'changes fingerprint when new monit file is added' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo', 'foo.monit') + + b1 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + f1 = b1.fingerprint + + add_monit('foo', 'bar.monit') + b2 = new_builder('foo', ['foo', 'bar'], + ['a.conf', 'b.yml'], ['foo', 'bar']) + expect(b2.fingerprint).not_to eq(f1) + end + + it 'can read template file names from hash' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + builder = new_builder('foo', ['foo', 'bar', 'baz'], + { 'a.conf' => 1, 'b.yml' => 2 }, + ['foo', 'bar', 'baz']) + expect(builder.templates).to match_array(['a.conf', 'b.yml']) + end + + it 'whines if name is blank' do + expect { + new_builder('') + }.to raise_error(Bosh::Cli::InvalidJob, 'Job name is missing') + end + + it 'whines on funny characters in name' do + expect { + new_builder('@#!', []) + }.to raise_error(Bosh::Cli::InvalidJob, + "`@#!' is not a valid BOSH identifier") + end + + it 'whines if some templates are missing' do + add_templates('foo', 'a.conf', 'b.conf') + + expect { + new_builder('foo', [], ['a.conf', 'b.conf', 'c.conf']) + }.to raise_error(Bosh::Cli::InvalidJob, + "Some template files required by 'foo' job " + + 'are missing: c.conf') + end + + it 'whines about extra packages' do + add_templates('foo', 'a.conf', 'b.conf') + + expect { + new_builder('foo', [], ['a.conf'], []) + }.to raise_error(Bosh::Cli::InvalidJob, + "There are unused template files for job 'foo'" + + ': b.conf') + end + + it 'whines if some packages are missing' do + expect { + new_builder('foo', ['foo', 'bar', 'baz', 'app42'], { }, ['foo', 'bar']) + }.to raise_error(Bosh::Cli::InvalidJob, + "Some packages required by 'foo' job are missing: " + + 'baz, app42') + end + + it 'whines if there is no spec file' do + expect { + new_builder('foo', ['foo', 'bar', 'baz', 'app42'], { }, + ['foo', 'bar', 'baz', 'app42'], false) + }.to raise_error(Bosh::Cli::InvalidJob, + "Cannot find spec file for 'foo'") + end + + it 'whines if there is no monit file' do + expect { add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - builder = make_builder('foo', ['foo', 'bar', 'baz'], - { 'a.conf' => 1, 'b.yml' => 2 }, - ['foo', 'bar', 'baz']) - expect(builder.templates).to match_array(['a.conf', 'b.yml']) - end - - it 'whines if name is blank' do - expect { - make_builder('') - }.to raise_error(Bosh::Cli::InvalidJob, 'Job name is missing') - end - - it 'whines on funny characters in name' do - expect { - make_builder('@#!', []) - }.to raise_error(Bosh::Cli::InvalidJob, - "`@#!' is not a valid BOSH identifier") - end - - it 'whines if some templates are missing' do - add_templates('foo', 'a.conf', 'b.conf') - - expect { - make_builder('foo', [], ['a.conf', 'b.conf', 'c.conf']) - }.to raise_error(Bosh::Cli::InvalidJob, - "Some template files required by 'foo' job " + - 'are missing: c.conf') - end - - it 'whines about extra packages' do - add_templates('foo', 'a.conf', 'b.conf') - - expect { - make_builder('foo', [], ['a.conf'], []) - }.to raise_error(Bosh::Cli::InvalidJob, - "There are unused template files for job 'foo'" + - ': b.conf') - end - - it 'whines if some packages are missing' do - expect { - make_builder('foo', ['foo', 'bar', 'baz', 'app42'], { }, ['foo', 'bar']) - }.to raise_error(Bosh::Cli::InvalidJob, - "Some packages required by 'foo' job are missing: " + - 'baz, app42') - end - - it 'whines if there is no spec file' do - expect { - make_builder('foo', ['foo', 'bar', 'baz', 'app42'], { }, - ['foo', 'bar', 'baz', 'app42'], false) - }.to raise_error(Bosh::Cli::InvalidJob, - "Cannot find spec file for 'foo'") - end - - it 'whines if there is no monit file' do - expect { - add_templates('foo', 'a.conf', 'b.yml') - make_builder('foo', ['foo', 'bar', 'baz', 'app42'], - ['a.conf', 'b.yml'], ['foo', 'bar', 'baz', 'app42']) - }.to raise_error(Bosh::Cli::InvalidJob, - "Cannot find monit file for 'foo'") - - add_monit('foo') - expect { - make_builder('foo', ['foo', 'bar', 'baz', 'app42'], - ['a.conf', 'b.yml'], ['foo', 'bar', 'baz', 'app42']) - }.not_to raise_error - end - - it 'supports preparation script' do - spec = { - 'name' => 'foo', - 'packages' => ['bar', 'baz'], - 'templates' => ['a.conf', 'b.yml'] - } - spec_yaml = Psych.dump(spec) - - script = <<-SCRIPT.gsub(/^\s*/, '') - #!/bin/sh - mkdir templates - touch templates/a.conf - touch templates/b.yml - echo '#{spec_yaml}' > spec - touch monit - SCRIPT - - add_job('foo', 'prepare', script) - script_path = release_dir.join('jobs', 'foo', 'prepare') - FileUtils.chmod(0755, script_path) - Bosh::Cli::JobBuilder.run_prepare_script(script_path) - - builder = make_builder('foo', ['bar', 'baz'], ['a.conf', 'b.yml'], - ['foo', 'bar', 'baz', 'app42'], false) - expect(builder.copy_files).to eq(4) - - Dir.chdir(builder.build_dir) do - expect(File.directory?('templates')).to be(true) - ['templates/a.conf', 'templates/b.yml'].each do |file| - expect(File.file?(file)).to be(true) - end - expect(File.file?('job.MF')).to be(true) - expect(File.read('job.MF')).to eq(File.read( - release_dir.join('jobs', 'foo', 'spec'))) - expect(File.exists?('monit')).to be(true) - expect(File.exists?('prepare')).to be(false) + new_builder('foo', ['foo', 'bar', 'baz', 'app42'], + ['a.conf', 'b.yml'], ['foo', 'bar', 'baz', 'app42']) + }.to raise_error(Bosh::Cli::InvalidJob, + "Cannot find monit file for 'foo'") + + add_monit('foo') + expect { + new_builder('foo', ['foo', 'bar', 'baz', 'app42'], + ['a.conf', 'b.yml'], ['foo', 'bar', 'baz', 'app42']) + }.not_to raise_error + end + + it 'supports preparation script' do + spec = { + 'name' => 'foo', + 'packages' => ['bar', 'baz'], + 'templates' => ['a.conf', 'b.yml'] + } + spec_yaml = Psych.dump(spec) + + script = <<-SCRIPT.gsub(/^\s*/, '') + #!/bin/sh + mkdir templates + touch templates/a.conf + touch templates/b.yml + echo '#{spec_yaml}' > spec + touch monit + SCRIPT + + add_file('foo', 'prepare', script) + script_path = File.join(@release_dir, 'jobs', 'foo', 'prepare') + FileUtils.chmod(0755, script_path) + Bosh::Cli::JobBuilder.run_prepare_script(script_path) + + builder = new_builder('foo', ['bar', 'baz'], ['a.conf', 'b.yml'], + ['foo', 'bar', 'baz', 'app42'], false) + expect(builder.copy_files).to eq(4) + + Dir.chdir(builder.build_dir) do + expect(File.directory?('templates')).to be(true) + ['templates/a.conf', 'templates/b.yml'].each do |file| + expect(File.file?(file)).to be(true) end + expect(File.file?('job.MF')).to be(true) + expect(File.read('job.MF')).to eq(File.read( + File.join(@release_dir, 'jobs', 'foo', 'spec'))) + expect(File.exists?('monit')).to be(true) + expect(File.exists?('prepare')).to be(false) end - - it 'copies job files' do - add_templates('foo', 'a.conf', 'b.yml') - add_monit('foo') - builder = make_builder('foo', ['foo', 'bar', 'baz', 'app42'], - ['a.conf', 'b.yml'], ['foo', 'bar', 'baz', 'app42']) - - expect(builder.copy_files).to eq(4) - - Dir.chdir(builder.build_dir) do - expect(File.directory?('templates')).to be(true) - ['templates/a.conf', 'templates/b.yml'].each do |file| - expect(File.file?(file)).to be(true) - end - expect(File.file?('job.MF')).to be(true) - expect(File.read('job.MF')).to eq(File.read( - release_dir.join('jobs', 'foo', 'spec'))) - expect(File.exists?('monit')).to be(true) + end + + it 'copies job files' do + add_templates('foo', 'a.conf', 'b.yml') + add_monit('foo') + builder = new_builder('foo', ['foo', 'bar', 'baz', 'app42'], + ['a.conf', 'b.yml'], ['foo', 'bar', 'baz', 'app42']) + + expect(builder.copy_files).to eq(4) + + Dir.chdir(builder.build_dir) do + expect(File.directory?('templates')).to be(true) + ['templates/a.conf', 'templates/b.yml'].each do |file| + expect(File.file?(file)).to be(true) end + expect(File.file?('job.MF')).to be(true) + expect(File.read('job.MF')).to eq(File.read( + File.join(@release_dir, 'jobs', 'foo', 'spec'))) + expect(File.exists?('monit')).to be(true) end - - it 'generates tarball' do - add_templates('foo', 'bar', 'baz') - add_monit('foo') - - builder = make_builder('foo', ['p1', 'p2'], ['bar', 'baz'], ['p1', 'p2']) - expect(builder.generate_tarball).to be(true) - end - - it 'supports versioning' do - add_templates('foo', 'bar', 'baz') - add_monit('foo') - - builder = make_builder('foo', [], ['bar', 'baz'], []) - - v1_fingerprint = builder.fingerprint - expect(release_dir).to_not have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - builder.build - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - - add_templates('foo', 'zb.yml') - builder = make_builder('foo', [], ['bar', 'baz', 'zb.yml'], []) - builder.build - v2_fingerprint = builder.fingerprint - - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v2_fingerprint}.tgz") - - remove_templates('foo', 'zb.yml') - - builder = make_builder('foo', [], ['bar', 'baz'], []) - builder.build - expect(builder.version).to eq(v1_fingerprint) - - expect(builder.fingerprint).to eq(v1_fingerprint) - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v2_fingerprint}.tgz") - end - - it 'can point to either dev or a final version of a job' do - add_templates('foo', 'bar', 'baz') - add_monit('foo') - fingerprint = '44cf6c4f4976f482ec497dfe77e47d876a7a83a1' - - job_name = 'foo' - final_storage_dir = release_dir.join('.final_builds', 'jobs', job_name) - final_versions = Bosh::Cli::Versions::VersionsIndex.new(final_storage_dir) - final_storage = Bosh::Cli::Versions::LocalVersionStorage.new(final_storage_dir) - - dev_storage_dir = release_dir.join('.dev_builds', 'jobs', job_name) - dev_versions = Bosh::Cli::Versions::VersionsIndex.new(dev_storage_dir) - dev_storage = Bosh::Cli::Versions::LocalVersionStorage.new(dev_storage_dir) - - add_version(final_versions, final_storage, - fingerprint, - { 'version' => fingerprint, 'blobstore_id' => '12321' }, - get_tmp_file_path('payload')) - - add_version(dev_versions, dev_storage, - fingerprint, - { 'version' => fingerprint }, - get_tmp_file_path('dev_payload')) - - builder = make_builder(job_name, [], ['bar', 'baz'], []) - - expect(builder.fingerprint).to eq(fingerprint) - - builder.use_final_version - expect(builder.version).to eq(fingerprint) - expect(builder.tarball_path).to eq(release_dir.join( - '.final_builds', 'jobs', job_name, "#{fingerprint}.tgz")) - - builder.use_dev_version - expect(builder.version).to eq(fingerprint) - expect(builder.tarball_path).to eq(release_dir.join( - '.dev_builds', 'jobs', job_name, "#{fingerprint}.tgz")) - end - - it 'bumps major dev version in sync with final version' do - add_templates('foo', 'bar', 'baz') - add_monit('foo') - - builder = make_builder('foo', [], ['bar', 'baz'], []) - builder.build - - expect(builder.version).to eq(builder.fingerprint) - - blobstore = double('blobstore') - expect(blobstore).to receive(:create).and_return('object_id') - final_builder = make_builder('foo', [], ['bar', 'baz'], [], - true, true, blobstore) - final_builder.build - - expect(final_builder.version).to eq(final_builder.fingerprint) - - add_templates('foo', 'bzz') - builder2 = make_builder('foo', [], ['bar', 'baz', 'bzz'], []) - builder2.build - expect(builder2.version).to eq(builder2.fingerprint) - end - - it 'allows template subdirectories' do - add_templates('foo', 'foo/bar', 'bar/baz') - add_monit('foo') - - blobstore = double('blobstore') - builder = make_builder('foo', [], ['foo/bar', 'bar/baz'], - [], true, false, blobstore) - builder.build - - Dir.chdir(builder.build_dir) do - expect(File.directory?('templates')).to be(true) - ['templates/foo/bar', 'templates/bar/baz'].each do |file| - expect(File.file?(file)).to be(true) - end + end + + it 'generates tarball' do + add_templates('foo', 'bar', 'baz') + add_monit('foo') + + builder = new_builder('foo', ['p1', 'p2'], ['bar', 'baz'], ['p1', 'p2']) + expect(builder.generate_tarball).to be(true) + end + + it 'supports versioning' do + add_templates('foo', 'bar', 'baz') + add_monit('foo') + + builder = new_builder('foo', [], ['bar', 'baz'], []) + + v1_fingerprint = builder.fingerprint + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")). + to be(false) + builder.build + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")). + to be(true) + + add_templates('foo', 'zb.yml') + builder = new_builder('foo', [], ['bar', 'baz', 'zb.yml'], []) + builder.build + v2_fingerprint = builder.fingerprint + + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")). + to be(true) + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v2_fingerprint}.tgz")). + to be(true) + + remove_templates('foo', 'zb.yml') + + builder = new_builder('foo', [], ['bar', 'baz'], []) + builder.build + expect(builder.version).to eq(v1_fingerprint) + + expect(builder.fingerprint).to eq(v1_fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")). + to be(true) + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v2_fingerprint}.tgz")). + to be(true) + end + + def add_version(index, storage, key, build, src_file_path) + index.add_version(key, build) + file_path = storage.put_file(key, src_file_path) + build['sha1'] = Digest::SHA1.file(file_path).hexdigest + index.update_version(key, build) + end + + it 'can point to either dev or a final version of a job' do + add_templates('foo', 'bar', 'baz') + add_monit('foo') + fingerprint = '44cf6c4f4976f482ec497dfe77e47d876a7a83a1' + + job_name = 'foo' + final_storage_dir = File.join(@release_dir, '.final_builds', 'jobs', job_name) + final_versions = Bosh::Cli::Versions::VersionsIndex.new(final_storage_dir) + final_storage = Bosh::Cli::Versions::LocalVersionStorage.new(final_storage_dir) + + dev_storage_dir = File.join(@release_dir, '.dev_builds', 'jobs', job_name) + dev_versions = Bosh::Cli::Versions::VersionsIndex.new(dev_storage_dir) + dev_storage = Bosh::Cli::Versions::LocalVersionStorage.new(dev_storage_dir) + + add_version(final_versions, final_storage, + fingerprint, + { 'version' => fingerprint, 'blobstore_id' => '12321' }, + get_tmp_file_path('payload')) + + add_version(dev_versions, dev_storage, + fingerprint, + { 'version' => fingerprint }, + get_tmp_file_path('dev_payload')) + + builder = new_builder(job_name, [], ['bar', 'baz'], []) + + expect(builder.fingerprint).to eq(fingerprint) + + builder.use_final_version + expect(builder.version).to eq(fingerprint) + expect(builder.tarball_path).to eq(File.join( + @release_dir, '.final_builds', 'jobs', job_name, "#{fingerprint}.tgz")) + + builder.use_dev_version + expect(builder.version).to eq(fingerprint) + expect(builder.tarball_path).to eq(File.join( + @release_dir, '.dev_builds', 'jobs', job_name, "#{fingerprint}.tgz")) + end + + it 'bumps major dev version in sync with final version' do + add_templates('foo', 'bar', 'baz') + add_monit('foo') + + builder = new_builder('foo', [], ['bar', 'baz'], []) + builder.build + + expect(builder.version).to eq(builder.fingerprint) + + blobstore = double('blobstore') + expect(blobstore).to receive(:create).and_return('object_id') + final_builder = new_builder('foo', [], ['bar', 'baz'], [], + true, true, blobstore) + final_builder.build + + expect(final_builder.version).to eq(final_builder.fingerprint) + + add_templates('foo', 'bzz') + builder2 = new_builder('foo', [], ['bar', 'baz', 'bzz'], []) + builder2.build + expect(builder2.version).to eq(builder2.fingerprint) + end + + it 'allows template subdirectories' do + add_templates('foo', 'foo/bar', 'bar/baz') + add_monit('foo') + + blobstore = double('blobstore') + builder = new_builder('foo', [], ['foo/bar', 'bar/baz'], + [], true, false, blobstore) + builder.build + + Dir.chdir(builder.build_dir) do + expect(File.directory?('templates')).to be(true) + ['templates/foo/bar', 'templates/bar/baz'].each do |file| + expect(File.file?(file)).to be(true) end end - - it 'supports dry run' do - add_templates('foo', 'bar', 'baz') - add_monit('foo') - - builder = make_builder('foo', [], ['bar', 'baz'], []) - builder.dry_run = true - builder.build - v1_fingerprint = builder.fingerprint - - expect(builder.version).to eq(v1_fingerprint) - expect(release_dir).to_not have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - - builder.dry_run = false - builder.reload.build - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - - blobstore = double('blobstore') - expect(blobstore).not_to receive(:create) - final_builder = make_builder('foo', [], ['bar', 'baz'], [], - true, true, blobstore) - final_builder.dry_run = true - final_builder.build - - # Shouldn't be promoted during dry run: - expect(final_builder.version).to eq(v1_fingerprint) - expect(release_dir).to_not have_file("/.final_builds/jobs/foo/#{v1_fingerprint}.tgz") - - add_templates('foo', 'bzz') - builder2 = make_builder('foo', [], ['bar', 'baz', 'bzz'], []) - builder2.dry_run = true - builder2.build - v2_fingerprint = builder2.fingerprint - expect(builder2.version).to eq(v2_fingerprint) - - expect(release_dir).to have_file("/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz") - expect(release_dir).to_not have_file("/.dev_builds/jobs/foo/#{v2_fingerprint}.tgz") - end end + + it 'supports dry run' do + add_templates('foo', 'bar', 'baz') + add_monit('foo') + + builder = new_builder('foo', [], ['bar', 'baz'], []) + builder.dry_run = true + builder.build + v1_fingerprint = builder.fingerprint + + expect(builder.version).to eq(v1_fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")). + to be(false) + + builder.dry_run = false + builder.reload.build + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")). + to be(true) + + blobstore = double('blobstore') + expect(blobstore).not_to receive(:create) + final_builder = new_builder('foo', [], ['bar', 'baz'], [], + true, true, blobstore) + final_builder.dry_run = true + final_builder.build + + # Shouldn't be promoted during dry run: + expect(final_builder.version).to eq(v1_fingerprint) + expect(File.exists?(@release_dir + "/.final_builds/jobs/foo/#{v1_fingerprint}.tgz")).to be(false) + + add_templates('foo', 'bzz') + builder2 = new_builder('foo', [], ['bar', 'baz', 'bzz'], []) + builder2.dry_run = true + builder2.build + v2_fingerprint = builder2.fingerprint + expect(builder2.version).to eq(v2_fingerprint) + + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v1_fingerprint}.tgz")).to be(true) + expect(File.exists?(@release_dir + "/.dev_builds/jobs/foo/#{v2_fingerprint}.tgz")).to be(false) + end + end diff --git a/bosh_cli/spec/unit/license_builder_spec.rb b/bosh_cli/spec/unit/license_builder_spec.rb deleted file mode 100644 index d13856ae59f..00000000000 --- a/bosh_cli/spec/unit/license_builder_spec.rb +++ /dev/null @@ -1,181 +0,0 @@ -require 'spec_helper' - -describe Bosh::Cli::LicenseBuilder, 'dev build' do - with_release_directory do |dir| - let(:release_dir) { dir } - let(:basedir) { nil } - - def make_builder(final = false) - blobstore = double('blobstore') - Bosh::Cli::LicenseBuilder.new(release_dir.path, final, blobstore) - end - - describe 'copying package files' do - let(:builder) { make_builder } - - before do - release_dir.add_file(basedir, 'LICENSE') - release_dir.add_file(basedir, 'NOTICE') - end - - it 'copies the LICENSE file' do - builder.copy_files - expect(File).to exist(File.join(builder.build_dir, 'LICENSE')) - end - - it 'copies the NOTICE file' do - builder.copy_files - expect(File).to exist(File.join(builder.build_dir, 'NOTICE')) - end - - it 'does not copy undesired files' do - release_dir.add_file(basedir, 'AUTHORS') - builder.copy_files - expect(File).to_not exist(File.join(builder.build_dir, 'AUTHORS')) - end - - it 'warns when there is no LICENSE file' do - release_dir.remove_file(basedir, 'LICENSE') - - expect(builder).to receive(:warn) - .with("Does not contain LICENSE within #{release_dir.path}") - builder.copy_files - end - - it 'warns when there is no NOTICE file' do - release_dir.remove_file(basedir, 'NOTICE') - - expect(builder).to receive(:warn) - .with("Does not contain NOTICE within #{release_dir.path}") - builder.copy_files - end - end - - describe 'the checksum' do - let(:builder) { make_builder } - - before do - release_dir.add_file(basedir, 'LICENSE') - release_dir.add_file(basedir, 'NOTICE') - end - - it 'exists when the builder has built' do - builder.build - expect(builder.checksum).to match(/[0-9a-f]+/) - end - - it 'raises an exception if not yet built' do - expect { - builder.checksum - }.to raise_error(RuntimeError, - 'cannot read checksum for not yet ' + - 'generated package/job/license') - end - end - - describe 'the fingerprint' do - let(:builder) { make_builder } - - before do - release_dir.add_file(basedir, 'LICENSE') - release_dir.add_file(basedir, 'NOTICE') - end - - it 'is stable' do - expect { - builder.reload - }.to_not change { builder.fingerprint } - end - end - - describe 'generating a tarball' do - let(:builder) { make_builder } - - before do - release_dir.add_file(basedir, 'LICENSE', '1') - release_dir.add_file(basedir, 'NOTICE', '1') - end - - it 'includes the fingerprint in the archive filename' do - builder.generate_tarball - expect(release_dir).to have_file(".dev_builds/license/#{builder.fingerprint}.tgz") - end - end - - describe 'building the license archive' do - let(:builder) { make_builder } - - before do - release_dir.add_file(basedir, 'LICENSE', '1') - release_dir.add_file(basedir, 'NOTICE', '1') - end - - it 'includes the fingerprint in the archive filename' do - builder.build - expect(release_dir).to have_file(".dev_builds/license/#{builder.fingerprint}.tgz") - end - - it 'creates a new version when the LICENSE is updated' do - builder.build - v1_fingerprint = builder.fingerprint - - expect(release_dir).to have_file(".dev_builds/license/#{v1_fingerprint}.tgz") - - release_dir.add_file(basedir, 'LICENSE', '2') - builder = make_builder() - builder.build - - expect(builder.fingerprint).to_not eq(v1_fingerprint) - expect(release_dir).to have_file(".dev_builds/license/#{builder.fingerprint}.tgz") - end - - it 'creates a new version when the NOTICE is updated' do - builder.build - v1_fingerprint = builder.fingerprint - - expect(release_dir).to have_file(".dev_builds/license/#{v1_fingerprint}.tgz") - - release_dir.add_file(basedir, 'NOTICE', '2') - builder = make_builder() - builder.build - - expect(builder.fingerprint).to_not eq(v1_fingerprint) - expect(release_dir).to have_file(".dev_builds/license/#{builder.fingerprint}.tgz") - end - - context 'building dev and final versions' do - let(:fingerprint) { 'fake-digest' } - - before do - allow(Digest::SHA1).to receive(:hexdigest).and_return(fingerprint) - release_dir.add_file(basedir, 'LICENSE', '1') - release_dir.add_file(basedir, 'NOTICE', '1') - end - - it 'successfully builds a dev version' do - storage_dir = '.dev_builds/license' - - release_dir.add_version(fingerprint, storage_dir, 'payload', - { 'version' => fingerprint }) - - builder = make_builder - builder.use_dev_version - - expect(builder.tarball_path).to eql(release_dir.join(storage_dir, "#{fingerprint}.tgz")) - end - - it 'successfully builds a final version' do - storage_dir = '.final_builds/license' - - release_dir.add_version(fingerprint, storage_dir, 'payload', - { 'version' => fingerprint, 'blobstore_id' => '12321' }) - - builder = make_builder(true) - builder.use_final_version - - expect(builder.tarball_path).to eql(release_dir.join(storage_dir, "#{fingerprint}.tgz")) - end - end - end - end -end diff --git a/bosh_cli/spec/unit/package_builder_spec.rb b/bosh_cli/spec/unit/package_builder_spec.rb index 4691ed0ec19..9164230d4cf 100644 --- a/bosh_cli/spec/unit/package_builder_spec.rb +++ b/bosh_cli/spec/unit/package_builder_spec.rb @@ -1,501 +1,539 @@ require 'spec_helper' describe Bosh::Cli::PackageBuilder, 'dev build' do - with_release_directory do |dir| - let(:release_dir) { dir } + before do + @release_dir = Dir.mktmpdir + FileUtils.mkdir(File.join(@release_dir, 'src')) + FileUtils.mkdir(File.join(@release_dir, 'blobs')) + FileUtils.mkdir(File.join(@release_dir, 'src_alt')) + end - before do - release_dir.add_dir('blobs') - release_dir.add_dir('src') - release_dir.add_dir('src_alt') + def add_file(dir, path, contents = nil) + full_path = File.join(@release_dir, dir, path) + FileUtils.mkdir_p(File.dirname(full_path)) + if contents + File.open(full_path, 'w') { |f| f.write(contents) } + else + FileUtils.touch(full_path) end + end - def make_builder(name, files, dependencies = [], sources_dir = nil, excluded_files=[]) - blobstore = double('blobstore') - spec = { - 'name' => name, - 'files' => files, - 'dependencies' => dependencies, - 'excluded_files' => excluded_files, - } - - Bosh::Cli::PackageBuilder.new(spec, release_dir.path, - false, blobstore, sources_dir) - end + def remove_file(dir, path) + FileUtils.rm(File.join(@release_dir, dir, path)) + end - it 'whines on missing name' do - expect { - make_builder(' ', []) - }.to raise_error(Bosh::Cli::InvalidPackage, 'Package name is missing') - end + def add_files(dir, names) + names.each { |name| add_file(dir, name) } + end - it 'whines on funny characters in name' do - expect { - make_builder('@#!', []) - }.to raise_error(Bosh::Cli::InvalidPackage, - 'Package name should be a valid BOSH identifier') - end + def remove_files(dir, names) + names.each { |name| remove_file(dir, name) } + end - it 'whines on empty files' do - expect { - make_builder('aa', []) - }.to raise_error(Bosh::Cli::InvalidPackage, "Package 'aa' doesn't include any files") - end + def make_builder(name, files, dependencies = [], sources_dir = nil, excluded_files=[]) + blobstore = double('blobstore') + spec = { + 'name' => name, + 'files' => files, + 'dependencies' => dependencies, + 'excluded_files' => excluded_files, + } + + Bosh::Cli::PackageBuilder.new(spec, @release_dir, + false, blobstore, sources_dir) + end - it 'whines on metadata file having the same name as one of package files' do - expect { - builder = make_builder('aa', %w(*.rb packaging)) + it 'whines on missing name' do + expect { + make_builder(' ', []) + }.to raise_error(Bosh::Cli::InvalidPackage, 'Package name is missing') + end - release_dir.add_files('src', %w(1.rb packaging)) + it 'whines on funny characters in name' do + expect { + make_builder('@#!', []) + }.to raise_error(Bosh::Cli::InvalidPackage, + 'Package name should be a valid BOSH identifier') + end - expect(builder.glob_matches.size).to eql(2) - release_dir.add_file('packages', 'aa/packaging', 'make install') + it 'whines on empty files' do + expect { + make_builder('aa', []) + }.to raise_error(Bosh::Cli::InvalidPackage, "Package 'aa' doesn't include any files") + end - builder.copy_files - }.to raise_error(Bosh::Cli::InvalidPackage, - "Package 'aa' has 'packaging' file which " + - 'conflicts with BOSH packaging') - end + it 'whines on metadata file having the same name as one of package files' do + expect { + builder = make_builder('aa', %w(*.rb packaging)) - it 'whines on globs not yielding any file names' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb baz)) - builder = make_builder('foo', %w(lib/*.rb baz bar)) + add_files('src', %w(1.rb packaging)) - expect { - builder.build - }.to raise_error(Bosh::Cli::InvalidPackage, - "Package `foo' has a glob that resolves " + - 'to an empty file list: bar') - end + expect(builder.glob_matches.size).to eql(2) + add_file('packages', 'aa/packaging', 'make install') - it 'has no way to calculate checksum for not yet generated package' do - expect { - builder = make_builder('aa', %w(*.rb packaging)) - release_dir.add_files('src', %w(1.rb packaging)) - builder.checksum - }.to raise_error(RuntimeError, - 'cannot read checksum for not yet ' + - 'generated package/job/license') - end + builder.copy_files + }.to raise_error(Bosh::Cli::InvalidPackage, + "Package 'aa' has 'packaging' file which " + + 'conflicts with BOSH packaging') + end + + it 'whines on globs not yielding any file names' do + add_files('src', %w(lib/1.rb lib/2.rb baz)) + builder = make_builder('foo', %w(lib/*.rb baz bar)) - it 'has a checksum for a generated package' do - builder = make_builder('aa', %w(*.rb)) - release_dir.add_files('src', %w(1.rb 2.rb)) + expect { builder.build - expect(builder.checksum).to match(/[0-9a-f]+/) - end + }.to raise_error(Bosh::Cli::InvalidPackage, + "Package `foo' has a glob that resolves " + + 'to an empty file list: bar') + end - it 'is created with name and globs' do - builder = make_builder('aa', %w(1 */*)) - expect(builder.name).to eql('aa') - expect(builder.globs).to eql(%w(1 */*)) - end + it 'has no way to calculate checksum for not yet generated package' do + expect { + builder = make_builder('aa', %w(*.rb packaging)) + add_files('src', %w(1.rb packaging)) + builder.checksum + }.to raise_error(RuntimeError, + 'cannot read checksum for not yet ' + + 'generated package/job') + end - it 'resolves globs and generates fingerprint' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) + it 'has a checksum for a generated package' do + builder = make_builder('aa', %w(*.rb)) + add_files('src', %w(1.rb 2.rb)) + builder.build + expect(builder.checksum).to match(/[0-9a-f]+/) + end - builder = make_builder('A', %w(lib/*.rb README.*)) - expect(builder.glob_matches.size).to eql(4) - expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') - end + it 'is created with name and globs' do + builder = make_builder('aa', %w(1 */*)) + expect(builder.name).to eql('aa') + expect(builder.globs).to eql(%w(1 */*)) + end - it 'has stable fingerprint' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) - builder = make_builder('A', %w(lib/*.rb README.*)) - s1 = builder.fingerprint + it 'resolves globs and generates fingerprint' do + add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) - expect(builder.reload.fingerprint).to eql(s1) - end + builder = make_builder('A', %w(lib/*.rb README.*)) + expect(builder.glob_matches.size).to eql(4) + expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') + end - it 'changes fingerprint when new file that matches glob is added' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) + it 'has stable fingerprint' do + add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) + builder = make_builder('A', %w(lib/*.rb README.*)) + s1 = builder.fingerprint - builder = make_builder('A', %w(lib/*.rb README.*)) - s1 = builder.fingerprint - release_dir.add_files('src', %w(lib/3.rb)) - expect(builder.reload.fingerprint).to_not eql(s1) + expect(builder.reload.fingerprint).to eql(s1) + end - release_dir.remove_files('src', %w(lib/3.rb)) - expect(builder.reload.fingerprint).to eql(s1) - end + it 'changes fingerprint when new file that matches glob is added' do + add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) - it 'changes fingerprint when one of the matched files changes' do - release_dir.add_files('src', %w(lib/2.rb lib/README.txt README.2 README.md)) - release_dir.add_file('src', 'lib/1.rb', '1') + builder = make_builder('A', %w(lib/*.rb README.*)) + s1 = builder.fingerprint + add_files('src', %w(lib/3.rb)) + expect(builder.reload.fingerprint).to_not eql(s1) - builder = make_builder('A', %w(lib/*.rb README.*)) - s1 = builder.fingerprint + remove_files('src', %w(lib/3.rb)) + expect(builder.reload.fingerprint).to eql(s1) + end - release_dir.add_file('src', 'lib/1.rb', '2') - expect(builder.reload.fingerprint).to_not eql(s1) + it 'changes fingerprint when one of the matched files changes' do + add_files('src', %w(lib/2.rb lib/README.txt README.2 README.md)) + add_file('src', 'lib/1.rb', '1') - release_dir.add_file('src', 'lib/1.rb', '1') - expect(builder.reload.fingerprint).to eql(s1) - end + builder = make_builder('A', %w(lib/*.rb README.*)) + s1 = builder.fingerprint - it 'changes fingerprint when empty directory added/removed' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb baz)) - builder = make_builder('foo', %w(lib/*.rb baz bar/*)) - release_dir.add_dir('src/bar/zb') + add_file('src', 'lib/1.rb', '2') + expect(builder.reload.fingerprint).to_not eql(s1) - s1 = builder.fingerprint + add_file('src', 'lib/1.rb', '1') + expect(builder.reload.fingerprint).to eql(s1) + end - release_dir.add_dir('src/bar/zb2') - s2 = builder.reload.fingerprint - expect(s2).to_not eql(s1) + it 'changes fingerprint when empty directory added/removed' do + add_files('src', %w(lib/1.rb lib/2.rb baz)) + builder = make_builder('foo', %w(lib/*.rb baz bar/*)) + FileUtils.mkdir_p(File.join(@release_dir, 'src', 'bar', 'zb')) - release_dir.remove_dir('src/bar/zb2') - expect(builder.reload.fingerprint).to eql(s1) - end + s1 = builder.fingerprint - it "doesn't change fingerprint when files that doesn't match glob is added" do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) - builder = make_builder('A', %w(lib/*.rb README.*)) - s1 = builder.fingerprint + FileUtils.mkdir_p(File.join(@release_dir, 'src', 'bar', 'zb2')) + s2 = builder.reload.fingerprint + expect(s2).to_not eql(s1) - release_dir.add_file('src', 'lib/a.out') - expect(builder.reload.fingerprint).to eql(s1) - end + FileUtils.rm_rf(File.join(@release_dir, 'src', 'bar', 'zb2')) + expect(builder.reload.fingerprint).to eql(s1) + end - it 'changes fingerprint when dependencies change' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) + it "doesn't change fingerprint when files that doesn't match glob is added" do + add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) + builder = make_builder('A', %w(lib/*.rb README.*)) + s1 = builder.fingerprint - builder1 = make_builder('A', %w(lib/*.rb README.*), %w(foo bar)) - s1 = builder1.fingerprint - builder2 = make_builder('A', %w(lib/*.rb README.*), %w(bar foo)) - s2 = builder2.fingerprint - expect(s1).to eql(s2) # Order doesn't matter + add_file('src', 'lib/a.out') + expect(builder.reload.fingerprint).to eql(s1) + end - builder3 = make_builder('A', %w(lib/*.rb README.*), %w(bar foo baz)) - s3 = builder3.fingerprint - expect(s3).to_not eql(s1) # Set does matter - end + it 'changes fingerprint when dependencies change' do + add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) - it 'copies files to build directory' do - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - globs = %w(foo/**/* baz) + builder1 = make_builder('A', %w(lib/*.rb README.*), %w(foo bar)) + s1 = builder1.fingerprint + builder2 = make_builder('A', %w(lib/*.rb README.*), %w(bar foo)) + s2 = builder2.fingerprint + expect(s1).to eql(s2) # Order doesn't matter - builder = make_builder('bar', globs) - expect(builder.copy_files).to eql(5) + builder3 = make_builder('A', %w(lib/*.rb README.*), %w(bar foo baz)) + s3 = builder3.fingerprint + expect(s3).to_not eql(s1) # Set does matter + end - builder2 = make_builder('bar', globs, [], builder.build_dir) + it 'copies files to build directory' do + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + globs = %w(foo/**/* baz) - # Also turned out to be a nice test for directory portability - expect(builder.fingerprint).to eql(builder2.fingerprint) - end + builder = make_builder('bar', globs) + expect(builder.copy_files).to eql(5) - it 'excludes excluded_files from build directory' do - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README foo/.git baz)) - release_dir.add_files('blobs', %w(bar/bar.tgz bar/fake.tgz)) - globs = %w(foo/**/* baz bar/**) - excluded_globs = %w(foo/.git bar/fake.tgz) + builder2 = make_builder('bar', globs, [], builder.build_dir) - builder = make_builder('bar', globs, [], nil, excluded_globs) + # Also turned out to be a nice test for directory portability + expect(builder.fingerprint).to eql(builder2.fingerprint) + end - expect(builder.copy_files).to eq(6) - excluded_file = File.join(builder.build_dir, 'foo', '.git') - expect(File).to_not exist(excluded_file) + it 'excludes excluded_files from build directory' do + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README foo/.git baz)) + add_files('blobs', %w(bar/bar.tgz bar/fake.tgz)) + globs = %w(foo/**/* baz bar/**) + excluded_globs = %w(foo/.git bar/fake.tgz) - excluded_blob_file = File.join(builder.build_dir, 'blobs', 'bar.tgz') - expect(File).to_not exist(excluded_blob_file) - end + builder = make_builder('bar', globs, [], nil, excluded_globs) - it 'generates tarball' do - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - builder = make_builder('bar', %w(foo/**/* baz)) - expect(builder.generate_tarball).to eql(true) - end + expect(builder.copy_files).to eq(6) + excluded_file = File.join(builder.build_dir, 'foo', '.git') + expect(File).to_not exist(excluded_file) - it 'can point to either dev or a final version of a package' do - fingerprint = 'fake-fingerprint' - allow(Digest::SHA1).to receive(:hexdigest).and_return(fingerprint) + excluded_blob_file = File.join(builder.build_dir, 'blobs', 'bar.tgz') + expect(File).to_not exist(excluded_blob_file) + end - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - globs = %w(foo/**/* baz) + it 'generates tarball' do + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + builder = make_builder('bar', %w(foo/**/* baz)) + expect(builder.generate_tarball).to eql(true) + end - package_name = 'bar' - final_storage_dir = ".final_builds/packages/#{package_name}" - dev_storage_dir = ".dev_builds/packages/#{package_name}" + def add_version(index, storage, key, build, src_file_path) + index.add_version(key, build) + file_path = storage.put_file(key, src_file_path) + build['sha1'] = Digest::SHA1.file(file_path).hexdigest + index.update_version(key, build) + end - release_dir.add_version(fingerprint, final_storage_dir, 'payload', - { 'version' => fingerprint, 'blobstore_id' => '12321' }) + it 'can point to either dev or a final version of a package' do + fingerprint = 'fake-fingerprint' + allow(Digest::SHA1).to receive(:hexdigest).and_return(fingerprint) - release_dir.add_version(fingerprint, dev_storage_dir, 'dev_payload', - { 'version' => fingerprint }) + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + globs = %w(foo/**/* baz) - builder = make_builder(package_name, globs) + package_name = 'bar' + final_storage_dir = File.join(@release_dir, '.final_builds', 'packages', package_name) + final_versions = Bosh::Cli::Versions::VersionsIndex.new(final_storage_dir) + final_storage = Bosh::Cli::Versions::LocalVersionStorage.new(final_storage_dir) - builder.use_final_version - expect(builder.tarball_path).to eql(release_dir.join('.final_builds', 'packages', package_name, "#{fingerprint}.tgz")) + dev_storage_dir = File.join(@release_dir, '.dev_builds', 'packages', package_name) + dev_versions = Bosh::Cli::Versions::VersionsIndex.new(dev_storage_dir) + dev_storage = Bosh::Cli::Versions::LocalVersionStorage.new(dev_storage_dir) - builder.use_dev_version - expect(builder.tarball_path).to eql(release_dir.join('.dev_builds', 'packages', package_name, "#{fingerprint}.tgz")) - end + add_version(final_versions, final_storage, + fingerprint, + { 'version' => fingerprint, 'blobstore_id' => '12321' }, + get_tmp_file_path('payload')) - it 'creates a new version tarball' do - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - globs = %w(foo/**/* baz) - builder = make_builder('bar', globs) + add_version(dev_versions, dev_storage, + fingerprint, + { 'version' => fingerprint }, + get_tmp_file_path('dev_payload')) - v1_fingerprint = builder.fingerprint + builder = make_builder(package_name, globs) - expect(release_dir).to_not have_file(".dev_builds/packages/bar/#{v1_fingerprint}.tgz") - builder.build - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v1_fingerprint}.tgz") + builder.use_final_version + expect(builder.tarball_path).to eql(File.join( + @release_dir, '.final_builds', 'packages', package_name, "#{fingerprint}.tgz")) - builder = make_builder('bar', globs) - builder.build + builder.use_dev_version + expect(builder.tarball_path).to eql(File.join( + @release_dir, '.dev_builds', 'packages', package_name, "#{fingerprint}.tgz")) + end - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v1_fingerprint}.tgz") - expect(release_dir).to_not have_file(".dev_builds/packages/bar/other-fingerprint.tgz") + it 'creates a new version tarball' do + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + globs = %w(foo/**/* baz) + builder = make_builder('bar', globs) - release_dir.add_file('src', 'foo/3.rb') - builder = make_builder('bar', globs) - builder.build + v1_fingerprint = builder.fingerprint - v2_fingerprint = builder.fingerprint + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v1_fingerprint}.tgz")).to eql(false) + builder.build + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v1_fingerprint}.tgz")).to eql(true) - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v1_fingerprint}.tgz") - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v2_fingerprint}.tgz") + builder = make_builder('bar', globs) + builder.build - release_dir.remove_file('src', 'foo/3.rb') - builder = make_builder('bar', globs) - builder.build - expect(builder.version).to eql(v1_fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v1_fingerprint}.tgz")).to eql(true) + expect(File.exists?(@release_dir + '/.dev_builds/packages/bar/other-fingerprint.tgz')).to eql(false) - expect(builder.fingerprint).to eql(v1_fingerprint) + add_file('src', 'foo/3.rb') + builder = make_builder('bar', globs) + builder.build - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v1_fingerprint}.tgz") - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v2_fingerprint}.tgz") + v2_fingerprint = builder.fingerprint - # Now add packaging - release_dir.add_file('packages', 'bar/packaging', 'make install') - builder = make_builder('bar', globs) - builder.build - v3_fingerprint = builder.fingerprint - expect(builder.version).to eql(v3_fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v1_fingerprint}.tgz")).to eql(true) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v2_fingerprint}.tgz")).to eql(true) - # Add prepackaging - release_dir.add_file('packages', 'bar/pre_packaging', 'echo 0; exit 0') - builder = make_builder('bar', globs) - v4_fingerprint = builder.fingerprint + remove_file('src', 'foo/3.rb') + builder = make_builder('bar', globs) + builder.build + expect(builder.version).to eql(v1_fingerprint) - builder.build + expect(builder.fingerprint).to eql(v1_fingerprint) - expect(release_dir).to have_file(".dev_builds/packages/bar/#{v4_fingerprint}.tgz") - end + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v1_fingerprint}.tgz")).to eql(true) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v2_fingerprint}.tgz")).to eql(true) - it 'stops if pre_packaging fails' do - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - globs = %w(foo/**/* baz) + # Now add packaging + add_file('packages', 'bar/packaging', 'make install') + builder = make_builder('bar', globs) + builder.build + v3_fingerprint = builder.fingerprint + expect(builder.version).to eql(v3_fingerprint) - builder = make_builder('bar', globs) - release_dir.add_file('packages', 'bar/pre_packaging', 'exit 1') + # Add prepackaging + add_file('packages', 'bar/pre_packaging', 'echo 0; exit 0') + builder = make_builder('bar', globs) + v4_fingerprint = builder.fingerprint - expect { - builder.build - }.to raise_error(Bosh::Cli::InvalidPackage, - "`bar' pre-packaging failed") - end + builder.build - it 'bumps major dev version in sync with final version' do - release_dir.remove_dir('src_alt') - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - globs = %w(foo/**/* baz) - builder = make_builder('bar', globs) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{v4_fingerprint}.tgz")).to eql(true) + end + + it 'stops if pre_packaging fails' do + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + globs = %w(foo/**/* baz) + + builder = make_builder('bar', globs) + add_file('packages', 'bar/pre_packaging', 'exit 1') + + expect { builder.build + }.to raise_error(Bosh::Cli::InvalidPackage, + "`bar' pre-packaging failed") + end - expect(builder.version).to eql(builder.fingerprint) + it 'bumps major dev version in sync with final version' do + FileUtils.rm_rf(File.join(@release_dir, 'src_alt')) - blobstore = double('blobstore') - expect(blobstore).to receive(:create).and_return('object_id') - final_builder = Bosh::Cli::PackageBuilder.new({ 'name' => 'bar', - 'files' => globs }, - release_dir.path, - true, blobstore) - final_builder.build - expect(final_builder.version).to eql(builder.fingerprint) + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + globs = %w(foo/**/* baz) + builder = make_builder('bar', globs) + builder.build - release_dir.add_file('src', 'foo/foo15.rb') + expect(builder.version).to eql(builder.fingerprint) - builder2 = make_builder('bar', globs) - builder2.build - expect(builder2.version).to eql(builder2.fingerprint) + blobstore = double('blobstore') + expect(blobstore).to receive(:create).and_return('object_id') + final_builder = Bosh::Cli::PackageBuilder.new({ 'name' => 'bar', + 'files' => globs }, + @release_dir, + true, blobstore) + final_builder.build + expect(final_builder.version).to eql(builder.fingerprint) - expect(builder2.version).to_not eq(builder.version) - end + add_file('src', 'foo/foo15.rb') + + builder2 = make_builder('bar', globs) + builder2.build + expect(builder2.version).to eql(builder2.fingerprint) - it 'includes dotfiles in a fingerprint' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) + expect(builder2.version).to_not eq(builder.version) + end - builder = make_builder('A', %w(lib/*.rb README.*)) - expect(builder.glob_matches.size).to eql(4) - expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') + it 'includes dotfiles in a fingerprint' do + add_files('src', %w(lib/1.rb lib/2.rb lib/README.txt README.2 README.md)) - release_dir.add_file('src', 'lib/.zb.rb') - builder.reload + builder = make_builder('A', %w(lib/*.rb README.*)) + expect(builder.glob_matches.size).to eql(4) + expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') - expect(builder.glob_matches.size).to eql(5) - expect(builder.fingerprint).to eql('8e07f3d3176170c0e17baa9e2ad4e9b8b38d024a') + add_file('src', 'lib/.zb.rb') + builder.reload - release_dir.remove_file('src', 'lib/.zb.rb') - builder.reload + expect(builder.glob_matches.size).to eql(5) + expect(builder.fingerprint).to eql('8e07f3d3176170c0e17baa9e2ad4e9b8b38d024a') - expect(builder.glob_matches.size).to eql(4) - expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') - end + remove_file('src', 'lib/.zb.rb') + builder.reload - it 'supports dry run' do - release_dir.remove_dir('src_alt') + expect(builder.glob_matches.size).to eql(4) + expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') + end - release_dir.add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) - globs = %w(foo/**/* baz) - builder = make_builder('bar', globs) - builder.dry_run = true - builder.build + it 'supports dry run' do + FileUtils.rm_rf(File.join(@release_dir, 'src_alt')) + + add_files('src', %w(foo/foo.rb foo/lib/1.rb foo/lib/2.rb foo/README baz)) + globs = %w(foo/**/* baz) + builder = make_builder('bar', globs) + builder.dry_run = true + builder.build + + expect(builder.version).to eql(builder.fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{builder.fingerprint}.tgz")).to eql(false) + + builder.dry_run = false + builder.reload.build + expect(builder.version).to eql(builder.fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{builder.fingerprint}.tgz")).to eql(true) + + blobstore = double('blobstore') + expect(blobstore).to_not receive(:create) + final_builder = Bosh::Cli::PackageBuilder.new( + { 'name' => 'bar', 'files' => globs }, @release_dir, true, blobstore) + final_builder.dry_run = true + final_builder.build + + # Hasn't been promoted b/c of dry run + expect(final_builder.version).to eql(builder.version) + + add_file('src', 'foo/foo15.rb') + builder2 = make_builder('bar', globs) + builder2.dry_run = true + builder2.build + expect(builder2.version).to eql(builder2.fingerprint) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{builder.fingerprint}.tgz")).to eql(true) + expect(File.exists?(@release_dir + "/.dev_builds/packages/bar/#{builder2.fingerprint}.tgz")).to eql(false) + end - expect(builder.version).to eql(builder.fingerprint) - expect(release_dir).to_not have_file(".dev_builds/packages/bar/#{builder.fingerprint}.tgz") - - builder.dry_run = false - builder.reload.build - expect(builder.version).to eql(builder.fingerprint) - expect(release_dir).to have_file(".dev_builds/packages/bar/#{builder.fingerprint}.tgz") - - blobstore = double('blobstore') - expect(blobstore).to_not receive(:create) - final_builder = Bosh::Cli::PackageBuilder.new( - { 'name' => 'bar', 'files' => globs }, release_dir.path, true, blobstore) - final_builder.dry_run = true - final_builder.build - - # Hasn't been promoted b/c of dry run - expect(final_builder.version).to eql(builder.version) - - release_dir.add_file('src', 'foo/foo15.rb') - builder2 = make_builder('bar', globs) - builder2.dry_run = true - builder2.build - expect(builder2.version).to eql(builder2.fingerprint) - expect(release_dir).to have_file(".dev_builds/packages/bar/#{builder.fingerprint}.tgz") - expect(release_dir).to_not have_file(".dev_builds/packages/bar/#{builder2.fingerprint}.tgz") - end + it 'uses blobs directory to look up files as well' do + add_files('src', %w(lib/1.rb lib/2.rb)) + add_files('blobs', %w(lib/README.txt README.2 README.md)) - it 'uses blobs directory to look up files as well' do - release_dir.add_files('src', %w(lib/1.rb lib/2.rb)) - release_dir.add_files('blobs', %w(lib/README.txt README.2 README.md)) + builder = make_builder('A', %w(lib/*.rb README.*)) + expect(builder.glob_matches.size).to eql(4) + expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') + end - builder = make_builder('A', %w(lib/*.rb README.*)) - expect(builder.glob_matches.size).to eql(4) - expect(builder.fingerprint).to eql('167bd0b339d78606cf00a8740791b54b1cf619a6') + it "moving files to blobs directory doesn't change fingerprint" do + add_file('src', 'README.txt', 'README contents') + add_file('src', 'README.md', 'README contents 2') + add_file('src', 'lib/1.rb', "puts 'Hello world'") + add_file('src', 'lib/2.rb', "puts 'Bye world'") + + builder = make_builder('A', %w(lib/*.rb README.*)) + s1 = builder.fingerprint + + FileUtils.mkdir_p(File.join(@release_dir, 'blobs', 'lib')) + + FileUtils.mv(File.join(@release_dir, 'src', 'lib', '1.rb'), + File.join(@release_dir, 'blobs', 'lib', '1.rb')) + + s2 = builder.reload.fingerprint + expect(s2).to eql(s1) + end + + it "doesn't include the same path twice" do + add_file('src', 'test/foo/README.txt', 'README contents') + add_file('src', 'test/foo/NOTICE.txt', 'NOTICE contents') + fp1 = make_builder('A', %w(test/**/*)).fingerprint + + remove_file('src', 'test/foo/NOTICE.txt') # src has test/foo + add_file('blobs', 'test/foo/NOTICE.txt', 'NOTICE contents') # blobs has test/foo + + expect(File.directory?(File.join(@release_dir, 'src', 'test', 'foo'))).to eql(true) + + fp2 = make_builder('A', %w(test/**/*)).fingerprint + expect(fp1).to eql(fp2) + end + + describe 'file overriding via src_alt' do + it 'includes top-level files from src_alt instead of src' do + add_file('src', 'file1', 'original') + + builder = make_builder('A', %w(file*)) + s1 = builder.fingerprint + + add_file('src', 'file1', 'altered') + add_file('src_alt', 'file1', 'original') + expect(builder.reload.fingerprint).to eql(s1) end - it "moving files to blobs directory doesn't change fingerprint" do - release_dir.add_file('src', 'README.txt', 'README contents') - release_dir.add_file('src', 'README.md', 'README contents 2') - release_dir.add_file('src', 'lib/1.rb', "puts 'Hello world'") - release_dir.add_file('src', 'lib/2.rb', "puts 'Bye world'") + it 'includes top-level files from src if not present in src_alt' do + add_file('src', 'file1', 'original1') + add_file('src', 'file2', 'original2') + builder = make_builder('A', %w(file*)) + s1 = builder.fingerprint - builder = make_builder('A', %w(lib/*.rb README.*)) + add_file('src', 'file1', 'altered1') + add_file('src_alt', 'file1', 'original1') + expect(builder.reload.fingerprint).to eql(s1) + end + + it 'includes top-level-dir files from src_alt instead of src' do + add_file('src', 'dir1/file1', 'original1') + builder = make_builder('A', %w(dir1/*)) s1 = builder.fingerprint - release_dir.add_dir('blobs/lib') + add_file('src', 'dir1/file1', 'altered1') + add_file('src_alt', 'dir1/file1', 'original1') + expect(builder.reload.fingerprint).to eql(s1) + end - FileUtils.mv(release_dir.join('src', 'lib', '1.rb'), - release_dir.join('blobs', 'lib', '1.rb')) + it 'does not include top-level-dir files from src if not present in src_alt' do + add_file('src', 'dir1/file1', 'original1') + builder = make_builder('A', %w(dir1/*)) + s1 = builder.fingerprint - s2 = builder.reload.fingerprint - expect(s2).to eql(s1) + add_file('src', 'dir1/file2', 'new2') + add_file('src_alt', 'dir1/file1', 'original1') + expect(builder.reload.fingerprint).to eql(s1) end - it "doesn't include the same path twice" do - release_dir.add_file('src', 'test/foo/README.txt', 'README contents') - release_dir.add_file('src', 'test/foo/NOTICE.txt', 'NOTICE contents') - fp1 = make_builder('A', %w(test/**/*)).fingerprint + it "checks if glob top-level-dir is present in src_alt but doesn't match" do + add_file('src', 'dir1/file1', 'original1') + FileUtils.mkdir(File.join(@release_dir, 'src_alt', 'dir1')) - release_dir.remove_file('src', 'test/foo/NOTICE.txt') # src has test/foo - release_dir.add_file('blobs', 'test/foo/NOTICE.txt', 'NOTICE contents') # blobs has test/foo + builder = make_builder('A', %w(dir1/*)) - expect(release_dir).to have_file("src/test/foo") + expect { + builder.fingerprint + }.to raise_error( + "Package `A' has a glob that doesn't match " + + "in `src_alt' but matches in `src'. However " + + "`src_alt/dir1' exists, so this might be an error." + ) + end - fp2 = make_builder('A', %w(test/**/*)).fingerprint - expect(fp1).to eql(fp2) + it 'raises an error if glob does not match any files in src or src_alt' do + builder = make_builder('A', %w(dir1/*)) + + expect { + builder.reload.fingerprint + }.to raise_error("Package `A' has a glob that resolves to an empty file list: dir1/*") end - describe 'file overriding via src_alt' do - it 'includes top-level files from src_alt instead of src' do - release_dir.add_file('src', 'file1', 'original') - - builder = make_builder('A', %w(file*)) - s1 = builder.fingerprint - - release_dir.add_file('src', 'file1', 'altered') - release_dir.add_file('src_alt', 'file1', 'original') - expect(builder.reload.fingerprint).to eql(s1) - end - - it 'includes top-level files from src if not present in src_alt' do - release_dir.add_file('src', 'file1', 'original1') - release_dir.add_file('src', 'file2', 'original2') - builder = make_builder('A', %w(file*)) - s1 = builder.fingerprint - - release_dir.add_file('src', 'file1', 'altered1') - release_dir.add_file('src_alt', 'file1', 'original1') - expect(builder.reload.fingerprint).to eql(s1) - end - - it 'includes top-level-dir files from src_alt instead of src' do - release_dir.add_file('src', 'dir1/file1', 'original1') - builder = make_builder('A', %w(dir1/*)) - s1 = builder.fingerprint - - release_dir.add_file('src', 'dir1/file1', 'altered1') - release_dir.add_file('src_alt', 'dir1/file1', 'original1') - expect(builder.reload.fingerprint).to eql(s1) - end - - it 'does not include top-level-dir files from src if not present in src_alt' do - release_dir.add_file('src', 'dir1/file1', 'original1') - builder = make_builder('A', %w(dir1/*)) - s1 = builder.fingerprint - - release_dir.add_file('src', 'dir1/file2', 'new2') - release_dir.add_file('src_alt', 'dir1/file1', 'original1') - expect(builder.reload.fingerprint).to eql(s1) - end - - it "checks if glob top-level-dir is present in src_alt but doesn't match" do - release_dir.add_file('src', 'dir1/file1', 'original1') - release_dir.add_dir('src_alt/dir1') - - builder = make_builder('A', %w(dir1/*)) - - expect { - builder.fingerprint - }.to raise_error( - "Package `A' has a glob that doesn't match " + - "in `src_alt' but matches in `src'. However " + - "`src_alt/dir1' exists, so this might be an error." - ) - end - - it 'raises an error if glob does not match any files in src or src_alt' do - builder = make_builder('A', %w(dir1/*)) - - expect { - builder.reload.fingerprint - }.to raise_error("Package `A' has a glob that resolves to an empty file list: dir1/*") - end - - it 'prevents building final version with src_alt' do - expect { - Bosh::Cli::PackageBuilder.new({ - 'name' => 'bar', - 'files' => 'foo/**/*' - }, release_dir.path, true, double('blobstore')) - }.to raise_error(/Please remove `src_alt' first/) - end + it 'prevents building final version with src_alt' do + expect { + Bosh::Cli::PackageBuilder.new({ + 'name' => 'bar', + 'files' => 'foo/**/*' + }, @release_dir, true, double('blobstore')) + }.to raise_error(/Please remove `src_alt' first/) end end end diff --git a/bosh_cli/spec/unit/release_builder_spec.rb b/bosh_cli/spec/unit/release_builder_spec.rb index b2115f4bf28..90e0b018b63 100644 --- a/bosh_cli/spec/unit/release_builder_spec.rb +++ b/bosh_cli/spec/unit/release_builder_spec.rb @@ -2,229 +2,209 @@ module Bosh::Cli describe ReleaseBuilder do - with_release_directory do |dir| - let(:release_dir) { dir } - let(:release_name) { 'bosh-release' } - let(:release) { Bosh::Cli::Release.new(release_dir.path) } + let(:release_name) { 'bosh-release' } - before do - release_dir.add_dir('config') - end - - def new_builder(options = {}) - ReleaseBuilder.new(release, [], [], [], release_name, options) - end - - context 'when there is a final release' do - it 'bumps the least significant segment for the next version' do - final_storage_dir = release_dir.join('releases', release_name) - final_index = Versions::VersionsIndex.new(final_storage_dir) - - final_index.add_version('deadbeef', { 'version' => '7.4.1' }) - final_index.add_version('deadcafe', { 'version' => '7.3.1' }) - - builder = new_builder(final: true) - expect(builder.version).to eq('7.4.2') - builder.build - end - - it 'creates a dev version in sync with latest final version' do - final_storage_dir = release_dir.join('releases', release_name) - final_index = Versions::VersionsIndex.new(final_storage_dir) - - final_index.add_version('deadbeef', { 'version' => '7.4' }) - final_index.add_version('deadcafe', { 'version' => '7.3.1' }) - - builder = new_builder - expect(builder.version).to eq('7.4+dev.1') - builder.build - end - - it 'bumps the dev version matching the latest final release' do - final_storage_dir = release_dir.join('releases', release_name) - final_index = Versions::VersionsIndex.new(final_storage_dir) - - final_index.add_version('deadbeef', { 'version' => '7.3' }) - final_index.add_version('deadcafe', { 'version' => '7.2' }) - - dev_storage_dir = release_dir.join('dev_releases', release_name) - dev_index = Versions::VersionsIndex.new(dev_storage_dir) - - dev_index.add_version('deadabcd', { 'version' => '7.4.1-dev' }) - dev_index.add_version('deadbeef', { 'version' => '7.3.2.1-dev' }) - dev_index.add_version('deadturkey', { 'version' => '7.3.2-dev' }) - dev_index.add_version('deadcafe', { 'version' => '7.3.1-dev' }) - - builder = new_builder - expect(builder.version).to eq('7.3+dev.3') - builder.build - end - end - - context 'when there are no final releases' do - it 'starts with version 0+dev.1' do - expect(new_builder.version).to eq('0+dev.1') - end - - it 'increments the dev version' do - dev_storage_dir = release_dir.join('dev_releases', release_name) - dev_index = Versions::VersionsIndex.new(dev_storage_dir) - - dev_index.add_version('deadbeef', { 'version' => '0.1-dev' }) - - expect(new_builder.version).to eq('0+dev.2') - end + before do + @release_dir = Dir.mktmpdir + FileUtils.mkdir_p(File.join(@release_dir, 'config')) + @release = Bosh::Cli::Release.new(@release_dir) + end + + def new_builder(options = {}) + ReleaseBuilder.new(@release, [], [], release_name, options) + end + + context 'when there is a final release' do + it 'bumps the least significant segment for the next version' do + final_storage_dir = File.join(@release_dir, 'releases', release_name) + final_index = Versions::VersionsIndex.new(final_storage_dir) + + final_index.add_version('deadbeef', { 'version' => '7.4.1' }) + final_index.add_version('deadcafe', { 'version' => '7.3.1' }) + + builder = new_builder(final: true) + expect(builder.version).to eq('7.4.2') + builder.build end - - it 'builds a release' do + + it 'creates a dev version in sync with latest final version' do + final_storage_dir = File.join(@release_dir, 'releases', release_name) + final_index = Versions::VersionsIndex.new(final_storage_dir) + + final_index.add_version('deadbeef', { 'version' => '7.4' }) + final_index.add_version('deadcafe', { 'version' => '7.3.1' }) + builder = new_builder + expect(builder.version).to eq('7.4+dev.1') builder.build - - expected_tarball_path = release_dir.join( - 'dev_releases', - release_name, - "#{release_name}-0+dev.1.tgz") - - expect(builder.tarball_path).to eq(expected_tarball_path) - expect(File).to exist(expected_tarball_path) end - - it 'should include git hash and uncommitted change state in manifest' do - builder = new_builder({commit_hash: '12345678', uncommitted_changes: true}) + + it 'bumps the dev version matching the latest final release' do + final_storage_dir = File.join(@release_dir, 'releases', release_name) + final_index = Versions::VersionsIndex.new(final_storage_dir) + + final_index.add_version('deadbeef', { 'version' => '7.3' }) + final_index.add_version('deadcafe', { 'version' => '7.2' }) + + dev_storage_dir = File.join(@release_dir, 'dev_releases', release_name) + dev_index = Versions::VersionsIndex.new(dev_storage_dir) + + dev_index.add_version('deadabcd', { 'version' => '7.4.1-dev' }) + dev_index.add_version('deadbeef', { 'version' => '7.3.2.1-dev' }) + dev_index.add_version('deadturkey', { 'version' => '7.3.2-dev' }) + dev_index.add_version('deadcafe', { 'version' => '7.3.1-dev' }) + + builder = new_builder + expect(builder.version).to eq('7.3+dev.3') builder.build - - manifest = Psych.load_file(builder.manifest_path) - expect(manifest['commit_hash']).to eq('12345678') - expect(manifest['uncommitted_changes']).to be(true) end - - it 'allows building a new release when no content has changed' do - release_path = File.join('dev_releases', release_name) - expect(release_dir).to_not have_file("#{release_path}/#{release_name}-0+dev.1.tgz") + end + + context 'when there are no final releases' do + it 'starts with version 0+dev.1' do + expect(new_builder.version).to eq('0+dev.1') + end - new_builder.build - expect(release_dir).to have_file("#{release_path}/#{release_name}-0+dev.1.tgz") - expect(release_dir).to_not have_file("#{release_path}/#{release_name}-0+dev.2.tgz") + it 'increments the dev version' do + dev_storage_dir = File.join(@release_dir, 'dev_releases', release_name) + dev_index = Versions::VersionsIndex.new(dev_storage_dir) - new_builder.build - expect(release_dir).to have_file("#{release_path}/#{release_name}-0+dev.2.tgz") + dev_index.add_version('deadbeef', { 'version' => '0.1-dev' }) + + expect(new_builder.version).to eq('0+dev.2') end - - it 'errors when trying to re-create the same final version' do - release_path = File.join('releases', release_name) + end + + it 'builds a release' do + builder = new_builder + builder.build + + expected_tarball_path = File.join(@release_dir, + 'dev_releases', + release_name, + "#{release_name}-0+dev.1.tgz") + + expect(builder.tarball_path).to eq(expected_tarball_path) + expect(File).to exist(expected_tarball_path) + end + + it 'should include git hash and uncommitted change state in manifest' do + builder = new_builder({commit_hash: '12345678', uncommitted_changes: true}) + builder.build + + manifest = Psych.load_file(builder.manifest_path) + expect(manifest['commit_hash']).to eq('12345678') + expect(manifest['uncommitted_changes']).to be(true) + end + + it 'allows building a new release when no content has changed' do + release_path = File.join(@release_dir, 'dev_releases', release_name) + expect(File).to_not exist(File.join(release_path, "#{release_name}-0+dev.1.tgz")) + + new_builder.build + expect(File).to exist(File.join(release_path, "#{release_name}-0+dev.1.tgz")) + expect(File).to_not exist(File.join(release_path, "#{release_name}-0+dev.2.tgz")) + + new_builder.build + expect(File).to exist(File.join(release_path, "#{release_name}-0+dev.2.tgz")) + end + + it 'errors when trying to re-create the same final version' do + new_builder({:version => '1', :final => true}).build + expect(File).to exist(File.join(@release_dir, 'releases', release_name, "#{release_name}-1.tgz")) + + expect{ new_builder({:version => '1', :final => true}).build - expect(release_dir).to have_file("#{release_path}/#{release_name}-1.tgz") + }.to raise_error(ReleaseVersionError, 'Release version already exists') + end - expect{ - new_builder({:version => '1', :final => true}).build - }.to raise_error(ReleaseVersionError, 'Release version already exists') - end - - it 'has a list of jobs affected by building this release' do - jobs = [] - jobs << double(:job, :new_version? => true, :packages => %w(bar baz), :name => 'job1') - jobs << double(:job, :new_version? => false, :packages => %w(foo baz), :name => 'job2') - jobs << double(:job, :new_version? => false, :packages => %w(baz zb), :name => 'job3') - jobs << double(:job, :new_version? => false, :packages => %w(bar baz), :name => 'job4') - - packages = [] - packages << double(:package, :name => 'foo', :new_version? => true) - packages << double(:package, :name => 'bar', :new_version? => false) - packages << double(:package, :name => 'baz', :new_version? => false) - packages << double(:package, :name => 'zb', :new_version? => true) - - licenses = [] - licenses << double(:license, :name => 'license1', :new_version? => true) - licenses << double(:license, :name => 'license2', :new_version? => false) - - builder = ReleaseBuilder.new(release, packages, jobs,licenses, release_name) - - expect(builder.affected_jobs).to eq(jobs[0...-1]) # exclude last job - end - - it 'has packages and jobs fingerprints in spec' do - job = double( - JobBuilder, - :name => 'job1', - :version => '1.1', - :new_version? => true, - :packages => %w(foo), - :fingerprint => 'deadbeef', - :checksum => 'cafebad' - ) - - package = double( - PackageBuilder, - :name => 'foo', - :version => '42', - :new_version? => true, - :fingerprint => 'deadcafe', - :checksum => 'baddeed', - :dependencies => [] - ) - - license = double( - LicenseBuilder, - :name => 'license', - :version => '1.0', - :new_version? => true, - :fingerprint => 'dabbadoo', - :checksum => 'badfood' - ) - - - builder = ReleaseBuilder.new(release, [package], [job], [license], release_name) - expect(builder).to receive(:copy_jobs) - expect(builder).to receive(:copy_packages) - expect(builder).to receive(:copy_licenses) - - - builder.build - - manifest = Psych.load_file(builder.manifest_path) - - expect(manifest['jobs'][0]['fingerprint']).to eq('deadbeef') - expect(manifest['packages'][0]['fingerprint']).to eq('deadcafe') - expect(manifest['license'][0]['fingerprint']).to eq('dabbadoo') - end - - context 'when version options is passed into initializer' do - context 'when creating final release' do - context 'when given release version already exists' do - it 'raises error' do - final_storage_dir = release_dir.join('releases', release_name) - final_index = Versions::VersionsIndex.new(final_storage_dir) - - final_index.add_version('deadbeef', { 'version' => '7.3' }) - - FileUtils.touch(File.join(final_storage_dir, "#{release_name}-7.3.tgz")) - - expect { new_builder({ final: true, version: '7.3' }) }.to raise_error(ReleaseVersionError, 'Release version already exists') - end - end - - context 'when given version does not exist' do - it 'uses given version' do - builder = new_builder({ final: true, version: '3.123' }) - expect(builder.version).to eq('3.123') - builder.build - end + it 'has a list of jobs affected by building this release' do + jobs = [] + jobs << double(:job, :new_version? => true, :packages => %w(bar baz), :name => 'job1') + jobs << double(:job, :new_version? => false, :packages => %w(foo baz), :name => 'job2') + jobs << double(:job, :new_version? => false, :packages => %w(baz zb), :name => 'job3') + jobs << double(:job, :new_version? => false, :packages => %w(bar baz), :name => 'job4') + + packages = [] + packages << double(:package, :name => 'foo', :new_version? => true) + packages << double(:package, :name => 'bar', :new_version? => false) + packages << double(:package, :name => 'baz', :new_version? => false) + packages << double(:package, :name => 'zb', :new_version? => true) + + builder = ReleaseBuilder.new(@release, packages, jobs, release_name) + + expect(builder.affected_jobs).to eq(jobs[0...-1]) # exclude last job + end + + it 'has packages and jobs fingerprints in spec' do + job = double( + JobBuilder, + :name => 'job1', + :version => '1.1', + :new_version? => true, + :packages => %w(foo), + :fingerprint => 'deadbeef', + :checksum => 'cafebad' + ) + + package = double( + PackageBuilder, + :name => 'foo', + :version => '42', + :new_version? => true, + :fingerprint => 'deadcafe', + :checksum => 'baddeed', + :dependencies => [] + ) + + builder = ReleaseBuilder.new(@release, [package], [job], release_name) + expect(builder).to receive(:copy_jobs) + expect(builder).to receive(:copy_packages) + + builder.build + + manifest = Psych.load_file(builder.manifest_path) + + expect(manifest['jobs'][0]['fingerprint']).to eq('deadbeef') + expect(manifest['packages'][0]['fingerprint']).to eq('deadcafe') + end + + context 'when version options is passed into initializer' do + context 'when creating final release' do + context 'when given release version already exists' do + it 'raises error' do + final_storage_dir = File.join(@release_dir, 'releases', release_name) + final_index = Versions::VersionsIndex.new(final_storage_dir) + + final_index.add_version('deadbeef', { 'version' => '7.3' }) + + FileUtils.touch(File.join(final_storage_dir, "#{release_name}-7.3.tgz")) + + expect { new_builder({ final: true, version: '7.3' }) }.to raise_error(ReleaseVersionError, 'Release version already exists') end end - - context 'when creating dev release' do - it 'does not allow a version to be specified for dev releases' do + + context 'when given version does not exist' do + it 'uses given version' do builder = new_builder({ final: true, version: '3.123' }) expect(builder.version).to eq('3.123') builder.build - - expect{ new_builder({ version: '3.123.1-dev' }) }.to raise_error( - ReleaseVersionError, - 'Version numbers cannot be specified for dev releases' - ) end end end + + context 'when creating dev release' do + it 'does not allow a version to be specified for dev releases' do + builder = new_builder({ final: true, version: '3.123' }) + expect(builder.version).to eq('3.123') + builder.build + + expect{ new_builder({ version: '3.123.1-dev' }) }.to raise_error( + ReleaseVersionError, + 'Version numbers cannot be specified for dev releases' + ) + end + end end end end diff --git a/bosh_cli/spec/unit/release_spec.rb b/bosh_cli/spec/unit/release_spec.rb index eb0add68906..7ca194075f7 100644 --- a/bosh_cli/spec/unit/release_spec.rb +++ b/bosh_cli/spec/unit/release_spec.rb @@ -1,288 +1,285 @@ require "spec_helper" describe Bosh::Cli::Release do - with_release_directory do |dir| - let(:release_dir) { dir } + before do + @release_dir = Dir.mktmpdir + FileUtils.mkdir_p(File.join(@release_dir, "config")) + end - before do - release_dir.add_dir('config') - end + def new_release(dir) + Bosh::Cli::Release.new(@release_dir) + end - def new_release(dir) - Bosh::Cli::Release.new(release_dir.path) - end - - it "persists release attributes" do - r = new_release(nil) - - expect(r.dev_name).to be_nil - expect(r.final_name).to be_nil - expect(r.latest_release_filename).to be_nil - - r.dev_name = "dev-release" - r.final_name = "prod-release" - r.latest_release_filename = "foobar" - r.save_config - - r2 = new_release(release_dir) - expect(r2.dev_name).to eq("dev-release") - expect(r2.final_name).to eq("prod-release") - expect(r2.latest_release_filename).to eq("foobar") + it "persists release attributes" do + r = new_release(@dir) + + expect(r.dev_name).to be_nil + expect(r.final_name).to be_nil + expect(r.latest_release_filename).to be_nil + + r.dev_name = "dev-release" + r.final_name = "prod-release" + r.latest_release_filename = "foobar" + r.save_config + + r2 = new_release(@release_dir) + expect(r2.dev_name).to eq("dev-release") + expect(r2.final_name).to eq("prod-release") + expect(r2.latest_release_filename).to eq("foobar") + end + + it "has attributes persisted in bosh user config" do + r = new_release(@release_dir) + r.dev_name = "dev-release" + r.final_name = "prod-release" + r.save_config + + FileUtils.rm_rf(File.join(@release_dir, "config", "dev.yml")) + + r = new_release(@release_dir) + expect(r.dev_name).to be_nil + expect(r.final_name).to eq("prod-release") + end + + it "has attributes persisted in public release config" do + r = new_release(@release_dir) + r.dev_name = "dev-release" + r.final_name = "prod-release" + r.save_config + + FileUtils.rm_rf(File.join(@release_dir, "config", "final.yml")) + + r = new_release(@release_dir) + expect(r.dev_name).to eq("dev-release") + expect(r.final_name).to be_nil + end + + describe "#blobstore" do + let(:local_release) { Bosh::Cli::Release.new(spec_asset("config/local")) } + + it "returns a blobstore client" do + opts = { + :blobstore_path => "/tmp/blobstore" + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", opts).and_call_original + expect(local_release.blobstore).to be_kind_of(Bosh::Blobstore::BaseClient) end - - it "has attributes persisted in bosh user config" do - r = new_release(release_dir) - r.dev_name = "dev-release" - r.final_name = "prod-release" - r.save_config - - release_dir.remove_file("config", "dev.yml") - - r = new_release(release_dir) - expect(r.dev_name).to be_nil - expect(r.final_name).to eq("prod-release") + + it "returns the cached blobstore client if previously constructed" do + expect(Bosh::Blobstore::Client).to receive(:safe_create).and_call_original + blobstore = local_release.blobstore + + expect(Bosh::Blobstore::Client).to_not receive(:safe_create) + new_blobstore = local_release.blobstore + + expect(blobstore).to be(new_blobstore) end - - it "has attributes persisted in public release config" do - r = new_release(release_dir) - r.dev_name = "dev-release" - r.final_name = "prod-release" - r.save_config - - release_dir.remove_file("config", "final.yml") - - r = new_release(release_dir) - expect(r.dev_name).to eq("dev-release") - expect(r.final_name).to be_nil + + it "raises an error when an unknown blobstore provider is configured" do + r = Bosh::Cli::Release.new(spec_asset("config/unknown-provider")) + expect { + r.blobstore + }.to raise_error(Bosh::Cli::CliError, + /Cannot initialize blobstore.*Unknown client provider 'unknown-provider-name'/) end - - describe "#blobstore" do - let(:local_release) { Bosh::Cli::Release.new(spec_asset("config/local")) } - - it "returns a blobstore client" do - opts = { - :blobstore_path => "/tmp/blobstore" - } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", opts).and_call_original - expect(local_release.blobstore).to be_kind_of(Bosh::Blobstore::BaseClient) - end - - it "returns the cached blobstore client if previously constructed" do - expect(Bosh::Blobstore::Client).to receive(:safe_create).and_call_original - blobstore = local_release.blobstore - - expect(Bosh::Blobstore::Client).to_not receive(:safe_create) - new_blobstore = local_release.blobstore - - expect(blobstore).to be(new_blobstore) - end - - it "raises an error when an unknown blobstore provider is configured" do - r = Bosh::Cli::Release.new(spec_asset("config/unknown-provider")) - expect { - r.blobstore - }.to raise_error(Bosh::Cli::CliError, - /Cannot initialize blobstore.*Unknown client provider 'unknown-provider-name'/) - end - - context "when creating a final release" do - let(:final) { true } - let(:config_dir) { nil } - let(:release) { Bosh::Cli::Release.new(config_dir, final) } - - context "when a blobstore is not configured" do - let(:config_dir) { spec_asset("config/no-blobstore") } - - it "raises an error" do - release = Bosh::Cli::Release.new(spec_asset("config/no-blobstore"), final) - expect { - release.blobstore - }.to raise_error(Bosh::Cli::CliError, - "Missing blobstore configuration, please update config/final.yml") - end - end - - context "when a blobstore secret is not configured" do - let(:config_dir) { spec_asset("config/no-blobstore-secret") } - - it "raises an error" do - expect { - release.blobstore - }.to raise_error(Bosh::Cli::CliError, - "Missing blobstore secret configuration, please update config/private.yml") - end - end - - context "when a blobstore is configured" do - let(:config_dir) { spec_asset("config/local") } - - it "returns the configured blobstore" do - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", {blobstore_path: "/tmp/blobstore"}).and_call_original - expect(release.blobstore).to be_kind_of(Bosh::Blobstore::BaseClient) - end + + context "when creating a final release" do + let(:final) { true } + let(:config_dir) { nil } + let(:release) { Bosh::Cli::Release.new(config_dir, final) } + + context "when a blobstore is not configured" do + let(:config_dir) { spec_asset("config/no-blobstore") } + + it "raises an error" do + release = Bosh::Cli::Release.new(spec_asset("config/no-blobstore"), final) + expect { + release.blobstore + }.to raise_error(Bosh::Cli::CliError, + "Missing blobstore configuration, please update config/final.yml") end end - - context "when creating a dev release" do - let(:final) { false } - let(:config_dir) { nil } - let(:release) { Bosh::Cli::Release.new(config_dir, final) } - - context "when a blobstore is not configured" do - let(:config_dir) { spec_asset("config/no-blobstore") } - - it "prints warning and returns nil" do - expect(release).to receive(:warning). - with("Missing blobstore configuration, please update config/final.yml before making a final release") - expect(Bosh::Blobstore::Client).to_not receive(:safe_create) - expect(release.blobstore).to be_nil - end + + context "when a blobstore secret is not configured" do + let(:config_dir) { spec_asset("config/no-blobstore-secret") } + + it "raises an error" do + expect { + release.blobstore + }.to raise_error(Bosh::Cli::CliError, + "Missing blobstore secret configuration, please update config/private.yml") end - - context "when a blobstore is configured" do - let(:config_dir) { spec_asset("config/local") } - - it "returns the configured blobstore" do - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", {blobstore_path: "/tmp/blobstore"}).and_call_original - expect(release.blobstore).to be_kind_of(Bosh::Blobstore::BaseClient) - end + end + + context "when a blobstore is configured" do + let(:config_dir) { spec_asset("config/local") } + + it "returns the configured blobstore" do + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", {blobstore_path: "/tmp/blobstore"}).and_call_original + expect(release.blobstore).to be_kind_of(Bosh::Blobstore::BaseClient) end end end - - describe "merging final.yml with private.yml" do - it "should print a warning when it contains blobstore_secret" do - r = Bosh::Cli::Release.new(spec_asset("config/deprecation")) - opts = { - :uid => "bosh", - :secret => "bar" - } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("atmos", opts) - expect(r).to receive(:say) - - r.blobstore - end - - it "should detect blobstore secrets for deprecated options" do - r = Bosh::Cli::Release.new(spec_asset("config/deprecation")) - expect(r.has_blobstore_secret?).to eq(true) - end - - it "should merge s3 secrets into options" do - r = Bosh::Cli::Release.new(spec_asset("config/s3")) - opts = { - :bucket_name => "test", - :secret_access_key => "foo", - :access_key_id => "bar" - } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("s3", opts) - r.blobstore - end - - it "should detect blobstore secrets for s3 options" do - r = Bosh::Cli::Release.new(spec_asset("config/s3")) - expect(r.has_blobstore_secret?).to eq(true) - end - - it "should merge atmos secrets into options" do - r = Bosh::Cli::Release.new(spec_asset("config/atmos")) - opts = { - :uid => "bosh", - :secret => "bar" - } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("atmos", opts) - r.blobstore - end - - it "should detect blobstore secrets for atmos options" do - r = Bosh::Cli::Release.new(spec_asset("config/atmos")) - expect(r.has_blobstore_secret?).to eq(true) - end - - it "should merge swift (HP) secrets into options" do - r = Bosh::Cli::Release.new(spec_asset("config/swift-hp")) - opts = { - :container_name => "test", - :swift_provider => "hp", - :hp => { - :hp_access_key => "foo", - :hp_secret_key => "bar", - :hp_tenant_id => "foo", - :hp_avl_zone => "avl" - } - } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("swift", opts) - r.blobstore + + context "when creating a dev release" do + let(:final) { false } + let(:config_dir) { nil } + let(:release) { Bosh::Cli::Release.new(config_dir, final) } + + context "when a blobstore is not configured" do + let(:config_dir) { spec_asset("config/no-blobstore") } + + it "prints warning and returns nil" do + expect(release).to receive(:warning). + with("Missing blobstore configuration, please update config/final.yml before making a final release") + expect(Bosh::Blobstore::Client).to_not receive(:safe_create) + expect(release.blobstore).to be_nil + end end - - it "should detect blobstore secrets for swift (HP) options" do - r = Bosh::Cli::Release.new(spec_asset("config/swift-hp")) - expect(r.has_blobstore_secret?).to eq(true) + + context "when a blobstore is configured" do + let(:config_dir) { spec_asset("config/local") } + + it "returns the configured blobstore" do + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", {blobstore_path: "/tmp/blobstore"}).and_call_original + expect(release.blobstore).to be_kind_of(Bosh::Blobstore::BaseClient) + end end - - it "should merge swift (OpenStack) secrets into options" do - r = Bosh::Cli::Release.new(spec_asset("config/swift-openstack")) - opts = { - :container_name => "test", - :swift_provider => "openstack", - :openstack => { - :openstack_auth_url => "url", - :openstack_username => "foo", - :openstack_api_key => "bar", - :openstack_tenant => "foo", - :openstack_region => "reg" - } + end + end + + describe "merging final.yml with private.yml" do + it "should print a warning when it contains blobstore_secret" do + r = Bosh::Cli::Release.new(spec_asset("config/deprecation")) + opts = { + :uid => "bosh", + :secret => "bar" + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("atmos", opts) + expect(r).to receive(:say) + + r.blobstore + end + + it "should detect blobstore secrets for deprecated options" do + r = Bosh::Cli::Release.new(spec_asset("config/deprecation")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it "should merge s3 secrets into options" do + r = Bosh::Cli::Release.new(spec_asset("config/s3")) + opts = { + :bucket_name => "test", + :secret_access_key => "foo", + :access_key_id => "bar" + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("s3", opts) + r.blobstore + end + + it "should detect blobstore secrets for s3 options" do + r = Bosh::Cli::Release.new(spec_asset("config/s3")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it "should merge atmos secrets into options" do + r = Bosh::Cli::Release.new(spec_asset("config/atmos")) + opts = { + :uid => "bosh", + :secret => "bar" + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("atmos", opts) + r.blobstore + end + + it "should detect blobstore secrets for atmos options" do + r = Bosh::Cli::Release.new(spec_asset("config/atmos")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it "should merge swift (HP) secrets into options" do + r = Bosh::Cli::Release.new(spec_asset("config/swift-hp")) + opts = { + :container_name => "test", + :swift_provider => "hp", + :hp => { + :hp_access_key => "foo", + :hp_secret_key => "bar", + :hp_tenant_id => "foo", + :hp_avl_zone => "avl" } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("swift", opts) - r.blobstore - end - - it "should detect blobstore secrets for swift (OpenStack) options" do - r = Bosh::Cli::Release.new(spec_asset("config/swift-openstack")) - expect(r.has_blobstore_secret?).to eq(true) - end - - it "should merge swift (Rackspace) secrets into options" do - r = Bosh::Cli::Release.new(spec_asset("config/swift-rackspace")) - opts = { - :container_name => "test", - :swift_provider => "rackspace", - :rackspace => { - :rackspace_username => "foo", - :rackspace_api_key => "bar", - :rackspace_region => "reg" - } + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("swift", opts) + r.blobstore + end + + it "should detect blobstore secrets for swift (HP) options" do + r = Bosh::Cli::Release.new(spec_asset("config/swift-hp")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it "should merge swift (OpenStack) secrets into options" do + r = Bosh::Cli::Release.new(spec_asset("config/swift-openstack")) + opts = { + :container_name => "test", + :swift_provider => "openstack", + :openstack => { + :openstack_auth_url => "url", + :openstack_username => "foo", + :openstack_api_key => "bar", + :openstack_tenant => "foo", + :openstack_region => "reg" } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("swift", opts) - r.blobstore - end - - it "should detect blobstore secrets for swift (Rackspace) options" do - r = Bosh::Cli::Release.new(spec_asset("config/swift-rackspace")) - expect(r.has_blobstore_secret?).to eq(true) - end - - it 'should not use credentials for a local blobstore' do - r = Bosh::Cli::Release.new(spec_asset("config/local")) - expect(r.has_blobstore_secret?).to eq(true) - end - - it "should not throw an error when merging empty secrets into options" do - r = Bosh::Cli::Release.new(spec_asset("config/local")) - opts = { - :blobstore_path => "/tmp/blobstore" + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("swift", opts) + r.blobstore + end + + it "should detect blobstore secrets for swift (OpenStack) options" do + r = Bosh::Cli::Release.new(spec_asset("config/swift-openstack")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it "should merge swift (Rackspace) secrets into options" do + r = Bosh::Cli::Release.new(spec_asset("config/swift-rackspace")) + opts = { + :container_name => "test", + :swift_provider => "rackspace", + :rackspace => { + :rackspace_username => "foo", + :rackspace_api_key => "bar", + :rackspace_region => "reg" } - expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", opts) + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("swift", opts) + r.blobstore + end + + it "should detect blobstore secrets for swift (Rackspace) options" do + r = Bosh::Cli::Release.new(spec_asset("config/swift-rackspace")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it 'should not use credentials for a local blobstore' do + r = Bosh::Cli::Release.new(spec_asset("config/local")) + expect(r.has_blobstore_secret?).to eq(true) + end + + it "should not throw an error when merging empty secrets into options" do + r = Bosh::Cli::Release.new(spec_asset("config/local")) + opts = { + :blobstore_path => "/tmp/blobstore" + } + expect(Bosh::Blobstore::Client).to receive(:safe_create).with("local", opts) + r.blobstore + end + + it "throws an error when blobstore providers does not match" do + r = Bosh::Cli::Release.new(spec_asset("config/bad-providers")) + expect { r.blobstore - end - - it "throws an error when blobstore providers does not match" do - r = Bosh::Cli::Release.new(spec_asset("config/bad-providers")) - expect { - r.blobstore - }.to raise_error(Bosh::Cli::CliError, - "blobstore private provider does not match final provider") - end + }.to raise_error(Bosh::Cli::CliError, + "blobstore private provider does not match final provider") end end end diff --git a/release/.gitignore b/release/.gitignore index ed0da1377a8..10ab9acbb11 100644 --- a/release/.gitignore +++ b/release/.gitignore @@ -1,20 +1,16 @@ +config/dev.yml +config/private.yml +releases/*.tgz +releases/**/*.tgz +dev_releases .blobs +blobs .dev_builds .idea .DS_Store -.final_builds/**/*.tgz +.final_builds/jobs/**/*.tgz +.final_builds/packages/**/*.tgz *.swp *~ *# #* -blobs -config/dev.yml -config/private.yml -dev-releases -dev_releases -jobs/micro_*/monit -jobs/micro_*/spec -jobs/micro_*/templates/ -releases/*.tgz -releases/**/*.tgz -src/bosh/*