bosh_cli 1.2831.0 → 1.2839.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.
@@ -0,0 +1,32 @@
1
+ module Bosh::Cli
2
+ class GlobMatch
3
+ # Helper class encapsulating the data we know about the glob. We need
4
+ # both directory and file path, as we match the same path in several
5
+ # directories (src, src_alt, blobs)
6
+ attr_reader :dir
7
+ attr_reader :path
8
+
9
+ def initialize(dir, path)
10
+ @dir = dir
11
+ @path = path
12
+ end
13
+
14
+ def full_path
15
+ File.join(dir, path)
16
+ end
17
+
18
+ def <=>(other)
19
+ @path <=> other.path
20
+ end
21
+
22
+ # GlobMatch will be used as Hash key (as implied by using Set),
23
+ # hence we need to define both eql? and hash
24
+ def eql?(other)
25
+ @path == other.path
26
+ end
27
+
28
+ def hash
29
+ @path.hash
30
+ end
31
+ end
32
+ end
@@ -8,12 +8,12 @@ module Bosh::Cli
8
8
  include Enumerable
9
9
  include Bosh::Template::PropertyHelper
10
10
 
11
- # @param [JobBuilder] job_builder Which job this property collection is for
11
+ # @param [Bosh::Cli::Resources::Job] job Which job this property collection is for
12
12
  # @param [Hash] global_properties Globally defined properties
13
13
  # @param [Hash] job_properties Properties defined for this job only
14
14
  # @param [Hash] mappings Property mappings for this job
15
- def initialize(job_builder, global_properties, job_properties, mappings)
16
- @job_builder = job_builder
15
+ def initialize(job, global_properties, job_properties, mappings)
16
+ @job = job
17
17
 
18
18
  @job_properties = Bosh::Common::DeepCopy.copy(job_properties || {})
19
19
  merge(@job_properties, Bosh::Common::DeepCopy.copy(global_properties))
@@ -52,7 +52,7 @@ module Bosh::Cli
52
52
 
53
53
  # @return [void] Modifies @properties
54
54
  def filter_properties
55
- if @job_builder.properties.empty?
55
+ if @job.properties.empty?
56
56
  # If at least one template doesn't have properties defined, we
57
57
  # need all properties to be available to job (backward-compatibility)
58
58
  @properties = @job_properties
@@ -61,7 +61,7 @@ module Bosh::Cli
61
61
 
62
62
  @properties = {}
63
63
 
64
- @job_builder.properties.each_pair do |name, definition|
64
+ @job.properties.each_pair do |name, definition|
65
65
  copy_property(
66
66
  @properties, @job_properties, name, definition["default"])
67
67
  end
@@ -7,7 +7,7 @@ module Bosh::Cli
7
7
  attr_reader :template_errors
8
8
  attr_reader :jobs_without_properties
9
9
 
10
- # @param [Array<JobBuilder>] built_jobs Built job templates
10
+ # @param [Array<Bosh::Cli::Resources::Job>] built_jobs Built job templates
11
11
  # @param [Hash] manifest Deployment manifest
12
12
  def initialize(built_jobs, manifest)
13
13
  @built_jobs = {}
@@ -44,7 +44,7 @@ module Bosh::Cli
44
44
  end
45
45
 
46
46
  if job["template"].nil?
47
- bad_manifest("Job `#{job_name}' doesn't have a template")
47
+ bad_manifest("Job '#{job_name}' doesn't have a template")
48
48
  end
49
49
  end
50
50
 
@@ -68,7 +68,7 @@ module Bosh::Cli
68
68
  built_job = @built_jobs[job_spec["template"]]
69
69
 
70
70
  if built_job.nil?
71
- raise CliError, "Job `#{job_spec["template"]}' has not been built"
71
+ raise CliError, "Job '#{job_spec["template"]}' has not been built"
72
72
  end
73
73
 
74
74
  collection = JobPropertyCollection.new(
@@ -86,8 +86,8 @@ module Bosh::Cli
86
86
  'properties' => collection.to_hash
87
87
  }
88
88
 
89
- built_job.all_templates.each do |template_path|
90
- evaluate_template(built_job, template_path, spec)
89
+ built_job.files.each do |file_tuple|
90
+ evaluate_template(built_job, file_tuple.first, spec)
91
91
  end
92
92
  end
93
93
 
