9898: add unlock method also on the container model.
[arvados.git] / services / api / test / unit / container_test.rb
1 require 'test_helper'
2
3 class ContainerTest < ActiveSupport::TestCase
4   include DbCurrentTime
5
6   DEFAULT_ATTRS = {
7     command: ['echo', 'foo'],
8     container_image: 'img',
9     output_path: '/tmp',
10     priority: 1,
11     runtime_constraints: {"vcpus" => 1, "ram" => 1},
12   }
13
14   def minimal_new attrs={}
15     cr = ContainerRequest.new DEFAULT_ATTRS.merge(attrs)
16     act_as_user users(:active) do
17       cr.save!
18     end
19     c = Container.new DEFAULT_ATTRS.merge(attrs)
20     act_as_system_user do
21       c.save!
22       assert cr.update_attributes(container_uuid: c.uuid,
23                                   state: ContainerRequest::Committed,
24                                   ), show_errors(cr)
25     end
26     return c, cr
27   end
28
29   def check_illegal_updates c, bad_updates
30     bad_updates.each do |u|
31       refute c.update_attributes(u), u.inspect
32       refute c.valid?, u.inspect
33       c.reload
34     end
35   end
36
37   def check_illegal_modify c
38     check_illegal_updates c, [{command: ["echo", "bar"]},
39                               {container_image: "img2"},
40                               {cwd: "/tmp2"},
41                               {environment: {"FOO" => "BAR"}},
42                               {mounts: {"FOO" => "BAR"}},
43                               {output_path: "/tmp3"},
44                               {locked_by_uuid: "zzzzz-gj3su-027z32aux8dg2s1"},
45                               {auth_uuid: "zzzzz-gj3su-017z32aux8dg2s1"},
46                               {runtime_constraints: {"FOO" => "BAR"}}]
47   end
48
49   def check_bogus_states c
50     check_illegal_updates c, [{state: nil},
51                               {state: "Flubber"}]
52   end
53
54   def check_no_change_from_cancelled c
55     check_illegal_modify c
56     check_bogus_states c
57     check_illegal_updates c, [{ priority: 3 },
58                               { state: Container::Queued },
59                               { state: Container::Locked },
60                               { state: Container::Running },
61                               { state: Container::Complete }]
62   end
63
64   test "Container create" do
65     act_as_system_user do
66       c, _ = minimal_new(environment: {},
67                       mounts: {"BAR" => "FOO"},
68                       output_path: "/tmp",
69                       priority: 1,
70                       runtime_constraints: {"vcpus" => 1, "ram" => 1})
71
72       check_illegal_modify c
73       check_bogus_states c
74
75       c.reload
76       c.priority = 2
77       c.save!
78     end
79   end
80
81   test "Container running" do
82     c, _ = minimal_new priority: 1
83
84     set_user_from_auth :dispatch1
85     check_illegal_updates c, [{state: Container::Running},
86                               {state: Container::Complete}]
87
88     c.lock
89     c.update_attributes! state: Container::Running
90
91     check_illegal_modify c
92     check_bogus_states c
93
94     check_illegal_updates c, [{state: Container::Queued}]
95     c.reload
96
97     c.update_attributes! priority: 3
98   end
99
100   test "Lock and unlock" do
101     c, cr = minimal_new priority: 0
102
103     set_user_from_auth :dispatch1
104     assert_equal Container::Queued, c.state
105
106     assert_raise(ActiveRecord::RecordInvalid) {c.lock} # "no priority"
107     c.reload
108     assert cr.update_attributes priority: 1
109
110     refute c.update_attributes(state: Container::Running), "not locked"
111     c.reload
112     refute c.update_attributes(state: Container::Complete), "not locked"
113     c.reload
114
115     assert c.lock, show_errors(c)
116     assert c.locked_by_uuid
117     assert c.auth_uuid
118
119     assert_raise(ArvadosModel::AlreadyLockedError) {c.lock}
120     c.reload
121
122     assert c.unlock, show_errors(c)
123     refute c.locked_by_uuid
124     refute c.auth_uuid
125
126     refute c.update_attributes(state: Container::Running), "not locked"
127     c.reload
128     refute c.locked_by_uuid
129     refute c.auth_uuid
130
131     assert c.lock, show_errors(c)
132     assert c.update_attributes(state: Container::Running), show_errors(c)
133     assert c.locked_by_uuid
134     assert c.auth_uuid
135
136     auth_uuid_was = c.auth_uuid
137
138     assert_raise(ActiveRecord::RecordInvalid) {c.lock} # Running to Locked is not allowed
139     c.reload
140     assert_raise(ActiveRecord::RecordInvalid) {c.unlock} # Running to Queued is not allowed
141     c.reload
142
143     assert c.update_attributes(state: Container::Complete), show_errors(c)
144     refute c.locked_by_uuid
145     refute c.auth_uuid
146
147     auth_exp = ApiClientAuthorization.find_by_uuid(auth_uuid_was).expires_at
148     assert_operator auth_exp, :<, db_current_time
149   end
150
151   test "Container queued cancel" do
152     c, _ = minimal_new
153     set_user_from_auth :dispatch1
154     assert c.update_attributes(state: Container::Cancelled), show_errors(c)
155     check_no_change_from_cancelled c
156   end
157
158   test "Container locked cancel" do
159     c, _ = minimal_new
160     set_user_from_auth :dispatch1
161     assert c.lock, show_errors(c)
162     assert c.update_attributes(state: Container::Cancelled), show_errors(c)
163     check_no_change_from_cancelled c
164   end
165
166   test "Container running cancel" do
167     c, _ = minimal_new
168     set_user_from_auth :dispatch1
169     c.lock
170     c.update_attributes! state: Container::Running
171     c.update_attributes! state: Container::Cancelled
172     check_no_change_from_cancelled c
173   end
174
175   test "Container create forbidden for non-admin" do
176     set_user_from_auth :active_trustedclient
177     c = Container.new DEFAULT_ATTRS
178     c.environment = {}
179     c.mounts = {"BAR" => "FOO"}
180     c.output_path = "/tmp"
181     c.priority = 1
182     c.runtime_constraints = {}
183     assert_raises(ArvadosModel::PermissionDeniedError) do
184       c.save!
185     end
186   end
187
188   test "Container only set exit code on complete" do
189     c, _ = minimal_new
190     set_user_from_auth :dispatch1
191     c.lock
192     c.update_attributes! state: Container::Running
193
194     check_illegal_updates c, [{exit_code: 1},
195                               {exit_code: 1, state: Container::Cancelled}]
196
197     assert c.update_attributes(exit_code: 1, state: Container::Complete)
198   end
199 end