bosh_cli 1.2671.0 → 1.2682.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d4c1e1cbf0d51f7f8c6474986297c23727e40717
4
- data.tar.gz: 9cba9a5e26641af58639cfb67c686e5827ce04f7
3
+ metadata.gz: 6c465feaf450a9d1578bdcb091559697af1ca32d
4
+ data.tar.gz: 32eaf038e07206d75d5b13f604908df2d37eeac5
5
5
  SHA512:
6
- metadata.gz: ba53edc2f056dc17be341c9252cba7ae3d9cb6f42b7b08ed3726ec8e6b435aaddd298ba0ba2f8fb9a3b553e93cb7cf0ae832dd2d597257b9b13b9d16ace7bb70
7
- data.tar.gz: ca3d07521f5537aa40cbc7379e134f69b5bada1565de8ff09dd6636f324c3671bbba796783dbe4a0ca2400d7685c705c101194c4175ae45120e6dd61d545ac0c
6
+ metadata.gz: 59097f2f31a97d3103137d366fd750e413bbbd19d98aa000afcc54f0e75b08a0fb4c4674157214afde9a21a4707b9948385769ad24ac3ff90b316a173f704d2c
7
+ data.tar.gz: f1aa9306cdae45a81eefc22b6222f9e4651be01c7238a14dc4743985bc55f98f123b8f1336d103c3f015c3f11c07b0b836abb7d6fe4e85ad53cbf3be45d54696
data/lib/cli.rb CHANGED
@@ -31,6 +31,8 @@ require 'zlib'
31
31
  require 'archive/tar/minitar'
32
32
  include Archive::Tar
33
33
 
34
+ require 'semi_semantic/version'
35
+
34
36
  require 'bosh/template/evaluation_context'
35
37
 
36
38
  unless defined?(Bosh::Cli::VERSION)
@@ -61,10 +63,15 @@ require 'cli/director_task'
61
63
  require 'cli/line_wrap'
62
64
  require 'cli/backup_destination_path'
63
65
 
66
+ require 'cli/source_control/git_ignore'
67
+
64
68
  require 'cli/versions/versions_index'
65
69
  require 'cli/versions/local_version_storage'
66
70
  require 'cli/versions/release_versions_index'
71
+ require 'cli/versions/releases_dir_migrator'
67
72
  require 'cli/versions/version_file_resolver'
73
+ require 'cli/versions/multi_release_support'
74
+
68
75
  require 'cli/packaging_helper'
69
76
  require 'cli/package_builder'
70
77
  require 'cli/job_builder'
@@ -93,6 +100,6 @@ tmpdir = Dir.mktmpdir
93
100
  at_exit { FileUtils.rm_rf(tmpdir) }
94
101
  ENV['TMPDIR'] = tmpdir
95
102
 
96
- Dir[File.dirname(__FILE__) + '/cli/commands/*.rb'].each do |file|
103
+ Dir[File.dirname(__FILE__) + '/cli/commands/**/*.rb'].each do |file|
97
104
  require file
98
105
  end
