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 resultsDir := c.MkDir()
164 // Run costanalyzer with 1 container request uuid
165 exitcode := Command.RunCommand("costanalyzer.test", []string{"-output", resultsDir, arvadostest.CompletedContainerRequestUUID}, &bytes.Buffer{}, &stdout, &stderr)
166 c.Check(exitcode, check.Equals, 0)
167 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
169 uuidReport, err := ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedContainerRequestUUID + ".csv")
170 c.Assert(err, check.IsNil)
171 // Make sure the 'preemptible' flag was picked up
172 c.Check(string(uuidReport), check.Matches, "(?ms).*,Standard_E4s_v3,true,.*")
173 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
174 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
175 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
177 aggregateCostReport, err := ioutil.ReadFile(matches[1])
178 c.Assert(err, check.IsNil)
180 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,7.01302889")
183 func (*Suite) TestCollectionUUID(c *check.C) {
184 var stdout, stderr bytes.Buffer
186 resultsDir := c.MkDir()
187 // Run costanalyzer with 1 collection uuid, without 'container_request' property
188 exitcode := Command.RunCommand("costanalyzer.test", []string{"-output", resultsDir, arvadostest.FooCollection}, &bytes.Buffer{}, &stdout, &stderr)
189 c.Check(exitcode, check.Equals, 2)
190 c.Assert(stderr.String(), check.Matches, "(?ms).*does not have a 'container_request' property.*")
192 // Update the collection, attach a 'container_request' property
193 ac := arvados.NewClientFromEnv()
194 var coll arvados.Collection
196 // Update collection record
197 err := ac.RequestAndDecode(&coll, "PUT", "arvados/v1/collections/"+arvadostest.FooCollection, nil, map[string]interface{}{
198 "collection": map[string]interface{}{
199 "properties": map[string]interface{}{
200 "container_request": arvadostest.CompletedContainerRequestUUID,
204 c.Assert(err, check.IsNil)
209 // Run costanalyzer with 1 collection uuid
210 resultsDir = c.MkDir()
211 exitcode = Command.RunCommand("costanalyzer.test", []string{"-output", resultsDir, arvadostest.FooCollection}, &bytes.Buffer{}, &stdout, &stderr)
212 c.Check(exitcode, check.Equals, 0)
213 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
215 uuidReport, err := ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedContainerRequestUUID + ".csv")
216 c.Assert(err, check.IsNil)
217 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
218 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
219 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
221 aggregateCostReport, err := ioutil.ReadFile(matches[1])
222 c.Assert(err, check.IsNil)
224 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,7.01302889")
227 func (*Suite) TestDoubleContainerRequestUUID(c *check.C) {
228 var stdout, stderr bytes.Buffer
229 resultsDir := c.MkDir()
230 // Run costanalyzer with 2 container request uuids
231 exitcode := Command.RunCommand("costanalyzer.test", []string{"-output", resultsDir, arvadostest.CompletedContainerRequestUUID, arvadostest.CompletedContainerRequestUUID2}, &bytes.Buffer{}, &stdout, &stderr)
232 c.Check(exitcode, check.Equals, 0)
233 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
235 uuidReport, err := ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedContainerRequestUUID + ".csv")
236 c.Assert(err, check.IsNil)
237 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
239 uuidReport2, err := ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedContainerRequestUUID2 + ".csv")
240 c.Assert(err, check.IsNil)
241 c.Check(string(uuidReport2), check.Matches, "(?ms).*TOTAL,,,,,,,,,42.27031111")
243 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
244 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
246 aggregateCostReport, err := ioutil.ReadFile(matches[1])
247 c.Assert(err, check.IsNil)
249 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,49.28334000")
253 // Now move both container requests into an existing project, and then re-run
254 // the analysis with the project uuid. The results should be identical.
255 ac := arvados.NewClientFromEnv()
256 var cr arvados.ContainerRequest
257 err = ac.RequestAndDecode(&cr, "PUT", "arvados/v1/container_requests/"+arvadostest.CompletedContainerRequestUUID, nil, map[string]interface{}{
258 "container_request": map[string]interface{}{
259 "owner_uuid": arvadostest.AProjectUUID,
262 c.Assert(err, check.IsNil)
263 err = ac.RequestAndDecode(&cr, "PUT", "arvados/v1/container_requests/"+arvadostest.CompletedContainerRequestUUID2, nil, map[string]interface{}{
264 "container_request": map[string]interface{}{
265 "owner_uuid": arvadostest.AProjectUUID,
268 c.Assert(err, check.IsNil)
270 // Run costanalyzer with the project uuid
271 resultsDir = c.MkDir()
272 exitcode = Command.RunCommand("costanalyzer.test", []string{"-cache=false", "-log-level", "debug", "-output", resultsDir, arvadostest.AProjectUUID}, &bytes.Buffer{}, &stdout, &stderr)
273 c.Check(exitcode, check.Equals, 0)
274 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
276 uuidReport, err = ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedContainerRequestUUID + ".csv")
277 c.Assert(err, check.IsNil)
278 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,7.01302889")
280 uuidReport2, err = ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedContainerRequestUUID2 + ".csv")
281 c.Assert(err, check.IsNil)
282 c.Check(string(uuidReport2), check.Matches, "(?ms).*TOTAL,,,,,,,,,42.27031111")
284 re = regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
285 matches = re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
287 aggregateCostReport, err = ioutil.ReadFile(matches[1])
288 c.Assert(err, check.IsNil)
290 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,49.28334000")
293 func (*Suite) TestMultipleContainerRequestUUIDWithReuse(c *check.C) {
294 var stdout, stderr bytes.Buffer
295 // Run costanalyzer with 2 container request uuids, without output directory specified
296 exitcode := Command.RunCommand("costanalyzer.test", []string{arvadostest.CompletedDiagnosticsContainerRequest1UUID, arvadostest.CompletedDiagnosticsContainerRequest2UUID}, &bytes.Buffer{}, &stdout, &stderr)
297 c.Check(exitcode, check.Equals, 0)
298 c.Assert(stderr.String(), check.Not(check.Matches), "(?ms).*supplied uuids in .*")
300 // Check that the total amount was printed to stdout
301 c.Check(stdout.String(), check.Matches, "0.01492030\n")
306 // Run costanalyzer with 2 container request uuids
307 resultsDir := c.MkDir()
308 exitcode = Command.RunCommand("costanalyzer.test", []string{"-output", resultsDir, arvadostest.CompletedDiagnosticsContainerRequest1UUID, arvadostest.CompletedDiagnosticsContainerRequest2UUID}, &bytes.Buffer{}, &stdout, &stderr)
309 c.Check(exitcode, check.Equals, 0)
310 c.Assert(stderr.String(), check.Matches, "(?ms).*supplied uuids in .*")
312 uuidReport, err := ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedDiagnosticsContainerRequest1UUID + ".csv")
313 c.Assert(err, check.IsNil)
314 c.Check(string(uuidReport), check.Matches, "(?ms).*TOTAL,,,,,,,,,0.00916192")
316 uuidReport2, err := ioutil.ReadFile(resultsDir + "/" + arvadostest.CompletedDiagnosticsContainerRequest2UUID + ".csv")
317 c.Assert(err, check.IsNil)
318 c.Check(string(uuidReport2), check.Matches, "(?ms).*TOTAL,,,,,,,,,0.00588088")
320 re := regexp.MustCompile(`(?ms).*supplied uuids in (.*?)\n`)
321 matches := re.FindStringSubmatch(stderr.String()) // matches[1] contains a string like 'results/2020-11-02-18-57-45-aggregate-costaccounting.csv'
323 aggregateCostReport, err := ioutil.ReadFile(matches[1])
324 c.Assert(err, check.IsNil)
326 c.Check(string(aggregateCostReport), check.Matches, "(?ms).*TOTAL,0.01492030")