google.golang.org/grpc@v1.72.2/credentials/tls/certprovider/pemfile/watcher_test.go (about) 1 /* 2 * 3 * Copyright 2020 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package pemfile 20 21 import ( 22 "context" 23 "fmt" 24 "os" 25 "path" 26 "testing" 27 "time" 28 29 "github.com/google/go-cmp/cmp" 30 "google.golang.org/grpc/credentials/tls/certprovider" 31 "google.golang.org/grpc/internal/grpctest" 32 "google.golang.org/grpc/internal/testutils" 33 "google.golang.org/grpc/testdata" 34 ) 35 36 const ( 37 // These are the names of files inside temporary directories, which the 38 // plugin is asked to watch. 39 certFile = "cert.pem" 40 keyFile = "key.pem" 41 rootFile = "ca.pem" 42 spiffeBundleFile = "spiffebundle.json" 43 44 defaultTestRefreshDuration = 100 * time.Millisecond 45 defaultTestTimeout = 5 * time.Second 46 ) 47 48 type s struct { 49 grpctest.Tester 50 } 51 52 func Test(t *testing.T) { 53 grpctest.RunSubTests(t, s{}) 54 } 55 56 func compareKeyMaterial(got, want *certprovider.KeyMaterial) error { 57 if len(got.Certs) != len(want.Certs) { 58 return fmt.Errorf("keyMaterial certs = %+v, want %+v", got, want) 59 } 60 for i := 0; i < len(got.Certs); i++ { 61 if !got.Certs[i].Leaf.Equal(want.Certs[i].Leaf) { 62 return fmt.Errorf("keyMaterial certs = %+v, want %+v", got, want) 63 } 64 } 65 66 if gotR, wantR := got.Roots, want.Roots; !gotR.Equal(wantR) { 67 return fmt.Errorf("keyMaterial roots = %v, want %v", gotR, wantR) 68 } 69 70 if gotBundle, wantBundle := got.SPIFFEBundleMap, want.SPIFFEBundleMap; !cmp.Equal(gotBundle, wantBundle) { 71 return fmt.Errorf("keyMaterial spiffe bundle map = %v, want %v", gotBundle, wantBundle) 72 } 73 74 return nil 75 } 76 77 // TestNewProvider tests the NewProvider() function with different inputs. 78 func (s) TestNewProvider(t *testing.T) { 79 tests := []struct { 80 desc string 81 options Options 82 wantError bool 83 }{ 84 { 85 desc: "No credential files specified", 86 options: Options{}, 87 wantError: true, 88 }, 89 { 90 desc: "Only identity cert is specified", 91 options: Options{ 92 CertFile: testdata.Path("x509/client1_cert.pem"), 93 }, 94 wantError: true, 95 }, 96 { 97 desc: "Only identity key is specified", 98 options: Options{ 99 KeyFile: testdata.Path("x509/client1_key.pem"), 100 }, 101 wantError: true, 102 }, 103 { 104 desc: "Identity cert/key pair is specified", 105 options: Options{ 106 KeyFile: testdata.Path("x509/client1_key.pem"), 107 CertFile: testdata.Path("x509/client1_cert.pem"), 108 }, 109 }, 110 { 111 desc: "Only root certs are specified", 112 options: Options{ 113 RootFile: testdata.Path("x509/client_ca_cert.pem"), 114 }, 115 }, 116 { 117 desc: "Only spiffe bundle map specified", 118 options: Options{ 119 SPIFFEBundleMapFile: testdata.Path("spiffe/spiffebundle.json"), 120 }, 121 }, 122 { 123 desc: "Everything is specified", 124 options: Options{ 125 KeyFile: testdata.Path("x509/client1_key.pem"), 126 CertFile: testdata.Path("x509/client1_cert.pem"), 127 RootFile: testdata.Path("x509/client_ca_cert.pem"), 128 SPIFFEBundleMapFile: testdata.Path("spiffe/spiffebundle.json"), 129 }, 130 wantError: false, 131 }, 132 } 133 for _, test := range tests { 134 t.Run(test.desc, func(t *testing.T) { 135 provider, err := NewProvider(test.options) 136 if (err != nil) != test.wantError { 137 t.Fatalf("NewProvider(%v) = %v, want %v", test.options, err, test.wantError) 138 } 139 if err != nil { 140 return 141 } 142 provider.Close() 143 }) 144 } 145 } 146 147 // wrappedDistributor wraps a distributor and pushes on a channel whenever new 148 // key material is pushed to the distributor. 149 type wrappedDistributor struct { 150 *certprovider.Distributor 151 distCh *testutils.Channel 152 } 153 154 func newWrappedDistributor(distCh *testutils.Channel) *wrappedDistributor { 155 return &wrappedDistributor{ 156 distCh: distCh, 157 Distributor: certprovider.NewDistributor(), 158 } 159 } 160 161 func (wd *wrappedDistributor) Set(km *certprovider.KeyMaterial, err error) { 162 wd.Distributor.Set(km, err) 163 wd.distCh.Send(nil) 164 } 165 166 func createTmpFile(t *testing.T, src, dst string) { 167 t.Helper() 168 169 data, err := os.ReadFile(src) 170 if err != nil { 171 t.Fatalf("os.ReadFile(%q) failed: %v", src, err) 172 } 173 if err := os.WriteFile(dst, data, os.ModePerm); err != nil { 174 t.Fatalf("os.WriteFile(%q) failed: %v", dst, err) 175 } 176 t.Logf("Wrote file at: %s", dst) 177 t.Logf("%s", string(data)) 178 } 179 180 func removeTmpFile(t *testing.T, filePath string) { 181 t.Helper() 182 if err := os.Remove(filePath); err != nil { 183 t.Fatalf("os.RemoveFIle(%q) failed: %v", filePath, err) 184 } 185 t.Logf("Removed file at: %s", filePath) 186 } 187 188 // createTempDirWithFiles creates a temporary directory under the system default 189 // tempDir with the given dirSuffix. It also reads from certSrc, keySrc and 190 // rootSrc files are creates appropriate files under the newly create tempDir. 191 // Returns the name of the created tempDir. 192 func createTmpDirWithFiles(t *testing.T, dirSuffix, certSrc, keySrc, rootSrc, spiffeBundleSrc string) string { 193 t.Helper() 194 195 // Create a temp directory. Passing an empty string for the first argument 196 // uses the system temp directory. 197 dir, err := os.MkdirTemp("", dirSuffix) 198 if err != nil { 199 t.Fatalf("os.MkdirTemp() failed: %v", err) 200 } 201 t.Logf("Using tmpdir: %s", dir) 202 203 createTmpFile(t, testdata.Path(certSrc), path.Join(dir, certFile)) 204 createTmpFile(t, testdata.Path(keySrc), path.Join(dir, keyFile)) 205 createTmpFile(t, testdata.Path(rootSrc), path.Join(dir, rootFile)) 206 createTmpFile(t, testdata.Path(spiffeBundleSrc), path.Join(dir, spiffeBundleFile)) 207 return dir 208 } 209 210 // initializeProvider performs setup steps common to all tests (except the one 211 // which uses symlinks). 212 func initializeProvider(t *testing.T, testName string, useSPIFFEBundle bool) (string, certprovider.Provider, *testutils.Channel, func()) { 213 t.Helper() 214 215 // Override the newDistributor to one which pushes on a channel that we 216 // can block on. 217 origDistributorFunc := newDistributor 218 distCh := testutils.NewChannel() 219 d := newWrappedDistributor(distCh) 220 newDistributor = func() distributor { return d } 221 222 // Create a new provider to watch the files in tmpdir. 223 dir := createTmpDirWithFiles(t, testName+"*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/client_ca_cert.pem", "spiffe/spiffebundle.json") 224 opts := Options{ 225 CertFile: path.Join(dir, certFile), 226 KeyFile: path.Join(dir, keyFile), 227 RootFile: path.Join(dir, rootFile), 228 RefreshDuration: defaultTestRefreshDuration, 229 } 230 if useSPIFFEBundle { 231 opts.SPIFFEBundleMapFile = path.Join(dir, spiffeBundleFile) 232 } 233 prov, err := NewProvider(opts) 234 if err != nil { 235 t.Fatalf("NewProvider(%+v) failed: %v", opts, err) 236 } 237 238 // Make sure the provider picks up the files and pushes the key material on 239 // to the distributors. 240 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 241 defer cancel() 242 for i := 0; i < 2; i++ { 243 // Since we have root and identity certs, we need to make sure the 244 // update is pushed on both of them. 245 if _, err := distCh.Receive(ctx); err != nil { 246 t.Fatalf("Timeout waiting for provider to read files and push key material to distributor: %v", err) 247 } 248 } 249 250 return dir, prov, distCh, func() { 251 newDistributor = origDistributorFunc 252 prov.Close() 253 } 254 } 255 256 // TestProvider_NoUpdate tests the case where a file watcher plugin is created 257 // successfully, and the underlying files do not change. Verifies that the 258 // plugin does not push new updates to the distributor in this case. 259 func (s) TestProvider_NoUpdate(t *testing.T) { 260 baseName := "no_update" 261 for _, useSPIFFEBundle := range []bool{true, false} { 262 testName := baseName 263 if useSPIFFEBundle { 264 testName = testName + "_" + "withSPIFFEBundle" 265 } 266 t.Run(testName, func(t *testing.T) { 267 _, prov, distCh, cancel := initializeProvider(t, "no_update", useSPIFFEBundle) 268 defer cancel() 269 270 // Make sure the provider is healthy and returns key material. 271 ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) 272 defer cc() 273 if _, err := prov.KeyMaterial(ctx); err != nil { 274 t.Fatalf("provider.KeyMaterial() failed: %v", err) 275 } 276 277 // Files haven't change. Make sure no updates are pushed by the provider. 278 sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) 279 defer sc() 280 if _, err := distCh.Receive(sCtx); err == nil { 281 t.Fatal("New key material pushed to distributor when underlying files did not change") 282 } 283 }) 284 } 285 } 286 287 // TestProvider_UpdateSuccess tests the case where a file watcher plugin is 288 // created successfully and the underlying files change. Verifies that the 289 // changes are picked up by the provider. 290 func (s) TestProvider_UpdateSuccess(t *testing.T) { 291 baseName := "update_success" 292 for _, useSPIFFEBundle := range []bool{true, false} { 293 testName := baseName 294 if useSPIFFEBundle { 295 testName = testName + "_" + "withSPIFFEBundle" 296 } 297 t.Run(testName, func(t *testing.T) { 298 dir, prov, distCh, cancel := initializeProvider(t, "update_success", useSPIFFEBundle) 299 defer cancel() 300 301 // Make sure the provider is healthy and returns key material. 302 ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) 303 defer cc() 304 km1, err := prov.KeyMaterial(ctx) 305 if err != nil { 306 t.Fatalf("provider.KeyMaterial() failed: %v", err) 307 } 308 309 // Change only the root file. 310 if useSPIFFEBundle { 311 createTmpFile(t, testdata.Path("spiffe/spiffebundle2.json"), path.Join(dir, spiffeBundleFile)) 312 } else { 313 createTmpFile(t, testdata.Path("x509/server_ca_cert.pem"), path.Join(dir, rootFile)) 314 } 315 if _, err := distCh.Receive(ctx); err != nil { 316 t.Fatal("Timeout waiting for new key material to be pushed to the distributor") 317 } 318 319 // Make sure update is picked up. 320 km2, err := prov.KeyMaterial(ctx) 321 if err != nil { 322 t.Fatalf("provider.KeyMaterial() failed: %v", err) 323 } 324 if err := compareKeyMaterial(km1, km2); err == nil { 325 t.Fatal("Expected provider to return new key material after update to underlying file") 326 } 327 328 // Change only cert/key files. 329 createTmpFile(t, testdata.Path("x509/client2_cert.pem"), path.Join(dir, certFile)) 330 createTmpFile(t, testdata.Path("x509/client2_key.pem"), path.Join(dir, keyFile)) 331 if _, err := distCh.Receive(ctx); err != nil { 332 t.Fatal("Timeout waiting for new key material to be pushed to the distributor") 333 } 334 335 // Make sure update is picked up. 336 km3, err := prov.KeyMaterial(ctx) 337 if err != nil { 338 t.Fatalf("provider.KeyMaterial() failed: %v", err) 339 } 340 if err := compareKeyMaterial(km2, km3); err == nil { 341 t.Fatal("Expected provider to return new key material after update to underlying file") 342 } 343 }) 344 } 345 } 346 347 // TestProvider_UpdateSuccessWithSymlink tests the case where a file watcher 348 // plugin is created successfully to watch files through a symlink and the 349 // symlink is updates to point to new files. Verifies that the changes are 350 // picked up by the provider. 351 func (s) TestProvider_UpdateSuccessWithSymlink(t *testing.T) { 352 baseName := "update_with_symlink" 353 for _, useSPIFFEBundle := range []bool{true, false} { 354 testName := baseName 355 if useSPIFFEBundle { 356 testName = testName + "_" + "withSPIFFEBundle" 357 } 358 t.Run(testName, func(t *testing.T) { 359 // Override the newDistributor to one which pushes on a channel that we 360 // can block on. 361 origDistributorFunc := newDistributor 362 distCh := testutils.NewChannel() 363 d := newWrappedDistributor(distCh) 364 newDistributor = func() distributor { return d } 365 defer func() { newDistributor = origDistributorFunc }() 366 367 // Create two tempDirs with different files. 368 dir1 := createTmpDirWithFiles(t, "update_with_symlink1_*", "x509/client1_cert.pem", "x509/client1_key.pem", "x509/client_ca_cert.pem", "spiffe/spiffebundle.json") 369 dir2 := createTmpDirWithFiles(t, "update_with_symlink2_*", "x509/server1_cert.pem", "x509/server1_key.pem", "x509/server_ca_cert.pem", "spiffe/spiffebundle2.json") 370 371 // Create a symlink under a new tempdir, and make it point to dir1. 372 tmpdir, err := os.MkdirTemp("", "test_symlink_*") 373 if err != nil { 374 t.Fatalf("os.MkdirTemp() failed: %v", err) 375 } 376 symLinkName := path.Join(tmpdir, "test_symlink") 377 if err := os.Symlink(dir1, symLinkName); err != nil { 378 t.Fatalf("Failed to create symlink to %q: %v", dir1, err) 379 } 380 381 // Create a provider which watches the files pointed to by the symlink. 382 opts := Options{ 383 CertFile: path.Join(symLinkName, certFile), 384 KeyFile: path.Join(symLinkName, keyFile), 385 RootFile: path.Join(symLinkName, rootFile), 386 SPIFFEBundleMapFile: path.Join(symLinkName, spiffeBundleFile), 387 RefreshDuration: defaultTestRefreshDuration, 388 } 389 if useSPIFFEBundle { 390 opts.SPIFFEBundleMapFile = path.Join(symLinkName, spiffeBundleFile) 391 } 392 prov, err := NewProvider(opts) 393 if err != nil { 394 t.Fatalf("NewProvider(%+v) failed: %v", opts, err) 395 } 396 defer prov.Close() 397 398 // Make sure the provider picks up the files and pushes the key material on 399 // to the distributors. 400 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 401 defer cancel() 402 for i := 0; i < 2; i++ { 403 // Since we have root and identity certs, we need to make sure the 404 // update is pushed on both of them. 405 if _, err := distCh.Receive(ctx); err != nil { 406 t.Fatalf("Timeout waiting for provider to read files and push key material to distributor: %v", err) 407 } 408 } 409 km1, err := prov.KeyMaterial(ctx) 410 if err != nil { 411 t.Fatalf("provider.KeyMaterial() failed: %v", err) 412 } 413 414 // Update the symlink to point to dir2. 415 symLinkTmpName := path.Join(tmpdir, "test_symlink.tmp") 416 if err := os.Symlink(dir2, symLinkTmpName); err != nil { 417 t.Fatalf("Failed to create symlink to %q: %v", dir2, err) 418 } 419 if err := os.Rename(symLinkTmpName, symLinkName); err != nil { 420 t.Fatalf("Failed to update symlink: %v", err) 421 } 422 423 // Make sure the provider picks up the new files and pushes the key material 424 // on to the distributors. 425 for i := 0; i < 2; i++ { 426 // Since we have root and identity certs, we need to make sure the 427 // update is pushed on both of them. 428 if _, err := distCh.Receive(ctx); err != nil { 429 t.Fatalf("Timeout waiting for provider to read files and push key material to distributor: %v", err) 430 } 431 } 432 km2, err := prov.KeyMaterial(ctx) 433 if err != nil { 434 t.Fatalf("provider.KeyMaterial() failed: %v", err) 435 } 436 437 if err := compareKeyMaterial(km1, km2); err == nil { 438 t.Fatal("Expected provider to return new key material after symlink update") 439 } 440 }) 441 } 442 } 443 444 // TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key 445 // files fail. Verifies that the failed update does not push anything on the 446 // distributor. Then the update succeeds, and the test verifies that the key 447 // material is updated. 448 func (s) TestProvider_UpdateFailure_ThenSuccess(t *testing.T) { 449 dir, prov, distCh, cancel := initializeProvider(t, "update_failure", false) 450 defer cancel() 451 452 // Make sure the provider is healthy and returns key material. 453 ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) 454 defer cc() 455 km1, err := prov.KeyMaterial(ctx) 456 if err != nil { 457 t.Fatalf("provider.KeyMaterial() failed: %v", err) 458 } 459 460 // Update only the cert file. The key file is left unchanged. This should 461 // lead to these two files being not compatible with each other. This 462 // simulates the case where the watching goroutine might catch the files in 463 // the midst of an update. 464 createTmpFile(t, testdata.Path("x509/server1_cert.pem"), path.Join(dir, certFile)) 465 466 // Since the last update left the files in an incompatible state, the update 467 // should not be picked up by our provider. 468 sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) 469 defer sc() 470 if _, err := distCh.Receive(sCtx); err == nil { 471 t.Fatal("New key material pushed to distributor when underlying files did not change") 472 } 473 474 // The provider should return key material corresponding to the old state. 475 km2, err := prov.KeyMaterial(ctx) 476 if err != nil { 477 t.Fatalf("provider.KeyMaterial() failed: %v", err) 478 } 479 if err := compareKeyMaterial(km1, km2); err != nil { 480 t.Fatalf("Expected provider to not update key material: %v", err) 481 } 482 483 // Update the key file to match the cert file. 484 createTmpFile(t, testdata.Path("x509/server1_key.pem"), path.Join(dir, keyFile)) 485 486 // Make sure update is picked up. 487 if _, err := distCh.Receive(ctx); err != nil { 488 t.Fatal("Timeout waiting for new key material to be pushed to the distributor") 489 } 490 km3, err := prov.KeyMaterial(ctx) 491 if err != nil { 492 t.Fatalf("provider.KeyMaterial() failed: %v", err) 493 } 494 if err := compareKeyMaterial(km2, km3); err == nil { 495 t.Fatal("Expected provider to return new key material after update to underlying file") 496 } 497 } 498 499 // TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key 500 // files fail. Verifies that the failed update does not push anything on the 501 // distributor. Then the update succeeds, and the test verifies that the key 502 // material is updated. 503 func (s) TestProvider_UpdateFailureSPIFFE(t *testing.T) { 504 tests := []struct { 505 name string 506 badFile string 507 }{ 508 { 509 name: "malformed spiffe", 510 badFile: "spiffe/spiffebundle_malformed.json", 511 }, 512 { 513 name: "invalid bundle", 514 badFile: "spiffe/spiffebundle_wrong_kty.json", 515 }, 516 { 517 name: "cert in the x5c field is invalid", 518 badFile: "spiffe/spiffebundle_corrupted_cert.json", 519 }, 520 } 521 for _, tc := range tests { 522 t.Run(tc.name, func(t *testing.T) { 523 dir, prov, distCh, cancel := initializeProvider(t, tc.name, true) 524 defer cancel() 525 526 // Make sure the provider is healthy and returns key material. 527 ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) 528 defer cc() 529 km1, err := prov.KeyMaterial(ctx) 530 if err != nil { 531 t.Fatalf("provider.KeyMaterial() failed: %v", err) 532 } 533 534 // Update the file with a bad update 535 createTmpFile(t, testdata.Path(tc.badFile), path.Join(dir, spiffeBundleFile)) 536 537 // Since the last update left the files in an incompatible state, the update 538 // should not be picked up by our provider. 539 sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) 540 defer sc() 541 if _, err := distCh.Receive(sCtx); err == nil { 542 t.Fatal("New key material pushed to distributor when underlying files did not change") 543 } 544 545 // The provider should return key material corresponding to the old state. 546 km2, err := prov.KeyMaterial(ctx) 547 if err != nil { 548 t.Fatalf("provider.KeyMaterial() failed: %v", err) 549 } 550 if err := compareKeyMaterial(km1, km2); err != nil { 551 t.Fatalf("Expected provider to not update key material: %v", err) 552 } 553 }) 554 } 555 } 556 557 // TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key 558 // files fail. Verifies that the failed update does not push anything on the 559 // distributor. Then the update succeeds, and the test verifies that the key 560 // material is updated. 561 func (s) TestProvider_UpdateFailureSPIFFE_MissingFile(t *testing.T) { 562 dir, prov, distCh, cancel := initializeProvider(t, "Delete spiffe file being read", true) 563 defer cancel() 564 565 // Make sure the provider is healthy and returns key material. 566 ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) 567 defer cc() 568 km1, err := prov.KeyMaterial(ctx) 569 if err != nil { 570 t.Fatalf("provider.KeyMaterial() failed: %v", err) 571 } 572 573 // Remove the file that we are reading 574 removeTmpFile(t, path.Join(dir, spiffeBundleFile)) 575 576 // Since the last update left the files in an incompatible state, the update 577 // should not be picked up by our provider. 578 sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) 579 defer sc() 580 if _, err := distCh.Receive(sCtx); err == nil { 581 t.Fatal("new key material pushed to distributor when underlying files did not change") 582 } 583 584 // The provider should return key material corresponding to the old state. 585 km2, err := prov.KeyMaterial(ctx) 586 if err != nil { 587 t.Fatalf("provider.KeyMaterial() failed: %v", err) 588 } 589 if err := compareKeyMaterial(km1, km2); err != nil { 590 t.Fatalf("expected provider to not update key material: %v", err) 591 } 592 } 593 594 // TestProvider_UpdateFailure_ThenSuccess tests the case where updating cert/key 595 // files fail. Verifies that the failed update does not push anything on the 596 // distributor. Then the update succeeds, and the test verifies that the key 597 // material is updated. 598 func (s) TestProvider_UpdateFailureRoot_MissingFile(t *testing.T) { 599 dir, prov, distCh, cancel := initializeProvider(t, "Delete root file being read", false) 600 defer cancel() 601 602 // Make sure the provider is healthy and returns key material. 603 ctx, cc := context.WithTimeout(context.Background(), defaultTestTimeout) 604 defer cc() 605 km1, err := prov.KeyMaterial(ctx) 606 if err != nil { 607 t.Fatalf("provider.KeyMaterial() failed: %v", err) 608 } 609 610 // Remove the file that we are reading 611 removeTmpFile(t, path.Join(dir, rootFile)) 612 613 // Since the last update left the files in an incompatible state, the update 614 // should not be picked up by our provider. 615 sCtx, sc := context.WithTimeout(context.Background(), 2*defaultTestRefreshDuration) 616 defer sc() 617 if _, err := distCh.Receive(sCtx); err == nil { 618 t.Fatal("new key material pushed to distributor when underlying files did not change") 619 } 620 621 // The provider should return key material corresponding to the old state. 622 km2, err := prov.KeyMaterial(ctx) 623 if err != nil { 624 t.Fatalf("provider.KeyMaterial() failed: %v", err) 625 } 626 if err := compareKeyMaterial(km1, km2); err != nil { 627 t.Fatalf("expected provider to not update key material: %v", err) 628 } 629 }