@@ -104,7 +104,7 @@ module Bosh::Cli
104
104
 
105
105
  private
106
106
 
107
- # @param [JobBuilder] job Job builder
107
+ # @param [Bosh::Cli::Resources::Job] job Job builder
108
108
  # @param [String] template_path Template path
109
109
  # @param [Hash] spec Fake instance spec
110
110
  def evaluate_template(job, template_path, spec)
@@ -127,7 +127,7 @@ module Bosh::Cli
127
127
  attr_reader :exception
128
128
  attr_reader :line
129
129
 
130
- # @param [JobBuilder] job
130
+ # @param [Bosh::Cli::Resources::Job] job
131
131
  # @param [String] template_path
132
132
  # @param [Exception] exception
133
133
  def initialize(job, template_path, exception)
@@ -138,4 +138,4 @@ module Bosh::Cli
138
138
  end
139
139
  end
140
140
  end
141
- end
141
+ end
@@ -5,16 +5,16 @@ module Bosh::Cli
5
5
  attr_reader :release, :packages, :jobs, :name, :version, :build_dir, :commit_hash, :uncommitted_changes
6
6
 
7
7
  # @param [Bosh::Cli::Release] release Current release
8
- # @param [Array<Bosh::Cli::PackageBuilder>] packages Built packages
9
- # @param [Array<Bosh::Cli::JobBuilder>] jobs Built jobs
8
+ # @param [Array<Bosh::Cli::BuildArtifact>] package_artifacts Built packages
9
+ # @param [Array<Bosh::Cli::BuildArtifact>] job_artifacts Built jobs
10
10
  # @param [Hash] options Release build options
11
- def initialize(release, packages, jobs, name, options = { })
11
+ def initialize(release, package_artifacts, job_artifacts, name, options = { })
12
12
  @release = release
13
13
  @final = options.has_key?(:final) ? !!options[:final] : false
14
14
  @commit_hash = options.fetch(:commit_hash, '00000000')
15
15
  @uncommitted_changes = options.fetch(:uncommitted_changes, true)
16
- @packages = packages
17
- @jobs = jobs
16
+ @packages = package_artifacts # todo
17
+ @jobs = job_artifacts # todo
18
18
  @name = name
19
19
  raise 'Release name is blank' if name.blank?
20
20
 
@@ -49,19 +49,18 @@ module Bosh::Cli
49
49
  @final
50
50
  end
51
51
 
52
- # @return [Array] List of jobs affected by this release compared
53
- # to the previous one.
52
+ # @return [Array<Bosh::Cli::BuildArtifact>] List of job artifacts
53
+ # affected by this release compared to the previous one.
54
54
  def affected_jobs
55
- result = Set.new(@jobs.select { |job| job.new_version? })
56
- return result if @packages.empty?
55
+ result = Set.new(@jobs.select { |job_artifact| job_artifact.new_version? })
56
+ return result.to_a if @packages.empty?
57
57
 
58
- new_package_names = @packages.inject([]) do |list, package|
59
- list << package.name if package.new_version?
60
- list
61
- end
58
+ new_package_names = @packages.map do |package_artifact|
59
+ package_artifact.name if package_artifact.new_version?
60
+ end.compact
62
61
 
63
62
  @jobs.each do |job|
64
- result << job if (new_package_names & job.packages).size > 0
63
+ result << job if (new_package_names & job.dependencies).size > 0
65
64
  end
66
65
 
67
66
  result.to_a
@@ -83,22 +82,25 @@ module Bosh::Cli
83
82
 
84
83
  # Copies packages into release
85
84
  def copy_packages
