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.
@@ -1,634 +1,27 @@
1
+ require 'bosh/director/api/controllers/backups_controller'
2
+ require 'bosh/director/api/controllers/deployments_controller'
3
+ require 'bosh/director/api/controllers/packages_controller'
4
+ require 'bosh/director/api/controllers/info_controller'
5
+ require 'bosh/director/api/controllers/releases_controller'
6
+ require 'bosh/director/api/controllers/resources_controller'
7
+ require 'bosh/director/api/controllers/resurrection_controller'
8
+ require 'bosh/director/api/controllers/stemcells_controller'
9
+ require 'bosh/director/api/controllers/tasks_controller'
10
+ require 'bosh/director/api/controllers/users_controller'
11
+
1
12
  module Bosh::Director
2
13
  module Api
3
14
  class Controller < Sinatra::Base
4
- PUBLIC_URLS = %w(/info)
5
-
6
- include ApiHelper
7
- include Http
8
- include DnsHelper
9
-
10
- def initialize
11
- super
12
- @deployment_manager = DeploymentManager.new
13
- @backup_manager = BackupManager.new
14
- @instance_manager = InstanceManager.new
15
- @resurrector_manager = ResurrectorManager.new
16
- @problem_manager = ProblemManager.new
17
- @property_manager = PropertyManager.new
18
- @resource_manager = ResourceManager.new
19
- @release_manager = ReleaseManager.new
20
- @snapshot_manager = SnapshotManager.new
21
- @stemcell_manager = StemcellManager.new
22
- @task_manager = TaskManager.new
23
- @user_manager = UserManager.new
24
- @vm_state_manager = VmStateManager.new
25
- @logger = Config.logger
26
- end
27
-
28
- mime_type :tgz, 'application/x-compressed'
29
-
30
- def self.consumes(*types)
31
- types = Set.new(types)
32
- types.map! { |t| mime_type(t) }
33
-
34
- condition do
35
- types.include?(request.content_type)
36
- end
37
- end
38
-
39
- def authenticate(user, password)
40
- if @user_manager.authenticate(user, password)
41
- @user = user
42
- true
43
- else
44
- false
45
- end
46
- end
47
-
48
- helpers ControllerHelpers
49
-
50
- before do
51
- auth_provided = %w(HTTP_AUTHORIZATION X-HTTP_AUTHORIZATION X_HTTP_AUTHORIZATION).detect do |key|
52
- request.env.has_key?(key)
53
- end
54
-
55
- protected! if auth_provided || !PUBLIC_URLS.include?(request.path_info)
56
- end
57
-
58
- after { headers('Date' => Time.now.rfc822) } # As thin doesn't inject date
59
-
60
- configure do
61
- set(:show_exceptions, false)
62
- set(:raise_errors, false)
63
- set(:dump_errors, false)
64
- end
65
-
66
- error do
67
- exception = request.env['sinatra.error']
68
- if exception.kind_of?(DirectorError)
69
- @logger.debug('Request failed, ' +
70
- "response code: #{exception.response_code}, " +
71
- "error code: #{exception.error_code}, " +
72
- "error message: #{exception.message}")
73
- status(exception.response_code)
74
- error_payload = {
75
- 'code' => exception.error_code,
76
- 'description' => exception.message
77
- }
78
- json_encode(error_payload)
79
- else
80
- msg = ["#{exception.class} - #{exception.message}:"]
81
- msg.concat(exception.backtrace)
82
- @logger.error(msg.join("\n"))
83
- status(500)
84
- end
85
- end
86
-
87
- post '/users', :consumes => [:json] do
88
- user = @user_manager.get_user_from_request(request)
89
- @user_manager.create_user(user)
90
- status(204)
91
- nil
92
- end
93
-
94
- put '/users/:username', :consumes => [:json] do
95
- user = @user_manager.get_user_from_request(request)
96
- if user.username != params[:username]
97
- raise UserImmutableUsername, 'The username is immutable'
98
- end
99
- @user_manager.update_user(user)
100
- status(204)
101
- nil
102
- end
103
-
104
- delete '/users/:username' do
105
- @user_manager.delete_user(params[:username])
106
- status(204)
107
- nil
108
- end
109
-
110
- post '/packages/matches', :consumes => :yaml do
111
- manifest = Psych.load(request.body)
112
- unless manifest.is_a?(Hash) && manifest['packages'].is_a?(Array)
113
- raise BadManifest, "Manifest doesn't have a usable packages section"
114
- end
115
-
116
- fp_list = []
117
- sha1_list = []
118
-
119
- manifest['packages'].each do |package|
120
- fp_list << package['fingerprint'] if package['fingerprint']
121
- sha1_list << package['sha1'] if package['sha1']
122
- end
123
-
124
- filter = {:fingerprint => fp_list, :sha1 => sha1_list}.sql_or
125
-
126
- result = Models::Package.where(filter).all.map { |package|
127
- [package.sha1, package.fingerprint]
128
- }.flatten.compact.uniq
129
-
130
- json_encode(result)
131
- end
132
-
133
- post '/releases', :consumes => :tgz do
134
- options = {}
135
- options['remote'] = false
136
- options['rebase'] = true if params['rebase'] == 'true'
137
-
138
- task = @release_manager.create_release(@user, request.body, options)
139
- redirect "/tasks/#{task.id}"
140
- end
141
-
142
- post '/releases', :consumes => :json do
143
- options = {}
144
- options['remote'] = true
145
- options['rebase'] = true if params['rebase'] == 'true'
146
- payload = json_decode(request.body)
147
-
148
- task = @release_manager.create_release(@user, payload['location'], options)
149
- redirect "/tasks/#{task.id}"
150
- end
151
-
152
- get '/releases' do
153
- releases = Models::Release.order_by(:name.asc).map do |release|
154
- release_versions = release.versions_dataset.order_by(:version.asc).map do |rv|
155
- Hash['version', rv.version.to_s,
156
- 'commit_hash', rv.commit_hash,
157
- 'uncommitted_changes', rv.uncommitted_changes,
158
- 'currently_deployed', !rv.deployments.empty?,
159
- 'job_names', rv.templates.map(&:name)]
160
- end
161
-
162
- Hash['name', release.name,
163
- 'release_versions', release_versions]
164
- end
165
-
166
- json_encode(releases)
167
- end
168
-
169
- get '/releases/:name' do
170
- name = params[:name].to_s.strip
171
- release = @release_manager.find_by_name(name)
172
-
173
- result = { }
174
-
175
- result['packages'] = release.packages.map do |package|
176
- {
177
- 'name' => package.name,
178
- 'sha1' => package.sha1,
179
- 'version' => package.version.to_s,
180
- 'dependencies' => package.dependency_set.to_a
181
- }
182
- end
183
-
184
- result['jobs'] = release.templates.map do |template|
185
- {
186
- 'name' => template.name,
187
- 'sha1' => template.sha1,
188
- 'version' => template.version.to_s,
189
- 'packages' => template.package_names
190
- }
191
- end
192
-
193
- result['versions'] = release.versions.map do |rv|
194
- rv.version.to_s
195
- end
196
-
197
- content_type(:json)
198
- json_encode(result)
199
- end
200
-
201
- delete '/releases/:name' do
202
- release = @release_manager.find_by_name(params[:name])
203
-
204
- options = {}
205
- options['force'] = true if params['force'] == 'true'
206
- options['version'] = params['version']
207
-
208
- task = @release_manager.delete_release(@user, release, options)
209
- redirect "/tasks/#{task.id}"
210
- end
211
-
212
- post '/stemcells', :consumes => :tgz do
213
- task = @stemcell_manager.create_stemcell(@user, request.body, :remote => false)
214
- redirect "/tasks/#{task.id}"
215
- end
216
-
217
- post '/stemcells', :consumes => :json do
218
- payload = json_decode(request.body)
219
- task = @stemcell_manager.create_stemcell(@user, payload['location'], :remote => true)
220
- redirect "/tasks/#{task.id}"
221
- end
222
-
223
- get '/stemcells' do
224
- stemcells = Models::Stemcell.order_by(:name.asc).map do |stemcell|
225
- {
226
- 'name' => stemcell.name,
227
- 'version' => stemcell.version,
228
- 'cid' => stemcell.cid
229
- }
230
- end
231
- json_encode(stemcells)
232
- end
233
-
234
- delete '/stemcells/:name/:version' do
235
- name, version = params[:name], params[:version]
236
- options = {}
237
- options['force'] = true if params['force'] == 'true'
238
- stemcell = @stemcell_manager.find_by_name_and_version(name, version)
239
- task = @stemcell_manager.delete_stemcell(@user, stemcell, options)
240
- redirect "/tasks/#{task.id}"
241
- end
242
-
243
- post '/deployments', :consumes => :yaml do
244
- options = {}
245
- options['recreate'] = true if params['recreate'] == 'true'
246
-
247
- task = @deployment_manager.create_deployment(@user, request.body, options)
248
- redirect "/tasks/#{task.id}"
249
- end
250
-
251
- get '/deployments/:deployment/jobs/:job/:index' do
252
- instance = @instance_manager.find_by_name(params[:deployment], params[:job], params[:index])
253
-
254
- response = {
255
- deployment: params[:deployment],
256
- job: instance.job,
257
- index: instance.index,
258
- state: instance.state,
259
- disks: instance.persistent_disks.map {|d| d.disk_cid}
260
- }
261
-
262
- json_encode(response)
263
- end
264
-
265
- # PUT /deployments/foo/jobs/dea?new_name=dea_new
266
- put '/deployments/:deployment/jobs/:job', :consumes => :yaml do
267
- if params['state']
268
- options = {
269
- 'job_states' => {
270
- params[:job] => {
271
- 'state' => params['state']
272
- }
273
- }
274
- }
275
- else
276
- unless params['new_name']
277
- raise DirectorError, "Missing operation on job `#{params[:job]}'"
278
- end
279
- options = {
280
- 'job_rename' => {
281
- 'old_name' => params[:job],
282
- 'new_name' => params['new_name']
283
- }
284
- }
285
- options['job_rename']['force'] = true if params['force'] == 'true'
286
- end
287
-
288
- # we get the deployment here even though it isn't used here, to make sure
289
- # the call returns a 404 if the deployment doesn't exist
290
- @deployment_manager.find_by_name(params[:deployment])
291
- task = @deployment_manager.create_deployment(@user, request.body, options)
292
- redirect "/tasks/#{task.id}"
293
- end
294
-
295
- # PUT /deployments/foo/jobs/dea/2?state={started,stopped,detached,restart,recreate}
296
- put '/deployments/:deployment/jobs/:job/:index', :consumes => :yaml do
297
- begin
298
- index = Integer(params[:index])
299
- rescue ArgumentError
300
- raise InstanceInvalidIndex, "Invalid instance index `#{params[:index]}'"
301
- end
302
-
303
- options = {
304
- 'job_states' => {
305
- params[:job] => {
306
- 'instance_states' => {
307
- index => params['state']
308
- }
309
- }
310
- }
311
- }
312
-
313
- deployment = @deployment_manager.find_by_name(params[:deployment])
314
- manifest = request.content_length.nil? ? StringIO.new(deployment.manifest) : request.body
315
- task = @deployment_manager.create_deployment(@user, manifest, options)
316
- redirect "/tasks/#{task.id}"
317
- end
318
-
319
- # GET /deployments/foo/jobs/dea/2/logs
320
- get '/deployments/:deployment/jobs/:job/:index/logs' do
321
- deployment = params[:deployment]
322
- job = params[:job]
323
- index = params[:index]
324
-
325
- options = {
326
- 'type' => params[:type].to_s.strip,
327
- 'filters' => params[:filters].to_s.strip.split(/[\s\,]+/)
328
- }
329
-
330
- task = @instance_manager.fetch_logs(@user, deployment, job, index, options)
331
- redirect "/tasks/#{task.id}"
332
- end
333
-
334
- get '/deployments/:deployment/snapshots' do
335
- deployment = @deployment_manager.find_by_name(params[:deployment])
336
- json_encode(@snapshot_manager.snapshots(deployment))
337
- end
338
-
339
- get '/deployments/:deployment/jobs/:job/:index/snapshots' do
340
- deployment = @deployment_manager.find_by_name(params[:deployment])
341
- json_encode(@snapshot_manager.snapshots(deployment, params[:job], params[:index]))
342
- end
343
-
344
- post '/deployments/:deployment/snapshots' do
345
- deployment = @deployment_manager.find_by_name(params[:deployment])
346
- # until we can tell the agent to flush and wait, all snapshots are considered dirty
347
- options = {clean: false}
348
-
349
- task = @snapshot_manager.create_deployment_snapshot_task(@user, deployment, options)
350
- redirect "/tasks/#{task.id}"
351
- end
352
-
353
- put '/deployments/:deployment/jobs/:job/:index/resurrection', consumes: :json do
354
- payload = json_decode(request.body)
355
-
356
- @resurrector_manager.set_pause_for_instance(params[:deployment], params[:job], params[:index], payload['resurrection_paused'])
357
- end
358
-
359
- post '/deployments/:deployment/jobs/:job/:index/snapshots' do
360
- instance = @instance_manager.find_by_name(params[:deployment], params[:job], params[:index])
361
- # until we can tell the agent to flush and wait, all snapshots are considered dirty
362
- options = {clean: false}
363
-
364
- task = @snapshot_manager.create_snapshot_task(@user, instance, options)
365
- redirect "/tasks/#{task.id}"
366
- end
367
-
368
- delete '/deployments/:deployment/snapshots' do
369
- deployment = @deployment_manager.find_by_name(params[:deployment])
370
-
371
- task = @snapshot_manager.delete_deployment_snapshots_task(@user, deployment)
372
- redirect "/tasks/#{task.id}"
373
- end
374
-
375
- delete '/deployments/:deployment/snapshots/:cid' do
376
- deployment = @deployment_manager.find_by_name(params[:deployment])
377
- snapshot = @snapshot_manager.find_by_cid(deployment, params[:cid])
378
-
379
- task = @snapshot_manager.delete_snapshots_task(@user, [params[:cid]])
380
- redirect "/tasks/#{task.id}"
381
- end
382
-
383
- get '/deployments' do
384
- deployments = Models::Deployment.order_by(:name.asc).map { |deployment|
385
- name = deployment.name
386
-
387
- releases = deployment.release_versions.map { |rv|
388
- Hash['name', rv.release.name, 'version', rv.version.to_s]
389
- }
390
-
391
- stemcells = deployment.stemcells.map { |sc|
392
- Hash['name', sc.name, 'version', sc.version]
393
- }
394
-
395
- Hash['name', name, 'releases', releases, 'stemcells', stemcells]
396
- }
397
-
398
- json_encode(deployments)
399
- end
400
-
401
- get '/deployments/:name' do
402
- deployment = @deployment_manager.find_by_name(params[:name])
403
- @deployment_manager.deployment_to_json(deployment)
404
- end
405
-
406
- get '/deployments/:name/vms' do
407
- deployment = @deployment_manager.find_by_name(params[:name])
408
-
409
- format = params[:format]
410
- if format == 'full'
411
- task = @vm_state_manager.fetch_vm_state(@user, deployment, format)
412
- redirect "/tasks/#{task.id}"
413
- else
414
- @deployment_manager.deployment_vms_to_json(deployment)
415
- end
416
- end
417
-
418
- delete '/deployments/:name' do
419
- deployment = @deployment_manager.find_by_name(params[:name])
420
-
421
- options = {}
422
- options['force'] = true if params['force'] == 'true'
423
- options['keep_snapshots'] = true if params['keep_snapshots'] == 'true'
424
- task = @deployment_manager.delete_deployment(@user, deployment, options)
425
- redirect "/tasks/#{task.id}"
426
- end
427
-
428
- # Property management
429
- get '/deployments/:deployment/properties' do
430
- properties = @property_manager.get_properties(params[:deployment]).map do |property|
431
- { 'name' => property.name, 'value' => property.value }
432
- end
433
- json_encode(properties)
434
- end
435
-
436
- get '/deployments/:deployment/properties/:property' do
437
- property = @property_manager.get_property(params[:deployment], params[:property])
438
- json_encode('value' => property.value)
439
- end
440
-
441
- post '/deployments/:deployment/properties', :consumes => [:json] do
442
- payload = json_decode(request.body)
443
- @property_manager.create_property(params[:deployment], payload['name'], payload['value'])
444
- status(204)
445
- end
446
-
447
- post '/deployments/:deployment/ssh', :consumes => [:json] do
448
- payload = json_decode(request.body)
449
- task = @instance_manager.ssh(@user, payload)
450
- redirect "/tasks/#{task.id}"
451
- end
452
-
453
- put '/deployments/:deployment/properties/:property', :consumes => [:json] do
454
- payload = json_decode(request.body)
455
- @property_manager.update_property(params[:deployment], params[:property], payload['value'])
456
- status(204)
457
- end
458
-
459
- delete '/deployments/:deployment/properties/:property' do
460
- @property_manager.delete_property(params[:deployment], params[:property])
461
- status(204)
462
- end
463
-
464
- # Cloud check
465
-
466
- # Initiate deployment scan
467
- post '/deployments/:deployment/scans' do
468
- start_task { @problem_manager.perform_scan(@user, params[:deployment]) }
469
- end
470
-
471
- # Get the list of problems for a particular deployment
472
- get '/deployments/:deployment/problems' do
473
- problems = @problem_manager.get_problems(params[:deployment]).map do |problem|
474
- {
475
- 'id' => problem.id,
476
- 'type' => problem.type,
477
- 'data' => problem.data,
478
- 'description' => problem.description,
479
- 'resolutions' => problem.resolutions
480
- }
481
- end
482
-
483
- json_encode(problems)
484
- end
485
-
486
- # Try to resolve a set of problems
487
- put '/deployments/:deployment/problems', :consumes => [:json] do
488
- payload = json_decode(request.body)
489
- start_task { @problem_manager.apply_resolutions(@user, params[:deployment], payload['resolutions']) }
490
- end
491
-
492
- put '/deployments/:deployment/scan_and_fix', :consumes => :json do
493
- jobs_json = json_decode(request.body)['jobs']
494
- # payload: [['j1', 'i1'], ['j1', 'i2'], ['j2', 'i1'], ...]
495
- payload = convert_job_instance_hash(jobs_json)
496
-
497
- start_task { @problem_manager.scan_and_fix(@user, params[:deployment], payload) }
498
- end
499
-
500
- get '/tasks' do
501
- dataset = Models::Task.dataset
502
- limit = params['limit']
503
- if limit
504
- limit = limit.to_i
505
- limit = 1 if limit < 1
506
- dataset = dataset.limit(limit)
507
- end
508
-
509
- states = params['state'].to_s.split(',')
510
-
511
- if states.size > 0
512
- dataset = dataset.filter(:state => states)
513
- end
514
-
515
- verbose = params['verbose'] || '1'
516
- if verbose == '1'
517
- dataset = dataset.filter(type: %w[
518
- update_deployment
519
- delete_deployment
520
- update_release
521
- delete_release
522
- update_stemcell
523
- delete_stemcell
524
- create_snapshot
525
- delete_snapshot
526
- snapshot_deployment
527
- ])
528
- end
529
-
530
- tasks = dataset.order_by(:timestamp.desc).map do |task|
531
- if task_timeout?(task)
532
- task.state = :timeout
533
- task.save
534
- end
535
- @task_manager.task_to_hash(task)
536
- end
537
-
538
- content_type(:json)
539
- json_encode(tasks)
540
- end
541
-
542
- get '/tasks/:id' do
543
- task = @task_manager.find_task(params[:id])
544
- if task_timeout?(task)
545
- task.state = :timeout
546
- task.save
547
- end
548
-
549
- content_type(:json)
550
- json_encode(@task_manager.task_to_hash(task))
551
- end
552
-
553
- # Sends back output of given task id and params[:type]
554
- # Example: `get /tasks/5/output?type=event` will send back the file
555
- # at /var/vcap/store/director/tasks/5/event
556
- get '/tasks/:id/output' do
557
- log_type = params[:type] || 'debug'
558
- task = @task_manager.find_task(params[:id])
559
-
560
- if task.output.nil?
561
- halt(204)
562
- end
563
-
564
- log_file = @task_manager.log_file(task, log_type)
565
-
566
- if File.file?(log_file)
567
- send_file(log_file, :type => 'text/plain')
568
- else
569
- status(204)
570
- end
571
- end
572
-
573
- delete '/task/:id' do
574
- task_id = params[:id]
575
- task = @task_manager.find_task(task_id)
576
-
577
- if task.state != 'processing' && task.state != 'queued'
578
- status(400)
579
- body("Cannot cancel task #{task_id}: invalid state (#{task.state})")
580
- else
581
- task.state = :cancelling
582
- task.save
583
- status(204)
584
- body("Cancelling task #{task_id}")
585
- end
586
- end
587
-
588
- get '/resources/:id' do
589
- tmp_file = @resource_manager.get_resource_path(params[:id])
590
- send_disposable_file(tmp_file, :type => 'application/x-gzip')
591
- end
592
-
593
- post '/backups' do
594
- start_task { @backup_manager.create_backup(@user) }
595
- end
596
-
597
- get '/backups' do
598
- send_file @backup_manager.destination_path
599
- end
600
-
601
- get '/info' do
602
- status = {
603
- 'name' => Config.name,
604
- 'uuid' => Config.uuid,
605
- 'version' => "#{VERSION} (#{Config.revision})",
606
- 'user' => @user,
607
- 'cpi' => Config.cloud_type,
608
- 'features' => {
609
- 'dns' => {
610
- 'status' => Config.dns_enabled?,
611
- 'extras' => { 'domain_name' => dns_domain_name }
612
- },
613
- 'compiled_package_cache' => {
614
- 'status' => Config.use_compiled_package_cache?,
615
- 'extras' => { 'provider' => Config.compiled_package_cache_provider }
616
- },
617
- 'snapshots' => {
618
- 'status' => Config.enable_snapshots
619
- }
620
- }
621
- }
622
- content_type(:json)
623
- json_encode(status)
624
- end
625
-
626
- put '/resurrection', consumes: :json do
627
- payload = json_decode(request.body)
628
-
629
- @resurrector_manager.set_pause_for_all(payload['resurrection_paused'])
630
- status(200)
631
- end
15
+ use Controllers::BackupsController
16
+ use Controllers::DeploymentsController
17
+ use Controllers::InfoController
18
+ use Controllers::PackagesController
19
+ use Controllers::ReleasesController
20
+ use Controllers::ResourcesController
21
+ use Controllers::ResurrectionController
22
+ use Controllers::StemcellsController
23
+ use Controllers::TasksController
24
+ use Controllers::UsersController
632
25
  end
633
26
  end
634
27
  end