13164: Lock tables in order for container updates.
[arvados.git] / services / api / app / controllers / arvados / v1 / containers_controller.rb
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: AGPL-3.0
4
5 class Arvados::V1::ContainersController < ApplicationController
6   accept_attribute_as_json :environment, Hash
7   accept_attribute_as_json :mounts, Hash
8   accept_attribute_as_json :runtime_constraints, Hash
9   accept_attribute_as_json :command, Array
10   accept_attribute_as_json :scheduling_parameters, Hash
11
12   skip_before_filter :find_object_by_uuid, only: [:current]
13   skip_before_filter :render_404_if_no_object, only: [:current]
14
15   def auth
16     if @object.locked_by_uuid != Thread.current[:api_client_authorization].uuid
17       raise ArvadosModel::PermissionDeniedError.new("Not locked by your token")
18     end
19     @object = @object.auth
20     show
21   end
22
23   def update
24     # container updates can trigger container request lookups, which
25     # can deadlock if we don't lock the container_requests table
26     # first.
27     @object.transaction do
28       ActiveRecord::Base.connection.execute('LOCK container_requests, containers IN EXCLUSIVE MODE')
29       @object.reload
30       super
31     end
32   end
33
34   def find_objects_for_index
35     super
36     if action_name == 'lock' || action_name == 'unlock'
37       # Avoid loading more fields than we need
38       @objects = @objects.select(:id, :uuid, :state, :priority, :auth_uuid, :locked_by_uuid)
39       @select = %w(uuid state priority auth_uuid locked_by_uuid)
40     end
41   end
42
43   def lock
44     @object.lock
45     show
46   end
47
48   def unlock
49     @object.unlock
50     show
51   end
52
53   def current
54     if Thread.current[:api_client_authorization].nil?
55       send_error("Not logged in", status: 401)
56     else
57       c = Container.where(auth_uuid: Thread.current[:api_client_authorization].uuid).first
58       if c.nil?
59         send_error("Token is not associated with a container.", status: 404)
60       else
61         @object = c
62         show
63       end
64     end
65   end
66
67   def secret_mounts
68     if @object &&
69        @object.auth_uuid &&
70        @object.auth_uuid == Thread.current[:api_client_authorization].uuid
71       send_json({"secret_mounts" => @object.secret_mounts})
72     else
73       send_error("Token is not associated with this container.", status: 403)
74     end
75   end
76 end