86
- packages.each do |package|
87
- say("%-40s %s" % [package.name.make_green,
88
- pretty_size(package.tarball_path)])
89
- FileUtils.cp(package.tarball_path,
90
- File.join(build_dir, "packages", "#{package.name}.tgz"),
85
+ packages.each do |package_artifact|
86
+ name = package_artifact.name
87
+ tarball_path = package_artifact.tarball_path
88
+ say("%-40s %s" % [name.make_green, pretty_size(tarball_path)])
89
+ FileUtils.cp(tarball_path,
90
+ File.join(build_dir, "packages", "#{name}.tgz"),
91
91
  :preserve => true)
92
92
  end
93
93
  @packages_copied = true
94
94
  end
95
95
 
96
- # Copies jobs into release
96
+ # Copies jobs into release todo DRY vs copy_packages
97
97
  def copy_jobs
98
- jobs.each do |job|
99
- say("%-40s %s" % [job.name.make_green, pretty_size(job.tarball_path)])
100
- FileUtils.cp(job.tarball_path,
101
- File.join(build_dir, "jobs", "#{job.name}.tgz"),
98
+ jobs.each do |job_artifact|
99
+ name = job_artifact.name
100
+ tarball_path = job_artifact.tarball_path
101
+ say("%-40s %s" % [name.make_green, pretty_size(tarball_path)])
102
+ FileUtils.cp(tarball_path,
103
+ File.join(build_dir, "jobs", "#{name}.tgz"),
102
104
  :preserve => true)
103
105
  end
104
106
  @jobs_copied = true
@@ -107,40 +109,37 @@ module Bosh::Cli
107
109
  # Generates release manifest
108
110
  def generate_manifest
109
111
  manifest = {}
110
- manifest["packages"] = []
111
-
112
- manifest["packages"] = packages.map do |package|
112
+ manifest['packages'] = packages.map do |build_artifact|
113
113
  {
114
- "name" => package.name,
115
- "version" => package.version,
116
- "sha1" => package.checksum,
117
- "fingerprint" => package.fingerprint,
118
- "dependencies" => package.dependencies
114
+ 'name' => build_artifact.name,
115
+ 'version' => build_artifact.version,
116
+ 'fingerprint' => build_artifact.fingerprint,
117
+ 'sha1' => build_artifact.sha1,
118
+ 'dependencies' => build_artifact.dependencies,
119
119
  }
120
120
  end
121
-
122
- manifest["jobs"] = jobs.map do |job|
121
+ manifest['jobs'] = jobs.map do |build_artifact|
123
122
  {
124
- "name" => job.name,
125
- "version" => job.version,
126
- "fingerprint" => job.fingerprint,
127
- "sha1" => job.checksum,
123
+ 'name' => build_artifact.name,
124
+ 'version' => build_artifact.version,
125
+ 'fingerprint' => build_artifact.fingerprint,
126
+ 'sha1' => build_artifact.sha1,
128
127
  }
129
128
  end
130
129
 
131
- manifest["commit_hash"] = commit_hash
132
- manifest["uncommitted_changes"] = uncommitted_changes
130
+ manifest['commit_hash'] = commit_hash
131
+ manifest['uncommitted_changes'] = uncommitted_changes
133
132
 
134
133
  unless @name.bosh_valid_id?
135
- raise InvalidRelease, "Release name `#{@name}' is not a valid BOSH identifier"
134
+ raise InvalidRelease, "Release name '#{@name}' is not a valid BOSH identifier"
136
135
  end
137
- manifest["name"] = @name
136
+ manifest['name'] = @name
138
137
 
139
138
  # New release versions are allowed to have the same fingerprint as old versions.
140
139
  # For reverse compatibility, random uuids are stored instead.
141
- @index.add_version(SecureRandom.uuid, { "version" => version })
140
+ @index.add_version(SecureRandom.uuid, { 'version' => version })
142
141
 
143
- manifest["version"] = version
142
+ manifest['version'] = version
144
143
  manifest_yaml = Psych.dump(manifest)
145
144
 
146
145
  say("Writing manifest...")
@@ -14,13 +14,13 @@ module Bosh::Cli
14
14
  # @param [Bosh::Blobstore::Client] blobstore Blobstore client
15
15
  # @param [Array] package_matches List of package checksums that director
16
16
  # can match
17
- # @param [String] release_dir Release directory
17
+ # @param [String] release_source Release directory
18
18
  def initialize(manifest_file, blobstore,
19
- package_matches = [], release_dir = nil)
19
+ package_matches = [], release_source = nil)
20
20
 
21
21
  @blobstore = blobstore
22
- @release_dir = release_dir || Dir.pwd
23
- @manifest_file = File.expand_path(manifest_file, @release_dir)
22
+ @release_source = release_source || Dir.pwd
23
+ @manifest_file = File.expand_path(manifest_file, @release_source)
24
24
  @tarball_path = nil
25
25
 
26
26
  @build_dir = Dir.mktmpdir
@@ -95,18 +95,18 @@ module Bosh::Cli
95
95
 
96
96
  def find_package(package)
97
97
  name = package.name
98
- final_package_dir = File.join(@release_dir, '.final_builds', 'packages', name)
98
+ final_package_dir = File.join(@release_source, '.final_builds', 'packages', name)
99
99
  final_index = Versions::VersionsIndex.new(final_package_dir)
100
- dev_package_dir = File.join(@release_dir, '.dev_builds', 'packages', name)
100
+ dev_package_dir = File.join(@release_source, '.dev_builds', 'packages', name)
101
101
  dev_index = Versions::VersionsIndex.new(dev_package_dir)
102
102
  find_in_indices(final_index, dev_index, package, 'package')
103
103
  end
104
104
 
105
105
  def find_job(job)
106
106
  name = job.name
107
- final_jobs_dir = File.join(@release_dir, '.final_builds', 'jobs', name)
107
+ final_jobs_dir = File.join(@release_source, '.final_builds', 'jobs', name)
108
108
  final_index = Versions::VersionsIndex.new(final_jobs_dir)
109
- dev_jobs_dir = File.join(@release_dir, '.dev_builds', 'jobs', name)
109
+ dev_jobs_dir = File.join(@release_source, '.dev_builds', 'jobs', name)
110
110
  dev_index = Versions::VersionsIndex.new(dev_jobs_dir)
111
111
  find_in_indices(final_index, dev_index, job, 'job')
112
112
  end
@@ -148,9 +148,9 @@ module Bosh::Cli
148
148
  # @return [Boolean]
149
149
  def remote_package_exists?(local_package)
150
150
  # If checksum is known to director we can always match it
151
- @package_matches.include?(local_package.sha1) ||
152
- (local_package.fingerprint &&
153
- @package_matches.include?(local_package.fingerprint))
151
+ @package_matches.include?(local_package.sha1) || # !!! Needs test coverage
152
+ (local_package.fingerprint && # !!! Needs test coverage
153
+ @package_matches.include?(local_package.fingerprint)) # !!! Needs test coverage
154
154
  end
155
155
 
156
156
  # Checks if local job is already known remotely
@@ -0,0 +1,190 @@
1
+ module Bosh::Cli::Resources
2
+ class Job
3
+ BUILD_HOOK_FILES = ['prepare']
4
+
5
+ # @param [String] directory base Release directory
6
+ def self.discover(release_base, packages)
7
+ Dir[File.join(release_base, 'jobs', '*')].inject([]) do |jobs, job_base|
8
+ next unless File.directory?(job_base)
9
+ jobs << new(job_base, release_base, packages)
10
+ end
11
+ end
12
+
13
+ attr_reader :job_base, :release_base, :package_dependencies
14
+
15
+ def initialize(job_base, release_base, packages)
16
+ @release_base = Pathname.new(release_base)
17
+ @job_base = Pathname.new(job_base)
18
+ @package_dependencies = packages
19
+ end
20
+
21
+ def spec
22
+ @spec ||= load_yaml_file(job_base.join('spec'))
23
+ rescue
24
+ raise Bosh::Cli::InvalidJob, 'Job spec is missing'
25
+ end
26
+
27
+ def name
28
+ spec['name']
29
+ end
30
+
31
+ def dependencies
32
+ package_dependencies #TODO: should this be packages or package_dependencies?
33
+ end
34
+
35
+ def singular_type
36
+ 'job'
37
+ end
38
+
39
+ def plural_type
40
+ 'jobs'
41
+ end
42
+
43
+ def files
44
+ validate!
45
+
46
+ files = (templates_files + monit_files).map { |absolute_path| [absolute_path, relative_path(absolute_path)] }
47
+ files << [File.join(job_base, 'spec'), 'job.MF']
48
+ files
49
+ end
50
+
51
+ # TODO: check dependency packages
52
+ def validate!
53
+ if name.blank?
54
+ raise Bosh::Cli::InvalidJob, 'Job name is missing'
55
+ end
56
+
57
+ unless name.bosh_valid_id?
58
+ raise Bosh::Cli::InvalidJob, "'#{name}' is not a valid BOSH identifier"
59
+ end
60
+
61
+ unless spec['templates'].is_a?(Hash)
62
+ raise Bosh::Cli::InvalidJob, "Incorrect templates section in '#{name}' job spec (Hash expected, #{spec['templates'].class} given)"
63
+ end
64
+
65
+ if extra_templates.size > 0
66
+ raise Bosh::Cli::InvalidJob, "There are unused template files for job '#{name}': #{extra_templates.join(", ")}"
67
+ end
68
+
69
+ if missing_templates.size > 0
70
+ raise Bosh::Cli::InvalidJob, "Some template files required by '#{name}' job are missing: #{missing_templates.join(", ")}"
71
+ end
72
+
73
+ if missing_packages.size > 0
74
+ raise Bosh::Cli::InvalidJob, "Some packages required by '#{name}' job are missing: #{missing_packages.join(", ")}"
75
+ end
76
+
77
+ if spec.has_key?('properties')
78
+ unless spec['properties'].is_a?(Hash)
79
+ raise Bosh::Cli::InvalidJob, "Incorrect properties section in '#{name}' job spec (Hash expected, #{spec['properties'].class} given)"
80
+ end
81
+ end
82
+
83
+ unless monit_files.size > 0
84
+ raise Bosh::Cli::InvalidJob, "Cannot find monit file for '#{name}'"
85
+ end
86
+ end
87
+
88
+ def additional_fingerprints
89
+ []
90
+ end
91
+
92
+ def format_fingerprint(digest, filename, name, file_mode)
93
+ "%s%s%s" % [File.basename(filename), digest, file_mode]
94
+ end
95
+
96
+ def run_script(script_name, *args)
97
+ if BUILD_HOOK_FILES.include?(script_name.to_s)
98
+ send(:"run_script_#{script_name}", *args)
99
+ end
100
+ end
101
+
102
+ def properties
103
+ spec['properties'] || {}
104
+ end
105
+
106
+ private
107
+
108
+ def extra_templates
109
+ return [] if !File.directory?(templates_dir)
110
+
111
+ Dir.chdir(templates_dir) do
112
+ Dir["**/*"].reject do |file|
113
+ File.directory?(file) || templates.include?(file)
114
+ end
115
+ end
116
+ end
117
+
118
+ def missing_packages
119
+ @missing_packages ||= (packages - package_dependencies)
120
+ end
121
+
122
+ def missing_templates
123
+ templates.select do |template|
124
+ !File.exists?(File.join(templates_dir, template))
125
+ end
126
+ end
127
+
128
+ def monit_files
129
+ monit = File.join(job_base, 'monit')
130
+ files = Dir.glob(File.join(job_base, '*.monit'))
131
+ files << monit if File.exist?(monit)
132
+ files
133
+ end
134
+
135
+ def packages
136
+ spec['packages'] || []
137
+ end
138
+
139
+ def relative_path(path)
140
+ Pathname.new(path).relative_path_from(job_base).to_s
141
+ end
142
+
143
+ def templates
144
+ spec['templates'].keys
145
+ end
146
+
147
+ def templates_dir
148
+ @templates_dir ||= job_base.join('templates')
149
+ end
150
+
151
+ def templates_files
152
+ templates.map { |file| File.join(templates_dir, file) }
153
+ end
154
+
155
+ def run_script_prepare
156
+ script_path = File.join(job_base, 'prepare')
157
+
158
+ return nil unless File.exists?(script_path)
159
+
160
+ unless File.executable?(script_path)
161
+ raise Bosh::Cli::InvalidJob, "Prepare script at '#{script_path}' is not executable"
162
+ end
163
+
164
+ old_env = ENV
165
+ script_dir = File.dirname(script_path)
166
+ script_name = File.basename(script_path)
167
+
168
+ begin
169
+ # We need to temporarily delete some rubygems related artefacts
170
+ # because preparation scripts shouldn't share any assumptions
171
+ # with CLI itself
172
+ %w{ BUNDLE_GEMFILE RUBYOPT }.each { |key| ENV.delete(key) }
173
+
174
+ output = nil
175
+ Dir.chdir(script_dir) do
176
+ cmd = "./#{script_name} 2>&1"
177
+ output = `#{cmd}`
178
+ end
179
+
180
+ unless $?.exitstatus == 0
181
+ raise Bosh::Cli::InvalidJob, "'#{script_path}' script failed: #{output}"
182
+ end
183
+
184
+ output
185
+ ensure
186
+ ENV.each_pair { |k, v| ENV[k] = old_env[k] }
187
+ end
188
+ end
189
+ end
190
+ end