github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/providercache/installer_test.go (about) 1 package providercache 2 3 import ( 4 "context" 5 "encoding/json" 6 "log" 7 "net/http" 8 "net/http/httptest" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/apparentlymart/go-versions/versions" 14 "github.com/apparentlymart/go-versions/versions/constraints" 15 "github.com/davecgh/go-spew/spew" 16 "github.com/google/go-cmp/cmp" 17 svchost "github.com/hashicorp/terraform-svchost" 18 "github.com/hashicorp/terraform-svchost/disco" 19 20 "github.com/eliastor/durgaform/internal/addrs" 21 "github.com/eliastor/durgaform/internal/depsfile" 22 "github.com/eliastor/durgaform/internal/getproviders" 23 ) 24 25 func TestEnsureProviderVersions(t *testing.T) { 26 // This is a sort of hybrid between table-driven and imperative-style 27 // testing, because the overall sequence of steps is the same for all 28 // of the test cases but the setup and verification have enough different 29 // permutations that it ends up being more concise to express them as 30 // normal code. 31 type Test struct { 32 Source getproviders.Source 33 Prepare func(*testing.T, *Installer, *Dir) 34 LockFile string 35 Reqs getproviders.Requirements 36 Mode InstallMode 37 Check func(*testing.T, *Dir, *depsfile.Locks) 38 WantErr string 39 WantEvents func(*Installer, *Dir) map[addrs.Provider][]*testInstallerEventLogItem 40 } 41 42 // noProvider is just the zero value of addrs.Provider, which we're 43 // using in this test as the key for installer events that are not 44 // specific to a particular provider. 45 var noProvider addrs.Provider 46 beepProvider := addrs.MustParseProviderSourceString("example.com/foo/beep") 47 beepProviderDir := getproviders.PackageLocalDir("testdata/beep-provider") 48 fakePlatform := getproviders.Platform{OS: "bleep", Arch: "bloop"} 49 wrongPlatform := getproviders.Platform{OS: "wrong", Arch: "wrong"} 50 beepProviderHash := getproviders.HashScheme1.New("2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=") 51 durgaformProvider := addrs.MustParseProviderSourceString("terraform.io/builtin/terraform") 52 53 tests := map[string]Test{ 54 "no dependencies": { 55 Mode: InstallNewProvidersOnly, 56 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 57 if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { 58 t.Errorf("unexpected cache directory entries\n%s", spew.Sdump(allCached)) 59 } 60 if allLocked := locks.AllProviders(); len(allLocked) != 0 { 61 t.Errorf("unexpected provider lock entries\n%s", spew.Sdump(allLocked)) 62 } 63 }, 64 WantEvents: func(*Installer, *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 65 return map[addrs.Provider][]*testInstallerEventLogItem{ 66 noProvider: { 67 { 68 Event: "PendingProviders", 69 Args: map[addrs.Provider]getproviders.VersionConstraints(nil), 70 }, 71 }, 72 } 73 }, 74 }, 75 "successful initial install of one provider": { 76 Source: getproviders.NewMockSource( 77 []getproviders.PackageMeta{ 78 { 79 Provider: beepProvider, 80 Version: getproviders.MustParseVersion("1.0.0"), 81 TargetPlatform: fakePlatform, 82 Location: beepProviderDir, 83 }, 84 { 85 Provider: beepProvider, 86 Version: getproviders.MustParseVersion("2.0.0"), 87 TargetPlatform: fakePlatform, 88 Location: beepProviderDir, 89 }, 90 { 91 Provider: beepProvider, 92 Version: getproviders.MustParseVersion("2.1.0"), 93 TargetPlatform: fakePlatform, 94 Location: beepProviderDir, 95 }, 96 }, 97 nil, 98 ), 99 Mode: InstallNewProvidersOnly, 100 Reqs: getproviders.Requirements{ 101 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 102 }, 103 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 104 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 105 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 106 } 107 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 108 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 109 } 110 111 gotLock := locks.Provider(beepProvider) 112 wantLock := depsfile.NewProviderLock( 113 beepProvider, 114 getproviders.MustParseVersion("2.1.0"), 115 getproviders.MustParseVersionConstraints(">= 2.0.0"), 116 []getproviders.Hash{beepProviderHash}, 117 ) 118 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 119 t.Errorf("wrong lock entry\n%s", diff) 120 } 121 122 gotEntry := dir.ProviderLatestVersion(beepProvider) 123 wantEntry := &CachedProvider{ 124 Provider: beepProvider, 125 Version: getproviders.MustParseVersion("2.1.0"), 126 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 127 } 128 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 129 t.Errorf("wrong cache entry\n%s", diff) 130 } 131 }, 132 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 133 return map[addrs.Provider][]*testInstallerEventLogItem{ 134 noProvider: { 135 { 136 Event: "PendingProviders", 137 Args: map[addrs.Provider]getproviders.VersionConstraints{ 138 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 139 }, 140 }, 141 { 142 Event: "ProvidersFetched", 143 Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ 144 beepProvider: nil, 145 }, 146 }, 147 }, 148 beepProvider: { 149 { 150 Event: "QueryPackagesBegin", 151 Provider: beepProvider, 152 Args: struct { 153 Constraints string 154 Locked bool 155 }{">= 2.0.0", false}, 156 }, 157 { 158 Event: "QueryPackagesSuccess", 159 Provider: beepProvider, 160 Args: "2.1.0", 161 }, 162 { 163 Event: "FetchPackageMeta", 164 Provider: beepProvider, 165 Args: "2.1.0", 166 }, 167 { 168 Event: "FetchPackageBegin", 169 Provider: beepProvider, 170 Args: struct { 171 Version string 172 Location getproviders.PackageLocation 173 }{"2.1.0", beepProviderDir}, 174 }, 175 { 176 Event: "ProvidersLockUpdated", 177 Provider: beepProvider, 178 Args: struct { 179 Version string 180 Local []getproviders.Hash 181 Signed []getproviders.Hash 182 Prior []getproviders.Hash 183 }{ 184 "2.1.0", 185 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 186 nil, 187 nil, 188 }, 189 }, 190 { 191 Event: "FetchPackageSuccess", 192 Provider: beepProvider, 193 Args: struct { 194 Version string 195 LocalDir string 196 AuthResult string 197 }{ 198 "2.1.0", 199 filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 200 "unauthenticated", 201 }, 202 }, 203 }, 204 } 205 }, 206 }, 207 "successful initial install of one provider through a cold global cache": { 208 Source: getproviders.NewMockSource( 209 []getproviders.PackageMeta{ 210 { 211 Provider: beepProvider, 212 Version: getproviders.MustParseVersion("2.0.0"), 213 TargetPlatform: fakePlatform, 214 Location: beepProviderDir, 215 }, 216 { 217 Provider: beepProvider, 218 Version: getproviders.MustParseVersion("2.1.0"), 219 TargetPlatform: fakePlatform, 220 Location: beepProviderDir, 221 }, 222 }, 223 nil, 224 ), 225 Prepare: func(t *testing.T, inst *Installer, dir *Dir) { 226 globalCacheDirPath := tmpDir(t) 227 globalCacheDir := NewDirWithPlatform(globalCacheDirPath, fakePlatform) 228 inst.SetGlobalCacheDir(globalCacheDir) 229 }, 230 Mode: InstallNewProvidersOnly, 231 Reqs: getproviders.Requirements{ 232 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 233 }, 234 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 235 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 236 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 237 } 238 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 239 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 240 } 241 242 gotLock := locks.Provider(beepProvider) 243 wantLock := depsfile.NewProviderLock( 244 beepProvider, 245 getproviders.MustParseVersion("2.1.0"), 246 getproviders.MustParseVersionConstraints(">= 2.0.0"), 247 []getproviders.Hash{beepProviderHash}, 248 ) 249 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 250 t.Errorf("wrong lock entry\n%s", diff) 251 } 252 253 gotEntry := dir.ProviderLatestVersion(beepProvider) 254 wantEntry := &CachedProvider{ 255 Provider: beepProvider, 256 Version: getproviders.MustParseVersion("2.1.0"), 257 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 258 } 259 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 260 t.Errorf("wrong cache entry\n%s", diff) 261 } 262 }, 263 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 264 return map[addrs.Provider][]*testInstallerEventLogItem{ 265 noProvider: { 266 { 267 Event: "PendingProviders", 268 Args: map[addrs.Provider]getproviders.VersionConstraints{ 269 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 270 }, 271 }, 272 { 273 Event: "ProvidersFetched", 274 Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ 275 beepProvider: nil, 276 }, 277 }, 278 }, 279 beepProvider: { 280 { 281 Event: "QueryPackagesBegin", 282 Provider: beepProvider, 283 Args: struct { 284 Constraints string 285 Locked bool 286 }{">= 2.0.0", false}, 287 }, 288 { 289 Event: "QueryPackagesSuccess", 290 Provider: beepProvider, 291 Args: "2.1.0", 292 }, 293 { 294 Event: "FetchPackageMeta", 295 Provider: beepProvider, 296 Args: "2.1.0", 297 }, 298 { 299 Event: "FetchPackageBegin", 300 Provider: beepProvider, 301 Args: struct { 302 Version string 303 Location getproviders.PackageLocation 304 }{"2.1.0", beepProviderDir}, 305 }, 306 { 307 Event: "ProvidersLockUpdated", 308 Provider: beepProvider, 309 Args: struct { 310 Version string 311 Local []getproviders.Hash 312 Signed []getproviders.Hash 313 Prior []getproviders.Hash 314 }{ 315 "2.1.0", 316 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 317 nil, 318 nil, 319 }, 320 }, 321 { 322 Event: "FetchPackageSuccess", 323 Provider: beepProvider, 324 Args: struct { 325 Version string 326 LocalDir string 327 AuthResult string 328 }{ 329 "2.1.0", 330 // NOTE: With global cache enabled, the fetch 331 // goes into the global cache dir and 332 // we then to it from the local cache dir. 333 filepath.Join(inst.globalCacheDir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 334 "unauthenticated", 335 }, 336 }, 337 }, 338 } 339 }, 340 }, 341 "successful initial install of one provider through a warm global cache": { 342 Source: getproviders.NewMockSource( 343 []getproviders.PackageMeta{ 344 { 345 Provider: beepProvider, 346 Version: getproviders.MustParseVersion("2.0.0"), 347 TargetPlatform: fakePlatform, 348 Location: beepProviderDir, 349 }, 350 { 351 Provider: beepProvider, 352 Version: getproviders.MustParseVersion("2.1.0"), 353 TargetPlatform: fakePlatform, 354 Location: beepProviderDir, 355 }, 356 }, 357 nil, 358 ), 359 Prepare: func(t *testing.T, inst *Installer, dir *Dir) { 360 globalCacheDirPath := tmpDir(t) 361 globalCacheDir := NewDirWithPlatform(globalCacheDirPath, fakePlatform) 362 _, err := globalCacheDir.InstallPackage( 363 context.Background(), 364 getproviders.PackageMeta{ 365 Provider: beepProvider, 366 Version: getproviders.MustParseVersion("2.1.0"), 367 TargetPlatform: fakePlatform, 368 Location: beepProviderDir, 369 }, 370 nil, 371 ) 372 if err != nil { 373 t.Fatalf("failed to populate global cache: %s", err) 374 } 375 inst.SetGlobalCacheDir(globalCacheDir) 376 }, 377 Mode: InstallNewProvidersOnly, 378 Reqs: getproviders.Requirements{ 379 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 380 }, 381 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 382 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 383 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 384 } 385 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 386 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 387 } 388 389 gotLock := locks.Provider(beepProvider) 390 wantLock := depsfile.NewProviderLock( 391 beepProvider, 392 getproviders.MustParseVersion("2.1.0"), 393 getproviders.MustParseVersionConstraints(">= 2.0.0"), 394 []getproviders.Hash{beepProviderHash}, 395 ) 396 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 397 t.Errorf("wrong lock entry\n%s", diff) 398 } 399 400 gotEntry := dir.ProviderLatestVersion(beepProvider) 401 wantEntry := &CachedProvider{ 402 Provider: beepProvider, 403 Version: getproviders.MustParseVersion("2.1.0"), 404 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 405 } 406 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 407 t.Errorf("wrong cache entry\n%s", diff) 408 } 409 }, 410 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 411 return map[addrs.Provider][]*testInstallerEventLogItem{ 412 noProvider: { 413 { 414 Event: "PendingProviders", 415 Args: map[addrs.Provider]getproviders.VersionConstraints{ 416 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 417 }, 418 }, 419 }, 420 beepProvider: { 421 { 422 Event: "QueryPackagesBegin", 423 Provider: beepProvider, 424 Args: struct { 425 Constraints string 426 Locked bool 427 }{">= 2.0.0", false}, 428 }, 429 { 430 Event: "QueryPackagesSuccess", 431 Provider: beepProvider, 432 Args: "2.1.0", 433 }, 434 { 435 Event: "LinkFromCacheBegin", 436 Provider: beepProvider, 437 Args: struct { 438 Version string 439 CacheRoot string 440 }{ 441 "2.1.0", 442 inst.globalCacheDir.BasePath(), 443 }, 444 }, 445 { 446 Event: "ProvidersLockUpdated", 447 Provider: beepProvider, 448 Args: struct { 449 Version string 450 Local []getproviders.Hash 451 Signed []getproviders.Hash 452 Prior []getproviders.Hash 453 }{ 454 "2.1.0", 455 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 456 nil, 457 nil, 458 }, 459 }, 460 { 461 Event: "LinkFromCacheSuccess", 462 Provider: beepProvider, 463 Args: struct { 464 Version string 465 LocalDir string 466 }{ 467 "2.1.0", 468 filepath.Join(dir.BasePath(), "/example.com/foo/beep/2.1.0/bleep_bloop"), 469 }, 470 }, 471 }, 472 } 473 }, 474 }, 475 "successful reinstall of one previously-locked provider": { 476 Source: getproviders.NewMockSource( 477 []getproviders.PackageMeta{ 478 { 479 Provider: beepProvider, 480 Version: getproviders.MustParseVersion("1.0.0"), 481 TargetPlatform: fakePlatform, 482 Location: beepProviderDir, 483 }, 484 { 485 Provider: beepProvider, 486 Version: getproviders.MustParseVersion("2.0.0"), 487 TargetPlatform: fakePlatform, 488 Location: beepProviderDir, 489 }, 490 { 491 Provider: beepProvider, 492 Version: getproviders.MustParseVersion("2.1.0"), 493 TargetPlatform: fakePlatform, 494 Location: beepProviderDir, 495 }, 496 }, 497 nil, 498 ), 499 LockFile: ` 500 provider "example.com/foo/beep" { 501 version = "2.0.0" 502 constraints = ">= 2.0.0" 503 hashes = [ 504 "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", 505 ] 506 } 507 `, 508 Mode: InstallNewProvidersOnly, 509 Reqs: getproviders.Requirements{ 510 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 511 }, 512 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 513 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 514 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 515 } 516 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 517 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 518 } 519 520 gotLock := locks.Provider(beepProvider) 521 wantLock := depsfile.NewProviderLock( 522 beepProvider, 523 getproviders.MustParseVersion("2.0.0"), 524 getproviders.MustParseVersionConstraints(">= 2.0.0"), 525 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 526 ) 527 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 528 t.Errorf("wrong lock entry\n%s", diff) 529 } 530 531 gotEntry := dir.ProviderLatestVersion(beepProvider) 532 wantEntry := &CachedProvider{ 533 Provider: beepProvider, 534 Version: getproviders.MustParseVersion("2.0.0"), 535 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.0.0/bleep_bloop"), 536 } 537 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 538 t.Errorf("wrong cache entry\n%s", diff) 539 } 540 }, 541 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 542 return map[addrs.Provider][]*testInstallerEventLogItem{ 543 noProvider: { 544 { 545 Event: "PendingProviders", 546 Args: map[addrs.Provider]getproviders.VersionConstraints{ 547 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 548 }, 549 }, 550 { 551 Event: "ProvidersFetched", 552 Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ 553 beepProvider: nil, 554 }, 555 }, 556 }, 557 beepProvider: { 558 { 559 Event: "QueryPackagesBegin", 560 Provider: beepProvider, 561 Args: struct { 562 Constraints string 563 Locked bool 564 }{">= 2.0.0", true}, 565 }, 566 { 567 Event: "QueryPackagesSuccess", 568 Provider: beepProvider, 569 Args: "2.0.0", 570 }, 571 { 572 Event: "FetchPackageMeta", 573 Provider: beepProvider, 574 Args: "2.0.0", 575 }, 576 { 577 Event: "FetchPackageBegin", 578 Provider: beepProvider, 579 Args: struct { 580 Version string 581 Location getproviders.PackageLocation 582 }{"2.0.0", beepProviderDir}, 583 }, 584 { 585 Event: "ProvidersLockUpdated", 586 Provider: beepProvider, 587 Args: struct { 588 Version string 589 Local []getproviders.Hash 590 Signed []getproviders.Hash 591 Prior []getproviders.Hash 592 }{ 593 "2.0.0", 594 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 595 nil, 596 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 597 }, 598 }, 599 { 600 Event: "FetchPackageSuccess", 601 Provider: beepProvider, 602 Args: struct { 603 Version string 604 LocalDir string 605 AuthResult string 606 }{ 607 "2.0.0", 608 filepath.Join(dir.BasePath(), "example.com/foo/beep/2.0.0/bleep_bloop"), 609 "unauthenticated", 610 }, 611 }, 612 }, 613 } 614 }, 615 }, 616 "skipped install of one previously-locked and installed provider": { 617 Source: getproviders.NewMockSource( 618 []getproviders.PackageMeta{ 619 { 620 Provider: beepProvider, 621 Version: getproviders.MustParseVersion("2.0.0"), 622 TargetPlatform: fakePlatform, 623 Location: beepProviderDir, 624 }, 625 }, 626 nil, 627 ), 628 LockFile: ` 629 provider "example.com/foo/beep" { 630 version = "2.0.0" 631 constraints = ">= 2.0.0" 632 hashes = [ 633 "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", 634 ] 635 } 636 `, 637 Prepare: func(t *testing.T, inst *Installer, dir *Dir) { 638 _, err := dir.InstallPackage( 639 context.Background(), 640 getproviders.PackageMeta{ 641 Provider: beepProvider, 642 Version: getproviders.MustParseVersion("2.0.0"), 643 TargetPlatform: fakePlatform, 644 Location: beepProviderDir, 645 }, 646 nil, 647 ) 648 if err != nil { 649 t.Fatalf("installation to the test dir failed: %s", err) 650 } 651 }, 652 Mode: InstallNewProvidersOnly, 653 Reqs: getproviders.Requirements{ 654 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 655 }, 656 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 657 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 658 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 659 } 660 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 661 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 662 } 663 664 gotLock := locks.Provider(beepProvider) 665 wantLock := depsfile.NewProviderLock( 666 beepProvider, 667 getproviders.MustParseVersion("2.0.0"), 668 getproviders.MustParseVersionConstraints(">= 2.0.0"), 669 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 670 ) 671 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 672 t.Errorf("wrong lock entry\n%s", diff) 673 } 674 675 gotEntry := dir.ProviderLatestVersion(beepProvider) 676 wantEntry := &CachedProvider{ 677 Provider: beepProvider, 678 Version: getproviders.MustParseVersion("2.0.0"), 679 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.0.0/bleep_bloop"), 680 } 681 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 682 t.Errorf("wrong cache entry\n%s", diff) 683 } 684 }, 685 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 686 return map[addrs.Provider][]*testInstallerEventLogItem{ 687 noProvider: { 688 { 689 Event: "PendingProviders", 690 Args: map[addrs.Provider]getproviders.VersionConstraints{ 691 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 692 }, 693 }, 694 }, 695 beepProvider: { 696 { 697 Event: "QueryPackagesBegin", 698 Provider: beepProvider, 699 Args: struct { 700 Constraints string 701 Locked bool 702 }{">= 2.0.0", true}, 703 }, 704 { 705 Event: "QueryPackagesSuccess", 706 Provider: beepProvider, 707 Args: "2.0.0", 708 }, 709 { 710 Event: "ProviderAlreadyInstalled", 711 Provider: beepProvider, 712 Args: versions.Version{Major: 2, Minor: 0, Patch: 0}, 713 }, 714 }, 715 } 716 }, 717 }, 718 "successful upgrade of one previously-locked provider": { 719 Source: getproviders.NewMockSource( 720 []getproviders.PackageMeta{ 721 { 722 Provider: beepProvider, 723 Version: getproviders.MustParseVersion("1.0.0"), 724 TargetPlatform: fakePlatform, 725 Location: beepProviderDir, 726 }, 727 { 728 Provider: beepProvider, 729 Version: getproviders.MustParseVersion("2.0.0"), 730 TargetPlatform: fakePlatform, 731 Location: beepProviderDir, 732 }, 733 { 734 Provider: beepProvider, 735 Version: getproviders.MustParseVersion("2.1.0"), 736 TargetPlatform: fakePlatform, 737 Location: beepProviderDir, 738 }, 739 }, 740 nil, 741 ), 742 LockFile: ` 743 provider "example.com/foo/beep" { 744 version = "2.0.0" 745 constraints = ">= 2.0.0" 746 hashes = [ 747 "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", 748 ] 749 } 750 `, 751 Mode: InstallUpgrades, 752 Reqs: getproviders.Requirements{ 753 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 754 }, 755 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 756 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 757 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 758 } 759 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 760 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 761 } 762 763 gotLock := locks.Provider(beepProvider) 764 wantLock := depsfile.NewProviderLock( 765 beepProvider, 766 getproviders.MustParseVersion("2.1.0"), 767 getproviders.MustParseVersionConstraints(">= 2.0.0"), 768 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 769 ) 770 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 771 t.Errorf("wrong lock entry\n%s", diff) 772 } 773 774 gotEntry := dir.ProviderLatestVersion(beepProvider) 775 wantEntry := &CachedProvider{ 776 Provider: beepProvider, 777 Version: getproviders.MustParseVersion("2.1.0"), 778 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 779 } 780 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 781 t.Errorf("wrong cache entry\n%s", diff) 782 } 783 }, 784 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 785 return map[addrs.Provider][]*testInstallerEventLogItem{ 786 noProvider: { 787 { 788 Event: "PendingProviders", 789 Args: map[addrs.Provider]getproviders.VersionConstraints{ 790 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 791 }, 792 }, 793 { 794 Event: "ProvidersFetched", 795 Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ 796 beepProvider: nil, 797 }, 798 }, 799 }, 800 beepProvider: { 801 { 802 Event: "QueryPackagesBegin", 803 Provider: beepProvider, 804 Args: struct { 805 Constraints string 806 Locked bool 807 }{">= 2.0.0", false}, 808 }, 809 { 810 Event: "QueryPackagesSuccess", 811 Provider: beepProvider, 812 Args: "2.1.0", 813 }, 814 { 815 Event: "FetchPackageMeta", 816 Provider: beepProvider, 817 Args: "2.1.0", 818 }, 819 { 820 Event: "FetchPackageBegin", 821 Provider: beepProvider, 822 Args: struct { 823 Version string 824 Location getproviders.PackageLocation 825 }{"2.1.0", beepProviderDir}, 826 }, 827 { 828 Event: "ProvidersLockUpdated", 829 Provider: beepProvider, 830 Args: struct { 831 Version string 832 Local []getproviders.Hash 833 Signed []getproviders.Hash 834 Prior []getproviders.Hash 835 }{ 836 "2.1.0", 837 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 838 nil, 839 nil, 840 }, 841 }, 842 { 843 Event: "FetchPackageSuccess", 844 Provider: beepProvider, 845 Args: struct { 846 Version string 847 LocalDir string 848 AuthResult string 849 }{ 850 "2.1.0", 851 filepath.Join(dir.BasePath(), "example.com/foo/beep/2.1.0/bleep_bloop"), 852 "unauthenticated", 853 }, 854 }, 855 }, 856 } 857 }, 858 }, 859 "successful install of a built-in provider": { 860 Source: getproviders.NewMockSource( 861 []getproviders.PackageMeta{}, 862 nil, 863 ), 864 Prepare: func(t *testing.T, inst *Installer, dir *Dir) { 865 inst.SetBuiltInProviderTypes([]string{"durgaform"}) 866 }, 867 Mode: InstallNewProvidersOnly, 868 Reqs: getproviders.Requirements{ 869 durgaformProvider: nil, 870 }, 871 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 872 // Built-in providers are neither included in the cache 873 // directory nor mentioned in the lock file, because they 874 // are compiled directly into the Durgaform executable. 875 if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { 876 t.Errorf("wrong number of cache directory entries; want none\n%s", spew.Sdump(allCached)) 877 } 878 if allLocked := locks.AllProviders(); len(allLocked) != 0 { 879 t.Errorf("wrong number of provider lock entries; want none\n%s", spew.Sdump(allLocked)) 880 } 881 }, 882 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 883 return map[addrs.Provider][]*testInstallerEventLogItem{ 884 noProvider: { 885 { 886 Event: "PendingProviders", 887 Args: map[addrs.Provider]getproviders.VersionConstraints{ 888 durgaformProvider: constraints.IntersectionSpec(nil), 889 }, 890 }, 891 }, 892 durgaformProvider: { 893 { 894 Event: "BuiltInProviderAvailable", 895 Provider: durgaformProvider, 896 }, 897 }, 898 } 899 }, 900 }, 901 "remove no-longer-needed provider from lock file": { 902 Source: getproviders.NewMockSource( 903 []getproviders.PackageMeta{ 904 { 905 Provider: beepProvider, 906 Version: getproviders.MustParseVersion("1.0.0"), 907 TargetPlatform: fakePlatform, 908 Location: beepProviderDir, 909 }, 910 }, 911 nil, 912 ), 913 LockFile: ` 914 provider "example.com/foo/beep" { 915 version = "1.0.0" 916 constraints = ">= 1.0.0" 917 hashes = [ 918 "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", 919 ] 920 } 921 provider "example.com/foo/obsolete" { 922 version = "2.0.0" 923 constraints = ">= 2.0.0" 924 hashes = [ 925 "no:irrelevant", 926 ] 927 } 928 `, 929 Mode: InstallNewProvidersOnly, 930 Reqs: getproviders.Requirements{ 931 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 932 }, 933 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 934 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 935 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 936 } 937 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 938 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 939 } 940 941 gotLock := locks.Provider(beepProvider) 942 wantLock := depsfile.NewProviderLock( 943 beepProvider, 944 getproviders.MustParseVersion("1.0.0"), 945 getproviders.MustParseVersionConstraints(">= 1.0.0"), 946 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 947 ) 948 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 949 t.Errorf("wrong lock entry\n%s", diff) 950 } 951 952 gotEntry := dir.ProviderLatestVersion(beepProvider) 953 wantEntry := &CachedProvider{ 954 Provider: beepProvider, 955 Version: getproviders.MustParseVersion("1.0.0"), 956 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/1.0.0/bleep_bloop"), 957 } 958 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 959 t.Errorf("wrong cache entry\n%s", diff) 960 } 961 }, 962 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 963 return map[addrs.Provider][]*testInstallerEventLogItem{ 964 noProvider: { 965 { 966 Event: "PendingProviders", 967 Args: map[addrs.Provider]getproviders.VersionConstraints{ 968 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 969 }, 970 }, 971 { 972 Event: "ProvidersFetched", 973 Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ 974 beepProvider: nil, 975 }, 976 }, 977 }, 978 // Note: intentionally no entries for example.com/foo/obsolete 979 // here, because it's no longer needed and therefore not 980 // installed. 981 beepProvider: { 982 { 983 Event: "QueryPackagesBegin", 984 Provider: beepProvider, 985 Args: struct { 986 Constraints string 987 Locked bool 988 }{">= 1.0.0", true}, 989 }, 990 { 991 Event: "QueryPackagesSuccess", 992 Provider: beepProvider, 993 Args: "1.0.0", 994 }, 995 { 996 Event: "FetchPackageMeta", 997 Provider: beepProvider, 998 Args: "1.0.0", 999 }, 1000 { 1001 Event: "FetchPackageBegin", 1002 Provider: beepProvider, 1003 Args: struct { 1004 Version string 1005 Location getproviders.PackageLocation 1006 }{"1.0.0", beepProviderDir}, 1007 }, 1008 { 1009 Event: "ProvidersLockUpdated", 1010 Provider: beepProvider, 1011 Args: struct { 1012 Version string 1013 Local []getproviders.Hash 1014 Signed []getproviders.Hash 1015 Prior []getproviders.Hash 1016 }{ 1017 "1.0.0", 1018 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 1019 nil, 1020 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 1021 }, 1022 }, 1023 { 1024 Event: "FetchPackageSuccess", 1025 Provider: beepProvider, 1026 Args: struct { 1027 Version string 1028 LocalDir string 1029 AuthResult string 1030 }{ 1031 "1.0.0", 1032 filepath.Join(dir.BasePath(), "example.com/foo/beep/1.0.0/bleep_bloop"), 1033 "unauthenticated", 1034 }, 1035 }, 1036 }, 1037 } 1038 }, 1039 }, 1040 "failed install of a non-existing built-in provider": { 1041 Source: getproviders.NewMockSource( 1042 []getproviders.PackageMeta{}, 1043 nil, 1044 ), 1045 Prepare: func(t *testing.T, inst *Installer, dir *Dir) { 1046 // NOTE: We're intentionally not calling 1047 // inst.SetBuiltInProviderTypes to make the "durgaform" 1048 // built-in provider available here, so requests for it 1049 // should fail. 1050 }, 1051 Mode: InstallNewProvidersOnly, 1052 Reqs: getproviders.Requirements{ 1053 durgaformProvider: nil, 1054 }, 1055 WantErr: `some providers could not be installed: 1056 - durgaform.io/builtin/terraform: this Durgaform release has no built-in provider named "terraform"`, 1057 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1058 return map[addrs.Provider][]*testInstallerEventLogItem{ 1059 noProvider: { 1060 { 1061 Event: "PendingProviders", 1062 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1063 durgaformProvider: constraints.IntersectionSpec(nil), 1064 }, 1065 }, 1066 }, 1067 durgaformProvider: { 1068 { 1069 Event: "BuiltInProviderFailure", 1070 Provider: durgaformProvider, 1071 Args: `this Durgaform release has no built-in provider named "durgaform"`, 1072 }, 1073 }, 1074 } 1075 }, 1076 }, 1077 "failed install when a built-in provider has a version constraint": { 1078 Source: getproviders.NewMockSource( 1079 []getproviders.PackageMeta{}, 1080 nil, 1081 ), 1082 Prepare: func(t *testing.T, inst *Installer, dir *Dir) { 1083 inst.SetBuiltInProviderTypes([]string{"durgaform"}) 1084 }, 1085 Mode: InstallNewProvidersOnly, 1086 Reqs: getproviders.Requirements{ 1087 durgaformProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1088 }, 1089 WantErr: `some providers could not be installed: 1090 - durgaform.io/builtin/terraform: built-in providers do not support explicit version constraints`, 1091 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1092 return map[addrs.Provider][]*testInstallerEventLogItem{ 1093 noProvider: { 1094 { 1095 Event: "PendingProviders", 1096 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1097 durgaformProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1098 }, 1099 }, 1100 }, 1101 durgaformProvider: { 1102 { 1103 Event: "BuiltInProviderFailure", 1104 Provider: durgaformProvider, 1105 Args: `built-in providers do not support explicit version constraints`, 1106 }, 1107 }, 1108 } 1109 }, 1110 }, 1111 "locked version is excluded by new version constraint": { 1112 Source: getproviders.NewMockSource( 1113 []getproviders.PackageMeta{ 1114 { 1115 Provider: beepProvider, 1116 Version: getproviders.MustParseVersion("1.0.0"), 1117 TargetPlatform: fakePlatform, 1118 Location: beepProviderDir, 1119 }, 1120 { 1121 Provider: beepProvider, 1122 Version: getproviders.MustParseVersion("2.0.0"), 1123 TargetPlatform: fakePlatform, 1124 Location: beepProviderDir, 1125 }, 1126 }, 1127 nil, 1128 ), 1129 LockFile: ` 1130 provider "example.com/foo/beep" { 1131 version = "1.0.0" 1132 constraints = ">= 1.0.0" 1133 hashes = [ 1134 "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", 1135 ] 1136 } 1137 `, 1138 Mode: InstallNewProvidersOnly, 1139 Reqs: getproviders.Requirements{ 1140 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 1141 }, 1142 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 1143 if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { 1144 t.Errorf("wrong number of cache directory entries; want none\n%s", spew.Sdump(allCached)) 1145 } 1146 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 1147 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 1148 } 1149 1150 gotLock := locks.Provider(beepProvider) 1151 wantLock := depsfile.NewProviderLock( 1152 beepProvider, 1153 getproviders.MustParseVersion("1.0.0"), 1154 getproviders.MustParseVersionConstraints(">= 1.0.0"), 1155 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 1156 ) 1157 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 1158 t.Errorf("wrong lock entry\n%s", diff) 1159 } 1160 }, 1161 WantErr: `some providers could not be installed: 1162 - example.com/foo/beep: locked provider example.com/foo/beep 1.0.0 does not match configured version constraint >= 2.0.0; must use durgaform init -upgrade to allow selection of new versions`, 1163 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1164 return map[addrs.Provider][]*testInstallerEventLogItem{ 1165 noProvider: { 1166 { 1167 Event: "PendingProviders", 1168 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1169 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 1170 }, 1171 }, 1172 }, 1173 beepProvider: { 1174 { 1175 Event: "QueryPackagesBegin", 1176 Provider: beepProvider, 1177 Args: struct { 1178 Constraints string 1179 Locked bool 1180 }{">= 2.0.0", true}, 1181 }, 1182 { 1183 Event: "QueryPackagesFailure", 1184 Provider: beepProvider, 1185 Args: `locked provider example.com/foo/beep 1.0.0 does not match configured version constraint >= 2.0.0; must use durgaform init -upgrade to allow selection of new versions`, 1186 }, 1187 }, 1188 } 1189 }, 1190 }, 1191 "locked version is no longer available": { 1192 Source: getproviders.NewMockSource( 1193 []getproviders.PackageMeta{ 1194 { 1195 Provider: beepProvider, 1196 Version: getproviders.MustParseVersion("1.0.0"), 1197 TargetPlatform: fakePlatform, 1198 Location: beepProviderDir, 1199 }, 1200 { 1201 Provider: beepProvider, 1202 Version: getproviders.MustParseVersion("2.0.0"), 1203 TargetPlatform: fakePlatform, 1204 Location: beepProviderDir, 1205 }, 1206 }, 1207 nil, 1208 ), 1209 LockFile: ` 1210 provider "example.com/foo/beep" { 1211 version = "1.2.0" 1212 constraints = ">= 1.0.0" 1213 hashes = [ 1214 "h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84=", 1215 ] 1216 } 1217 `, 1218 Mode: InstallNewProvidersOnly, 1219 Reqs: getproviders.Requirements{ 1220 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1221 }, 1222 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 1223 if allCached := dir.AllAvailablePackages(); len(allCached) != 0 { 1224 t.Errorf("wrong number of cache directory entries; want none\n%s", spew.Sdump(allCached)) 1225 } 1226 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 1227 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 1228 } 1229 1230 gotLock := locks.Provider(beepProvider) 1231 wantLock := depsfile.NewProviderLock( 1232 beepProvider, 1233 getproviders.MustParseVersion("1.2.0"), 1234 getproviders.MustParseVersionConstraints(">= 1.0.0"), 1235 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 1236 ) 1237 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 1238 t.Errorf("wrong lock entry\n%s", diff) 1239 } 1240 }, 1241 WantErr: `some providers could not be installed: 1242 - example.com/foo/beep: the previously-selected version 1.2.0 is no longer available`, 1243 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1244 return map[addrs.Provider][]*testInstallerEventLogItem{ 1245 noProvider: { 1246 { 1247 Event: "PendingProviders", 1248 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1249 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1250 }, 1251 }, 1252 }, 1253 beepProvider: { 1254 { 1255 Event: "QueryPackagesBegin", 1256 Provider: beepProvider, 1257 Args: struct { 1258 Constraints string 1259 Locked bool 1260 }{">= 1.0.0", true}, 1261 }, 1262 { 1263 Event: "QueryPackagesFailure", 1264 Provider: beepProvider, 1265 Args: `the previously-selected version 1.2.0 is no longer available`, 1266 }, 1267 }, 1268 } 1269 }, 1270 }, 1271 "no versions match the version constraint": { 1272 Source: getproviders.NewMockSource( 1273 []getproviders.PackageMeta{ 1274 { 1275 Provider: beepProvider, 1276 Version: getproviders.MustParseVersion("1.0.0"), 1277 TargetPlatform: fakePlatform, 1278 Location: beepProviderDir, 1279 }, 1280 }, 1281 nil, 1282 ), 1283 Mode: InstallNewProvidersOnly, 1284 Reqs: getproviders.Requirements{ 1285 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 1286 }, 1287 WantErr: `some providers could not be installed: 1288 - example.com/foo/beep: no available releases match the given constraints >= 2.0.0`, 1289 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1290 return map[addrs.Provider][]*testInstallerEventLogItem{ 1291 noProvider: { 1292 { 1293 Event: "PendingProviders", 1294 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1295 beepProvider: getproviders.MustParseVersionConstraints(">= 2.0.0"), 1296 }, 1297 }, 1298 }, 1299 beepProvider: { 1300 { 1301 Event: "QueryPackagesBegin", 1302 Provider: beepProvider, 1303 Args: struct { 1304 Constraints string 1305 Locked bool 1306 }{">= 2.0.0", false}, 1307 }, 1308 { 1309 Event: "QueryPackagesFailure", 1310 Provider: beepProvider, 1311 Args: `no available releases match the given constraints >= 2.0.0`, 1312 }, 1313 }, 1314 } 1315 }, 1316 }, 1317 "version exists but doesn't support the current platform": { 1318 Source: getproviders.NewMockSource( 1319 []getproviders.PackageMeta{ 1320 { 1321 Provider: beepProvider, 1322 Version: getproviders.MustParseVersion("1.0.0"), 1323 TargetPlatform: wrongPlatform, 1324 Location: beepProviderDir, 1325 }, 1326 }, 1327 nil, 1328 ), 1329 Mode: InstallNewProvidersOnly, 1330 Reqs: getproviders.Requirements{ 1331 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1332 }, 1333 WantErr: `some providers could not be installed: 1334 - example.com/foo/beep: provider example.com/foo/beep 1.0.0 is not available for bleep_bloop`, 1335 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1336 return map[addrs.Provider][]*testInstallerEventLogItem{ 1337 noProvider: { 1338 { 1339 Event: "PendingProviders", 1340 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1341 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1342 }, 1343 }, 1344 }, 1345 beepProvider: { 1346 { 1347 Event: "QueryPackagesBegin", 1348 Provider: beepProvider, 1349 Args: struct { 1350 Constraints string 1351 Locked bool 1352 }{">= 1.0.0", false}, 1353 }, 1354 { 1355 Event: "QueryPackagesSuccess", 1356 Provider: beepProvider, 1357 Args: "1.0.0", 1358 }, 1359 { 1360 Event: "FetchPackageMeta", 1361 Provider: beepProvider, 1362 Args: "1.0.0", 1363 }, 1364 { 1365 Event: "FetchPackageFailure", 1366 Provider: beepProvider, 1367 Args: struct { 1368 Version string 1369 Error string 1370 }{ 1371 "1.0.0", 1372 "provider example.com/foo/beep 1.0.0 is not available for bleep_bloop", 1373 }, 1374 }, 1375 }, 1376 } 1377 }, 1378 }, 1379 "available package doesn't match locked hash": { 1380 Source: getproviders.NewMockSource( 1381 []getproviders.PackageMeta{ 1382 { 1383 Provider: beepProvider, 1384 Version: getproviders.MustParseVersion("1.0.0"), 1385 TargetPlatform: fakePlatform, 1386 Location: beepProviderDir, 1387 }, 1388 }, 1389 nil, 1390 ), 1391 LockFile: ` 1392 provider "example.com/foo/beep" { 1393 version = "1.0.0" 1394 constraints = ">= 1.0.0" 1395 hashes = [ 1396 "h1:does-not-match", 1397 ] 1398 } 1399 `, 1400 Mode: InstallNewProvidersOnly, 1401 Reqs: getproviders.Requirements{ 1402 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1403 }, 1404 WantErr: `some providers could not be installed: 1405 - example.com/foo/beep: the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms); for more information: https://www.durgaform.io/language/provider-checksum-verification`, 1406 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1407 return map[addrs.Provider][]*testInstallerEventLogItem{ 1408 noProvider: { 1409 { 1410 Event: "PendingProviders", 1411 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1412 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1413 }, 1414 }, 1415 }, 1416 beepProvider: { 1417 { 1418 Event: "QueryPackagesBegin", 1419 Provider: beepProvider, 1420 Args: struct { 1421 Constraints string 1422 Locked bool 1423 }{">= 1.0.0", true}, 1424 }, 1425 { 1426 Event: "QueryPackagesSuccess", 1427 Provider: beepProvider, 1428 Args: "1.0.0", 1429 }, 1430 { 1431 Event: "FetchPackageMeta", 1432 Provider: beepProvider, 1433 Args: "1.0.0", 1434 }, 1435 { 1436 Event: "FetchPackageBegin", 1437 Provider: beepProvider, 1438 Args: struct { 1439 Version string 1440 Location getproviders.PackageLocation 1441 }{"1.0.0", beepProviderDir}, 1442 }, 1443 { 1444 Event: "FetchPackageFailure", 1445 Provider: beepProvider, 1446 Args: struct { 1447 Version string 1448 Error string 1449 }{ 1450 "1.0.0", 1451 `the local package for example.com/foo/beep 1.0.0 doesn't match any of the checksums previously recorded in the dependency lock file (this might be because the available checksums are for packages targeting different platforms); for more information: https://www.durgaform.io/language/provider-checksum-verification`, 1452 }, 1453 }, 1454 }, 1455 } 1456 }, 1457 }, 1458 "force mode ignores hashes": { 1459 Source: getproviders.NewMockSource( 1460 []getproviders.PackageMeta{ 1461 { 1462 Provider: beepProvider, 1463 Version: getproviders.MustParseVersion("1.0.0"), 1464 TargetPlatform: fakePlatform, 1465 Location: beepProviderDir, 1466 }, 1467 }, 1468 nil, 1469 ), 1470 LockFile: ` 1471 provider "example.com/foo/beep" { 1472 version = "1.0.0" 1473 constraints = ">= 1.0.0" 1474 hashes = [ 1475 "h1:does-not-match", 1476 ] 1477 } 1478 `, 1479 Mode: InstallNewProvidersForce, 1480 Reqs: getproviders.Requirements{ 1481 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1482 }, 1483 Check: func(t *testing.T, dir *Dir, locks *depsfile.Locks) { 1484 if allCached := dir.AllAvailablePackages(); len(allCached) != 1 { 1485 t.Errorf("wrong number of cache directory entries; want only one\n%s", spew.Sdump(allCached)) 1486 } 1487 if allLocked := locks.AllProviders(); len(allLocked) != 1 { 1488 t.Errorf("wrong number of provider lock entries; want only one\n%s", spew.Sdump(allLocked)) 1489 } 1490 1491 gotLock := locks.Provider(beepProvider) 1492 wantLock := depsfile.NewProviderLock( 1493 beepProvider, 1494 getproviders.MustParseVersion("1.0.0"), 1495 getproviders.MustParseVersionConstraints(">= 1.0.0"), 1496 []getproviders.Hash{beepProviderHash, "h1:does-not-match"}, 1497 ) 1498 if diff := cmp.Diff(wantLock, gotLock, depsfile.ProviderLockComparer); diff != "" { 1499 t.Errorf("wrong lock entry\n%s", diff) 1500 } 1501 1502 gotEntry := dir.ProviderLatestVersion(beepProvider) 1503 wantEntry := &CachedProvider{ 1504 Provider: beepProvider, 1505 Version: getproviders.MustParseVersion("1.0.0"), 1506 PackageDir: filepath.Join(dir.BasePath(), "example.com/foo/beep/1.0.0/bleep_bloop"), 1507 } 1508 if diff := cmp.Diff(wantEntry, gotEntry); diff != "" { 1509 t.Errorf("wrong cache entry\n%s", diff) 1510 } 1511 }, 1512 WantEvents: func(inst *Installer, dir *Dir) map[addrs.Provider][]*testInstallerEventLogItem { 1513 return map[addrs.Provider][]*testInstallerEventLogItem{ 1514 noProvider: { 1515 { 1516 Event: "PendingProviders", 1517 Args: map[addrs.Provider]getproviders.VersionConstraints{ 1518 beepProvider: getproviders.MustParseVersionConstraints(">= 1.0.0"), 1519 }, 1520 }, 1521 { 1522 Event: "ProvidersFetched", 1523 Args: map[addrs.Provider]*getproviders.PackageAuthenticationResult{ 1524 beepProvider: nil, 1525 }, 1526 }, 1527 }, 1528 beepProvider: { 1529 { 1530 Event: "QueryPackagesBegin", 1531 Provider: beepProvider, 1532 Args: struct { 1533 Constraints string 1534 Locked bool 1535 }{">= 1.0.0", true}, 1536 }, 1537 { 1538 Event: "QueryPackagesSuccess", 1539 Provider: beepProvider, 1540 Args: "1.0.0", 1541 }, 1542 { 1543 Event: "FetchPackageMeta", 1544 Provider: beepProvider, 1545 Args: "1.0.0", 1546 }, 1547 { 1548 Event: "FetchPackageBegin", 1549 Provider: beepProvider, 1550 Args: struct { 1551 Version string 1552 Location getproviders.PackageLocation 1553 }{"1.0.0", beepProviderDir}, 1554 }, 1555 { 1556 Event: "ProvidersLockUpdated", 1557 Provider: beepProvider, 1558 Args: struct { 1559 Version string 1560 Local []getproviders.Hash 1561 Signed []getproviders.Hash 1562 Prior []getproviders.Hash 1563 }{ 1564 "1.0.0", 1565 []getproviders.Hash{"h1:2y06Ykj0FRneZfGCTxI9wRTori8iB7ZL5kQ6YyEnh84="}, 1566 nil, 1567 []getproviders.Hash{"h1:does-not-match"}, 1568 }, 1569 }, 1570 { 1571 Event: "FetchPackageSuccess", 1572 Provider: beepProvider, 1573 Args: struct { 1574 Version string 1575 LocalDir string 1576 AuthResult string 1577 }{ 1578 "1.0.0", 1579 filepath.Join(dir.BasePath(), "example.com/foo/beep/1.0.0/bleep_bloop"), 1580 "unauthenticated", 1581 }, 1582 }, 1583 }, 1584 } 1585 }, 1586 }, 1587 } 1588 1589 ctx := context.Background() 1590 1591 for name, test := range tests { 1592 t.Run(name, func(t *testing.T) { 1593 if test.Check == nil && test.WantEvents == nil && test.WantErr == "" { 1594 t.Fatalf("invalid test: must set at least one of Check, WantEvents, or WantErr") 1595 } 1596 1597 outputDir := NewDirWithPlatform(tmpDir(t), fakePlatform) 1598 source := test.Source 1599 if source == nil { 1600 source = getproviders.NewMockSource(nil, nil) 1601 } 1602 inst := NewInstaller(outputDir, source) 1603 if test.Prepare != nil { 1604 test.Prepare(t, inst, outputDir) 1605 } 1606 1607 locks, lockDiags := depsfile.LoadLocksFromBytes([]byte(test.LockFile), "test.lock.hcl") 1608 if lockDiags.HasErrors() { 1609 t.Fatalf("invalid lock file: %s", lockDiags.Err().Error()) 1610 } 1611 1612 providerEvents := make(map[addrs.Provider][]*testInstallerEventLogItem) 1613 eventsCh := make(chan *testInstallerEventLogItem) 1614 var newLocks *depsfile.Locks 1615 var instErr error 1616 go func(ch chan *testInstallerEventLogItem) { 1617 events := installerLogEventsForTests(ch) 1618 ctx := events.OnContext(ctx) 1619 newLocks, instErr = inst.EnsureProviderVersions(ctx, locks, test.Reqs, test.Mode) 1620 close(eventsCh) // exits the event loop below 1621 }(eventsCh) 1622 for evt := range eventsCh { 1623 // We do the event collection in the main goroutine, rather than 1624 // running the installer itself in the main goroutine, so that 1625 // we can safely t.Log in here without violating the testing.T 1626 // usage rules. 1627 if evt.Provider == (addrs.Provider{}) { 1628 t.Logf("%s(%s)", evt.Event, spew.Sdump(evt.Args)) 1629 } else { 1630 t.Logf("%s: %s(%s)", evt.Provider, evt.Event, spew.Sdump(evt.Args)) 1631 } 1632 providerEvents[evt.Provider] = append(providerEvents[evt.Provider], evt) 1633 } 1634 1635 if test.WantErr != "" { 1636 if instErr == nil { 1637 t.Errorf("succeeded; want error\nwant: %s", test.WantErr) 1638 } else if got, want := instErr.Error(), test.WantErr; got != want { 1639 t.Errorf("wrong error\ngot: %s\nwant: %s", got, want) 1640 } 1641 } else if instErr != nil { 1642 t.Errorf("unexpected error\ngot: %s", instErr.Error()) 1643 } 1644 1645 if test.Check != nil { 1646 test.Check(t, outputDir, newLocks) 1647 } 1648 1649 if test.WantEvents != nil { 1650 wantEvents := test.WantEvents(inst, outputDir) 1651 if diff := cmp.Diff(wantEvents, providerEvents); diff != "" { 1652 t.Errorf("wrong installer events\n%s", diff) 1653 } 1654 } 1655 }) 1656 } 1657 } 1658 1659 func TestEnsureProviderVersions_local_source(t *testing.T) { 1660 // create filesystem source using the test provider cache dir 1661 source := getproviders.NewFilesystemMirrorSource("testdata/cachedir") 1662 1663 // create a temporary workdir 1664 tmpDirPath := t.TempDir() 1665 1666 // set up the installer using the temporary directory and filesystem source 1667 platform := getproviders.Platform{OS: "linux", Arch: "amd64"} 1668 dir := NewDirWithPlatform(tmpDirPath, platform) 1669 installer := NewInstaller(dir, source) 1670 1671 tests := map[string]struct { 1672 provider string 1673 version string 1674 wantHash getproviders.Hash // getproviders.NilHash if not expected to be installed 1675 err string 1676 }{ 1677 "install-unpacked": { 1678 provider: "null", 1679 version: "2.0.0", 1680 wantHash: getproviders.HashScheme1.New("qjsREM4DqEWECD43FcPqddZ9oxCG+IaMTxvWPciS05g="), 1681 }, 1682 "invalid-zip-file": { 1683 provider: "null", 1684 version: "2.1.0", 1685 wantHash: getproviders.NilHash, 1686 err: "zip: not a valid zip file", 1687 }, 1688 "version-constraint-unmet": { 1689 provider: "null", 1690 version: "2.2.0", 1691 wantHash: getproviders.NilHash, 1692 err: "no available releases match the given constraints 2.2.0", 1693 }, 1694 "missing-executable": { 1695 provider: "missing/executable", 1696 version: "2.0.0", 1697 wantHash: getproviders.NilHash, // installation fails for a provider with no executable 1698 err: "provider binary not found: could not find executable file starting with durgaform-provider-executable", 1699 }, 1700 } 1701 1702 for name, test := range tests { 1703 t.Run(name, func(t *testing.T) { 1704 ctx := context.TODO() 1705 1706 provider := addrs.MustParseProviderSourceString(test.provider) 1707 versionConstraint := getproviders.MustParseVersionConstraints(test.version) 1708 version := getproviders.MustParseVersion(test.version) 1709 reqs := getproviders.Requirements{ 1710 provider: versionConstraint, 1711 } 1712 1713 newLocks, err := installer.EnsureProviderVersions(ctx, depsfile.NewLocks(), reqs, InstallNewProvidersOnly) 1714 gotProviderlocks := newLocks.AllProviders() 1715 wantProviderLocks := map[addrs.Provider]*depsfile.ProviderLock{ 1716 provider: depsfile.NewProviderLock( 1717 provider, 1718 version, 1719 getproviders.MustParseVersionConstraints("= 2.0.0"), 1720 []getproviders.Hash{ 1721 test.wantHash, 1722 }, 1723 ), 1724 } 1725 if test.wantHash == getproviders.NilHash { 1726 wantProviderLocks = map[addrs.Provider]*depsfile.ProviderLock{} 1727 } 1728 1729 if diff := cmp.Diff(wantProviderLocks, gotProviderlocks, depsfile.ProviderLockComparer); diff != "" { 1730 t.Errorf("wrong selected\n%s", diff) 1731 } 1732 1733 if test.err == "" && err == nil { 1734 return 1735 } 1736 1737 switch err := err.(type) { 1738 case InstallerError: 1739 providerError, ok := err.ProviderErrors[provider] 1740 if !ok { 1741 t.Fatalf("did not get error for provider %s", provider) 1742 } 1743 1744 if got := providerError.Error(); got != test.err { 1745 t.Fatalf("wrong result\ngot: %s\nwant: %s\n", got, test.err) 1746 } 1747 default: 1748 t.Fatalf("wrong error type. Expected InstallerError, got %T", err) 1749 } 1750 }) 1751 } 1752 } 1753 1754 // This test only verifies protocol errors and does not try for successfull 1755 // installation (at the time of writing, the test files aren't signed so the 1756 // signature verification fails); that's left to the e2e tests. 1757 func TestEnsureProviderVersions_protocol_errors(t *testing.T) { 1758 source, _, close := testRegistrySource(t) 1759 defer close() 1760 1761 // create a temporary workdir 1762 tmpDirPath := t.TempDir() 1763 1764 version0 := getproviders.MustParseVersionConstraints("0.1.0") // supports protocol version 1.0 1765 version1 := getproviders.MustParseVersion("1.2.0") // this is the expected result in tests with a match 1766 version2 := getproviders.MustParseVersionConstraints("2.0") // supports protocol version 99 1767 1768 // set up the installer using the temporary directory and mock source 1769 platform := getproviders.Platform{OS: "gameboy", Arch: "lr35902"} 1770 dir := NewDirWithPlatform(tmpDirPath, platform) 1771 installer := NewInstaller(dir, source) 1772 1773 tests := map[string]struct { 1774 provider addrs.Provider 1775 inputVersion getproviders.VersionConstraints 1776 wantVersion getproviders.Version 1777 }{ 1778 "too old": { 1779 addrs.MustParseProviderSourceString("example.com/awesomesauce/happycloud"), 1780 version0, 1781 version1, 1782 }, 1783 "too new": { 1784 addrs.MustParseProviderSourceString("example.com/awesomesauce/happycloud"), 1785 version2, 1786 version1, 1787 }, 1788 "unsupported": { 1789 addrs.MustParseProviderSourceString("example.com/weaksauce/unsupported-protocol"), 1790 version0, 1791 getproviders.UnspecifiedVersion, 1792 }, 1793 } 1794 1795 for name, test := range tests { 1796 t.Run(name, func(t *testing.T) { 1797 reqs := getproviders.Requirements{ 1798 test.provider: test.inputVersion, 1799 } 1800 ctx := context.TODO() 1801 _, err := installer.EnsureProviderVersions(ctx, depsfile.NewLocks(), reqs, InstallNewProvidersOnly) 1802 1803 switch err := err.(type) { 1804 case nil: 1805 t.Fatalf("expected error, got success") 1806 case InstallerError: 1807 providerError, ok := err.ProviderErrors[test.provider] 1808 if !ok { 1809 t.Fatalf("did not get error for provider %s", test.provider) 1810 } 1811 1812 switch providerError := providerError.(type) { 1813 case getproviders.ErrProtocolNotSupported: 1814 if !providerError.Suggestion.Same(test.wantVersion) { 1815 t.Fatalf("wrong result\ngot: %s\nwant: %s\n", providerError.Suggestion, test.wantVersion) 1816 } 1817 default: 1818 t.Fatalf("wrong error type. Expected ErrProtocolNotSupported, got %T", err) 1819 } 1820 default: 1821 t.Fatalf("wrong error type. Expected InstallerError, got %T", err) 1822 } 1823 }) 1824 } 1825 } 1826 1827 // testServices starts up a local HTTP server running a fake provider registry 1828 // service and returns a service discovery object pre-configured to consider 1829 // the host "example.com" to be served by the fake registry service. 1830 // 1831 // The returned discovery object also knows the hostname "not.example.com" 1832 // which does not have a provider registry at all and "too-new.example.com" 1833 // which has a "providers.v99" service that is inoperable but could be useful 1834 // to test the error reporting for detecting an unsupported protocol version. 1835 // It also knows fails.example.com but it refers to an endpoint that doesn't 1836 // correctly speak HTTP, to simulate a protocol error. 1837 // 1838 // The second return value is a function to call at the end of a test function 1839 // to shut down the test server. After you call that function, the discovery 1840 // object becomes useless. 1841 func testServices(t *testing.T) (services *disco.Disco, baseURL string, cleanup func()) { 1842 server := httptest.NewServer(http.HandlerFunc(fakeRegistryHandler)) 1843 1844 services = disco.New() 1845 services.ForceHostServices(svchost.Hostname("example.com"), map[string]interface{}{ 1846 "providers.v1": server.URL + "/providers/v1/", 1847 }) 1848 services.ForceHostServices(svchost.Hostname("not.example.com"), map[string]interface{}{}) 1849 services.ForceHostServices(svchost.Hostname("too-new.example.com"), map[string]interface{}{ 1850 // This service doesn't actually work; it's here only to be 1851 // detected as "too new" by the discovery logic. 1852 "providers.v99": server.URL + "/providers/v99/", 1853 }) 1854 services.ForceHostServices(svchost.Hostname("fails.example.com"), map[string]interface{}{ 1855 "providers.v1": server.URL + "/fails-immediately/", 1856 }) 1857 1858 // We'll also permit registry.durgaform.io here just because it's our 1859 // default and has some unique features that are not allowed on any other 1860 // hostname. It behaves the same as example.com, which should be preferred 1861 // if you're not testing something specific to the default registry in order 1862 // to ensure that most things are hostname-agnostic. 1863 services.ForceHostServices(svchost.Hostname("registry.durgaform.io"), map[string]interface{}{ 1864 "providers.v1": server.URL + "/providers/v1/", 1865 }) 1866 1867 return services, server.URL, func() { 1868 server.Close() 1869 } 1870 } 1871 1872 // testRegistrySource is a wrapper around testServices that uses the created 1873 // discovery object to produce a Source instance that is ready to use with the 1874 // fake registry services. 1875 // 1876 // As with testServices, the second return value is a function to call at the end 1877 // of your test in order to shut down the test server. 1878 func testRegistrySource(t *testing.T) (source *getproviders.RegistrySource, baseURL string, cleanup func()) { 1879 services, baseURL, close := testServices(t) 1880 source = getproviders.NewRegistrySource(services) 1881 return source, baseURL, close 1882 } 1883 1884 func fakeRegistryHandler(resp http.ResponseWriter, req *http.Request) { 1885 path := req.URL.EscapedPath() 1886 if strings.HasPrefix(path, "/fails-immediately/") { 1887 // Here we take over the socket and just close it immediately, to 1888 // simulate one possible way a server might not be an HTTP server. 1889 hijacker, ok := resp.(http.Hijacker) 1890 if !ok { 1891 // Not hijackable, so we'll just fail normally. 1892 // If this happens, tests relying on this will fail. 1893 resp.WriteHeader(500) 1894 resp.Write([]byte(`cannot hijack`)) 1895 return 1896 } 1897 conn, _, err := hijacker.Hijack() 1898 if err != nil { 1899 resp.WriteHeader(500) 1900 resp.Write([]byte(`hijack failed`)) 1901 return 1902 } 1903 conn.Close() 1904 return 1905 } 1906 1907 if strings.HasPrefix(path, "/pkg/") { 1908 switch path { 1909 case "/pkg/awesomesauce/happycloud_1.2.0.zip": 1910 resp.Write([]byte("some zip file")) 1911 case "/pkg/awesomesauce/happycloud_1.2.0_SHA256SUMS": 1912 resp.Write([]byte("000000000000000000000000000000000000000000000000000000000000f00d happycloud_1.2.0.zip\n")) 1913 case "/pkg/awesomesauce/happycloud_1.2.0_SHA256SUMS.sig": 1914 resp.Write([]byte("GPG signature")) 1915 default: 1916 resp.WriteHeader(404) 1917 resp.Write([]byte("unknown package file download")) 1918 } 1919 return 1920 } 1921 1922 if !strings.HasPrefix(path, "/providers/v1/") { 1923 resp.WriteHeader(404) 1924 resp.Write([]byte(`not a provider registry endpoint`)) 1925 return 1926 } 1927 1928 pathParts := strings.Split(path, "/")[3:] 1929 if len(pathParts) < 2 { 1930 resp.WriteHeader(404) 1931 resp.Write([]byte(`unexpected number of path parts`)) 1932 return 1933 } 1934 log.Printf("[TRACE] fake provider registry request for %#v", pathParts) 1935 if len(pathParts) == 2 { 1936 switch pathParts[0] + "/" + pathParts[1] { 1937 1938 case "-/legacy": 1939 // NOTE: This legacy lookup endpoint is specific to 1940 // registry.durgaform.io and not expected to work on any other 1941 // registry host. 1942 resp.Header().Set("Content-Type", "application/json") 1943 resp.WriteHeader(200) 1944 resp.Write([]byte(`{"namespace":"legacycorp"}`)) 1945 1946 default: 1947 resp.WriteHeader(404) 1948 resp.Write([]byte(`unknown namespace or provider type for direct lookup`)) 1949 } 1950 } 1951 1952 if len(pathParts) < 3 { 1953 resp.WriteHeader(404) 1954 resp.Write([]byte(`unexpected number of path parts`)) 1955 return 1956 } 1957 1958 if pathParts[2] == "versions" { 1959 if len(pathParts) != 3 { 1960 resp.WriteHeader(404) 1961 resp.Write([]byte(`extraneous path parts`)) 1962 return 1963 } 1964 1965 switch pathParts[0] + "/" + pathParts[1] { 1966 case "awesomesauce/happycloud": 1967 resp.Header().Set("Content-Type", "application/json") 1968 resp.WriteHeader(200) 1969 // Note that these version numbers are intentionally misordered 1970 // so we can test that the client-side code places them in the 1971 // correct order (lowest precedence first). 1972 resp.Write([]byte(`{"versions":[{"version":"0.1.0","protocols":["1.0"]},{"version":"2.0.0","protocols":["99.0"]},{"version":"1.2.0","protocols":["5.0"]}, {"version":"1.0.0","protocols":["5.0"]}]}`)) 1973 case "weaksauce/unsupported-protocol": 1974 resp.Header().Set("Content-Type", "application/json") 1975 resp.WriteHeader(200) 1976 resp.Write([]byte(`{"versions":[{"version":"0.1.0","protocols":["0.1"]}]}`)) 1977 case "weaksauce/no-versions": 1978 resp.Header().Set("Content-Type", "application/json") 1979 resp.WriteHeader(200) 1980 resp.Write([]byte(`{"versions":[]}`)) 1981 default: 1982 resp.WriteHeader(404) 1983 resp.Write([]byte(`unknown namespace or provider type`)) 1984 } 1985 return 1986 } 1987 1988 if len(pathParts) == 6 && pathParts[3] == "download" { 1989 switch pathParts[0] + "/" + pathParts[1] { 1990 case "awesomesauce/happycloud": 1991 if pathParts[4] == "nonexist" { 1992 resp.WriteHeader(404) 1993 resp.Write([]byte(`unsupported OS`)) 1994 return 1995 } 1996 version := pathParts[2] 1997 body := map[string]interface{}{ 1998 "protocols": []string{"99.0"}, 1999 "os": pathParts[4], 2000 "arch": pathParts[5], 2001 "filename": "happycloud_" + version + ".zip", 2002 "shasum": "000000000000000000000000000000000000000000000000000000000000f00d", 2003 "download_url": "/pkg/awesomesauce/happycloud_" + version + ".zip", 2004 "shasums_url": "/pkg/awesomesauce/happycloud_" + version + "_SHA256SUMS", 2005 "shasums_signature_url": "/pkg/awesomesauce/happycloud_" + version + "_SHA256SUMS.sig", 2006 "signing_keys": map[string]interface{}{ 2007 "gpg_public_keys": []map[string]interface{}{ 2008 { 2009 "ascii_armor": getproviders.HashicorpPublicKey, 2010 }, 2011 }, 2012 }, 2013 } 2014 enc, err := json.Marshal(body) 2015 if err != nil { 2016 resp.WriteHeader(500) 2017 resp.Write([]byte("failed to encode body")) 2018 } 2019 resp.Header().Set("Content-Type", "application/json") 2020 resp.WriteHeader(200) 2021 resp.Write(enc) 2022 case "weaksauce/unsupported-protocol": 2023 var protocols []string 2024 version := pathParts[2] 2025 switch version { 2026 case "0.1.0": 2027 protocols = []string{"1.0"} 2028 case "2.0.0": 2029 protocols = []string{"99.0"} 2030 default: 2031 protocols = []string{"5.0"} 2032 } 2033 2034 body := map[string]interface{}{ 2035 "protocols": protocols, 2036 "os": pathParts[4], 2037 "arch": pathParts[5], 2038 "filename": "happycloud_" + version + ".zip", 2039 "shasum": "000000000000000000000000000000000000000000000000000000000000f00d", 2040 "download_url": "/pkg/awesomesauce/happycloud_" + version + ".zip", 2041 "shasums_url": "/pkg/awesomesauce/happycloud_" + version + "_SHA256SUMS", 2042 "shasums_signature_url": "/pkg/awesomesauce/happycloud_" + version + "_SHA256SUMS.sig", 2043 "signing_keys": map[string]interface{}{ 2044 "gpg_public_keys": []map[string]interface{}{ 2045 { 2046 "ascii_armor": getproviders.HashicorpPublicKey, 2047 }, 2048 }, 2049 }, 2050 } 2051 enc, err := json.Marshal(body) 2052 if err != nil { 2053 resp.WriteHeader(500) 2054 resp.Write([]byte("failed to encode body")) 2055 } 2056 resp.Header().Set("Content-Type", "application/json") 2057 resp.WriteHeader(200) 2058 resp.Write(enc) 2059 default: 2060 resp.WriteHeader(404) 2061 resp.Write([]byte(`unknown namespace/provider/version/architecture`)) 2062 } 2063 return 2064 } 2065 2066 resp.WriteHeader(404) 2067 resp.Write([]byte(`unrecognized path scheme`)) 2068 } 2069 2070 // In order to be able to compare the recorded temp dir paths, we need to 2071 // normalize the path to match what the installer would report. 2072 func tmpDir(t *testing.T) string { 2073 unlinked, err := filepath.EvalSymlinks(t.TempDir()) 2074 if err != nil { 2075 t.Fatal(err) 2076 } 2077 return filepath.Clean(unlinked) 2078 }