github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/environs/sync/sync_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package sync_test 5 6 import ( 7 "bytes" 8 "compress/gzip" 9 "context" 10 "fmt" 11 "io" 12 "net/http" 13 "os" 14 "os/exec" 15 "path" 16 "path/filepath" 17 18 "github.com/juju/errors" 19 jujuhttp "github.com/juju/http/v2" 20 jujutesting "github.com/juju/testing" 21 jc "github.com/juju/testing/checkers" 22 "github.com/juju/utils/v3/tar" 23 "github.com/juju/version/v2" 24 "go.uber.org/mock/gomock" 25 gc "gopkg.in/check.v1" 26 27 "github.com/juju/juju/core/arch" 28 corebase "github.com/juju/juju/core/base" 29 coreos "github.com/juju/juju/core/os" 30 "github.com/juju/juju/core/os/ostype" 31 "github.com/juju/juju/environs/filestorage" 32 "github.com/juju/juju/environs/simplestreams" 33 "github.com/juju/juju/environs/storage" 34 "github.com/juju/juju/environs/sync" 35 envtesting "github.com/juju/juju/environs/testing" 36 envtools "github.com/juju/juju/environs/tools" 37 toolstesting "github.com/juju/juju/environs/tools/testing" 38 "github.com/juju/juju/juju/names" 39 coretesting "github.com/juju/juju/testing" 40 coretools "github.com/juju/juju/tools" 41 jujuversion "github.com/juju/juju/version" 42 ) 43 44 type syncSuite struct { 45 coretesting.FakeJujuXDGDataHomeSuite 46 envtesting.ToolsFixture 47 storage storage.Storage 48 localStorage string 49 } 50 51 var _ = gc.Suite(&syncSuite{}) 52 var _ = gc.Suite(&uploadSuite{}) 53 var _ = gc.Suite(&badBuildSuite{}) 54 55 func (s *syncSuite) setUpTest(c *gc.C) { 56 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 57 s.ToolsFixture.SetUpTest(c) 58 59 // It's important that this be v1.8.x to match the test data. 60 s.PatchValue(&jujuversion.Current, version.MustParse("1.8.3")) 61 62 // Create a source storage. 63 baseDir := c.MkDir() 64 stor, err := filestorage.NewFileStorageWriter(baseDir) 65 c.Assert(err, jc.ErrorIsNil) 66 s.storage = stor 67 68 // Create a local tools directory. 69 s.localStorage = c.MkDir() 70 71 // Populate both local and default tools locations with the public tools. 72 versionStrings := make([]string, len(vAll)) 73 for i, vers := range vAll { 74 versionStrings[i] = vers.String() 75 } 76 toolstesting.MakeTools(c, baseDir, "released", versionStrings) 77 toolstesting.MakeTools(c, s.localStorage, "released", versionStrings) 78 79 // Switch the default tools location. 80 baseURL, err := s.storage.URL(storage.BaseToolsPath) 81 c.Assert(err, jc.ErrorIsNil) 82 s.PatchValue(&envtools.DefaultBaseURL, baseURL) 83 } 84 85 func (s *syncSuite) tearDownTest(c *gc.C) { 86 s.ToolsFixture.TearDownTest(c) 87 s.FakeJujuXDGDataHomeSuite.TearDownTest(c) 88 } 89 90 var tests = []struct { 91 description string 92 ctx *sync.SyncContext 93 source bool 94 tools []version.Binary 95 major int 96 minor int 97 }{ 98 { 99 description: "copy newest from the filesystem", 100 ctx: &sync.SyncContext{ 101 ChosenVersion: version.MustParse("1.8.0"), 102 }, 103 source: true, 104 tools: v180all, 105 }, 106 { 107 description: "copy newest from the dummy model", 108 ctx: &sync.SyncContext{ 109 ChosenVersion: version.MustParse("1.8.0"), 110 }, 111 tools: v180all, 112 }, 113 { 114 description: "copy matching dev from the dummy model", 115 ctx: &sync.SyncContext{ 116 ChosenVersion: version.MustParse("1.9.0"), 117 }, 118 tools: v190all, 119 }, 120 { 121 description: "copy matching version from the dummy model", 122 ctx: &sync.SyncContext{ 123 ChosenVersion: version.MustParse("3.2.0"), 124 }, 125 tools: []version.Binary{v320u64}, 126 }, 127 { 128 description: "copy matching major, minor dev from the dummy model", 129 ctx: &sync.SyncContext{ 130 ChosenVersion: version.MustParse("3.1.0"), 131 }, 132 tools: []version.Binary{v310u64}, 133 }, 134 } 135 136 func (s *syncSuite) TestSyncing(c *gc.C) { 137 for i, test := range tests { 138 // Perform all tests in a "clean" environment. 139 func() { 140 s.setUpTest(c) 141 defer s.tearDownTest(c) 142 143 c.Logf("test %d: %s", i, test.description) 144 145 if test.source { 146 test.ctx.Source = s.localStorage 147 } 148 if test.ctx.ChosenVersion != version.Zero { 149 jujuversion.Current = test.ctx.ChosenVersion 150 } 151 152 uploader := fakeToolsUploader{ 153 uploaded: make(map[version.Binary]bool), 154 } 155 test.ctx.TargetToolsFinder = mockToolsFinder{} 156 test.ctx.TargetToolsUploader = &uploader 157 158 err := sync.SyncTools(test.ctx) 159 c.Assert(err, jc.ErrorIsNil) 160 161 ds, err := sync.SelectSourceDatasource(test.ctx) 162 c.Assert(err, jc.ErrorIsNil) 163 164 // This data source does not require to contain signed data. 165 // However, it may still contain it. 166 // Since we will always try to read signed data first, 167 // we want to be able to try to read this signed data 168 // with public key with Juju-known public key for tools. 169 // Bugs #1542127, #1542131 170 c.Assert(ds.PublicSigningKey(), gc.Not(gc.Equals), "") 171 172 var uploaded []version.Binary 173 for v := range uploader.uploaded { 174 uploaded = append(uploaded, v) 175 } 176 c.Assert(uploaded, jc.SameContents, test.tools) 177 }() 178 } 179 } 180 181 // regression test for https://pad.lv/2029881 182 func (s *syncSuite) TestSyncToolsOldPatchVersion(c *gc.C) { 183 s.setUpTest(c) 184 defer s.tearDownTest(c) 185 186 // Add some extra tools for the newer patch versions 187 toolstesting.MakeTools(c, s.localStorage, "released", []string{"1.8.3-ubuntu-amd64"}) 188 189 err := sync.SyncTools(&sync.SyncContext{ 190 Source: s.localStorage, 191 // Request an older patch version of the current series (1.8.x) 192 ChosenVersion: version.MustParse("1.8.0"), 193 TargetToolsUploader: &fakeToolsUploader{ 194 uploaded: make(map[version.Binary]bool), 195 }, 196 }) 197 c.Assert(err, jc.ErrorIsNil) 198 } 199 200 type fakeToolsUploader struct { 201 uploaded map[version.Binary]bool 202 } 203 204 func (u *fakeToolsUploader) UploadTools(_, _ string, tools *coretools.Tools, _ []byte) error { 205 u.uploaded[tools.Version] = true 206 return nil 207 } 208 209 var ( 210 v100u64 = version.MustParseBinary("1.0.0-ubuntu-amd64") 211 v100u32 = version.MustParseBinary("1.0.0-ubuntu-arm64") 212 v100all = []version.Binary{v100u64, v100u32} 213 v180u64 = version.MustParseBinary("1.8.0-ubuntu-amd64") 214 v180u32 = version.MustParseBinary("1.8.0-ubuntu-arm64") 215 v180all = []version.Binary{v180u64, v180u32} 216 v190u64 = version.MustParseBinary("1.9.0-ubuntu-amd64") 217 v190u32 = version.MustParseBinary("1.9.0-ubuntu-arm64") 218 v190all = []version.Binary{v190u64, v190u32} 219 v1all = append(append(v100all, v180all...), v190all...) 220 v200u64 = version.MustParseBinary("2.0.0-ubuntu-amd64") 221 v310u64 = version.MustParseBinary("3.1.0-ubuntu-amd64") 222 v320u64 = version.MustParseBinary("3.2.0-ubuntu-amd64") 223 vAll = append(append(v1all, v200u64), v310u64, v320u64) 224 ) 225 226 type uploadSuite struct { 227 coretesting.FakeJujuXDGDataHomeSuite 228 envtesting.ToolsFixture 229 targetStorage storage.Storage 230 } 231 232 func (s *uploadSuite) SetUpTest(c *gc.C) { 233 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 234 s.ToolsFixture.SetUpTest(c) 235 s.PatchValue(&corebase.UbuntuDistroInfo, "/path/notexists") 236 237 // Create a target storage. 238 stor, err := filestorage.NewFileStorageWriter(c.MkDir()) 239 c.Assert(err, jc.ErrorIsNil) 240 s.targetStorage = stor 241 } 242 243 func (s *uploadSuite) patchBundleTools(c *gc.C, v version.Number) { 244 // Mock out building of tools. Sync should not care about the contents 245 // of tools archives, other than that they hash correctly. 246 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(v)) 247 } 248 249 func (s *uploadSuite) assertEqualsCurrentVersion(c *gc.C, v version.Binary) { 250 c.Assert(v, gc.Equals, coretesting.CurrentVersion()) 251 } 252 253 func (s *uploadSuite) TearDownTest(c *gc.C) { 254 s.ToolsFixture.TearDownTest(c) 255 s.FakeJujuXDGDataHomeSuite.TearDownTest(c) 256 } 257 258 func (s *uploadSuite) TestUpload(c *gc.C) { 259 ctrl := gomock.NewController(c) 260 defer ctrl.Finish() 261 262 ss := NewMockSimplestreamsFetcher(ctrl) 263 ss.EXPECT().GetMetadata(gomock.Any(), gomock.Any()).AnyTimes() 264 265 forceVersion := jujuversion.Current 266 s.patchBundleTools(c, forceVersion) 267 t, err := sync.Upload(ss, s.targetStorage, "released", 268 func(version.Number) version.Number { return forceVersion }, 269 ) 270 c.Assert(err, jc.ErrorIsNil) 271 s.assertEqualsCurrentVersion(c, t.Version) 272 c.Assert(t.URL, gc.Not(gc.Equals), "") 273 hostOSType := coreos.HostOSTypeName() 274 s.assertUploadedTools(c, t, []string{hostOSType}, "released") 275 } 276 277 func (s *uploadSuite) TestUploadAndForceVersion(c *gc.C) { 278 ctrl := gomock.NewController(c) 279 defer ctrl.Finish() 280 281 ss := NewMockSimplestreamsFetcher(ctrl) 282 ss.EXPECT().GetMetadata(gomock.Any(), gomock.Any()).AnyTimes() 283 284 forceVersion := jujuversion.Current 285 forceVersion.Patch++ 286 s.patchBundleTools(c, forceVersion) 287 t, err := sync.Upload(ss, s.targetStorage, "released", 288 func(version.Number) version.Number { return forceVersion }, 289 ) 290 c.Assert(err, jc.ErrorIsNil) 291 c.Assert(t.Version, gc.Equals, coretesting.CurrentVersion()) 292 } 293 294 func (s *uploadSuite) TestSyncTools(c *gc.C) { 295 ctrl := gomock.NewController(c) 296 defer ctrl.Finish() 297 298 ss := NewMockSimplestreamsFetcher(ctrl) 299 ss.EXPECT().GetMetadata(gomock.Any(), gomock.Any()).AnyTimes() 300 301 forceVersion := jujuversion.Current 302 forceVersion.Patch++ 303 s.patchBundleTools(c, forceVersion) 304 builtTools, err := sync.BuildAgentTarball(true, "released", 305 func(version.Number) version.Number { return forceVersion }, 306 ) 307 c.Assert(err, jc.ErrorIsNil) 308 t, err := sync.SyncBuiltTools(ss, s.targetStorage, "released", builtTools) 309 c.Assert(err, jc.ErrorIsNil) 310 s.assertEqualsCurrentVersion(c, t.Version) 311 c.Assert(t.URL, gc.Not(gc.Equals), "") 312 } 313 314 func (s *uploadSuite) TestSyncAndForceVersion(c *gc.C) { 315 ctrl := gomock.NewController(c) 316 defer ctrl.Finish() 317 318 ss := NewMockSimplestreamsFetcher(ctrl) 319 ss.EXPECT().GetMetadata(gomock.Any(), gomock.Any()).AnyTimes() 320 321 forceVersion := jujuversion.Current 322 forceVersion.Patch++ 323 s.patchBundleTools(c, forceVersion) 324 builtTools, err := sync.BuildAgentTarball(true, "released", 325 func(version.Number) version.Number { return forceVersion }, 326 ) 327 c.Assert(err, jc.ErrorIsNil) 328 t, err := sync.SyncBuiltTools(ss, s.targetStorage, "released", builtTools) 329 c.Assert(err, jc.ErrorIsNil) 330 // Reported version from build call matches the real jujud version. 331 c.Assert(t.Version, gc.Equals, coretesting.CurrentVersion()) 332 } 333 334 func (s *uploadSuite) assertUploadedTools(c *gc.C, t *coretools.Tools, expectOSTypes []string, stream string) { 335 ctrl := gomock.NewController(c) 336 defer ctrl.Finish() 337 338 ss := NewMockSimplestreamsFetcher(ctrl) 339 ss.EXPECT().GetMetadata(gomock.Any(), gomock.Any()).AnyTimes() 340 341 s.assertEqualsCurrentVersion(c, t.Version) 342 expectRaw := downloadToolsRaw(c, t) 343 344 list, err := envtools.ReadList(s.targetStorage, stream, jujuversion.Current.Major, jujuversion.Current.Minor) 345 c.Assert(err, jc.ErrorIsNil) 346 c.Assert(list.AllReleases(), jc.SameContents, expectOSTypes) 347 for _, t := range list { 348 c.Logf("checking %s", t.URL) 349 c.Assert(t.Version.Number, gc.Equals, jujuversion.Current) 350 actualRaw := downloadToolsRaw(c, t) 351 c.Assert(string(actualRaw), gc.Equals, string(expectRaw)) 352 } 353 metadata, err := envtools.ReadMetadata(ss, s.targetStorage, stream) 354 c.Assert(err, jc.ErrorIsNil) 355 c.Assert(metadata, gc.HasLen, 0) 356 } 357 358 // downloadToolsRaw downloads the supplied tools and returns the raw bytes. 359 func downloadToolsRaw(c *gc.C, t *coretools.Tools) []byte { 360 client := jujuhttp.NewClient() 361 resp, err := client.Get(context.TODO(), t.URL) 362 c.Assert(err, jc.ErrorIsNil) 363 defer func() { _ = resp.Body.Close() }() 364 c.Assert(resp.StatusCode, gc.Equals, http.StatusOK) 365 var buf bytes.Buffer 366 _, err = io.Copy(&buf, resp.Body) 367 c.Assert(err, jc.ErrorIsNil) 368 return buf.Bytes() 369 } 370 371 func bundleTools(c *gc.C) (version.Binary, bool, string, error) { 372 f, err := os.CreateTemp("", "juju-tgz") 373 c.Assert(err, jc.ErrorIsNil) 374 defer func() { _ = f.Close() }() 375 defer func() { _ = os.Remove(f.Name()) }() 376 377 tvers, _, official, sha256hash, err := envtools.BundleTools(true, f, 378 func(version.Number) version.Number { return jujuversion.Current }, 379 ) 380 return tvers, official, sha256hash, err 381 } 382 383 type badBuildSuite struct { 384 jujutesting.LoggingSuite 385 jujutesting.CleanupSuite 386 envtesting.ToolsFixture 387 jujutesting.PatchExecHelper 388 } 389 390 var badGo = ` 391 #!/bin/bash --norc 392 exit 1 393 `[1:] 394 395 func (s *badBuildSuite) SetUpSuite(c *gc.C) { 396 s.CleanupSuite.SetUpSuite(c) 397 s.LoggingSuite.SetUpSuite(c) 398 } 399 400 func (s *badBuildSuite) TearDownSuite(c *gc.C) { 401 s.LoggingSuite.TearDownSuite(c) 402 s.CleanupSuite.TearDownSuite(c) 403 } 404 405 func (s *badBuildSuite) SetUpTest(c *gc.C) { 406 s.CleanupSuite.SetUpTest(c) 407 s.LoggingSuite.SetUpTest(c) 408 s.ToolsFixture.SetUpTest(c) 409 410 // Mock go cmd 411 testPath := c.MkDir() 412 s.PatchEnvPathPrepend(testPath) 413 path := filepath.Join(testPath, "go") 414 err := os.WriteFile(path, []byte(badGo), 0755) 415 c.Assert(err, jc.ErrorIsNil) 416 417 // Check mocked go cmd errors 418 out, err := exec.Command("go").CombinedOutput() 419 c.Assert(err, gc.ErrorMatches, "exit status 1") 420 c.Assert(string(out), gc.Equals, "") 421 } 422 423 func (s *badBuildSuite) TearDownTest(c *gc.C) { 424 s.ToolsFixture.TearDownTest(c) 425 s.LoggingSuite.TearDownTest(c) 426 s.CleanupSuite.TearDownTest(c) 427 } 428 429 func (s *badBuildSuite) assertEqualsCurrentVersion(c *gc.C, v version.Binary) { 430 current := coretesting.CurrentVersion() 431 c.Assert(v, gc.Equals, current) 432 } 433 434 func (s *badBuildSuite) TestBundleToolsBadBuild(c *gc.C) { 435 s.patchExecCommand(c) 436 437 // Test that original bundleTools Func fails as expected 438 vers, official, sha256Hash, err := bundleTools(c) 439 c.Assert(vers, gc.DeepEquals, version.Binary{}) 440 c.Assert(official, jc.IsFalse) 441 c.Assert(sha256Hash, gc.Equals, "") 442 c.Assert(err, gc.ErrorMatches, `(?m)cannot build jujud agent binary from source: .*`) 443 444 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(jujuversion.Current)) 445 446 // Test that BundleTools func passes after it is 447 // mocked out 448 vers, official, sha256Hash, err = bundleTools(c) 449 c.Assert(err, jc.ErrorIsNil) 450 c.Assert(vers.Number, gc.Equals, jujuversion.Current) 451 c.Assert(official, jc.IsFalse) 452 c.Assert(sha256Hash, gc.Equals, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855") 453 } 454 455 func (s *badBuildSuite) patchExecCommand(c *gc.C) { 456 execCommand := s.GetExecCommand(jujutesting.PatchExecConfig{ 457 Stdout: coretesting.CurrentVersion().String(), 458 Args: make(chan []string, 2), 459 }) 460 s.PatchValue(&envtools.ExecCommand, execCommand) 461 } 462 463 func (s *badBuildSuite) TestBuildToolsBadBuild(c *gc.C) { 464 s.patchExecCommand(c) 465 466 // Test that original BuildAgentTarball fails 467 builtTools, err := sync.BuildAgentTarball(true, "released", 468 func(version.Number) version.Number { return version.Zero }, 469 ) 470 c.Assert(err, gc.ErrorMatches, `(?m)cannot build jujud agent binary from source: .*`) 471 c.Assert(builtTools, gc.IsNil) 472 473 // Test that BuildAgentTarball func passes after BundleTools func is 474 // mocked out 475 forceVersion := coretesting.CurrentVersion().Number 476 s.PatchValue(&envtools.BundleTools, toolstesting.GetMockBundleTools(forceVersion)) 477 builtTools, err = sync.BuildAgentTarball(true, "released", 478 func(version.Number) version.Number { return forceVersion }, 479 ) 480 s.assertEqualsCurrentVersion(c, builtTools.Version) 481 c.Assert(err, jc.ErrorIsNil) 482 } 483 484 func (s *badBuildSuite) TestBuildToolsNoBinaryAvailable(c *gc.C) { 485 s.patchExecCommand(c) 486 487 builtTools, err := sync.BuildAgentTarball(false, "released", 488 func(version.Number) version.Number { return version.Zero }, 489 ) 490 c.Assert(err, gc.ErrorMatches, `no prepackaged agent available and no jujud binary can be found`) 491 c.Assert(builtTools, gc.IsNil) 492 } 493 494 func (s *uploadSuite) TestMockBundleTools(c *gc.C) { 495 var ( 496 writer io.Writer 497 forceVersion version.Number 498 n int 499 p bytes.Buffer 500 ) 501 p.WriteString("Hello World") 502 503 s.PatchValue(&envtools.BundleTools, 504 func( 505 build bool, writerArg io.Writer, 506 getForceVersion func(version.Number) version.Number, 507 ) (vers version.Binary, fVersion version.Number, official bool, sha256Hash string, err error) { 508 c.Assert(build, jc.IsTrue) 509 writer = writerArg 510 n, err = writer.Write(p.Bytes()) 511 c.Assert(err, jc.ErrorIsNil) 512 forceVersion = getForceVersion(version.Zero) 513 fVersion = forceVersion 514 vers.Number = jujuversion.Current 515 return 516 }, 517 ) 518 519 _, err := sync.BuildAgentTarball(true, "released", 520 func(version.Number) version.Number { return jujuversion.Current }, 521 ) 522 c.Assert(err, jc.ErrorIsNil) 523 c.Assert(forceVersion, gc.Equals, jujuversion.Current) 524 c.Assert(writer, gc.NotNil) 525 c.Assert(n, gc.Equals, len(p.Bytes())) 526 } 527 528 func (s *uploadSuite) TestMockBuildTools(c *gc.C) { 529 checkTools := func(tools *sync.BuiltAgent, vers version.Binary) { 530 c.Check(tools.StorageName, gc.Equals, "name") 531 c.Check(tools.Version, jc.DeepEquals, vers) 532 533 f, err := os.Open(filepath.Join(tools.Dir, "name")) 534 c.Assert(err, jc.ErrorIsNil) 535 defer f.Close() 536 537 gzr, err := gzip.NewReader(f) 538 c.Assert(err, jc.ErrorIsNil) 539 540 _, tr, err := tar.FindFile(gzr, names.Jujud) 541 c.Assert(err, jc.ErrorIsNil) 542 543 content, err := io.ReadAll(tr) 544 c.Assert(err, jc.ErrorIsNil) 545 c.Check(string(content), gc.Equals, fmt.Sprintf("jujud contents %s", vers)) 546 } 547 548 current := version.MustParseBinary("1.9.1-ubuntu-amd64") 549 s.PatchValue(&jujuversion.Current, current.Number) 550 s.PatchValue(&arch.HostArch, func() string { return current.Arch }) 551 s.PatchValue(&coreos.HostOS, func() ostype.OSType { return ostype.Ubuntu }) 552 buildToolsFunc := toolstesting.GetMockBuildTools(c) 553 builtTools, err := buildToolsFunc(true, "released", 554 func(version.Number) version.Number { return jujuversion.Current }, 555 ) 556 c.Assert(err, jc.ErrorIsNil) 557 checkTools(builtTools, current) 558 559 vers := version.MustParseBinary("1.5.3-ubuntu-amd64") 560 builtTools, err = buildToolsFunc(true, "released", 561 func(version.Number) version.Number { return vers.Number }, 562 ) 563 c.Assert(err, jc.ErrorIsNil) 564 checkTools(builtTools, vers) 565 } 566 567 func (s *uploadSuite) TestStorageToolsUploaderWriteMirrors(c *gc.C) { 568 s.testStorageToolsUploaderWriteMirrors(c, envtools.WriteMirrors) 569 } 570 571 func (s *uploadSuite) TestStorageToolsUploaderDontWriteMirrors(c *gc.C) { 572 s.testStorageToolsUploaderWriteMirrors(c, envtools.DoNotWriteMirrors) 573 } 574 575 func (s *uploadSuite) testStorageToolsUploaderWriteMirrors(c *gc.C, writeMirrors envtools.ShouldWriteMirrors) { 576 ctrl := gomock.NewController(c) 577 defer ctrl.Finish() 578 579 ss := NewMockSimplestreamsFetcher(ctrl) 580 ss.EXPECT().GetMetadata(gomock.Any(), gomock.Any()).AnyTimes() 581 582 storageDir := c.MkDir() 583 stor, err := filestorage.NewFileStorageWriter(storageDir) 584 c.Assert(err, jc.ErrorIsNil) 585 586 uploader := &sync.StorageToolsUploader{ 587 Fetcher: ss, 588 Storage: stor, 589 WriteMetadata: true, 590 WriteMirrors: writeMirrors, 591 } 592 593 err = uploader.UploadTools( 594 "released", 595 "released", 596 &coretools.Tools{ 597 Version: coretesting.CurrentVersion(), 598 Size: 7, 599 SHA256: "ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73", 600 }, []byte("content")) 601 c.Assert(err, jc.ErrorIsNil) 602 603 mirrorsPath := simplestreams.MirrorsPath(envtools.StreamsVersionV1) + simplestreams.UnsignedSuffix 604 r, err := stor.Get(path.Join(storage.BaseToolsPath, mirrorsPath)) 605 if writeMirrors == envtools.WriteMirrors { 606 c.Assert(err, jc.ErrorIsNil) 607 data, err := io.ReadAll(r) 608 r.Close() 609 c.Assert(err, jc.ErrorIsNil) 610 c.Assert(string(data), jc.Contains, `"mirrors":`) 611 } else { 612 c.Assert(err, jc.Satisfies, errors.IsNotFound) 613 } 614 } 615 616 type mockToolsFinder struct{} 617 618 func (mockToolsFinder) FindTools(major int, stream string) (coretools.List, error) { 619 return nil, coretools.ErrNoMatches 620 }