bosh-release 1.5.0.pre.1113

Sign up to get free protection for your applications and to get access to all the features.
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: []