github.phpd.cn/hashicorp/consul@v1.4.5/agent/consul/state/connect_ca_test.go (about)

     1  package state
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/hashicorp/consul/agent/connect"
     9  	"github.com/hashicorp/consul/agent/structs"
    10  	"github.com/hashicorp/go-memdb"
    11  	"github.com/pascaldekloe/goe/verify"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func TestStore_CAConfig(t *testing.T) {
    16  	s := testStateStore(t)
    17  
    18  	expected := &structs.CAConfiguration{
    19  		Provider: "consul",
    20  		Config: map[string]interface{}{
    21  			"PrivateKey":     "asdf",
    22  			"RootCert":       "qwer",
    23  			"RotationPeriod": 90 * 24 * time.Hour,
    24  		},
    25  	}
    26  
    27  	if err := s.CASetConfig(0, expected); err != nil {
    28  		t.Fatal(err)
    29  	}
    30  
    31  	idx, config, err := s.CAConfig()
    32  	if err != nil {
    33  		t.Fatal(err)
    34  	}
    35  	if idx != 0 {
    36  		t.Fatalf("bad: %d", idx)
    37  	}
    38  	if !reflect.DeepEqual(expected, config) {
    39  		t.Fatalf("bad: %#v, %#v", expected, config)
    40  	}
    41  }
    42  
    43  func TestStore_CAConfigCAS(t *testing.T) {
    44  	s := testStateStore(t)
    45  
    46  	expected := &structs.CAConfiguration{
    47  		Provider: "consul",
    48  	}
    49  
    50  	if err := s.CASetConfig(0, expected); err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	// Do an extra operation to move the index up by 1 for the
    54  	// check-and-set operation after this
    55  	if err := s.CASetConfig(1, expected); err != nil {
    56  		t.Fatal(err)
    57  	}
    58  
    59  	// Do a CAS with an index lower than the entry
    60  	ok, err := s.CACheckAndSetConfig(2, 0, &structs.CAConfiguration{
    61  		Provider: "static",
    62  	})
    63  	if ok || err != nil {
    64  		t.Fatalf("expected (false, nil), got: (%v, %#v)", ok, err)
    65  	}
    66  
    67  	// Check that the index is untouched and the entry
    68  	// has not been updated.
    69  	idx, config, err := s.CAConfig()
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	if idx != 1 {
    74  		t.Fatalf("bad: %d", idx)
    75  	}
    76  	if config.Provider != "consul" {
    77  		t.Fatalf("bad: %#v", config)
    78  	}
    79  
    80  	// Do another CAS, this time with the correct index
    81  	ok, err = s.CACheckAndSetConfig(2, 1, &structs.CAConfiguration{
    82  		Provider: "static",
    83  	})
    84  	if !ok || err != nil {
    85  		t.Fatalf("expected (true, nil), got: (%v, %#v)", ok, err)
    86  	}
    87  
    88  	// Make sure the config was updated
    89  	idx, config, err = s.CAConfig()
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  	if idx != 2 {
    94  		t.Fatalf("bad: %d", idx)
    95  	}
    96  	if config.Provider != "static" {
    97  		t.Fatalf("bad: %#v", config)
    98  	}
    99  }
   100  
   101  func TestStore_CAConfig_Snapshot_Restore(t *testing.T) {
   102  	s := testStateStore(t)
   103  	before := &structs.CAConfiguration{
   104  		Provider: "consul",
   105  		Config: map[string]interface{}{
   106  			"PrivateKey":     "asdf",
   107  			"RootCert":       "qwer",
   108  			"RotationPeriod": 90 * 24 * time.Hour,
   109  		},
   110  	}
   111  	if err := s.CASetConfig(99, before); err != nil {
   112  		t.Fatal(err)
   113  	}
   114  
   115  	snap := s.Snapshot()
   116  	defer snap.Close()
   117  
   118  	after := &structs.CAConfiguration{
   119  		Provider: "static",
   120  		Config:   map[string]interface{}{},
   121  	}
   122  	if err := s.CASetConfig(100, after); err != nil {
   123  		t.Fatal(err)
   124  	}
   125  
   126  	snapped, err := snap.CAConfig()
   127  	if err != nil {
   128  		t.Fatalf("err: %s", err)
   129  	}
   130  	verify.Values(t, "", before, snapped)
   131  
   132  	s2 := testStateStore(t)
   133  	restore := s2.Restore()
   134  	if err := restore.CAConfig(snapped); err != nil {
   135  		t.Fatalf("err: %s", err)
   136  	}
   137  	restore.Commit()
   138  
   139  	idx, res, err := s2.CAConfig()
   140  	if err != nil {
   141  		t.Fatalf("err: %s", err)
   142  	}
   143  	if idx != 99 {
   144  		t.Fatalf("bad index: %d", idx)
   145  	}
   146  	verify.Values(t, "", before, res)
   147  }
   148  
   149  // Make sure we handle the case of a leftover blank CA config that
   150  // got stuck in a snapshot, as in https://github.com/hashicorp/consul/issues/4954
   151  func TestStore_CAConfig_Snapshot_Restore_BlankConfig(t *testing.T) {
   152  	s := testStateStore(t)
   153  	before := &structs.CAConfiguration{}
   154  	if err := s.CASetConfig(99, before); err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	snap := s.Snapshot()
   159  	defer snap.Close()
   160  
   161  	snapped, err := snap.CAConfig()
   162  	if err != nil {
   163  		t.Fatalf("err: %s", err)
   164  	}
   165  	verify.Values(t, "", before, snapped)
   166  
   167  	s2 := testStateStore(t)
   168  	restore := s2.Restore()
   169  	if err := restore.CAConfig(snapped); err != nil {
   170  		t.Fatalf("err: %s", err)
   171  	}
   172  	restore.Commit()
   173  
   174  	idx, result, err := s2.CAConfig()
   175  	if err != nil {
   176  		t.Fatalf("err: %s", err)
   177  	}
   178  	if idx != 0 {
   179  		t.Fatalf("bad index: %d", idx)
   180  	}
   181  	if result != nil {
   182  		t.Fatalf("should be nil: %v", result)
   183  	}
   184  }
   185  
   186  func TestStore_CARootSetList(t *testing.T) {
   187  	assert := assert.New(t)
   188  	s := testStateStore(t)
   189  
   190  	// Call list to populate the watch set
   191  	ws := memdb.NewWatchSet()
   192  	_, _, err := s.CARoots(ws)
   193  	assert.Nil(err)
   194  
   195  	// Build a valid value
   196  	ca1 := connect.TestCA(t, nil)
   197  
   198  	// Set
   199  	ok, err := s.CARootSetCAS(1, 0, []*structs.CARoot{ca1})
   200  	assert.Nil(err)
   201  	assert.True(ok)
   202  
   203  	// Make sure the index got updated.
   204  	assert.Equal(s.maxIndex(caRootTableName), uint64(1))
   205  	assert.True(watchFired(ws), "watch fired")
   206  
   207  	// Read it back out and verify it.
   208  	expected := *ca1
   209  	expected.RaftIndex = structs.RaftIndex{
   210  		CreateIndex: 1,
   211  		ModifyIndex: 1,
   212  	}
   213  
   214  	ws = memdb.NewWatchSet()
   215  	_, roots, err := s.CARoots(ws)
   216  	assert.Nil(err)
   217  	assert.Len(roots, 1)
   218  	actual := roots[0]
   219  	assert.Equal(&expected, actual)
   220  }
   221  
   222  func TestStore_CARootSet_emptyID(t *testing.T) {
   223  	assert := assert.New(t)
   224  	s := testStateStore(t)
   225  
   226  	// Call list to populate the watch set
   227  	ws := memdb.NewWatchSet()
   228  	_, _, err := s.CARoots(ws)
   229  	assert.Nil(err)
   230  
   231  	// Build a valid value
   232  	ca1 := connect.TestCA(t, nil)
   233  	ca1.ID = ""
   234  
   235  	// Set
   236  	ok, err := s.CARootSetCAS(1, 0, []*structs.CARoot{ca1})
   237  	assert.NotNil(err)
   238  	assert.Contains(err.Error(), ErrMissingCARootID.Error())
   239  	assert.False(ok)
   240  
   241  	// Make sure the index got updated.
   242  	assert.Equal(s.maxIndex(caRootTableName), uint64(0))
   243  	assert.False(watchFired(ws), "watch fired")
   244  
   245  	// Read it back out and verify it.
   246  	ws = memdb.NewWatchSet()
   247  	_, roots, err := s.CARoots(ws)
   248  	assert.Nil(err)
   249  	assert.Len(roots, 0)
   250  }
   251  
   252  func TestStore_CARootSet_noActive(t *testing.T) {
   253  	assert := assert.New(t)
   254  	s := testStateStore(t)
   255  
   256  	// Call list to populate the watch set
   257  	ws := memdb.NewWatchSet()
   258  	_, _, err := s.CARoots(ws)
   259  	assert.Nil(err)
   260  
   261  	// Build a valid value
   262  	ca1 := connect.TestCA(t, nil)
   263  	ca1.Active = false
   264  	ca2 := connect.TestCA(t, nil)
   265  	ca2.Active = false
   266  
   267  	// Set
   268  	ok, err := s.CARootSetCAS(1, 0, []*structs.CARoot{ca1, ca2})
   269  	assert.NotNil(err)
   270  	assert.Contains(err.Error(), "exactly one active")
   271  	assert.False(ok)
   272  }
   273  
   274  func TestStore_CARootSet_multipleActive(t *testing.T) {
   275  	assert := assert.New(t)
   276  	s := testStateStore(t)
   277  
   278  	// Call list to populate the watch set
   279  	ws := memdb.NewWatchSet()
   280  	_, _, err := s.CARoots(ws)
   281  	assert.Nil(err)
   282  
   283  	// Build a valid value
   284  	ca1 := connect.TestCA(t, nil)
   285  	ca2 := connect.TestCA(t, nil)
   286  
   287  	// Set
   288  	ok, err := s.CARootSetCAS(1, 0, []*structs.CARoot{ca1, ca2})
   289  	assert.NotNil(err)
   290  	assert.Contains(err.Error(), "exactly one active")
   291  	assert.False(ok)
   292  }
   293  
   294  func TestStore_CARootActive_valid(t *testing.T) {
   295  	assert := assert.New(t)
   296  	s := testStateStore(t)
   297  
   298  	// Build a valid value
   299  	ca1 := connect.TestCA(t, nil)
   300  	ca1.Active = false
   301  	ca2 := connect.TestCA(t, nil)
   302  	ca3 := connect.TestCA(t, nil)
   303  	ca3.Active = false
   304  
   305  	// Set
   306  	ok, err := s.CARootSetCAS(1, 0, []*structs.CARoot{ca1, ca2, ca3})
   307  	assert.Nil(err)
   308  	assert.True(ok)
   309  
   310  	// Query
   311  	ws := memdb.NewWatchSet()
   312  	idx, res, err := s.CARootActive(ws)
   313  	assert.Equal(idx, uint64(1))
   314  	assert.Nil(err)
   315  	assert.NotNil(res)
   316  	assert.Equal(ca2.ID, res.ID)
   317  }
   318  
   319  // Test that querying the active CA returns the correct value.
   320  func TestStore_CARootActive_none(t *testing.T) {
   321  	assert := assert.New(t)
   322  	s := testStateStore(t)
   323  
   324  	// Querying with no results returns nil.
   325  	ws := memdb.NewWatchSet()
   326  	idx, res, err := s.CARootActive(ws)
   327  	assert.Equal(idx, uint64(0))
   328  	assert.Nil(res)
   329  	assert.Nil(err)
   330  }
   331  
   332  func TestStore_CARoot_Snapshot_Restore(t *testing.T) {
   333  	assert := assert.New(t)
   334  	s := testStateStore(t)
   335  
   336  	// Create some intentions.
   337  	roots := structs.CARoots{
   338  		connect.TestCA(t, nil),
   339  		connect.TestCA(t, nil),
   340  		connect.TestCA(t, nil),
   341  	}
   342  	for _, r := range roots[1:] {
   343  		r.Active = false
   344  	}
   345  
   346  	// Force the sort order of the UUIDs before we create them so the
   347  	// order is deterministic.
   348  	id := testUUID()
   349  	roots[0].ID = "a" + id[1:]
   350  	roots[1].ID = "b" + id[1:]
   351  	roots[2].ID = "c" + id[1:]
   352  
   353  	// Now create
   354  	ok, err := s.CARootSetCAS(1, 0, roots)
   355  	assert.Nil(err)
   356  	assert.True(ok)
   357  
   358  	// Snapshot the queries.
   359  	snap := s.Snapshot()
   360  	defer snap.Close()
   361  
   362  	// Alter the real state store.
   363  	ok, err = s.CARootSetCAS(2, 1, roots[:1])
   364  	assert.Nil(err)
   365  	assert.True(ok)
   366  
   367  	// Verify the snapshot.
   368  	assert.Equal(snap.LastIndex(), uint64(1))
   369  	dump, err := snap.CARoots()
   370  	assert.Nil(err)
   371  	assert.Equal(roots, dump)
   372  
   373  	// Restore the values into a new state store.
   374  	func() {
   375  		s := testStateStore(t)
   376  		restore := s.Restore()
   377  		for _, r := range dump {
   378  			assert.Nil(restore.CARoot(r))
   379  		}
   380  		restore.Commit()
   381  
   382  		// Read the restored values back out and verify that they match.
   383  		idx, actual, err := s.CARoots(nil)
   384  		assert.Nil(err)
   385  		assert.Equal(idx, uint64(2))
   386  		assert.Equal(roots, actual)
   387  	}()
   388  }
   389  
   390  func TestStore_CABuiltinProvider(t *testing.T) {
   391  	assert := assert.New(t)
   392  	s := testStateStore(t)
   393  
   394  	{
   395  		expected := &structs.CAConsulProviderState{
   396  			ID:         "foo",
   397  			PrivateKey: "a",
   398  			RootCert:   "b",
   399  		}
   400  
   401  		ok, err := s.CASetProviderState(0, expected)
   402  		assert.NoError(err)
   403  		assert.True(ok)
   404  
   405  		idx, state, err := s.CAProviderState(expected.ID)
   406  		assert.NoError(err)
   407  		assert.Equal(idx, uint64(0))
   408  		assert.Equal(expected, state)
   409  	}
   410  
   411  	{
   412  		expected := &structs.CAConsulProviderState{
   413  			ID:         "bar",
   414  			PrivateKey: "c",
   415  			RootCert:   "d",
   416  		}
   417  
   418  		ok, err := s.CASetProviderState(1, expected)
   419  		assert.NoError(err)
   420  		assert.True(ok)
   421  
   422  		idx, state, err := s.CAProviderState(expected.ID)
   423  		assert.NoError(err)
   424  		assert.Equal(idx, uint64(1))
   425  		assert.Equal(expected, state)
   426  	}
   427  }
   428  
   429  func TestStore_CABuiltinProvider_Snapshot_Restore(t *testing.T) {
   430  	assert := assert.New(t)
   431  	s := testStateStore(t)
   432  
   433  	// Create multiple state entries.
   434  	before := []*structs.CAConsulProviderState{
   435  		{
   436  			ID:         "bar",
   437  			PrivateKey: "y",
   438  			RootCert:   "z",
   439  		},
   440  		{
   441  			ID:         "foo",
   442  			PrivateKey: "a",
   443  			RootCert:   "b",
   444  		},
   445  	}
   446  
   447  	for i, state := range before {
   448  		ok, err := s.CASetProviderState(uint64(98+i), state)
   449  		assert.NoError(err)
   450  		assert.True(ok)
   451  	}
   452  
   453  	// Take a snapshot.
   454  	snap := s.Snapshot()
   455  	defer snap.Close()
   456  
   457  	// Modify the state store.
   458  	after := &structs.CAConsulProviderState{
   459  		ID:         "foo",
   460  		PrivateKey: "c",
   461  		RootCert:   "d",
   462  	}
   463  	ok, err := s.CASetProviderState(100, after)
   464  	assert.NoError(err)
   465  	assert.True(ok)
   466  
   467  	snapped, err := snap.CAProviderState()
   468  	assert.NoError(err)
   469  	assert.Equal(before, snapped)
   470  
   471  	// Restore onto a new state store.
   472  	s2 := testStateStore(t)
   473  	restore := s2.Restore()
   474  	for _, entry := range snapped {
   475  		assert.NoError(restore.CAProviderState(entry))
   476  	}
   477  	restore.Commit()
   478  
   479  	// Verify the restored values match those from before the snapshot.
   480  	for _, state := range before {
   481  		idx, res, err := s2.CAProviderState(state.ID)
   482  		assert.NoError(err)
   483  		assert.Equal(idx, uint64(99))
   484  		assert.Equal(state, res)
   485  	}
   486  }