6429: Rename request_finalize to handle_completed
[arvados.git] / services / api / app / models / container.rb
1 require 'whitelist_update'
2
3 class Container < ArvadosModel
4   include HasUuid
5   include KindAndEtag
6   include CommonApiTemplate
7   include WhitelistUpdate
8
9   serialize :environment, Hash
10   serialize :mounts, Hash
11   serialize :runtime_constraints, Hash
12   serialize :command, Array
13
14   before_validation :fill_field_defaults, :if => :new_record?
15   before_validation :set_timestamps
16   validates :command, :container_image, :output_path, :cwd, :priority, :presence => true
17   validate :validate_state_change
18   validate :validate_change
19   after_save :handle_completed
20
21   has_many :container_requests, :foreign_key => :container_uuid, :class_name => 'ContainerRequest', :primary_key => :uuid
22
23   api_accessible :user, extend: :common do |t|
24     t.add :command
25     t.add :container_image
26     t.add :cwd
27     t.add :environment
28     t.add :finished_at
29     t.add :log
30     t.add :mounts
31     t.add :output
32     t.add :output_path
33     t.add :priority
34     t.add :progress
35     t.add :runtime_constraints
36     t.add :started_at
37     t.add :state
38   end
39
40   # Supported states for a container
41   States =
42     [
43      (Queued = 'Queued'),
44      (Running = 'Running'),
45      (Complete = 'Complete'),
46      (Cancelled = 'Cancelled')
47     ]
48
49   State_transitions = {
50     nil => [Queued],
51     Queued => [Running, Cancelled],
52     Running => [Complete, Cancelled]
53   }
54
55   def state_transitions
56     State_transitions
57   end
58
59   def update_priority!
60     if [Queued, Running].include? self.state
61       # Update the priority of this container to the maximum priority of any of
62       # its committed container requests and save the record.
63       max = 0
64       ContainerRequest.where(container_uuid: uuid).each do |cr|
65         if cr.state == ContainerRequest::Committed and cr.priority > max
66           max = cr.priority
67         end
68       end
69       self.priority = max
70       self.save!
71     end
72   end
73
74   protected
75
76   def fill_field_defaults
77     self.state ||= Queued
78     self.environment ||= {}
79     self.runtime_constraints ||= {}
80     self.mounts ||= {}
81     self.cwd ||= "."
82     self.priority ||= 1
83   end
84
85   def permission_to_create
86     current_user.andand.is_admin
87   end
88
89   def permission_to_update
90     current_user.andand.is_admin
91   end
92
93   def set_timestamps
94     if self.state_changed? and self.state == Running
95       self.started_at ||= db_current_time
96     end
97
98     if self.state_changed? and [Complete, Cancelled].include? self.state
99       self.finished_at ||= db_current_time
100     end
101   end
102
103   def validate_change
104     permitted = []
105
106     if self.new_record?
107       permitted.push :owner_uuid, :command, :container_image, :cwd, :environment,
108                      :mounts, :output_path, :priority, :runtime_constraints, :state
109     end
110
111     case self.state
112     when Queued
113       # permit priority change only.
114       permitted.push :priority
115
116     when Running
117       if self.state_changed?
118         # At point of state change, can set state and started_at
119         permitted.push :state, :started_at
120       else
121         # While running, can update priority and progress.
122         permitted.push :priority, :progress
123       end
124
125     when Complete
126       if self.state_changed?
127         permitted.push :state, :finished_at, :output, :log
128       else
129         errors.add :state, "cannot update record"
130       end
131
132     when Cancelled
133       if self.state_changed?
134         if self.state_was == Running
135           permitted.push :state, :finished_at, :output, :log
136         elsif self.state_was == Queued
137           permitted.push :state, :finished_at
138         end
139       else
140         errors.add :state, "cannot update record"
141       end
142
143     else
144       errors.add :state, "invalid state"
145     end
146
147     check_update_whitelist permitted
148   end
149
150   def handle_completed
151     # This container is finished so finalize any associated container requests
152     # that are associated with this container.
153     if self.state_changed? and [Complete, Cancelled].include? self.state
154       act_as_system_user do
155         # Try to close container requests associated with this container
156         ContainerRequest.where(container_uuid: uuid,
157                                :state => ContainerRequest::Committed).each do |cr|
158           cr.state = ContainerRequest::Final
159           cr.save
160         end
161
162         # Try to cancel any outstanding container requests made by this container.
163         ContainerRequest.where(requesting_container_uuid: uuid,
164                                :state => ContainerRequest::Committed).each do |cr|
165           cr.priority = 0
166           cr.save
167         end
168       end
169     end
170   end
171
172 end