@@ -0,0 +1,279 @@
1
+ module Bosh::Cli::Command
2
+ module Release
3
+ class CreateRelease < Base
4
+ include Bosh::Cli::DependencyHelper
5
+
6
+ DEFAULT_RELEASE_NAME = 'bosh-release'
7
+
8
+ # bosh create release
9
+ usage 'create release'
10
+ desc 'Create release (assumes current directory to be a release repository)'
11
+ option '--force', 'bypass git dirty state check'
12
+ option '--final', 'create final release'
13
+ option '--with-tarball', 'create release tarball'
14
+ option '--dry-run', 'stop before writing release manifest'
15
+ option '--name NAME', 'specify a custom release name'
16
+ option '--version VERSION', 'specify a custom version number (ex: 1.0.0 or 1.0-beta.2+dev.10)'
17
+
18
+ def create(manifest_file = nil)
19
+ check_if_release_dir
20
+
21
+ migrate_to_support_multiple_releases
22
+
23
+ if manifest_file && File.file?(manifest_file)
24
+ if options[:version]
25
+ err('Cannot specify a custom version number when creating from a manifest. The manifest already specifies a version.'.make_red)
26
+ end
27
+
28
+ say('Recreating release from the manifest')
29
+ Bosh::Cli::ReleaseCompiler.compile(manifest_file, release.blobstore)
30
+ release_filename = manifest_file
31
+ else
32
+ version = options[:version]
33
+ version = Bosh::Common::Version::ReleaseVersion.parse(version).to_s unless version.nil?
34
+
35
+ release_filename = create_from_spec(version)
36
+ end
37
+
38
+ if release_filename
39
+ release.latest_release_filename = release_filename
40
+ release.save_config
41
+ end
42
+ rescue SemiSemantic::ParseError
43
+ err("Invalid version: `#{version}'. Please specify a valid version (ex: 1.0.0 or 1.0-beta.2+dev.10).".make_red)
44
+ rescue Bosh::Cli::ReleaseVersionError => e
45
+ err(e.message.make_red)
46
+ end
47
+
48
+ private
49
+
50
+ def migrate_to_support_multiple_releases
51
+ default_release_name = release.final_name
52
+
53
+ # can't migrate without a default release name
54
+ return if default_release_name.blank?
55
+
56
+ Bosh::Cli::Versions::MultiReleaseSupport.new(@work_dir, default_release_name, self).migrate
57
+ end
58
+
59
+ def create_from_spec(version)
60
+ final = options[:final]
61
+ force = options[:force]
62
+ name = options[:name]
63
+ manifest_only = !options[:with_tarball]
64
+ dry_run = options[:dry_run]
65
+
66
+ err("Can't create final release without blobstore secret") if final && !release.has_blobstore_secret?
67
+
68
+ dirty_blob_check(force)
69
+
70
+ raise_dirty_state_error if dirty_state? && !force
71
+
72
+ if final
73
+ confirm_final_release(dry_run)
74
+ unless name
75
+ save_final_release_name if release.final_name.blank?
76
+ name = release.final_name
77
+ end
78
+ header('Building FINAL release'.make_green)
79
+ else
80
+ unless name
81
+ save_dev_release_name if release.dev_name.blank?
82
+ name = release.dev_name
83
+ end
84
+ header('Building DEV release'.make_green)
85
+ end
86
+
87
+ header('Building packages')
88
+ packages = build_packages(dry_run, final)
89
+
90
+ header('Building jobs')
91
+ jobs = build_jobs(packages.map(&:name), dry_run, final)
92
+
93
+ header('Building release')
94
+ release_builder = build_release(dry_run, final, jobs, manifest_only, packages, name, version)
95
+
96
+ header('Release summary')
97
+ show_summary(release_builder)
98
+ nl
99
+
100
+ return nil if dry_run
101
+
102
+ say("Release name: #{name.make_green}")
103
+ say("Release version: #{release_builder.version.to_s.make_green}")
104
+ say("Release manifest: #{release_builder.manifest_path.make_green}")
105
+
106
+ unless manifest_only
107
+ say("Release tarball (#{pretty_size(release_builder.tarball_path)}): " +
108
+ release_builder.tarball_path.make_green)
109
+ end
110
+
111
+ release.save_config
112
+
113
+ release_builder.manifest_path
114
+ end
115
+
116
+ def confirm_final_release(dry_run)
117
+ confirmed = non_interactive? || agree("Are you sure you want to generate #{'final'.make_red} version? ")
118
+ if !dry_run && !confirmed
119
+ say('Canceled release generation'.make_green)
120
+ exit(1)
121
+ end
122
+ end
123
+
124
+ def dirty_blob_check(force)
125
+ blob_manager.sync
126
+ if blob_manager.dirty?
127
+ blob_manager.print_status
128
+ if force
129
+ say("Proceeding with dirty blobs as '--force' is given".make_red)
130
+ else
131
+ err("Please use '--force' or upload new blobs")
132
+ end
133
+ end
134
+ end
135
+
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|
145
+ say("Building #{package.name.make_green}...")
146
+ package.build
147
+ nl
148
+ end
149
+
150
+ if packages.size > 0
151
+ package_index = packages.inject({}) do |index, package|
152
+ index[package.name] = package.dependencies
153
+ index
154
+ end
155
+ sorted_packages = tsort_packages(package_index)
156
+ header('Resolving dependencies')
157
+ say('Dependencies resolved, correct build order is:')
158
+ sorted_packages.each do |package_name|
159
+ say('- %s' % [package_name])
160
+ end
161
+ nl
162
+ end
163
+
164
+ packages
165
+ end
166
+
167
+ def build_release(dry_run, final, jobs, manifest_only, packages, name, version)
168
+ release_builder = Bosh::Cli::ReleaseBuilder.new(release, packages, jobs, name,
169
+ final: final,
170
+ commit_hash: commit_hash,
171
+ version: version,
172
+ uncommitted_changes: dirty_state?
173
+ )
174
+
175
+ unless dry_run
176
+ if manifest_only
177
+ release_builder.build(:generate_tarball => false)
178
+ else
179
+ release_builder.build(:generate_tarball => true)
180
+ end
181
+ end
182
+
183
+ release_builder
184
+ end
185
+
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|
196
+ say("Building #{job.name.make_green}...")
197
+ job.build
198
+ nl
199
+ end
200
+
201
+ jobs
202
+ end
203
+
204
+ def save_final_release_name
205
+ release.final_name = DEFAULT_RELEASE_NAME
206
+ if interactive?
207
+ release.final_name = ask('Please enter final release name: ').to_s
208
+ err('Canceled release creation, no name given') if release.final_name.blank?
209
+ end
210
+ release.save_config
211
+ end
212
+
213
+ def save_dev_release_name
214
+ if interactive?
215
+ release.dev_name = ask('Please enter development release name: ') do |q|
216
+ q.default = release.final_name if release.final_name
217
+ end.to_s
218
+ err('Canceled release creation, no name given') if release.dev_name.blank?
219
+ else
220
+ release.dev_name = release.final_name ? release.final_name : DEFAULT_RELEASE_NAME
221
+ end
222
+ release.save_config
223
+ end
224
+
225
+ def show_summary(builder)
226
+ packages_table = table do |t|
227
+ t.headings = %w(Name Version Notes)
228
+ builder.packages.each do |package|
229
+ t << artifact_summary(package)
230
+ end
231
+ end
232
+
233
+ jobs_table = table do |t|
234
+ t.headings = %w(Name Version Notes)
235
+ builder.jobs.each do |job|
236
+ t << artifact_summary(job)
237
+ end
238
+ end
239
+
240
+ say('Packages')
241
+ say(packages_table)
242
+ nl
243
+ say('Jobs')
244
+ say(jobs_table)
245
+
246
+ affected_jobs = builder.affected_jobs
247
+
248
+ if affected_jobs.size > 0
249
+ nl
250
+ say('Jobs affected by changes in this release')
251
+
252
+ affected_jobs_table = table do |t|
253
+ t.headings = %w(Name Version)
254
+ affected_jobs.each do |job|
255
+ t << [job.name, job.version]
256
+ end
257
+ end
258
+
259
+ say(affected_jobs_table)
260
+ end
261
+ end
262
+
263
+ def artifact_summary(artefact)
264
+ result = []
265
+ result << artefact.name
266
+ result << artefact.version
267
+ result << artefact.notes.join(', ')
268
+ result
269
+ end
270
+
271
+ def commit_hash
272
+ status = Bosh::Exec.sh('git show-ref --head --hash=8 2> /dev/null')
273
+ status.output.split.first
274
+ rescue Bosh::Exec::Error
275
+ '00000000'
276
+ end
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,32 @@
1
+ module Bosh::Cli::Command
2
+ module Release
3
+ class DeleteRelease < Base
4
+
5
+ usage 'delete release'
6
+ desc 'Delete release (or a particular release version)'
7
+ option '--force', 'ignore errors during deletion'
8
+ def delete(name, version = nil)
9
+ auth_required
10
+ force = !!options[:force]
11
+
12
+ desc = "#{name}"
13
+ desc << "/#{version}" if version
14
+
15
+ if force
16
+ say("Deleting `#{desc}' (FORCED DELETE, WILL IGNORE ERRORS)".make_red)
17
+ else
18
+ say("Deleting `#{desc}'".make_red)
19
+ end
20
+
21
+ if confirmed?
22
+ status, task_id = director.delete_release(name, force: force, version: version)
23
+ task_report(status, task_id, "Deleted `#{desc}'")
24
+ else
25
+ say('Canceled deleting release'.make_green)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+
@@ -0,0 +1,45 @@
1
+ module Bosh::Cli::Command
2
+ module Release
3
+ class InitRelease < Base
4
+
5
+ # bosh init release
6
+ usage 'init release'
7
+ desc 'Initialize release directory'
8
+ option '--git', 'initialize git repository'
9
+ def init(base = nil)
10
+ if base
11
+ FileUtils.mkdir_p(base)
12
+ Dir.chdir(base)
13
+ end
14
+
15
+ err('Release already initialized') if in_release_dir?
16
+ git_init if options[:git]
17
+
18
+ %w[config jobs packages src blobs].each do |dir|
19
+ FileUtils.mkdir(dir)
20
+ end
21
+
22
+ # Initialize an empty blobs index
23
+ File.open(File.join('config', 'blobs.yml'), 'w') do |f|
24
+ Psych.dump({}, f)
25
+ end
26
+
27
+ say('Release directory initialized'.make_green)
28
+ end
29
+
30
+ private
31
+
32
+ def git_init
33
+ out = %x{git init 2>&1}
34
+ if $? != 0
35
+ say("error running 'git init':\n#{out}")
36
+ else
37
+ Bosh::Cli::SourceControl::GitIgnore.new(@work_dir).update
38
+ end
39
+ rescue Errno::ENOENT
40
+ say("Unable to run 'git init'".make_red)
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,123 @@
1
+ module Bosh::Cli::Command
2
+ module Release
3
+ class ListReleases < Base
4
+
5
+ usage 'releases'
6
+ desc 'Show the list of available releases'
7
+ option '--jobs', 'include job templates'
8
+ def list
9
+ auth_required
10
+ releases = director.list_releases.sort do |r1, r2|
11
+ r1['name'] <=> r2['name']
12
+ end
13
+
14
+ err('No releases') if releases.empty?
15
+
16
+ currently_deployed = false
17
+ uncommited_changes = false
18
+ if releases.first.has_key? 'release_versions'
19
+ releases_table = build_releases_table(releases, options)
20
+ currently_deployed, uncommited_changes = release_version_details(releases)
21
+ elsif releases.first.has_key? 'versions'
22
+ releases_table = build_releases_table_for_old_director(releases)
23
+ currently_deployed, uncommited_changes = release_version_details_for_old_director(releases)
24
+ end
25
+
26
+ nl
27
+ say(releases_table.render)
28
+
29
+ say('(*) Currently deployed') if currently_deployed
30
+ say('(+) Uncommitted changes') if uncommited_changes
31
+ nl
32
+ say('Releases total: %d' % releases.size)
33
+ end
34
+
35
+ private
36
+ def build_releases_table_for_old_director(releases)
37
+ table do |t|
38
+ t.headings = 'Name', 'Versions'
39
+ releases.each do |release|
40
+ versions = release['versions'].sort { |v1, v2|
41
+ Bosh::Common::Version::ReleaseVersion.parse_and_compare(v1, v2)
42
+ }.map { |v| ((release['in_use'] || []).include?(v)) ? "#{v}*" : v }
43
+
44
+ t << [release['name'], versions.join(', ')]
45
+ end
46
+ end
47
+ end
48
+
49
+ # Builds table of release information
50
+ # Default headings: "Name", "Versions", "Commit Hash"
51
+ # Extra headings: options[:job] => "Jobs"
52
+ def build_releases_table(releases, options = {})
53
+ show_jobs = options[:jobs]
54
+ table do |t|
55
+ t.headings = 'Name', 'Versions', 'Commit Hash'
56
+ t.headings << 'Jobs' if show_jobs
57
+ releases.each do |release|
58
+ versions, commit_hashes = formatted_versions(release).transpose
59
+ row = [release['name'], versions.join("\n"), commit_hashes.join("\n")]
60
+ if show_jobs
61
+ jobs = formatted_jobs(release).transpose
62
+ row << jobs.join("\n")
63
+ end
64
+ t << row
65
+ end
66
+ end
67
+ end
68
+
69
+ def formatted_versions(release)
70
+ sort_versions(release['release_versions']).map { |v| formatted_version_and_commit_hash(v) }
71
+ end
72
+
73
+ def sort_versions(versions)
74
+ versions.sort { |v1, v2| Bosh::Common::Version::ReleaseVersion.parse_and_compare(v1['version'], v2['version']) }
75
+ end
76
+
77
+ def formatted_version_and_commit_hash(version)
78
+ version_number = version['version'] + (version['currently_deployed'] ? '*' : '')
79
+ commit_hash = version['commit_hash'] + (version['uncommitted_changes'] ? '+' : '')
80
+ [version_number, commit_hash]
81
+ end
82
+
83
+ def formatted_jobs(release)
84
+ sort_versions(release['release_versions']).map do |v|
85
+ if job_names = v['job_names']
86
+ [job_names.join(', ')]
87
+ else
88
+ ['n/a '] # with enough whitespace to match "Jobs" header
89
+ end
90
+ end
91
+ end
92
+
93
+
94
+ def release_version_details(releases)
95
+ currently_deployed = false
96
+ uncommitted_changes = false
97
+ releases.each do |release|
98
+ release['release_versions'].each do |version|
99
+ currently_deployed ||= version['currently_deployed']
100
+ uncommitted_changes ||= version['uncommitted_changes']
101
+ if currently_deployed && uncommitted_changes
102
+ return true, true
103
+ end
104
+ end
105
+ end
106
+ return currently_deployed, uncommitted_changes
107
+ end
108
+
109
+ def release_version_details_for_old_director(releases)
110
+ currently_deployed = false
111
+ # old director did not support uncommitted changes
112
+ uncommitted_changes = false
113
+ releases.each do |release|
114
+ currently_deployed ||= release['in_use'].any?
115
+ if currently_deployed
116
+ return true, uncommitted_changes
117
+ end
118
+ end
119
+ return currently_deployed, uncommitted_changes
120
+ end
121
+ end
122
+ end
123
+ end