bosh-director 1.5.0.pre.1148 → 1.5.0.pre.1152

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ require 'bosh/director/api/controllers/base_controller'
2
+
3
+ module Bosh::Director
4
+ module Api::Controllers
5
+ class BackupsController < BaseController
6
+ post '/backups' do
7
+ start_task { @backup_manager.create_backup(@user) }
8
+ end
9
+
10
+ get '/backups' do
11
+ send_file @backup_manager.destination_path
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,90 @@
1
+ module Bosh::Director
2
+ module Api
3
+ module Controllers
4
+ class BaseController < Sinatra::Base
5
+ PUBLIC_URLS = %w(/info)
6
+
7
+ include ApiHelper
8
+ include Http
9
+ include DnsHelper
10
+
11
+ def initialize(*_)
12
+ super
13
+ @deployment_manager = DeploymentManager.new
14
+ @backup_manager = BackupManager.new
15
+ @instance_manager = InstanceManager.new
16
+ @resurrector_manager = ResurrectorManager.new
17
+ @problem_manager = ProblemManager.new
18
+ @property_manager = PropertyManager.new
19
+ @resource_manager = ResourceManager.new
20
+ @release_manager = ReleaseManager.new
21
+ @snapshot_manager = SnapshotManager.new
22
+ @stemcell_manager = StemcellManager.new
23
+ @task_manager = TaskManager.new
24
+ @user_manager = UserManager.new
25
+ @vm_state_manager = VmStateManager.new
26
+ @logger = Config.logger
27
+ end
28
+
29
+ mime_type :tgz, 'application/x-compressed'
30
+
31
+ def self.consumes(*types)
32
+ types = Set.new(types)
33
+ types.map! { |t| mime_type(t) }
34
+
35
+ condition do
36
+ types.include?(request.content_type)
37
+ end
38
+ end
39
+
40
+ def authenticate(user, password)
41
+ if @user_manager.authenticate(user, password)
42
+ @user = user
43
+ true
44
+ else
45
+ false
46
+ end
47
+ end
48
+
49
+ helpers ControllerHelpers
50
+
51
+ before do
52
+ auth_provided = %w(HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION).detect do |key|
53
+ request.env.has_key?(key)
54
+ end
55
+
56
+ protected! if auth_provided || !PUBLIC_URLS.include?(request.path_info)
57
+ end
58
+
59
+ after { headers('Date' => Time.now.rfc822) } # As thin doesn't inject date
60
+
61
+ configure do
62
+ set(:show_exceptions, false)
63
+ set(:raise_errors, false)
64
+ set(:dump_errors, false)
65
+ end
66
+
67
+ error do
68
+ exception = request.env['sinatra.error']
69
+ if exception.kind_of?(DirectorError)
70
+ @logger.debug('Request failed, ' +
71
+ "response code: #{exception.response_code}, " +
72
+ "error code: #{exception.error_code}, " +
73
+ "error message: #{exception.message}")
74
+ status(exception.response_code)
75
+ error_payload = {
76
+ 'code' => exception.error_code,
77
+ 'description' => exception.message
78
+ }
79
+ json_encode(error_payload)
80
+ else
81
+ msg = ["#{exception.class} - #{exception.message}:"]
82
+ msg.concat(exception.backtrace)
83
+ @logger.error(msg.join("\n"))
84
+ status(500)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,264 @@
1
+ require 'bosh/director/api/controllers/base_controller'
2
+
3
+ module Bosh::Director
4
+ module Api::Controllers
5
+ class DeploymentsController < BaseController
6
+ get '/deployments/:deployment/jobs/:job/:index' do
7
+ instance = @instance_manager.find_by_name(params[:deployment], params[:job], params[:index])
8
+
9
+ response = {
10
+ deployment: params[:deployment],
11
+ job: instance.job,
12
+ index: instance.index,
13
+ state: instance.state,
14
+ disks: instance.persistent_disks.map {|d| d.disk_cid}
15
+ }
16
+
17
+ json_encode(response)
18
+ end
19
+
20
+ # PUT /deployments/foo/jobs/dea?new_name=dea_new
21
+ put '/deployments/:deployment/jobs/:job', :consumes => :yaml do
22
+ if params['state']
23
+ options = {
24
+ 'job_states' => {
25
+ params[:job] => {
26
+ 'state' => params['state']
27
+ }
28
+ }
29
+ }
30
+ else
31
+ unless params['new_name']
32
+ raise DirectorError, "Missing operation on job `#{params[:job]}'"
33
+ end
34
+ options = {
35
+ 'job_rename' => {
36
+ 'old_name' => params[:job],
37
+ 'new_name' => params['new_name']
38
+ }
39
+ }
40
+ options['job_rename']['force'] = true if params['force'] == 'true'
41
+ end
42
+
43
+ # we get the deployment here even though it isn't used here, to make sure
44
+ # the call returns a 404 if the deployment doesn't exist
45
+ @deployment_manager.find_by_name(params[:deployment])
46
+ task = @deployment_manager.create_deployment(@user, request.body, options)
47
+ redirect "/tasks/#{task.id}"
48
+ end
49
+
50
+ # PUT /deployments/foo/jobs/dea/2?state={started,stopped,detached,restart,recreate}
51
+ put '/deployments/:deployment/jobs/:job/:index', :consumes => :yaml do
52
+ begin
53
+ index = Integer(params[:index])
54
+ rescue ArgumentError
55
+ raise InstanceInvalidIndex, "Invalid instance index `#{params[:index]}'"
56
+ end
57
+
58
+ options = {
59
+ 'job_states' => {
60
+ params[:job] => {
61
+ 'instance_states' => {
62
+ index => params['state']
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ deployment = @deployment_manager.find_by_name(params[:deployment])
69
+ manifest = request.content_length.nil? ? StringIO.new(deployment.manifest) : request.body
70
+ task = @deployment_manager.create_deployment(@user, manifest, options)
71
+ redirect "/tasks/#{task.id}"
72
+ end
73
+
74
+ # GET /deployments/foo/jobs/dea/2/logs
75
+ get '/deployments/:deployment/jobs/:job/:index/logs' do
76
+ deployment = params[:deployment]
77
+ job = params[:job]
78
+ index = params[:index]
79
+
80
+ options = {
81
+ 'type' => params[:type].to_s.strip,
82
+ 'filters' => params[:filters].to_s.strip.split(/[\s\,]+/)
83
+ }
84
+
85
+ task = @instance_manager.fetch_logs(@user, deployment, job, index, options)
86
+ redirect "/tasks/#{task.id}"
87
+ end
88
+
89
+ get '/deployments/:deployment/snapshots' do
90
+ deployment = @deployment_manager.find_by_name(params[:deployment])
91
+ json_encode(@snapshot_manager.snapshots(deployment))
92
+ end
93
+
94
+ get '/deployments/:deployment/jobs/:job/:index/snapshots' do
95
+ deployment = @deployment_manager.find_by_name(params[:deployment])
96
+ json_encode(@snapshot_manager.snapshots(deployment, params[:job], params[:index]))
97
+ end
98
+
99
+ post '/deployments/:deployment/snapshots' do
100
+ deployment = @deployment_manager.find_by_name(params[:deployment])
101
+ # until we can tell the agent to flush and wait, all snapshots are considered dirty
102
+ options = {clean: false}
103
+
104
+ task = @snapshot_manager.create_deployment_snapshot_task(@user, deployment, options)
105
+ redirect "/tasks/#{task.id}"
106
+ end
107
+
108
+ put '/deployments/:deployment/jobs/:job/:index/resurrection', consumes: :json do
109
+ payload = json_decode(request.body)
110
+
111
+ @resurrector_manager.set_pause_for_instance(params[:deployment], params[:job], params[:index], payload['resurrection_paused'])
112
+ end
113
+
114
+ post '/deployments/:deployment/jobs/:job/:index/snapshots' do
115
+ instance = @instance_manager.find_by_name(params[:deployment], params[:job], params[:index])
116
+ # until we can tell the agent to flush and wait, all snapshots are considered dirty
117
+ options = {clean: false}
118
+
119
+ task = @snapshot_manager.create_snapshot_task(@user, instance, options)
120
+ redirect "/tasks/#{task.id}"
121
+ end
122
+
123
+ delete '/deployments/:deployment/snapshots' do
124
+ deployment = @deployment_manager.find_by_name(params[:deployment])
125
+
126
+ task = @snapshot_manager.delete_deployment_snapshots_task(@user, deployment)
127
+ redirect "/tasks/#{task.id}"
128
+ end
129
+
130
+ delete '/deployments/:deployment/snapshots/:cid' do
131
+ deployment = @deployment_manager.find_by_name(params[:deployment])
132
+ snapshot = @snapshot_manager.find_by_cid(deployment, params[:cid])
133
+
134
+ task = @snapshot_manager.delete_snapshots_task(@user, [params[:cid]])
135
+ redirect "/tasks/#{task.id}"
136
+ end
137
+
138
+ get '/deployments' do
139
+ deployments = Models::Deployment.order_by(:name.asc).map { |deployment|
140
+ name = deployment.name
141
+
142
+ releases = deployment.release_versions.map { |rv|
143
+ Hash['name', rv.release.name, 'version', rv.version.to_s]
144
+ }
145
+
146
+ stemcells = deployment.stemcells.map { |sc|
147
+ Hash['name', sc.name, 'version', sc.version]
148
+ }
149
+
150
+ Hash['name', name, 'releases', releases, 'stemcells', stemcells]
151
+ }
152
+
153
+ json_encode(deployments)
154
+ end
155
+
156
+ get '/deployments/:name' do
157
+ deployment = @deployment_manager.find_by_name(params[:name])
158
+ @deployment_manager.deployment_to_json(deployment)
159
+ end
160
+
161
+ get '/deployments/:name/vms' do
162
+ deployment = @deployment_manager.find_by_name(params[:name])
163
+
164
+ format = params[:format]
165
+ if format == 'full'
166
+ task = @vm_state_manager.fetch_vm_state(@user, deployment, format)
167
+ redirect "/tasks/#{task.id}"
168
+ else
169
+ @deployment_manager.deployment_vms_to_json(deployment)
170
+ end
171
+ end
172
+
173
+ delete '/deployments/:name' do
174
+ deployment = @deployment_manager.find_by_name(params[:name])
175
+
176
+ options = {}
177
+ options['force'] = true if params['force'] == 'true'
178
+ options['keep_snapshots'] = true if params['keep_snapshots'] == 'true'
179
+ task = @deployment_manager.delete_deployment(@user, deployment, options)
180
+ redirect "/tasks/#{task.id}"
181
+ end
182
+
183
+ # Property management
184
+ get '/deployments/:deployment/properties' do
185
+ properties = @property_manager.get_properties(params[:deployment]).map do |property|
186
+ { 'name' => property.name, 'value' => property.value }
187
+ end
188
+ json_encode(properties)
189
+ end
190
+
191
+ get '/deployments/:deployment/properties/:property' do
192
+ property = @property_manager.get_property(params[:deployment], params[:property])
193
+ json_encode('value' => property.value)
194
+ end
195
+
196
+ post '/deployments/:deployment/properties', :consumes => [:json] do
197
+ payload = json_decode(request.body)
198
+ @property_manager.create_property(params[:deployment], payload['name'], payload['value'])
199
+ status(204)
200
+ end
201
+
202
+ post '/deployments/:deployment/ssh', :consumes => [:json] do
203
+ payload = json_decode(request.body)
204
+ task = @instance_manager.ssh(@user, payload)
205
+ redirect "/tasks/#{task.id}"
206
+ end
207
+
208
+ put '/deployments/:deployment/properties/:property', :consumes => [:json] do
209
+ payload = json_decode(request.body)
210
+ @property_manager.update_property(params[:deployment], params[:property], payload['value'])
211
+ status(204)
212
+ end
213
+
214
+ delete '/deployments/:deployment/properties/:property' do
215
+ @property_manager.delete_property(params[:deployment], params[:property])
216
+ status(204)
217
+ end
218
+
219
+ # Cloud check
220
+
221
+ # Initiate deployment scan
222
+ post '/deployments/:deployment/scans' do
223
+ start_task { @problem_manager.perform_scan(@user, params[:deployment]) }
224
+ end
225
+
226
+ # Get the list of problems for a particular deployment
227
+ get '/deployments/:deployment/problems' do
228
+ problems = @problem_manager.get_problems(params[:deployment]).map do |problem|
229
+ {
230
+ 'id' => problem.id,
231
+ 'type' => problem.type,
232
+ 'data' => problem.data,
233
+ 'description' => problem.description,
234
+ 'resolutions' => problem.resolutions
235
+ }
236
+ end
237
+
238
+ json_encode(problems)
239
+ end
240
+
241
+ # Try to resolve a set of problems
242
+ put '/deployments/:deployment/problems', :consumes => [:json] do
243
+ payload = json_decode(request.body)
244
+ start_task { @problem_manager.apply_resolutions(@user, params[:deployment], payload['resolutions']) }
245
+ end
246
+
247
+ put '/deployments/:deployment/scan_and_fix', :consumes => :json do
248
+ jobs_json = json_decode(request.body)['jobs']
249
+ # payload: [['j1', 'i1'], ['j1', 'i2'], ['j2', 'i1'], ...]
250
+ payload = convert_job_instance_hash(jobs_json)
251
+
252
+ start_task { @problem_manager.scan_and_fix(@user, params[:deployment], payload) }
253
+ end
254
+
255
+ post '/deployments', :consumes => :yaml do
256
+ options = {}
257
+ options['recreate'] = true if params['recreate'] == 'true'
258
+
259
+ task = @deployment_manager.create_deployment(@user, request.body, options)
260
+ redirect "/tasks/#{task.id}"
261
+ end
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,32 @@
1
+ require 'bosh/director/api/controllers/base_controller'
2
+
3
+ module Bosh::Director
4
+ module Api::Controllers
5
+ class InfoController < BaseController
6
+ get '/info' do
7
+ status = {
8
+ 'name' => Config.name,
9
+ 'uuid' => Config.uuid,
10
+ 'version' => "#{VERSION} (#{Config.revision})",
11
+ 'user' => @user,
12
+ 'cpi' => Config.cloud_type,
13
+ 'features' => {
14
+ 'dns' => {
15
+ 'status' => Config.dns_enabled?,
16
+ 'extras' => { 'domain_name' => dns_domain_name }
17
+ },
18
+ 'compiled_package_cache' => {
19
+ 'status' => Config.use_compiled_package_cache?,
20
+ 'extras' => { 'provider' => Config.compiled_package_cache_provider }
21
+ },
22
+ 'snapshots' => {
23
+ 'status' => Config.enable_snapshots
24
+ }
25
+ }
26
+ }
27
+ content_type(:json)
28
+ json_encode(status)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ require 'bosh/director/api/controllers/base_controller'
2
+
3
+ module Bosh::Director
4
+ module Api::Controllers
5
+ class PackagesController < BaseController
6
+ post '/packages/matches', :consumes => :yaml do
7
+ manifest = Psych.load(request.body)
8
+ unless manifest.is_a?(Hash) && manifest['packages'].is_a?(Array)
9
+ raise BadManifest, "Manifest doesn't have a usable packages section"
10
+ end
11
+
12
+ fp_list = []
13
+ sha1_list = []
14
+
15
+ manifest['packages'].each do |package|
16
+ fp_list << package['fingerprint'] if package['fingerprint']
17
+ sha1_list << package['sha1'] if package['sha1']
18
+ end
19
+
20
+ filter = {:fingerprint => fp_list, :sha1 => sha1_list}.sql_or
21
+
22
+ result = Models::Package.where(filter).all.map { |package|
23
+ [package.sha1, package.fingerprint]
24
+ }.flatten.compact.uniq
25
+
26
+ json_encode(result)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,86 @@
1
+ require 'bosh/director/api/controllers/base_controller'
2
+
3
+ module Bosh::Director
4
+ module Api::Controllers
5
+ class ReleasesController < BaseController
6
+ post '/releases', :consumes => :tgz do
7
+ options = {}
8
+ options['remote'] = false
9
+ options['rebase'] = true if params['rebase'] == 'true'
10
+
11
+ task = @release_manager.create_release(@user, request.body, options)
12
+ redirect "/tasks/#{task.id}"
13
+ end
14
+
15
+ post '/releases', :consumes => :json do
16
+ options = {}
17
+ options['remote'] = true
18
+ options['rebase'] = true if params['rebase'] == 'true'
19
+ payload = json_decode(request.body)
20
+
21
+ task = @release_manager.create_release(@user, payload['location'], options)
22
+ redirect "/tasks/#{task.id}"
23
+ end
24
+
25
+ get '/releases' do
26
+ releases = Models::Release.order_by(:name.asc).map do |release|
27
+ release_versions = release.versions_dataset.order_by(:version.asc).map do |rv|
28
+ Hash['version', rv.version.to_s,
29
+ 'commit_hash', rv.commit_hash,
30
+ 'uncommitted_changes', rv.uncommitted_changes,
31
+ 'currently_deployed', !rv.deployments.empty?,
32
+ 'job_names', rv.templates.map(&:name)]
33
+ end
34
+
35
+ Hash['name', release.name,
36
+ 'release_versions', release_versions]
37
+ end
38
+
39
+ json_encode(releases)
40
+ end
41
+
42
+ get '/releases/:name' do
43
+ name = params[:name].to_s.strip
44
+ release = @release_manager.find_by_name(name)
45
+
46
+ result = { }
47
+
48
+ result['packages'] = release.packages.map do |package|
49
+ {
50
+ 'name' => package.name,
51
+ 'sha1' => package.sha1,
52
+ 'version' => package.version.to_s,
53
+ 'dependencies' => package.dependency_set.to_a
54
+ }
55
+ end
56
+
57
+ result['jobs'] = release.templates.map do |template|
58
+ {
59
+ 'name' => template.name,
60
+ 'sha1' => template.sha1,
61
+ 'version' => template.version.to_s,
62
+ 'packages' => template.package_names
63
+ }
64
+ end
65
+
66
+ result['versions'] = release.versions.map do |rv|
67
+ rv.version.to_s
68
+ end
69
+
70
+ content_type(:json)
71
+ json_encode(result)
72
+ end
73
+
74
+ delete '/releases/:name' do
75
+ release = @release_manager.find_by_name(params[:name])
76
+
77
+ options = {}
78
+ options['force'] = true if params['force'] == 'true'
79
+ options['version'] = params['version']
80
+
81
+ task = @release_manager.delete_release(@user, release, options)
82
+ redirect "/tasks/#{task.id}"
83
+ end
84
+ end
85
+ end
86
+ end