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  }