1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
15 "git.arvados.org/arvados.git/sdk/go/arvados"
16 "git.arvados.org/arvados.git/sdk/go/arvadosclient"
17 "git.arvados.org/arvados.git/sdk/go/arvadostest"
18 "git.arvados.org/arvados.git/sdk/go/keepclient"
22 func Test(t *testing.T) {
26 var _ = check.Suite(&Suite{})
30 func (s *Suite) TearDownSuite(c *check.C) {
31 // Undo any changes/additions to the database so they don't affect subsequent tests.
32 arvadostest.ResetEnv()
35 func (s *Suite) SetUpSuite(c *check.C) {
36 arvadostest.StartAPI()
37 arvadostest.StartKeep(2, true)
39 // Get the various arvados, arvadosclient, and keep client objects
40 ac := arvados.NewClientFromEnv()
41 arv, err := arvadosclient.MakeArvadosClient()
42 c.Assert(err, check.Equals, nil)
43 arv.ApiToken = arvadostest.ActiveToken
44 kc, err := keepclient.MakeKeepClient(arv)
45 c.Assert(err, check.Equals, nil)
47 standardE4sV3JSON := `{
48 "Name": "Standard_E4s_v3",
49 "ProviderType": "Standard_E4s_v3",
52 "Scratch": 64000000000,
53 "IncludedScratch": 64000000000,
58 standardD32sV3JSON := `{
59 "Name": "Standard_D32s_v3",
60 "ProviderType": "Standard_D32s_v3",
63 "Scratch": 256000000000,
64 "IncludedScratch": 256000000000,
70 standardA1V2JSON := `{
72 "ProviderType": "Standard_A1_v2",
75 "Scratch": 10000000000,
76 "IncludedScratch": 10000000000,
82 standardA2V2JSON := `{
84 "ProviderType": "Standard_A2_v2",
87 "Scratch": 20000000000,
88 "IncludedScratch": 20000000000,
98 "size": "Standard_D1_v2"
100 "total_cpu_cores": 1,
101 "total_ram_mb": 3418,
102 "total_scratch_mb": 51170
106 // Our fixtures do not actually contain file contents. Populate the log collections we're going to use with the node.json file
107 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedContainerRequestUUID, arvadostest.LogCollectionUUID, standardE4sV3JSON)
108 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedContainerRequestUUID2, arvadostest.LogCollectionUUID2, standardD32sV3JSON)
110 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedDiagnosticsContainerRequest1UUID, arvadostest.DiagnosticsContainerRequest1LogCollectionUUID, standardA1V2JSON)
111 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedDiagnosticsContainerRequest2UUID, arvadostest.DiagnosticsContainerRequest2LogCollectionUUID, standardA1V2JSON)
112 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedDiagnosticsHasher1ContainerRequestUUID, arvadostest.Hasher1LogCollectionUUID, standardA1V2JSON)
113 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedDiagnosticsHasher2ContainerRequestUUID, arvadostest.Hasher2LogCollectionUUID, standardA2V2JSON)
114 createNodeJSON(c, arv, ac, kc, arvadostest.CompletedDiagnosticsHasher3ContainerRequestUUID, arvadostest.Hasher3LogCollectionUUID, legacyD1V2JSON)
117 func createNodeJSON(c *check.C, arv *arvadosclient.ArvadosClient, ac *arvados.Client, kc *keepclient.KeepClient, crUUID string, logUUID string, nodeJSON string) {
119 var cr arvados.ContainerRequest
120 err := ac.RequestAndDecode(&cr, "GET", "arvados/v1/container_requests/"+crUUID, nil, nil)
121 c.Assert(err, check.Equals, nil)
122 c.Assert(cr.LogUUID, check.Equals, logUUID)
124 // Get the log collection
125 var coll arvados.Collection
126 err = ac.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+cr.LogUUID, nil, nil)
127 c.Assert(err, check.IsNil)
129 // Create a node.json file -- the fixture doesn't actually contain the contents of the collection.
130 fs, err := coll.FileSystem(ac, kc)
131 c.Assert(err, check.IsNil)
132 f, err := fs.OpenFile("node.json", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0777)
133 c.Assert(err, check.IsNil)
134 _, err = io.WriteString(f, nodeJSON)
135 c.Assert(err, check.IsNil)
137 c.Assert(err, check.IsNil)
139 // Flush the data to Keep
140 mtxt, err := fs.MarshalManifest(".")
141 c.Assert(err, check.IsNil)
142 c.Assert(mtxt, check.NotNil)
144 // Update collection record
145 err = ac.RequestAndDecode(&coll, "PUT", "arvados/v1/collections/"+cr.LogUUID, nil, map[string]interface{}{
146 "collection": map[string]interface{}{
147 "manifest_text": mtxt,
150 c.Assert(err, check.IsNil)
153 func (*Suite) TestUsage(c *check.C) {
154 var stdout, stderr bytes.Buffer
155 exitcode := Command.RunCommand("costanalyzer.test", []string{"-help", "-log-level=debug"}, &bytes.Buffer{}, &stdout, &stderr)
156 c.Check(exitcode, check.Equals, 1)
157 c.Check(stdout.String(), check.Equals, "")
158 c.Check(stderr.String(), check.Matches, `(?ms).*Usage:.*`)
161 func (*Suite) TestContainerRequestUUID(c *check.C) {
162 var stdout, stderr bytes.Buffer
163 // Run costanalyzer with 1 container request uuid
164 exitcode := Command.RunCommand("costanalyzer.test", []string{"-output", "results", arvadostest.CompletedContainerRequestUUID}, &bytes.Buffer{}, &stdout, &stderr)
165 c.Check(exitcode, check.Equals, 0)
166 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
168 uuidReport, err := ioutil.ReadFile("results/" + arvadostest.CompletedContainerRequestUUID + ".csv")
169 c.Assert(err, check.IsNil)
170 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
171 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
172 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
174 aggregateCostReport, err := ioutil.ReadFile(matches[1])
175 c.Assert(err, check.IsNil)
177 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,7.01302889")
180 func (*Suite) TestCollectionUUID(c *check.C) {
181 var stdout, stderr bytes.Buffer
183 // Run costanalyzer with 1 collection uuid, without 'container_request' property
184 exitcode := Command.RunCommand("costanalyzer.test", []string{"-output", "results", arvadostest.FooCollection}, &bytes.Buffer{}, &stdout, &stderr)
185 c.Check(exitcode, check.Equals, 2)
186 c.Assert(stderr.String(), check.Matches, "(?ms).*does not have a 'container_request' property.*")
188 // Update the collection, attach a 'container_request' property
189 ac := arvados.NewClientFromEnv()
190 var coll arvados.Collection
192 // Update collection record
193 err := ac.RequestAndDecode(&coll, "PUT", "arvados/v1/collections/"+arvadostest.FooCollection, nil, map[string]interface{}{
194 "collection": map[string]interface{}{
195 "properties": map[string]interface{}{
196 "container_request": arvadostest.CompletedContainerRequestUUID,
200 c.Assert(err, check.IsNil)
205 // Run costanalyzer with 1 collection uuid
206 exitcode = Command.RunCommand("costanalyzer.test", []string{"-output", "results", arvadostest.FooCollection}, &bytes.Buffer{}, &stdout, &stderr)
207 c.Check(exitcode, check.Equals, 0)
208 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
210 uuidReport, err := ioutil.ReadFile("results/" + arvadostest.CompletedContainerRequestUUID + ".csv")
211 c.Assert(err, check.IsNil)
212 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
213 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
214 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
216 aggregateCostReport, err := ioutil.ReadFile(matches[1])
217 c.Assert(err, check.IsNil)
219 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,7.01302889")
222 func (*Suite) TestDoubleContainerRequestUUID(c *check.C) {
223 var stdout, stderr bytes.Buffer
224 // Run costanalyzer with 2 container request uuids
225 exitcode := Command.RunCommand("costanalyzer.test", []string{"-output", "results", arvadostest.CompletedContainerRequestUUID, arvadostest.CompletedContainerRequestUUID2}, &bytes.Buffer{}, &stdout, &stderr)
226 c.Check(exitcode, check.Equals, 0)
227 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
229 uuidReport, err := ioutil.ReadFile("results/" + arvadostest.CompletedContainerRequestUUID + ".csv")
230 c.Assert(err, check.IsNil)
231 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
233 uuidReport2, err := ioutil.ReadFile("results/" + arvadostest.CompletedContainerRequestUUID2 + ".csv")
234 c.Assert(err, check.IsNil)
235 c.Check(string(uuidReport2), check.Matches, "(?ms).*TOTAL,,,,,,,,,42.27031111")
237 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
238 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
240 aggregateCostReport, err := ioutil.ReadFile(matches[1])
241 c.Assert(err, check.IsNil)
243 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,49.28334000")
247 // Now move both container requests into an existing project, and then re-run
248 // the analysis with the project uuid. The results should be identical.
249 ac := arvados.NewClientFromEnv()
250 var cr arvados.ContainerRequest
251 err = ac.RequestAndDecode(&cr, "PUT", "arvados/v1/container_requests/"+arvadostest.CompletedContainerRequestUUID, nil, map[string]interface{}{
252 "container_request": map[string]interface{}{
253 "owner_uuid": arvadostest.AProjectUUID,
256 c.Assert(err, check.IsNil)
257 err = ac.RequestAndDecode(&cr, "PUT", "arvados/v1/container_requests/"+arvadostest.CompletedContainerRequestUUID2, nil, map[string]interface{}{
258 "container_request": map[string]interface{}{
259 "owner_uuid": arvadostest.AProjectUUID,
262 c.Assert(err, check.IsNil)
264 // Run costanalyzer with the project uuid
265 exitcode = Command.RunCommand("costanalyzer.test", []string{"-cache=false", "-log-level", "debug", "-output", "results", arvadostest.AProjectUUID}, &bytes.Buffer{}, &stdout, &stderr)
266 c.Check(exitcode, check.Equals, 0)
267 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
269 uuidReport, err = ioutil.ReadFile("results/" + arvadostest.CompletedContainerRequestUUID + ".csv")
270 c.Assert(err, check.IsNil)
271 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
273 uuidReport2, err = ioutil.ReadFile("results/" + arvadostest.CompletedContainerRequestUUID2 + ".csv")
274 c.Assert(err, check.IsNil)
275 c.Check(string(uuidReport2), check.Matches, "(?ms).*TOTAL,,,,,,,,,42.27031111")
277 re = regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
278 matches = re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
280 aggregateCostReport, err = ioutil.ReadFile(matches[1])
281 c.Assert(err, check.IsNil)
283 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,49.28334000")
286 func (*Suite) TestMultipleContainerRequestUUIDWithReuse(c *check.C) {
287 var stdout, stderr bytes.Buffer
288 // Run costanalyzer with 2 container request uuids, without output directory specified
289 exitcode := Command.RunCommand("costanalyzer.test", []string{arvadostest.CompletedDiagnosticsContainerRequest1UUID, arvadostest.CompletedDiagnosticsContainerRequest2UUID}, &bytes.Buffer{}, &stdout, &stderr)
290 c.Check(exitcode, check.Equals, 0)
291 c.Assert(stderr.String(), check.Not(check.Matches), "(?ms).*supplied uuids in .*")
293 // Check that the total amount was printed to stdout
294 c.Check(stdout.String(), check.Matches, "0.01492030\n")
299 // Run costanalyzer with 2 container request uuids
300 exitcode = Command.RunCommand("costanalyzer.test", []string{"-output", "results", arvadostest.CompletedDiagnosticsContainerRequest1UUID, arvadostest.CompletedDiagnosticsContainerRequest2UUID}, &bytes.Buffer{}, &stdout, &stderr)
301 c.Check(exitcode, check.Equals, 0)
302 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
304 uuidReport, err := ioutil.ReadFile("results/" + arvadostest.CompletedDiagnosticsContainerRequest1UUID + ".csv")
305 c.Assert(err, check.IsNil)
306 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,0.00916192")
308 uuidReport2, err := ioutil.ReadFile("results/" + arvadostest.CompletedDiagnosticsContainerRequest2UUID + ".csv")
309 c.Assert(err, check.IsNil)
310 c.Check(string(uuidReport2), check.Matches, "(?ms).*TOTAL,,,,,,,,,0.00588088")
312 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
313 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
315 aggregateCostReport, err := ioutil.ReadFile(matches[1])
316 c.Assert(err, check.IsNil)
318 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,0.01492030")