Merge branch 'master' into 9767-workflows-in-chooser
[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.update_attributes! state: Container::Locked
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     refute c.update_attributes(state: Container::Locked), "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.update_attributes(state: Container::Locked), show_errors(c)
116     assert c.locked_by_uuid
117     assert c.auth_uuid
118
119     assert c.update_attributes(state: Container::Queued), show_errors(c)
120     refute c.locked_by_uuid
121     refute c.auth_uuid
122
123     refute c.update_attributes(state: Container::Running), "not locked"
124     c.reload
125     refute c.locked_by_uuid
126     refute c.auth_uuid
127
128     assert c.update_attributes(state: Container::Locked), show_errors(c)
129     assert c.update_attributes(state: Container::Running), show_errors(c)
130     assert c.locked_by_uuid
131     assert c.auth_uuid
132
133     auth_uuid_was = c.auth_uuid
134
135     refute c.update_attributes(state: Container::Locked), "already running"
136     c.reload
137     refute c.update_attributes(state: Container::Queued), "already running"
138     c.reload
139
140     assert c.update_attributes(state: Container::Complete), show_errors(c)
141     refute c.locked_by_uuid
142     refute c.auth_uuid
143
144     auth_exp = ApiClientAuthorization.find_by_uuid(auth_uuid_was).expires_at
145     assert_operator auth_exp, :<, db_current_time
146   end
147
148   test "Container queued cancel" do
149     c, _ = minimal_new
150     set_user_from_auth :dispatch1
151     assert c.update_attributes(state: Container::Cancelled), show_errors(c)
152     check_no_change_from_cancelled c
153   end
154
155   test "Container locked cancel" do
156     c, _ = minimal_new
157     set_user_from_auth :dispatch1
158     assert c.update_attributes(state: Container::Locked), show_errors(c)
159     assert c.update_attributes(state: Container::Cancelled), show_errors(c)
160     check_no_change_from_cancelled c
161   end
162
163   test "Container running cancel" do
164     c, _ = minimal_new
165     set_user_from_auth :dispatch1
166     c.update_attributes! state: Container::Queued
167     c.update_attributes! state: Container::Locked
168     c.update_attributes! state: Container::Running
169     c.update_attributes! state: Container::Cancelled
170     check_no_change_from_cancelled c
171   end
172
173   test "Container create forbidden for non-admin" do
174     set_user_from_auth :active_trustedclient
175     c = Container.new DEFAULT_ATTRS
176     c.environment = {}
177     c.mounts = {"BAR" => "FOO"}
178     c.output_path = "/tmp"
179     c.priority = 1
180     c.runtime_constraints = {}
181     assert_raises(ArvadosModel::PermissionDeniedError) do
182       c.save!
183     end
184   end
185
186   test "Container only set exit code on complete" do
187     c, _ = minimal_new
188     set_user_from_auth :dispatch1
189     c.update_attributes! state: Container::Locked
190     c.update_attributes! state: Container::Running
191
192     check_illegal_updates c, [{exit_code: 1},
193                               {exit_code: 1, state: Container::Cancelled}]
194
195     assert c.update_attributes(exit_code: 1, state: Container::Complete)
196   end
197 end