9623: Removed fixtures additions meant for Container reuse tests because some of...
[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 serialized hash attributes sorted before save" do
82     env = {"C" => 3, "B" => 2, "A" => 1}
83     m = {"F" => 3, "E" => 2, "D" => 1}
84     rc = {"vcpus" => 1, "ram" => 1}
85     c, _ = minimal_new(environment: env, mounts: m, runtime_constraints: rc)
86     assert_equal c.environment.to_json, Container.deep_sort_hash(env).to_json
87     assert_equal c.mounts.to_json, Container.deep_sort_hash(m).to_json
88     assert_equal c.runtime_constraints.to_json, Container.deep_sort_hash(rc).to_json
89   end
90
91   test 'deep_sort_hash on array of hashes' do
92     a = {'z' => [[{'a' => 'a', 'b' => 'b'}]]}
93     b = {'z' => [[{'b' => 'b', 'a' => 'a'}]]}
94     assert_equal Container.deep_sort_hash(a).to_json, Container.deep_sort_hash(b).to_json
95   end
96
97   [
98     ["completed", :completed_older],
99     ["running", :running_older],
100     ["locked", :locked_higher_priority],
101     ["queued", :queued_higher_priority],
102     ["completed_vs_running", :completed_vs_running_winner],
103     ["running_vs_locked", :running_vs_locked_winner],
104     ["locked_vs_queued", :locked_vs_queued_winner]
105   ].each do |state, c_to_be_selected|
106     test "find reusable method for #{state} container" do
107       set_user_from_auth :active
108       c = Container.find_reusable(container_image: "test",
109                                   cwd: "test",
110                                   command: ["echo", "hello"],
111                                   output_path: "test",
112                                   runtime_constraints: {"vcpus" => 4, "ram" => 12000000000},
113                                   mounts: {"test" => {"kind" => "json"}},
114                                   environment: {"var" => state})
115       assert_not_nil c
116       assert_equal c.uuid, containers(c_to_be_selected).uuid
117     end
118   end
119
120   test "Container find reusable method should not select failed" do
121     set_user_from_auth :active
122     attrs = {container_image: "test",
123              cwd: "test",
124              command: ["echo", "hello"],
125              output_path: "test",
126              runtime_constraints: {"vcpus" => 4, "ram" => 12000000000},
127              mounts: {"test" => {"kind" => "json"}},
128              environment: {"var" => "failed"}}
129     cf = containers(:failed_container)
130     assert_equal cf.container_image, attrs[:container_image]
131     assert_equal cf.cwd, attrs[:cwd]
132     assert_equal cf.command, attrs[:command]
133     assert_equal cf.output_path, attrs[:output_path]
134     assert_equal cf.runtime_constraints, attrs[:runtime_constraints]
135     assert_equal cf.mounts, attrs[:mounts]
136     assert_equal cf.environment, attrs[:environment]
137     assert cf.exit_code != 0
138     c = Container.find_reusable(attrs)
139     assert_nil c
140   end
141
142   test "Container running" do
143     c, _ = minimal_new priority: 1
144
145     set_user_from_auth :dispatch1
146     check_illegal_updates c, [{state: Container::Running},
147                               {state: Container::Complete}]
148
149     c.update_attributes! state: Container::Locked
150     c.update_attributes! state: Container::Running
151
152     check_illegal_modify c
153     check_bogus_states c
154
155     check_illegal_updates c, [{state: Container::Queued}]
156     c.reload
157
158     c.update_attributes! priority: 3
159   end
160
161   test "Lock and unlock" do
162     c, cr = minimal_new priority: 0
163
164     set_user_from_auth :dispatch1
165     assert_equal Container::Queued, c.state
166
167     refute c.update_attributes(state: Container::Locked), "no priority"
168     c.reload
169     assert cr.update_attributes priority: 1
170
171     refute c.update_attributes(state: Container::Running), "not locked"
172     c.reload
173     refute c.update_attributes(state: Container::Complete), "not locked"
174     c.reload
175
176     assert c.update_attributes(state: Container::Locked), show_errors(c)
177     assert c.locked_by_uuid
178     assert c.auth_uuid
179
180     assert c.update_attributes(state: Container::Queued), show_errors(c)
181     refute c.locked_by_uuid
182     refute c.auth_uuid
183
184     refute c.update_attributes(state: Container::Running), "not locked"
185     c.reload
186     refute c.locked_by_uuid
187     refute c.auth_uuid
188
189     assert c.update_attributes(state: Container::Locked), show_errors(c)
190     assert c.update_attributes(state: Container::Running), show_errors(c)
191     assert c.locked_by_uuid
192     assert c.auth_uuid
193
194     auth_uuid_was = c.auth_uuid
195
196     refute c.update_attributes(state: Container::Locked), "already running"
197     c.reload
198     refute c.update_attributes(state: Container::Queued), "already running"
199     c.reload
200
201     assert c.update_attributes(state: Container::Complete), show_errors(c)
202     refute c.locked_by_uuid
203     refute c.auth_uuid
204
205     auth_exp = ApiClientAuthorization.find_by_uuid(auth_uuid_was).expires_at
206     assert_operator auth_exp, :<, db_current_time
207   end
208
209   test "Container queued cancel" do
210     c, _ = minimal_new
211     set_user_from_auth :dispatch1
212     assert c.update_attributes(state: Container::Cancelled), show_errors(c)
213     check_no_change_from_cancelled c
214   end
215
216   test "Container locked cancel" do
217     c, _ = minimal_new
218     set_user_from_auth :dispatch1
219     assert c.update_attributes(state: Container::Locked), show_errors(c)
220     assert c.update_attributes(state: Container::Cancelled), show_errors(c)
221     check_no_change_from_cancelled c
222   end
223
224   test "Container running cancel" do
225     c, _ = minimal_new
226     set_user_from_auth :dispatch1
227     c.update_attributes! state: Container::Queued
228     c.update_attributes! state: Container::Locked
229     c.update_attributes! state: Container::Running
230     c.update_attributes! state: Container::Cancelled
231     check_no_change_from_cancelled c
232   end
233
234   test "Container create forbidden for non-admin" do
235     set_user_from_auth :active_trustedclient
236     c = Container.new DEFAULT_ATTRS
237     c.environment = {}
238     c.mounts = {"BAR" => "FOO"}
239     c.output_path = "/tmp"
240     c.priority = 1
241     c.runtime_constraints = {}
242     assert_raises(ArvadosModel::PermissionDeniedError) do
243       c.save!
244     end
245   end
246
247   test "Container only set exit code on complete" do
248     c, _ = minimal_new
249     set_user_from_auth :dispatch1
250     c.update_attributes! state: Container::Locked
251     c.update_attributes! state: Container::Running
252
253     check_illegal_updates c, [{exit_code: 1},
254                               {exit_code: 1, state: Container::Cancelled}]
255
256     assert c.update_attributes(exit_code: 1, state: Container::Complete)
257   end
258 end