Add 'tools/arvbox/' from commit 'd3d368758db1f4a9fa5b89f77b5ee61d68ef5b72'
[arvados.git] / services / api / app / models / container_request.rb
1 require 'whitelist_update'
2
3 class ContainerRequest < ArvadosModel
4   include HasUuid
5   include KindAndEtag
6   include CommonApiTemplate
7   include WhitelistUpdate
8
9   serialize :properties, Hash
10   serialize :environment, Hash
11   serialize :mounts, Hash
12   serialize :runtime_constraints, Hash
13   serialize :command, Array
14
15   before_validation :fill_field_defaults, :if => :new_record?
16   before_validation :set_container
17   validates :command, :container_image, :output_path, :cwd, :presence => true
18   validate :validate_state_change
19   validate :validate_change
20   after_save :update_priority
21
22   api_accessible :user, extend: :common do |t|
23     t.add :command
24     t.add :container_count_max
25     t.add :container_image
26     t.add :container_uuid
27     t.add :cwd
28     t.add :description
29     t.add :environment
30     t.add :expires_at
31     t.add :filters
32     t.add :mounts
33     t.add :name
34     t.add :output_path
35     t.add :priority
36     t.add :properties
37     t.add :requesting_container_uuid
38     t.add :runtime_constraints
39     t.add :state
40   end
41
42   # Supported states for a container request
43   States =
44     [
45      (Uncommitted = 'Uncommitted'),
46      (Committed = 'Committed'),
47      (Final = 'Final'),
48     ]
49
50   State_transitions = {
51     nil => [Uncommitted, Committed],
52     Uncommitted => [Committed],
53     Committed => [Final]
54   }
55
56   def state_transitions
57     State_transitions
58   end
59
60   def skip_uuid_read_permission_check
61     # XXX temporary until permissions are sorted out.
62     %w(modified_by_client_uuid container_uuid requesting_container_uuid)
63   end
64
65   def container_completed!
66     # may implement retry logic here in the future.
67     self.state = ContainerRequest::Final
68     self.save!
69   end
70
71   protected
72
73   def fill_field_defaults
74     self.state ||= Uncommitted
75     self.environment ||= {}
76     self.runtime_constraints ||= {}
77     self.mounts ||= {}
78     self.cwd ||= "."
79   end
80
81   # Turn a container request into a container.
82   def resolve
83     # In the future this will do things like resolve symbolic git and keep
84     # references to content addresses.
85     Container.create!({ :command => self.command,
86                         :container_image => self.container_image,
87                         :cwd => self.cwd,
88                         :environment => self.environment,
89                         :mounts => self.mounts,
90                         :output_path => self.output_path,
91                         :runtime_constraints => self.runtime_constraints })
92   end
93
94   def set_container
95     if self.container_uuid_changed?
96       if not current_user.andand.is_admin and not self.container_uuid.nil?
97         errors.add :container_uuid, "can only be updated to nil."
98       end
99     else
100       if self.state_changed?
101         if self.state == Committed and (self.state_was == Uncommitted or self.state_was.nil?)
102           act_as_system_user do
103             self.container_uuid = self.resolve.andand.uuid
104           end
105         end
106       end
107     end
108   end
109
110   def validate_change
111     permitted = [:owner_uuid]
112
113     case self.state
114     when Uncommitted
115       # Permit updating most fields
116       permitted.push :command, :container_count_max,
117                      :container_image, :cwd, :description, :environment,
118                      :filters, :mounts, :name, :output_path, :priority,
119                      :properties, :requesting_container_uuid, :runtime_constraints,
120                      :state, :container_uuid
121
122     when Committed
123       if container_uuid.nil?
124         errors.add :container_uuid, "has not been resolved to a container."
125       end
126
127       if priority.nil?
128         errors.add :priority, "cannot be nil"
129       end
130
131       # Can update priority, container count.
132       permitted.push :priority, :container_count_max, :container_uuid
133
134       if self.state_changed?
135         # Allow create-and-commit in a single operation.
136         permitted.push :command, :container_image, :cwd, :description, :environment,
137                        :filters, :mounts, :name, :output_path, :properties,
138                        :requesting_container_uuid, :runtime_constraints,
139                        :state, :container_uuid
140       end
141
142     when Final
143       if not current_user.andand.is_admin
144         errors.add :state, "of container request can only be set to Final by system."
145       end
146
147       if self.state_changed?
148           permitted.push :state
149       else
150         errors.add :state, "does not allow updates"
151       end
152
153     else
154       errors.add :state, "invalid value"
155     end
156
157     check_update_whitelist permitted
158   end
159
160   def update_priority
161     if [Committed, Final].include? self.state and (self.state_changed? or
162                                                    self.priority_changed? or
163                                                    self.container_uuid_changed?)
164       [self.container_uuid_was, self.container_uuid].each do |cuuid|
165         unless cuuid.nil?
166           c = Container.find_by_uuid cuuid
167           act_as_system_user do
168             c.update_priority!
169           end
170         end
171       end
172     end
173   end
174
175 end