bosh_cli 1.2831.0 → 1.2839.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b9f61a1c4dc97a4d1f4e909b010779a432370856
4
- data.tar.gz: c430c18acb4e3d9803e0ea1d9ce521ed60888aee
3
+ metadata.gz: 3f11dc3119b4347b9e61c770c43d31b09ec3e1aa
4
+ data.tar.gz: 5d829eee1ce9742ea980476c1b7cb769887ab59e
5
5
  SHA512:
6
- metadata.gz: 15c4b0d2a9b40972f22184ccce08917a9a0b2196b306e81cb0f53761796f7c35fadcedd4563605091f82b6dba1fa1cd4f6172e17e63b6a77475df61b208a1e85
7
- data.tar.gz: 34889ef7e9ab28a490e7f6d51001694dc36e0780781cafc320ac56aa1bebb23deb1d904d01dae88bfe28772f6c4b466194975be062fda1f994333fe22476e5be
6
+ metadata.gz: bb7ac41c08a80eee35cec3672d9969c91de24cea903e0ae1c765bfbd1c4c86940e7b4ce3953c3cf5717c610b2988c8e0609ec7621520fc882fc67c449914ab29
7
+ data.tar.gz: a0ba29122f2ff944793adb8f99473b0e29ffd39ac49db87ad368446bf38e9bdeecaebe3c33d3ccedfaa0c92a70894fbaf70ae019eb615865906108adee3f76ec
data/lib/cli.rb CHANGED
@@ -51,6 +51,7 @@ require 'common/thread_pool'
51
51
  require 'cli/config'
52
52
  require 'cli/core_ext'
53
53
  require 'cli/errors'
54
+ require 'cli/glob_match'
54
55
  require 'cli/yaml_helper'
55
56
  require 'cli/dependency_helper'
56
57
  require 'cli/deployment_manifest'
@@ -75,9 +76,12 @@ require 'cli/versions/releases_dir_migrator'
75
76
  require 'cli/versions/version_file_resolver'
76
77
  require 'cli/versions/multi_release_support'
77
78
 
78
- require 'cli/packaging_helper'
79
- require 'cli/package_builder'
80
- require 'cli/job_builder'
79
+ require 'cli/archive_builder'
80
+ require 'cli/archive_repository_provider'
81
+ require 'cli/archive_repository'
82
+ require 'cli/build_artifact'
83
+ require 'cli/resources/job'
84
+ require 'cli/resources/package'
81
85
  require 'cli/changeset_helper'
82
86
  require 'cli/deployment_manifest_compiler'
83
87
  require 'cli/task_tracking'
