diff --git a/.changelog/12298.txt b/.changelog/12298.txt new file mode 100644 index 0000000000..b413e6ffa7 --- /dev/null +++ b/.changelog/12298.txt @@ -0,0 +1,3 @@ +```release-note:improvement +connect: reduce raft apply on CA configuration when no change is performed +``` diff --git a/agent/consul/leader_connect_ca.go b/agent/consul/leader_connect_ca.go index 35b4f343eb..ed5587ff57 100644 --- a/agent/consul/leader_connect_ca.go +++ b/agent/consul/leader_connect_ca.go @@ -693,7 +693,7 @@ func (c *CAManager) persistNewRootAndConfig(provider ca.Provider, newActiveRoot return fmt.Errorf("local CA not initialized yet") } // Exit early if the change is a no-op. - if newActiveRoot == nil && config != nil && config.Provider == storedConfig.Provider && reflect.DeepEqual(config.Config, storedConfig.Config) { + if !shouldPersistNewRootAndConfig(newActiveRoot, storedConfig, config) { return nil } @@ -758,6 +758,17 @@ func (c *CAManager) persistNewRootAndConfig(provider ca.Provider, newActiveRoot return nil } +func shouldPersistNewRootAndConfig(newActiveRoot *structs.CARoot, oldConfig, newConfig *structs.CAConfiguration) bool { + if newActiveRoot != nil { + return true + } + + if newConfig == nil { + return false + } + return newConfig.Provider == oldConfig.Provider && reflect.DeepEqual(newConfig.Config, oldConfig.Config) +} + func (c *CAManager) UpdateConfiguration(args *structs.CARequest) (reterr error) { // Attempt to update the state first. oldState, err := c.setState(caStateReconfig, true) diff --git a/agent/consul/leader_connect_ca_test.go b/agent/consul/leader_connect_ca_test.go index 73787700aa..1b4eaf38fd 100644 --- a/agent/consul/leader_connect_ca_test.go +++ b/agent/consul/leader_connect_ca_test.go @@ -693,6 +693,62 @@ func TestCAManager_Initialize_Vault_WithIntermediateAsPrimaryCA(t *testing.T) { }) } +func TestCAManager_Verify_Vault_NoChangeToSecondaryConfig(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + ca.SkipIfVaultNotPresent(t) + + vault := ca.NewTestVaultServer(t) + + _, sDC1 := testServerWithConfig(t, func(c *Config) { + c.CAConfig = &structs.CAConfiguration{ + Provider: "vault", + Config: map[string]interface{}{ + "Address": vault.Addr, + "Token": vault.RootToken, + "RootPKIPath": "pki-root/", + "IntermediatePKIPath": "pki-intermediate/", + }, + } + }) + defer sDC1.Shutdown() + testrpc.WaitForActiveCARoot(t, sDC1.RPC, "dc1", nil) + + _, sDC2 := testServerWithConfig(t, func(c *Config) { + c.Datacenter = "dc2" + c.PrimaryDatacenter = "dc1" + c.CAConfig = &structs.CAConfiguration{ + Provider: "vault", + Config: map[string]interface{}{ + "Address": vault.Addr, + "Token": vault.RootToken, + "RootPKIPath": "pki-root/", + "IntermediatePKIPath": "pki-intermediate-2/", + }, + } + }) + defer sDC2.Shutdown() + joinWAN(t, sDC2, sDC1) + testrpc.WaitForActiveCARoot(t, sDC2.RPC, "dc2", nil) + + codec := rpcClient(t, sDC2) + var configBefore structs.CAConfiguration + err := msgpackrpc.CallWithCodec(codec, "ConnectCA.ConfigurationGet", &structs.DCSpecificRequest{}, &configBefore) + require.NoError(t, err) + + renewLeafSigningCert(t, sDC1.caManager, sDC1.caManager.primaryRenewIntermediate) + + // Give the secondary some time to notice the update + time.Sleep(100 * time.Millisecond) + + var configAfter structs.CAConfiguration + err = msgpackrpc.CallWithCodec(codec, "ConnectCA.ConfigurationGet", &structs.DCSpecificRequest{}, &configAfter) + require.NoError(t, err) + + require.EqualValues(t, configBefore.ModifyIndex, configAfter.ModifyIndex) +} + func getLeafCert(t *testing.T, codec rpc.ClientCodec, trustDomain string, dc string) string { pk, _, err := connect.GeneratePrivateKey() require.NoError(t, err)