bosh-release 1.5.0.pre.1113

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.
data/README ADDED
@@ -0,0 +1,3 @@
1
+ Package compiler for compiling micro bosh (or in the future micro cloud) packages.
2
+
3
+ NOTE: It does not start the agent. It expects the agent to be up and running.
data/bin/bosh-release ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "trollop"
4
+ require "bosh/release"
5
+
6
+ CPIS = %w[aws openstack vcloud vsphere]
7
+ COMMANDS = %w[apply compile]
8
+
9
+ def usage(msg=nil)
10
+ $stderr.puts "error: #{msg}" if msg
11
+ $stderr.puts "usage: bosh-release <options> <command> <arguments>"
12
+ $stderr.puts " options:"
13
+ $stderr.puts " --cpi <cloud provider interface> (#{CPIS.join(', ')})"
14
+ $stderr.puts " --job <job to compile> (defaults to 'micro_<cpi>')"
15
+ $stderr.puts " commands:"
16
+ $stderr.puts " apply <apply_spec> <mbus_uri>"
17
+ $stderr.puts " compile <manifest> <release_tgz> <blobstore_path> <mbus_uri>"
18
+ exit 1
19
+ end
20
+
21
+ options = Trollop::options do
22
+ opt :cpi, "Cloud Provider Interface", :type => String, :required => true
23
+ opt :job, "Job to compile", :type => String
24
+ stop_on COMMANDS
25
+ end
26
+
27
+ unless CPIS.include?(options[:cpi])
28
+ Trollop::die :cpi, "unknown CPI '#{options[:cpi]}'"
29
+ end
30
+
31
+ unless options[:job_given]
32
+ options[:job] = "micro_#{options[:cpi]}"
33
+ end
34
+
35
+ options["command"] = ARGV.shift
36
+
37
+ case options["command"]
38
+ when "apply"
39
+ usage "apply requires 2 arguments" unless ARGV.size == 2
40
+ options["apply_spec"] = ARGV.shift
41
+ options["agent_uri"] = ARGV.shift
42
+ when "compile"
43
+ usage "compile requires 4 arguments" unless ARGV.size == 4
44
+ options["manifest"] = ARGV.shift
45
+ options["release"] = ARGV.shift
46
+ options["blobstore_options"] = {"blobstore_path" => ARGV.shift}
47
+ options["agent_uri"] = ARGV.shift
48
+ when nil
49
+ usage "no command given"
50
+ else
51
+ usage "unknown command: #{options["command"]}"
52
+ end
53
+
54
+ Bosh::Release::Runner.start(options)
@@ -0,0 +1,22 @@
1
+ module Bosh; module Release; end; end
2
+
3
+ require 'logger'
4
+ require 'yaml'
5
+ require 'blobstore_client'
6
+ require 'common/common'
7
+ require 'common/properties'
8
+ require 'agent_client'
9
+ require 'bosh/release/compiler'
10
+ require 'fileutils'
11
+
12
+ module Bosh
13
+ module Release
14
+ class Runner
15
+ class << self
16
+ def start(options)
17
+ Compiler.new(options).start
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,266 @@
1
+ module Bosh
2
+ module Release
3
+ class Compiler
4
+ include Bosh::Common::PropertyHelper
5
+
6
+ OPTIONS = {
7
+ "blobstore_options" => { "blobstore_path" => "/var/vcap/micro_bosh/data/cache" },
8
+ "blobstore_provider" => "local",
9
+ "base_dir" => "/var/vcap",
10
+ "platform_name" => "ubuntu",
11
+ "agent_uri" => "https://vcap:vcap@localhost:6969"
12
+ }
13
+
14
+ AGENT_START_RETRIES=16
15
+
16
+ def initialize(options)
17
+ @options = OPTIONS.merge(options)
18
+ @logger = Logger.new(@options["logfile"] || STDOUT)
19
+
20
+ FileUtils.mkdir_p(File.join(@options["base_dir"], "packages"))
21
+ bsc_provider = @options["blobstore_provider"]
22
+ bsc_options = @options["blobstore_options"]
23
+ @logger.info("Creating Blobstore client with #{bsc_provider} provider and options #{bsc_options}")
24
+ @blobstore_client = Bosh::Blobstore::Client.create(bsc_provider, bsc_options)
25
+ end
26
+
27
+ def start
28
+ # Start the "compile" or "apply"
29
+ send(@options["command"].to_sym)
30
+ end
31
+
32
+ def apply_spec
33
+ File.join(@options["base_dir"], "micro/apply_spec.yml")
34
+ end
35
+
36
+ def connect_to_agent
37
+ num_tries = 0
38
+ begin
39
+ @agent = Bosh::Agent::Client.create(@options["agent_uri"], "user" => "vcap", "password" => "vcap")
40
+ @agent.ping
41
+ rescue => e
42
+ num_tries += 1
43
+ sleep 0.1
44
+ # Dont retry forever
45
+ retry if num_tries < AGENT_START_RETRIES
46
+ @logger.warn("Error connecting to agent #{e.inspect}")
47
+ raise
48
+ end
49
+ end
50
+
51
+ def compile
52
+ @logger.info("Compiling #{@options["manifest"]} with tarball #{@options["release"]}")
53
+ connect_to_agent
54
+ deployment_mf = Psych.load_file(File.expand_path(@options["manifest"]))
55
+ @spec = prep_spec(deployment_mf)
56
+
57
+ @packages = {}
58
+ @spec["job"] = { "name" => @options[:job] }
59
+
60
+ untar(@options["release"]) do |dir|
61
+ release_mf = Psych.load_file("release.MF")
62
+ jobs = []
63
+
64
+ jobs_to_compile(@options[:job], deployment_mf).each do |spec_job|
65
+ job = find_by_name(release_mf["jobs"], spec_job)
66
+ job_path = File.expand_path("jobs/#{job["name"]}.tgz")
67
+ jobs << apply_spec_job(job, job_path)
68
+
69
+ if job["name"] == @options[:job]
70
+ @spec["job"]["version"] = job["version"].to_s
71
+ @spec["job"]["template"] = @options[:job]
72
+ @spec["job"]["sha1"] = job["sha1"]
73
+ @spec["job"]["blobstore_id"] = @blobstore_client.create(File.new(job_path))
74
+ end
75
+
76
+ untar(job_path) do
77
+ job = Psych.load_file("job.MF")
78
+
79
+ # add default job spec properties to apply spec
80
+ add_default_properties(@spec["properties"], job["properties"])
81
+
82
+ # Compile job packages
83
+ compile_packages(dir, release_mf, job["packages"])
84
+ end
85
+ end
86
+
87
+ @spec["job"]["templates"] = jobs
88
+ end
89
+ cleanup
90
+
91
+ # save apply spec
92
+ FileUtils.mkdir_p(File.dirname(apply_spec))
93
+ File.open(apply_spec, 'w') { |f| f.write(Psych.dump(@spec)) }
94
+
95
+ @spec["packages"]
96
+ rescue => e
97
+ @logger.error("Error #{e.message}, #{e.backtrace.join("\n")}")
98
+ end
99
+
100
+ def find_by_name(enum, name)
101
+ result = enum.find { |j| j["name"] == name }
102
+ if result
103
+ result
104
+ else
105
+ raise "Could not find name #{name} in #{enum}"
106
+ end
107
+ end
108
+
109
+ # Check manifest for job collocation
110
+ def jobs_to_compile(name, manifest)
111
+ compile_job = manifest["jobs"].find { |j| j["name"] == name } if manifest["jobs"]
112
+ if compile_job
113
+ compile_job["template"]
114
+ else
115
+ [name]
116
+ end
117
+ end
118
+
119
+ def apply_spec_job(job, job_path)
120
+ {
121
+ "name" => job["name"],
122
+ "version" => job["version"].to_s,
123
+ "sha1" => job["sha1"],
124
+ "blobstore_id" => @blobstore_client.create(File.new(job_path))
125
+ }
126
+ end
127
+
128
+ def cleanup
129
+ FileUtils.rm_rf("#{@options["base_dir"]}/data/compile")
130
+ FileUtils.rm_rf("#{@options["base_dir"]}/data/packages")
131
+ FileUtils.rm_rf("#{@options["base_dir"]}/data/tmp")
132
+ FileUtils.rm_rf("#{@options["base_dir"]}/packages")
133
+ end
134
+
135
+ def prep_spec(deployment)
136
+ spec = {}
137
+ spec["deployment"] = "micro"
138
+ spec["release"] = deployment["release"]
139
+ spec["properties"] = deployment["properties"]
140
+ spec["index"] = 0
141
+ spec["packages"] = {}
142
+ spec["configuration_hash"] = {}
143
+
144
+ case @options[:cpi]
145
+ when "vsphere", "vcloud"
146
+ spec["networks"] = {"local" => {"ip" => "127.0.0.1"}}
147
+ when "aws"
148
+ spec["networks"] = {"type" => "dynamic"}
149
+ when "openstack"
150
+ spec["networks"] = {"type" => "dynamic"}
151
+ else
152
+ puts "WARNING: no CPI specified"
153
+ end
154
+
155
+ spec
156
+ end
157
+
158
+ def compile_packages(dir, manifest, packages)
159
+ packages.each do |name|
160
+ package = find_package(manifest, name)
161
+ compile_packages(dir, manifest, package["dependencies"]) if package["dependencies"]
162
+
163
+ @logger.debug "compiling package #{name}"
164
+ compile_package(dir, package, name)
165
+ end
166
+ end
167
+
168
+ def find_package(manifest, name)
169
+ manifest["packages"].detect { |p| p["name"] == name }
170
+ end
171
+
172
+ def compile_package(dir, package, name)
173
+ # return if package is already compiled
174
+ return if @spec["packages"].has_key?(name)
175
+
176
+ src = "#{dir}/packages/#{name}.tgz"
177
+ version = package["version"]
178
+ dependencies = package["dependencies"]
179
+
180
+ # push source package into blobstore
181
+ file = File.new(src)
182
+ id = @blobstore_client.create(file)
183
+
184
+ sha1 = "sha1"
185
+ dependencies = {}
186
+ package["dependencies"].each do |name|
187
+ @logger.debug "dependency: #{name} = #{@spec["packages"][name]}"
188
+ dependencies[name] = @spec["packages"][name]
189
+ end
190
+
191
+ result = @agent.run_task(:compile_package, id, sha1, name, version, dependencies)
192
+ @logger.info("result is #{result}")
193
+
194
+ # remove source package from blobstore
195
+ @blobstore_client.delete(id)
196
+
197
+ id = result["result"]["blobstore_id"]
198
+ @logger.debug("stored package #{name} as #{id}")
199
+
200
+ @spec["packages"][name] = {
201
+ "name" => name,
202
+ "version" => version.to_s,
203
+ "sha1" => result["result"]["sha1"],
204
+ "blobstore_id" => id
205
+ }
206
+ end
207
+
208
+ def untar(file)
209
+ prev_dir = Dir.getwd
210
+ dir = Dir.mktmpdir
211
+ Dir.chdir(dir)
212
+ @logger.debug("untaring #{file} into #{dir}")
213
+ out = `tar xzf #{file} 2>&1`
214
+ raise RuntimeError, "untar of #{file} failed: #{out}" unless $? == 0
215
+ yield dir
216
+ ensure
217
+ Dir.chdir(prev_dir)
218
+ FileUtils.rm_rf dir
219
+ end
220
+
221
+ def apply
222
+ connect_to_agent
223
+ FileUtils.mkdir_p(File.join(@options["base_dir"], 'data/log'))
224
+ # Stop services
225
+ @logger.info("Stopping services")
226
+ begin
227
+ @agent.run_task(:stop)
228
+ rescue => e
229
+ @logger.warn("Ignoring error to stop services #{e.inspect}")
230
+ end
231
+
232
+ @spec = Psych.load_file(@options["apply_spec"])
233
+ @logger.info("#{@spec.inspect}")
234
+ update_bosh_spec
235
+ @agent.run_task(:apply, @spec)
236
+
237
+ @logger.info("Starting services")
238
+ @agent.run_task(:start)
239
+ end
240
+
241
+ def update_bosh_spec
242
+ uri = URI.parse(@options["agent_uri"])
243
+ ip = uri.host
244
+ properties = @spec["properties"]
245
+ properties["blobstore"]["address"] = ip
246
+ properties["postgres"]["address"] = ip
247
+ properties["director"]["address"] = ip
248
+ properties["redis"]["address"] = ip
249
+ properties["nats"]["address"] = ip
250
+ @spec["properties"] = properties
251
+ end
252
+
253
+ def add_default_properties(spec_properties, job_properties)
254
+ return unless job_properties
255
+
256
+ job_properties.each_pair do |name, definition|
257
+ unless definition["default"].nil?
258
+ copy_property(spec_properties, spec_properties, name, definition["default"])
259
+ end
260
+ end
261
+ end
262
+
263
+ end
264
+ end
265
+ end
266
+
@@ -0,0 +1,6 @@
1
+ module Bosh
2
+ module Release
3
+ VERSION = '1.5.0.pre.1113'
4
+ end
5
+ end
6
+
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bosh-release
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.5.0.pre.1113
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - VMware
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: agent_client
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.5.0.pre.1113
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.5.0.pre.1113
30
+ - !ruby/object:Gem::Dependency
31
+ name: blobstore_client
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 1.5.0.pre.1113
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 1.5.0.pre.1113
46
+ - !ruby/object:Gem::Dependency
47
+ name: bosh_common
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.5.0.pre.1113
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.5.0.pre.1113
62
+ - !ruby/object:Gem::Dependency
63
+ name: yajl-ruby
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.1.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 1.1.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: trollop
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.16'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.16'
94
+ description: ! 'Bosh package compiler
95
+
96
+ cfd471'
97
+ email: support@cloudfoundry.com
98
+ executables:
99
+ - bosh-release
100
+ extensions: []
101
+ extra_rdoc_files: []
102
+ files:
103
+ - lib/bosh/release.rb
104
+ - lib/bosh/release/compiler.rb
105
+ - lib/bosh/release/version.rb
106
+ - README
107
+ - bin/bosh-release
108
+ homepage: https://github.com/cloudfoundry/bosh
109
+ licenses:
110
+ - Apache 2.0
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ none: false
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: 1.9.3
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>'
125
+ - !ruby/object:Gem::Version
126
+ version: 1.3.1
127
+ requirements: []
128
+ rubyforge_project:
129
+ rubygems_version: 1.8.23
130
+ signing_key:
131
+ specification_version: 3
132
+ summary: Bosh package compiler
133
+ test_files: []