@@ -0,0 +1,119 @@
1
+ module Bosh::Cli
2
+ class ArchiveBuilder
3
+ attr_reader :options
4
+
5
+ def initialize(archive_repository_provider, options = {})
6
+ @archive_repository_provider = archive_repository_provider
7
+ @options = options
8
+ end
9
+
10
+ def build(resource)
11
+ @archive_repository = @archive_repository_provider.get(resource)
12
+ resource.run_script(:prepare)
13
+
14
+ artifact = nil
15
+ with_indent(' ') do
16
+ artifact = locate_artifact(resource)
17
+ if artifact.nil?
18
+ artifact = create_artifact(resource)
19
+ say("Generated version '#{artifact.fingerprint}'".make_green)
20
+
21
+ unless dry_run?
22
+ artifact = @archive_repository.install(artifact)
23
+ end
24
+ end
25
+
26
+ if final? && !dry_run?
27
+ say("Uploading final version '#{artifact.version}'...")
28
+ artifact, blobstore_id = @archive_repository.upload_to_blobstore(artifact)
29
+ say("Uploaded, blobstore id '#{blobstore_id}'")
30
+ end
31
+ end
32
+
33
+ artifact
34
+ rescue Bosh::Blobstore::BlobstoreError => e
35
+ raise BlobstoreError, "Blobstore error: #{e}"
36
+ end
37
+
38
+ def dry_run?
39
+ !!options[:dry_run]
40
+ end
41
+
42
+ def final?
43
+ !!options[:final]
44
+ end
45
+
46
+ private
47
+
48
+ def copy_files(resource)
49
+ resource.files.each do |src, dest|
50
+ dest_path = Pathname(staging_dir).join(dest)
51
+ if File.directory?(src)
52
+ FileUtils.mkdir_p(dest_path)
53
+ else
54
+ FileUtils.mkdir_p(dest_path.parent)
55
+ FileUtils.cp(src, dest_path, :preserve => true)
56
+ end
57
+ end
58
+ end
59
+
60
+ def locate_artifact(resource)
61
+ artifact = @archive_repository.lookup(resource)
62
+
63
+ if artifact.nil?
64
+ say("No artifact found for #{resource.name}".make_red)
65
+ return nil
66
+ end
67
+
68
+ if artifact.dev_artifact? && final? && !dry_run?
69
+ artifact = @archive_repository.copy_from_dev_to_final(artifact)
70
+ end
71
+
72
+ artifact
73
+ rescue Bosh::Cli::CorruptedArchive => e
74
+ say "#{"Warning".make_red}: #{e.message}"
75
+ nil
76
+ end
77
+
78
+ def create_artifact(resource)
79
+ tarball_path = safe_temp_file(resource.name, '.tgz')
80
+
81
+ say('Generating...')
82
+
83
+ copy_files(resource)
84
+ resource.run_script(:pre_packaging, staging_dir)
85
+
86
+ in_staging_dir do
87
+ tar_out = `tar -chzf #{tarball_path} . 2>&1`
88
+ unless $?.exitstatus == 0
89
+ raise PackagingError, "Cannot create tarball: #{tar_out}"
90
+ end
91
+ end
92
+
93
+ fingerprint = BuildArtifact.make_fingerprint(resource)
94
+
95
+ sha1 = BuildArtifact.checksum(tarball_path)
96
+ BuildArtifact.new(resource.name, fingerprint, tarball_path, sha1, resource.dependencies, true, !final?)
97
+ end
98
+
99
+ def file_checksum(path)
100
+ Digest::SHA1.file(path).hexdigest
101
+ end
102
+
103
+ def staging_dir
104
+ @staging_dir ||= Dir.mktmpdir
105
+ end
106
+
107
+ def in_staging_dir
108
+ Dir.chdir(staging_dir) { yield }
109
+ end
110
+
111
+ private
112
+
113
+ def safe_temp_file(prefix, suffix, dir = Dir.tmpdir)
114
+ Dir::Tmpname.create([prefix, suffix], dir) do |tmpname, _, _|
115
+ File.open(tmpname, File::RDWR|File::CREAT|File::EXCL).close
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,108 @@
1
+ module Bosh::Cli
2
+ class ArchiveRepository
3
+ def initialize(archive_dir, blobstore, resource)
4
+ @archive_dir = archive_dir
5
+ @blobstore = blobstore
6
+
7
+ dev_builds_dir = Pathname(@archive_dir).join(".dev_builds", resource.plural_type, resource.name).to_s
8
+ FileUtils.mkdir_p(dev_builds_dir)
9
+ @dev_index = Versions::VersionsIndex.new(dev_builds_dir)
10
+ @dev_storage = Versions::LocalVersionStorage.new(dev_builds_dir)
11
+
12
+ final_builds_dir = Pathname(@archive_dir).join(".final_builds", resource.plural_type, resource.name).to_s
13
+ FileUtils.mkdir_p(final_builds_dir)
14
+ @final_index = Versions::VersionsIndex.new(final_builds_dir)
15
+ @final_storage = Versions::LocalVersionStorage.new(final_builds_dir)
16
+
17
+ @final_resolver = Versions::VersionFileResolver.new(@final_storage, @blobstore)
18
+ end
19
+
20
+ def lookup(resource)
21
+ fingerprint = BuildArtifact.make_fingerprint(resource)
22
+
23
+ artifact_info = @final_index[fingerprint]
24
+ if artifact_info && artifact_info['blobstore_id']
25
+ blobstore_id = artifact_info['blobstore_id']
26
+ version = artifact_info['version'] || fingerprint
27
+ sha1 = artifact_info['sha1']
28
+
29
+ say('Using final version')
30
+ tarball_path = @final_resolver.find_file(blobstore_id, sha1, version, "#{resource.singular_type} #{resource.name} (#{version})") # todo: 'package' vs 'job'
31
+
32
+ BuildArtifact.new(resource.name, fingerprint, tarball_path, sha1, resource.dependencies, false, false)
33
+ else
34
+ artifact_info = @dev_index[fingerprint]
35
+ if artifact_info
36
+ version = artifact_info['version'] || fingerprint
37
+ if @dev_storage.has_file?(version)
38
+ say('Using dev version')
39
+
40
+ tarball_path = @dev_storage.get_file(version)
41
+ if file_checksum(tarball_path) != artifact_info['sha1']
42
+ raise CorruptedArchive, "#{resource.singular_type} #{resource.name} (#{version}) archive at #{tarball_path} corrupted"
43
+ end
44
+
45
+ BuildArtifact.new(resource.name, fingerprint, tarball_path, artifact_info['sha1'], resource.dependencies, false, true)
46
+ end
47
+ end
48
+ end
49
+
50
+ rescue Bosh::Blobstore::NotFound
51
+ raise BlobstoreError, "Final version of '#{name}' not found in blobstore"
52
+ rescue Bosh::Blobstore::BlobstoreError => e
53
+ raise BlobstoreError, "Blobstore error: #{e}"
54
+ end
55
+
56
+ def upload_to_blobstore(artifact)
57
+ artifact_info = @final_index[artifact.fingerprint]
58
+ # todo raise if artifact.dev_artifact?
59
+ return artifact, artifact_info['blobstore_id'] if artifact_info['blobstore_id']
60
+
61
+ blobstore_id = nil
62
+ File.open(artifact.tarball_path, 'r') do |f|
63
+ blobstore_id = @blobstore.create(f)
64
+ end
65
+
66
+ @final_index.update_version(artifact.fingerprint, {
67
+ 'version' => artifact.version,
68
+ 'sha1' => artifact.sha1,
69
+ 'blobstore_id' => blobstore_id
70
+ })
71
+ artifact = BuildArtifact.new(artifact.name, artifact.fingerprint, artifact.tarball_path, artifact.sha1, artifact.dependencies, artifact.new_version?, false)
72
+ return artifact, blobstore_id
73
+ end
74
+
75
+ def install(artifact)
76
+ fingerprint = artifact.fingerprint
77
+ origin_file = artifact.tarball_path
78
+ new_tarball_path = place_file_and_update_index(fingerprint, origin_file,
79
+ artifact.dev_artifact? ? @dev_index : @final_index,
80
+ artifact.dev_artifact? ? @dev_storage : @final_storage)
81
+
82
+ BuildArtifact.new(artifact.name, artifact.fingerprint, new_tarball_path, artifact.sha1, artifact.dependencies, artifact.new_version?, artifact.dev_artifact?)
83
+ end
84
+
85
+ def copy_from_dev_to_final(artifact)
86
+ final_tarball_path = place_file_and_update_index(artifact.fingerprint, artifact.tarball_path, @final_index, @final_storage)
87
+ BuildArtifact.new(artifact.name, artifact.fingerprint, final_tarball_path, artifact.sha1, artifact.dependencies, artifact.new_version?, false)
88
+ end
89
+
90
+ private
91
+
92
+ def place_file_and_update_index(fingerprint, origin_file, index, storage)
93
+ # add version (with its validation) before adding sha1
94
+ index.add_version(fingerprint, {'version' => fingerprint} )
95
+ tarball_path = storage.put_file(fingerprint, origin_file)
96
+ sha1 = file_checksum(tarball_path)
97
+ index.update_version(fingerprint, {'version' => fingerprint, 'sha1' => sha1})
98
+ tarball_path
99
+ end
100
+
101
+ def file_checksum(path)
102
+ Digest::SHA1.file(path).hexdigest
103
+ end
104
+ end
105
+
106
+ class CorruptedArchive < StandardError
107
+ end
108
+ end
@@ -0,0 +1,12 @@
1
+ module Bosh::Cli
2
+ class ArchiveRepositoryProvider
3
+ def initialize(archive_dir, blobstore)
4
+ @archive_dir = archive_dir
5
+ @blobstore = blobstore
6
+ end
7
+
8
+ def get(resource)
9
+ ArchiveRepository.new(@archive_dir, @blobstore, resource)
10
+ end
11
+ end
12
+ end
@@ -48,7 +48,7 @@ module Bosh::Cli
48
48
  def release
