github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/dashboard/app/asset_storage_test.go (about) 1 // Copyright 2022 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 package main 5 6 import ( 7 "fmt" 8 "sort" 9 "testing" 10 "time" 11 12 "github.com/google/syzkaller/dashboard/dashapi" 13 "github.com/google/syzkaller/pkg/email" 14 ) 15 16 func TestBuildAssetLifetime(t *testing.T) { 17 c := NewCtx(t) 18 defer c.Close() 19 20 build := testBuild(1) 21 build.Manager = "test_manager" 22 // Embed one of the assets right away. 23 build.Assets = []dashapi.NewAsset{ 24 { 25 Type: dashapi.KernelObject, 26 DownloadURL: "http://google.com/vmlinux", 27 }, 28 } 29 c.client2.UploadBuild(build) 30 31 // Add one more build, so that the assets of the previous one could be deprecated. 32 c.advanceTime(time.Minute) 33 build2 := testBuild(2) 34 build2.Manager = "test_manager" 35 c.client2.UploadBuild(build2) 36 37 // "Upload" several more assets. 38 c.expectOK(c.client2.AddBuildAssets(&dashapi.AddBuildAssetsReq{ 39 BuildID: build.ID, 40 Assets: []dashapi.NewAsset{ 41 { 42 Type: dashapi.BootableDisk, 43 DownloadURL: "http://google.com/bootable_disk", 44 }, 45 }, 46 })) 47 c.expectOK(c.client2.AddBuildAssets(&dashapi.AddBuildAssetsReq{ 48 BuildID: build.ID, 49 Assets: []dashapi.NewAsset{ 50 { 51 Type: dashapi.HTMLCoverageReport, 52 DownloadURL: "http://google.com/coverage.html", 53 }, 54 }, 55 })) 56 57 crash := testCrash(build, 1) 58 crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`, `idont@want.EMAILS`} 59 c.client2.ReportCrash(crash) 60 61 // Test that the reporting email is correct. 62 msg := c.pollEmailBug() 63 sender, extBugID, err := email.RemoveAddrContext(msg.Sender) 64 c.expectOK(err) 65 _, dbCrash, dbBuild := c.loadBug(extBugID) 66 crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) 67 kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) 68 c.expectEQ(sender, fromAddr(c.ctx)) 69 to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email 70 c.expectEQ(msg.To, []string{to}) 71 c.expectEQ(msg.Subject, crash.Title) 72 c.expectEQ(len(msg.Attachments), 0) 73 c.expectEQ(msg.Body, fmt.Sprintf(`Hello, 74 75 syzbot found the following issue on: 76 77 HEAD commit: 111111111111 kernel_commit_title1 78 git tree: repo1 branch1 79 console output: %[2]v 80 kernel config: %[3]v 81 dashboard link: https://testapp.appspot.com/bug?extid=%[1]v 82 compiler: compiler1 83 CC: [bar@foo.com foo@bar.com idont@want.EMAILS] 84 85 Unfortunately, I don't have any reproducer for this issue yet. 86 87 Downloadable assets: 88 disk image: http://google.com/bootable_disk 89 vmlinux: http://google.com/vmlinux 90 91 IMPORTANT: if you fix the issue, please add the following tag to the commit: 92 Reported-by: syzbot+%[1]v@testapp.appspotmail.com 93 94 report1 95 96 --- 97 This report is generated by a bot. It may contain errors. 98 See https://goo.gl/tpsmEJ for more information about syzbot. 99 syzbot engineers can be reached at syzkaller@googlegroups.com. 100 101 syzbot will keep track of this issue. See: 102 https://goo.gl/tpsmEJ#status for how to communicate with syzbot. 103 104 If the report is already addressed, let syzbot know by replying with: 105 #syz fix: exact-commit-title 106 107 If you want to overwrite report's subsystems, reply with: 108 #syz set subsystems: new-subsystem 109 (See the list of subsystem names on the web dashboard) 110 111 If the report is a duplicate of another one, reply with: 112 #syz dup: exact-subject-of-another-report 113 114 If you want to undo deduplication, reply with: 115 #syz undup`, 116 extBugID, crashLogLink, kernelConfigLink)) 117 c.checkURLContents(crashLogLink, crash.Log) 118 c.checkURLContents(kernelConfigLink, build.KernelConfig) 119 120 // We query the needed assets. We need all 3. 121 needed, err := c.client2.NeededAssetsList() 122 c.expectOK(err) 123 sort.Strings(needed.DownloadURLs) 124 allDownloadURLs := []string{ 125 "http://google.com/bootable_disk", 126 "http://google.com/coverage.html", 127 "http://google.com/vmlinux", 128 } 129 c.expectEQ(needed.DownloadURLs, allDownloadURLs) 130 131 // Invalidate the bug. 132 c.client.updateBug(extBugID, dashapi.BugStatusInvalid, "") 133 _, err = c.GET("/cron/deprecate_assets") 134 c.expectOK(err) 135 136 // Query the needed assets once more, so far there should be no change. 137 needed, err = c.client2.NeededAssetsList() 138 c.expectOK(err) 139 sort.Strings(needed.DownloadURLs) 140 c.expectEQ(needed.DownloadURLs, allDownloadURLs) 141 142 // Skip one month and deprecate assets. 143 c.advanceTime(time.Hour * 24 * 31) 144 _, err = c.GET("/cron/deprecate_assets") 145 c.expectOK(err) 146 147 // Only the html asset should have persisted. 148 needed, err = c.client2.NeededAssetsList() 149 c.expectOK(err) 150 c.expectEQ(needed.DownloadURLs, []string{"http://google.com/coverage.html"}) 151 } 152 153 func TestCoverReportDisplay(t *testing.T) { 154 c := NewCtx(t) 155 defer c.Close() 156 157 build := testBuild(1) 158 c.client.UploadBuild(build) 159 160 // Upload the second build to just make sure coverage reports are assigned per-manager. 161 c.client.UploadBuild(testBuild(2)) 162 163 // We expect no coverage reports to be present. 164 uiManagers, err := loadManagers(c.ctx, AccessAdmin, "test1", nil) 165 c.expectOK(err) 166 c.expectEQ(len(uiManagers), 2) 167 c.expectEQ(uiManagers[0].CoverLink, "") 168 c.expectEQ(uiManagers[1].CoverLink, "") 169 170 // Upload an asset. 171 origHTMLAsset := "http://google.com/coverage0.html" 172 c.expectOK(c.client.AddBuildAssets(&dashapi.AddBuildAssetsReq{ 173 BuildID: build.ID, 174 Assets: []dashapi.NewAsset{ 175 { 176 Type: dashapi.HTMLCoverageReport, 177 DownloadURL: origHTMLAsset, 178 }, 179 }, 180 })) 181 uiManagers, err = loadManagers(c.ctx, AccessAdmin, "test1", nil) 182 c.expectOK(err) 183 c.expectEQ(len(uiManagers), 2) 184 c.expectEQ(uiManagers[0].CoverLink, origHTMLAsset) 185 c.expectEQ(uiManagers[1].CoverLink, "") 186 187 // Upload a newer coverage. 188 newHTMLAsset := "http://google.com/coverage1.html" 189 c.expectOK(c.client.AddBuildAssets(&dashapi.AddBuildAssetsReq{ 190 BuildID: build.ID, 191 Assets: []dashapi.NewAsset{ 192 { 193 Type: dashapi.HTMLCoverageReport, 194 DownloadURL: newHTMLAsset, 195 }, 196 }, 197 })) 198 uiManagers, err = loadManagers(c.ctx, AccessAdmin, "test1", nil) 199 c.expectOK(err) 200 c.expectEQ(len(uiManagers), 2) 201 c.expectEQ(uiManagers[0].CoverLink, newHTMLAsset) 202 c.expectEQ(uiManagers[1].CoverLink, "") 203 } 204 205 func TestCoverReportDeprecation(t *testing.T) { 206 c := NewCtx(t) 207 defer c.Close() 208 209 ensureNeeded := func(needed []string) { 210 _, err := c.GET("/cron/deprecate_assets") 211 c.expectOK(err) 212 neededResp, err := c.client.NeededAssetsList() 213 c.expectOK(err) 214 sort.Strings(neededResp.DownloadURLs) 215 sort.Strings(needed) 216 c.expectEQ(neededResp.DownloadURLs, needed) 217 } 218 219 build := testBuild(1) 220 c.client.UploadBuild(build) 221 222 uploadReport := func(url string) { 223 c.expectOK(c.client.AddBuildAssets(&dashapi.AddBuildAssetsReq{ 224 BuildID: build.ID, 225 Assets: []dashapi.NewAsset{ 226 { 227 Type: dashapi.HTMLCoverageReport, 228 DownloadURL: url, 229 }, 230 }, 231 })) 232 } 233 234 // Week 1. Saturday Jan 1st, 2000. 235 weekOneFirst := "http://google.com/coverage1_1.html" 236 uploadReport(weekOneFirst) 237 238 // Week 1. Sunday Jan 2nd, 2000. 239 weekOneSecond := "http://google.com/coverage1_2.html" 240 c.advanceTime(time.Hour * 24) 241 uploadReport(weekOneSecond) 242 ensureNeeded([]string{weekOneFirst, weekOneSecond}) 243 244 // Week 2. Tuesday Jan 4nd, 2000. 245 weekTwoFirst := "http://google.com/coverage2_1.html" 246 c.advanceTime(time.Hour * 24 * 2) 247 uploadReport(weekTwoFirst) 248 ensureNeeded([]string{weekOneFirst, weekOneSecond, weekTwoFirst}) 249 250 // Week 2. Thu Jan 6nd, 2000. 251 weekTwoSecond := "http://google.com/coverage2_2.html" 252 c.advanceTime(time.Hour * 24 * 2) 253 uploadReport(weekTwoSecond) 254 ensureNeeded([]string{weekOneFirst, weekOneSecond, weekTwoFirst, weekTwoSecond}) 255 256 // Week 3. Monday Jan 10th, 2000. 257 weekThreeFirst := "http://google.com/coverage3_1.html" 258 c.advanceTime(time.Hour * 24 * 4) 259 uploadReport(weekThreeFirst) 260 ensureNeeded([]string{weekOneFirst, weekOneSecond, weekTwoFirst, weekTwoSecond, weekThreeFirst}) 261 262 // Week 4. Monday Jan 17th, 2000. 263 weekFourFirst := "http://google.com/coverage4_1.html" 264 c.advanceTime(time.Hour * 24 * 7) 265 uploadReport(weekFourFirst) 266 267 t.Logf("embargo is over, time is %s", timeNow(c.ctx)) 268 // Note that now that the two week deletion embargo has passed, the first asset 269 // begins to falls out. 270 ensureNeeded([]string{weekOneSecond, weekTwoFirst, weekTwoSecond, weekThreeFirst, weekFourFirst}) 271 272 // Week 5. Monday Jan 24th, 2000. 273 c.advanceTime(time.Hour * 24 * 7) 274 ensureNeeded([]string{weekOneSecond, weekTwoSecond, weekThreeFirst, weekFourFirst}) 275 276 // A year later. 277 c.advanceTime(time.Hour * 24 * 365) 278 ensureNeeded([]string{weekOneSecond, weekTwoSecond, weekThreeFirst, weekFourFirst}) 279 } 280 281 func TestFreshBuildAssets(t *testing.T) { 282 c := NewCtx(t) 283 defer c.Close() 284 285 ensureNeeded := func(needed []string) { 286 _, err := c.GET("/cron/deprecate_assets") 287 c.expectOK(err) 288 neededResp, err := c.client.NeededAssetsList() 289 c.expectOK(err) 290 sort.Strings(neededResp.DownloadURLs) 291 sort.Strings(needed) 292 c.expectEQ(neededResp.DownloadURLs, needed) 293 } 294 295 build := testBuild(1) 296 build.Manager = "manager" 297 build.Assets = []dashapi.NewAsset{ 298 { 299 Type: dashapi.KernelObject, 300 DownloadURL: "http://google.com/vmlinux", 301 }, 302 } 303 c.client.UploadBuild(build) 304 305 // No crashes yet, but it's the latest build, so the assets must be preserved. 306 ensureNeeded([]string{"http://google.com/vmlinux"}) 307 308 // Upload one more build for the same manager. 309 c.advanceTime(time.Minute) 310 build2 := testBuild(2) 311 build2.Manager = "manager" 312 build2.Assets = []dashapi.NewAsset{ 313 { 314 Type: dashapi.KernelObject, 315 DownloadURL: "http://google.com/vmlinux2", 316 }, 317 } 318 c.client.UploadBuild(build2) 319 320 // The assets of the previous build are reasonably new, so they must be kept. 321 ensureNeeded([]string{"http://google.com/vmlinux", "http://google.com/vmlinux2"}) 322 323 // The assets of the first build must be deprecated now. 324 c.advanceTime(time.Hour * 24 * 14) 325 ensureNeeded([]string{"http://google.com/vmlinux2"}) 326 327 // But even if a lot of time passes, but there are no new builds, the assets must stay. 328 c.advanceTime(time.Hour * 24 * 365) 329 ensureNeeded([]string{"http://google.com/vmlinux2"}) 330 } 331 332 func TestCrashAssetLifetime(t *testing.T) { 333 c := NewCtx(t) 334 defer c.Close() 335 336 build := testBuild(1) 337 c.client2.UploadBuild(build) 338 339 crash := testCrash(build, 1) 340 crash.Maintainers = []string{`"Foo Bar" <foo@bar.com>`, `bar@foo.com`, `idont@want.EMAILS`} 341 crash.Assets = []dashapi.NewAsset{ 342 { 343 Type: dashapi.MountInRepro, 344 DownloadURL: "http://google.com/disk_image", 345 }, 346 { 347 Type: dashapi.MountInRepro, 348 DownloadURL: "http://google.com/disk_image2", 349 }, 350 } 351 c.client2.ReportCrash(crash) 352 353 // Test that the reported email is correct. 354 msg := c.pollEmailBug() 355 sender, extBugID, err := email.RemoveAddrContext(msg.Sender) 356 c.expectOK(err) 357 _, dbCrash, dbBuild := c.loadBug(extBugID) 358 crashLogLink := externalLink(c.ctx, textCrashLog, dbCrash.Log) 359 kernelConfigLink := externalLink(c.ctx, textKernelConfig, dbBuild.KernelConfig) 360 c.expectEQ(sender, fromAddr(c.ctx)) 361 to := c.config().Namespaces["test2"].Reporting[0].Config.(*EmailConfig).Email 362 c.expectEQ(msg.To, []string{to}) 363 c.expectEQ(msg.Subject, crash.Title) 364 c.expectEQ(len(msg.Attachments), 0) 365 c.expectEQ(msg.Body, fmt.Sprintf(`Hello, 366 367 syzbot found the following issue on: 368 369 HEAD commit: 111111111111 kernel_commit_title1 370 git tree: repo1 branch1 371 console output: %[2]v 372 kernel config: %[3]v 373 dashboard link: https://testapp.appspot.com/bug?extid=%[1]v 374 compiler: compiler1 375 CC: [bar@foo.com foo@bar.com idont@want.EMAILS] 376 377 Unfortunately, I don't have any reproducer for this issue yet. 378 379 Downloadable assets: 380 mounted in repro #1: http://google.com/disk_image 381 mounted in repro #2: http://google.com/disk_image2 382 383 IMPORTANT: if you fix the issue, please add the following tag to the commit: 384 Reported-by: syzbot+%[1]v@testapp.appspotmail.com 385 386 report1 387 388 --- 389 This report is generated by a bot. It may contain errors. 390 See https://goo.gl/tpsmEJ for more information about syzbot. 391 syzbot engineers can be reached at syzkaller@googlegroups.com. 392 393 syzbot will keep track of this issue. See: 394 https://goo.gl/tpsmEJ#status for how to communicate with syzbot. 395 396 If the report is already addressed, let syzbot know by replying with: 397 #syz fix: exact-commit-title 398 399 If you want to overwrite report's subsystems, reply with: 400 #syz set subsystems: new-subsystem 401 (See the list of subsystem names on the web dashboard) 402 403 If the report is a duplicate of another one, reply with: 404 #syz dup: exact-subject-of-another-report 405 406 If you want to undo deduplication, reply with: 407 #syz undup`, 408 extBugID, crashLogLink, kernelConfigLink)) 409 c.checkURLContents(crashLogLink, crash.Log) 410 c.checkURLContents(kernelConfigLink, build.KernelConfig) 411 412 // We query the needed assets. We need all 2. 413 needed, err := c.client2.NeededAssetsList() 414 c.expectOK(err) 415 sort.Strings(needed.DownloadURLs) 416 allDownloadURLs := []string{ 417 "http://google.com/disk_image", 418 "http://google.com/disk_image2", 419 } 420 c.expectEQ(needed.DownloadURLs, allDownloadURLs) 421 422 // Invalidate the bug. 423 c.client.updateBug(extBugID, dashapi.BugStatusInvalid, "") 424 _, err = c.GET("/cron/deprecate_assets") 425 c.expectOK(err) 426 427 // Query the needed assets once more, so far there should be no change. 428 needed, err = c.client2.NeededAssetsList() 429 c.expectOK(err) 430 sort.Strings(needed.DownloadURLs) 431 c.expectEQ(needed.DownloadURLs, allDownloadURLs) 432 433 // Skip one month and deprecate assets. 434 c.advanceTime(time.Hour * 24 * 31) 435 _, err = c.GET("/cron/deprecate_assets") 436 c.expectOK(err) 437 438 // Nothing should have been persisted. 439 needed, err = c.client2.NeededAssetsList() 440 c.expectOK(err) 441 c.expectEQ(needed.DownloadURLs, []string{}) 442 }