49
49
  return @release if @release
50
50
  check_if_release_dir
51
- @release = Bosh::Cli::Release.new(@work_dir, options[:final])
51
+ @release = Bosh::Cli::Release.new(work_dir, options[:final])
52
52
  end
53
53
 
54
54
  def progress_renderer
@@ -0,0 +1,75 @@
1
+ module Bosh::Cli
2
+ class BuildArtifact
3
+ attr_reader :name, :fingerprint, :tarball_path, :sha1, :dependencies
4
+
5
+ def initialize(name, fingerprint, tarball_path, sha1, dependencies, is_new_version, is_dev_artifact)
6
+ @name = name
7
+ @fingerprint = fingerprint
8
+ @tarball_path = tarball_path
9
+ @sha1 = sha1
10
+ @dependencies = dependencies
11
+ @is_dev_artifact = is_dev_artifact
12
+ @notes = []
13
+ @is_new_version = is_new_version
14
+ end
15
+
16
+ def version
17
+ fingerprint
18
+ end
19
+
20
+ def dev_artifact?
21
+ @is_dev_artifact
22
+ end
23
+
24
+ def new_version?
25
+ @is_new_version
26
+ end
27
+
28
+ private
29
+
30
+ def self.checksum(tarball_path)
31
+ if tarball_path && File.exists?(tarball_path)
32
+ digest_file(tarball_path)
33
+ else
34
+ nil
35
+ end
36
+ end
37
+
38
+ def self.digest_file(filename)
39
+ File.file?(filename) ? Digest::SHA1.file(filename).hexdigest : ''
40
+ end
41
+
42
+ # Git doesn't really track file permissions, it just looks at executable
43
+ # bit and uses 0755 if it's set or 0644 if not. We have to mimic that
44
+ # behavior in the fingerprint calculation to avoid the situation where
45
+ # seemingly clean working copy would trigger new fingerprints for
46
+ # artifacts with changed permissions. Also we don't want current
47
+ # fingerprints to change, hence the exact values below.
48
+ def self.file_mode(path)
49
+ if File.directory?(path)
50
+ '40755'
51
+ elsif File.executable?(path)
52
+ '100755'
53
+ else
54
+ '100644'
55
+ end
56
+ end
57
+
58
+ # TODO: be sure we are handling the case in which there was an index, with a pre-defined fingerprint
59
+ def self.make_fingerprint(resource)
60
+ scheme = 2
61
+ contents = "v#{scheme}"
62
+
63
+ resource.files.each do |filename, name|
64
+ contents << resource.format_fingerprint(digest_file(filename), filename, name, file_mode(filename))
65
+ end
66
+
67
+ contents << resource.additional_fingerprints.join(",")
68
+ Digest::SHA1.hexdigest(contents)
69
+ end
70
+
71
+ def resource
72
+ raise
73
+ end
74
+ end
75
+ end
@@ -154,18 +154,13 @@ module Bosh::Cli::Command
154
154
  end
155
155
 
156
156
  say(" - discovering packages")
157
- packages = Bosh::Cli::PackageBuilder.discover(
158
- work_dir,
159
- :dry_run => true,
160
- :final => false
161
- )
157
+ packages = Bosh::Cli::Resources::Package.discover(work_dir)
162
158
 
163
159
  say(" - discovering jobs")
164
- jobs = Bosh::Cli::JobBuilder.discover(
160
+ jobs = Bosh::Cli::Resources::Job.discover(
165
161
  work_dir,
166
- :dry_run => true,
167
- :final => false,
168
- :package_names => packages.map {|package| package.name}
162
+ # TODO: be sure this is covered in integration
163
+ packages.map {|package| package['name']}
169
164
  )
170
165
 
171
166
  say(" - validating properties")
@@ -85,13 +85,13 @@ module Bosh::Cli::Command
85
85
  end
86
86
 
87
87
  header('Building packages')
88
- packages = build_packages(dry_run, final)
88
+ package_artifacts = build_packages(dry_run, final)
89
89
 
90
90
  header('Building jobs')
91
- jobs = build_jobs(packages.map(&:name), dry_run, final)
91
+ job_artifacts = build_jobs(package_artifacts.map { |artifact| artifact.name }, dry_run, final)
92
92
 
93
93
  header('Building release')
94
- release_builder = build_release(dry_run, final, jobs, manifest_only, packages, name, version)
94
+ release_builder = build_release(dry_run, final, job_artifacts, manifest_only, package_artifacts, name, version)
95
95
 
96
96
  header('Release summary')
97
97
  show_summary(release_builder)
@@ -134,22 +134,19 @@ module Bosh::Cli::Command
134
134
  end
135
135
 
136
136
  def build_packages(dry_run, final)
137
- packages = Bosh::Cli::PackageBuilder.discover(
138
- work_dir,
139
- :final => final,
140
- :blobstore => release.blobstore,
141
- :dry_run => dry_run
142
- )
143
-
144
- packages.each do |package|
137
+ archive_builder = Bosh::Cli::ArchiveBuilder.new(archive_repository_provider,
138
+ dry_run: dry_run, final: final)
139
+ packages = Bosh::Cli::Resources::Package.discover(work_dir)
140
+ artifacts = packages.map do |package|
145
141
  say("Building #{package.name.make_green}...")
146
- package.build
142
+ artifact = archive_builder.build(package)
147
143
  nl
144
+ artifact
148
145
  end
149
146
 
150
147
  if packages.size > 0
151
- package_index = packages.inject({}) do |index, package|
152
- index[package.name] = package.dependencies
148
+ package_index = artifacts.inject({}) do |index, artifact|
149
+ index[artifact.name] = artifact.dependencies
153
150
  index
154
151
  end
155
152
  sorted_packages = tsort_packages(package_index)
@@ -161,11 +158,15 @@ module Bosh::Cli::Command
161
158
  nl
162
159
  end
163
160
 
164
- packages
161
+ artifacts
162
+ end
163
+
164
+ def archive_repository_provider
165
+ @archive_repository_provider ||= Bosh::Cli::ArchiveRepositoryProvider.new(work_dir, release.blobstore)
165
166
  end
166
167
 
167
- def build_release(dry_run, final, jobs, manifest_only, packages, name, version)
168
- release_builder = Bosh::Cli::ReleaseBuilder.new(release, packages, jobs, name,
168
+ def build_release(dry_run, final, job_artifacts, manifest_only, package_artifacts, name, version)
169
+ release_builder = Bosh::Cli::ReleaseBuilder.new(release, package_artifacts, job_artifacts, name,
169
170
  final: final,
170
171
  commit_hash: commit_hash,
171
172
  version: version,
@@ -183,22 +184,18 @@ module Bosh::Cli::Command
183
184
  release_builder
184
185
  end
185
186
 
186
- def build_jobs(built_package_names, dry_run, final)
187
- jobs = Bosh::Cli::JobBuilder.discover(
188
- work_dir,
189
- :final => final,
190
- :blobstore => release.blobstore,
191
- :dry_run => dry_run,
192
- :package_names => built_package_names
193
- )
194
-
195
- jobs.each do |job|
187
+ def build_jobs(packages, dry_run, final)
188
+ archive_builder = Bosh::Cli::ArchiveBuilder.new(archive_repository_provider,
189
+ :final => final, :dry_run => dry_run)
190
+ jobs = Bosh::Cli::Resources::Job.discover(work_dir, packages)
191
+ artifacts = jobs.map do |job|
196
192
  say("Building #{job.name.make_green}...")
197
- job.build
193
+ artifact = archive_builder.build(job)
198
194
  nl
195
+ artifact
199
196
  end
200
197
 
201
- jobs
198
+ artifacts
202
199
  end
203
200
 
204
201
  def save_final_release_name
@@ -225,15 +222,15 @@ module Bosh::Cli::Command
225
222
  def show_summary(builder)
226
223
  packages_table = table do |t|
227
224
  t.headings = %w(Name Version Notes)
228
- builder.packages.each do |package|
229
- t << artifact_summary(package)
225
+ builder.packages.each do |package_artifact|
226
+ t << artifact_summary(package_artifact)
230
227
  end
231
228
  end
232
229
 
233
230
  jobs_table = table do |t|
234
231
  t.headings = %w(Name Version Notes)
235
- builder.jobs.each do |job|
236
- t << artifact_summary(job)
232
+ builder.jobs.each do |job_artifact|
233
+ t << artifact_summary(job_artifact)
237
234
  end
238
235
  end
239
236
 
@@ -260,12 +257,12 @@ module Bosh::Cli::Command
260
257
  end
261
258
  end
262
259
 
263
- def artifact_summary(artefact)
264
- result = []
265
- result << artefact.name
266
- result << artefact.version
267
- result << artefact.notes.join(', ')
268
- result
260
+ def artifact_summary(artifact)
261
+ [
262
+ artifact.name,
263
+ artifact.version,
264
+ artifact.new_version? ? 'new version' : '',
265
+ ]
269
266
  end
270
267
 
271
268
  def commit_hash