Hash based config entry replication (#19795)

* add a hash to config entries when normalizing

* add GetHash and implement comparing hashes

* only update if the Hash is different

* only update if the Hash is different and not 0

* fix proto to include the Hash

* fix proto gen

* buf format

* add SetHash and fix tests

* fix config load tests

* fix state test and config test

* recalculate hash when restoring config entries

* fix snapshot restore test

* add changelog

* fix missing normalize, fix proto indexes and add normalize test
This commit is contained in:
Dhia Ayachi 2023-12-12 08:29:13 -05:00 committed by GitHub
parent 90010587f0
commit f2b26ac194
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1498 additions and 984 deletions

3
.changelog/19795.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:enhancement
wan-federation: use a hash to diff config entries when replicating in the secondary DC to avoid unnecessary writes..
```

View File

@ -6227,6 +6227,9 @@ func (tc testCase) run(format string, dataDir string) func(t *testing.T) {
expected.ACLResolverSettings.NodeName = expected.NodeName
expected.ACLResolverSettings.EnterpriseMeta = *structs.NodeEnterpriseMetaInPartition(expected.PartitionOrDefault())
for i, e := range expected.ConfigEntryBootstrap {
e.SetHash(actual.ConfigEntryBootstrap[i].GetHash())
}
prototest.AssertDeepEqual(t, expected, actual, cmpopts.EquateEmpty())
if tc.cleanup != nil {
tc.cleanup()
@ -7122,6 +7125,9 @@ func TestLoad_FullConfig(t *testing.T) {
time.Date(2019, 11, 20, 5, 0, 0, 0, time.UTC)))
r, err := Load(opts)
require.NoError(t, err)
for i, e := range expected.ConfigEntryBootstrap {
e.SetHash(r.RuntimeConfig.ConfigEntryBootstrap[i].GetHash())
}
prototest.AssertDeepEqual(t, expected, r.RuntimeConfig)
require.ElementsMatch(t, expectedWarns, r.Warnings, "Warnings: %#v", r.Warnings)
})

View File

@ -38,3 +38,10 @@ func EqualID(e1, e2 structs.ConfigEntry) bool {
e1.GetEnterpriseMeta().IsSame(e2.GetEnterpriseMeta()) &&
e1.GetName() == e2.GetName()
}
func SameHash(e1, e2 structs.ConfigEntry) bool {
if e1.GetHash() == 0 || e2.GetHash() == 0 {
return false
}
return e1.GetHash() == e2.GetHash()
}

View File

@ -30,7 +30,9 @@ func diffConfigEntries(local []structs.ConfigEntry, remote []structs.ConfigEntry
if configentry.EqualID(local[localIdx], remote[remoteIdx]) {
// config is in both the local and remote state - need to check raft indices
if remote[remoteIdx].GetRaftIndex().ModifyIndex > lastRemoteIndex {
updates = append(updates, remote[remoteIdx])
if !configentry.SameHash(local[localIdx], remote[remoteIdx]) {
updates = append(updates, remote[remoteIdx])
}
}
// increment both indices when equal
localIdx += 1

View File

@ -6,6 +6,8 @@ package consul
import (
"context"
"fmt"
"github.com/oklog/ulid/v2"
"github.com/stretchr/testify/assert"
"os"
"testing"
@ -268,3 +270,63 @@ func TestReplication_ConfigEntries_GraphValidationErrorDuringReplication(t *test
checkSame(r)
})
}
func createConfigEntries(num int, indexStart int) []structs.ConfigEntry {
entries := make([]structs.ConfigEntry, num)
for i := range entries {
entries[i] = &structs.ServiceConfigEntry{Name: ulid.Make().String(), RaftIndex: structs.RaftIndex{ModifyIndex: uint64(i + indexStart)}}
}
return entries
}
func mutateIDs(e []structs.ConfigEntry, indexStart int) []structs.ConfigEntry {
entries := make([]structs.ConfigEntry, len(e))
for i := range entries {
entries[i] = &structs.ServiceConfigEntry{Name: e[i].GetName(), RaftIndex: structs.RaftIndex{ModifyIndex: uint64(i + indexStart)}}
}
return entries
}
func Test_diffConfigEntries(t *testing.T) {
type args struct {
local []structs.ConfigEntry
remote []structs.ConfigEntry
lastRemoteIndex uint64
normalize bool
}
entries1 := createConfigEntries(10, 10)
entries2 := createConfigEntries(10, 20)
entries3 := append(entries1, entries2...)
entries4 := mutateIDs(entries1, 20)
entries5 := mutateIDs(entries1, 0)
tests := []struct {
name string
args args
updated []structs.ConfigEntry
deleted []structs.ConfigEntry
}{
{"empty", args{local: make([]structs.ConfigEntry, 0), remote: make([]structs.ConfigEntry, 0), lastRemoteIndex: 0, normalize: true}, nil, nil},
{"same", args{local: entries1, remote: entries1, lastRemoteIndex: 0, normalize: true}, nil, nil},
{"new remote", args{local: nil, remote: entries1, lastRemoteIndex: 0, normalize: true}, entries1, nil},
{"extra remote", args{local: entries1, remote: entries3, lastRemoteIndex: 0, normalize: true}, entries2, nil},
{"extra local", args{local: entries3, remote: entries1, lastRemoteIndex: 0, normalize: true}, nil, entries2},
{"same, same size, different raft ID", args{local: entries1, remote: entries4, lastRemoteIndex: 0, normalize: true}, nil, nil},
{"when hash is empty, avoid hash compare", args{local: entries5, remote: entries4, lastRemoteIndex: 0, normalize: false}, entries4, nil},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.normalize {
for _, l := range tt.args.local {
require.NoError(t, l.Normalize())
}
for _, r := range tt.args.remote {
require.NoError(t, r.Normalize())
}
}
deletions, updates := diffConfigEntries(tt.args.local, tt.args.remote, tt.args.lastRemoteIndex)
assert.Equalf(t, tt.updated, updates, "updated diffConfigEntries(%v, %v, %v)", tt.args.local, tt.args.remote, tt.args.lastRemoteIndex)
assert.Equalf(t, tt.deleted, deletions, "deleted diffConfigEntries(%v, %v, %v)", tt.args.local, tt.args.remote, tt.args.lastRemoteIndex)
})
}
}

View File

@ -815,14 +815,17 @@ func TestFSM_SnapshotRestore_CE(t *testing.T) {
// Verify config entries are restored
_, serviceConfEntry, err := fsm2.state.ConfigEntry(nil, structs.ServiceDefaults, "foo", structs.DefaultEnterpriseMetaInDefaultPartition())
require.NoError(t, err)
serviceConfig.SetHash(serviceConfEntry.GetHash())
require.Equal(t, serviceConfig, serviceConfEntry)
_, proxyConfEntry, err := fsm2.state.ConfigEntry(nil, structs.ProxyDefaults, "global", structs.DefaultEnterpriseMetaInDefaultPartition())
require.NoError(t, err)
proxyConfig.SetHash(proxyConfEntry.GetHash())
require.Equal(t, proxyConfig, proxyConfEntry)
_, ingressRestored, err := fsm2.state.ConfigEntry(nil, structs.IngressGateway, "ingress", structs.DefaultEnterpriseMetaInDefaultPartition())
require.NoError(t, err)
ingress.SetHash(ingressRestored.GetHash())
require.Equal(t, ingress, ingressRestored)
_, restoredGatewayServices, err := fsm2.state.GatewayServices(nil, "ingress", structs.DefaultEnterpriseMetaInDefaultPartition())
@ -856,11 +859,13 @@ func TestFSM_SnapshotRestore_CE(t *testing.T) {
// Verify service-intentions is restored
_, serviceIxnEntry, err := fsm2.state.ConfigEntry(nil, structs.ServiceIntentions, "foo", structs.DefaultEnterpriseMetaInDefaultPartition())
require.NoError(t, err)
serviceIxn.SetHash(serviceIxnEntry.GetHash())
require.Equal(t, serviceIxn, serviceIxnEntry)
// Verify mesh config entry is restored
_, meshConfigEntry, err := fsm2.state.ConfigEntry(nil, structs.MeshConfig, structs.MeshConfigMesh, structs.DefaultEnterpriseMetaInDefaultPartition())
require.NoError(t, err)
meshConfig.SetHash(meshConfigEntry.GetHash())
require.Equal(t, meshConfig, meshConfigEntry)
_, restoredServiceNames, err := fsm2.state.ServiceNamesOfKind(nil, structs.ServiceKindTypical)

View File

@ -103,6 +103,13 @@ func (s *Snapshot) ConfigEntries() ([]structs.ConfigEntry, error) {
// ConfigEntry is used when restoring from a snapshot.
func (s *Restore) ConfigEntry(c structs.ConfigEntry) error {
// the hash is recalculated when restoring config entries
// in case a new field is added in a newer version.
h, err := structs.HashConfigEntry(c)
if err != nil {
return err
}
c.SetHash(h)
return insertConfigEntryWithTxn(s.tx, c.GetRaftIndex().ModifyIndex, c)
}

View File

@ -313,6 +313,7 @@ func testStore_IntentionMutation(t *testing.T, s *Store) {
src.LegacyMeta = nil
}
}
expect.SetHash(got.GetHash())
require.Equal(t, expect, got)
}

View File

@ -95,6 +95,16 @@ type ConfigEntry interface {
GetMeta() map[string]string
GetEnterpriseMeta() *acl.EnterpriseMeta
GetRaftIndex() *RaftIndex
GetHash() uint64
SetHash(h uint64)
}
func HashConfigEntry(conf ConfigEntry) (uint64, error) {
hash, err := hashstructure.Hash(conf, nil)
if err != nil {
return hash, err
}
return hash, nil
}
// ControlledConfigEntry is an optional interface implemented by a ConfigEntry
@ -169,8 +179,17 @@ type ServiceConfigEntry struct {
EnvoyExtensions EnvoyExtensions `json:",omitempty" alias:"envoy_extensions"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *ServiceConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ServiceConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *ServiceConfigEntry) Clone() *ServiceConfigEntry {
@ -225,6 +244,11 @@ func (e *ServiceConfigEntry) Normalize() error {
}
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return validationErr
}
@ -451,8 +475,17 @@ type ProxyConfigEntry struct {
PrioritizeByLocality *ServiceResolverPrioritizeByLocality `json:",omitempty" alias:"prioritize_by_locality"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *ProxyConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ProxyConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *ProxyConfigEntry) GetKind() string {
@ -491,7 +524,13 @@ func (e *ProxyConfigEntry) Normalize() error {
e.EnterpriseMeta.Normalize()
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
func (e *ProxyConfigEntry) Validate() error {

View File

@ -79,8 +79,17 @@ type ServiceRouterConfigEntry struct {
Routes []ServiceRoute
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *ServiceRouterConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ServiceRouterConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *ServiceRouterConfigEntry) GetKind() string {
@ -129,6 +138,11 @@ func (e *ServiceRouterConfigEntry) Normalize() error {
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -537,8 +551,17 @@ type ServiceSplitterConfigEntry struct {
Splits []ServiceSplit
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *ServiceSplitterConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ServiceSplitterConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *ServiceSplitterConfigEntry) GetKind() string {
@ -581,6 +604,11 @@ func (e *ServiceSplitterConfigEntry) Normalize() error {
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -876,8 +904,17 @@ type ServiceResolverConfigEntry struct {
LoadBalancer *LoadBalancer `json:",omitempty" alias:"load_balancer"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *ServiceResolverConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ServiceResolverConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *ServiceResolverConfigEntry) RelatedPeers() []string {
@ -998,6 +1035,11 @@ func (e *ServiceResolverConfigEntry) Normalize() error {
e.EnterpriseMeta.Normalize()
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -20,8 +20,17 @@ type ExportedServicesConfigEntry struct {
Services []ExportedService `json:",omitempty"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *ExportedServicesConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ExportedServicesConfigEntry) GetHash() uint64 {
return e.Hash
}
// ExportedService manages the exporting of a service in the local partition to
@ -79,6 +88,11 @@ func (e *ExportedServicesConfigEntry) Normalize() error {
for i := range e.Services {
e.Services[i].Namespace = acl.NormalizeNamespace(e.Services[i].Namespace)
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -45,8 +45,17 @@ type IngressGatewayConfigEntry struct {
Defaults *IngressServiceConfig `json:",omitempty"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *IngressGatewayConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *IngressGatewayConfigEntry) GetHash() uint64 {
return e.Hash
}
type IngressServiceConfig struct {
@ -196,6 +205,12 @@ func (e *IngressGatewayConfigEntry) Normalize() error {
e.Listeners[i] = listener
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -470,8 +485,17 @@ type TerminatingGatewayConfigEntry struct {
Services []LinkedService
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *TerminatingGatewayConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *TerminatingGatewayConfigEntry) GetHash() uint64 {
return e.Hash
}
// A LinkedService is a service represented by a terminating gateway
@ -529,6 +553,11 @@ func (e *TerminatingGatewayConfigEntry) Normalize() error {
e.Services[i].EnterpriseMeta.Normalize()
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -715,8 +744,17 @@ type APIGatewayConfigEntry struct {
Status Status
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *APIGatewayConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *APIGatewayConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *APIGatewayConfigEntry) GetKind() string { return APIGateway }
@ -767,6 +805,11 @@ func (e *APIGatewayConfigEntry) Normalize() error {
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -990,8 +1033,17 @@ type BoundAPIGatewayConfigEntry struct {
Services ServiceRouteReferences
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *BoundAPIGatewayConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *BoundAPIGatewayConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *BoundAPIGatewayConfigEntry) IsSame(other *BoundAPIGatewayConfigEntry) bool {
@ -1083,6 +1135,12 @@ func (e *BoundAPIGatewayConfigEntry) Normalize() error {
e.Listeners[i] = listener
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -31,13 +31,29 @@ type InlineCertificateConfigEntry struct {
PrivateKey string
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *InlineCertificateConfigEntry) GetKind() string { return InlineCertificate }
func (e *InlineCertificateConfigEntry) GetName() string { return e.Name }
func (e *InlineCertificateConfigEntry) Normalize() error { return nil }
func (e *InlineCertificateConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *InlineCertificateConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *InlineCertificateConfigEntry) GetKind() string { return InlineCertificate }
func (e *InlineCertificateConfigEntry) GetName() string { return e.Name }
func (e *InlineCertificateConfigEntry) Normalize() error {
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
func (e *InlineCertificateConfigEntry) GetMeta() map[string]string { return e.Meta }
func (e *InlineCertificateConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
return &e.EnterpriseMeta

View File

@ -26,7 +26,16 @@ type ServiceIntentionsConfigEntry struct {
Meta map[string]string `json:",omitempty"` // formerly Intention.Meta
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"` // formerly DestinationNS
RaftIndex
Hash uint64 `json:",omitempty" hash:"ignore"`
RaftIndex `hash:"ignore"`
}
func (e *ServiceIntentionsConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ServiceIntentionsConfigEntry) GetHash() uint64 {
return e.Hash
}
var _ UpdatableConfigEntry = (*ServiceIntentionsConfigEntry)(nil)
@ -577,6 +586,11 @@ func (e *ServiceIntentionsConfigEntry) normalize(legacyWrite bool) error {
return e.Sources[i].Precedence > e.Sources[j].Precedence
})
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -68,8 +68,17 @@ type JWTProviderConfigEntry struct {
CacheConfig *JWTCacheConfig `json:",omitempty" alias:"cache_config"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *JWTProviderConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *JWTProviderConfigEntry) GetHash() uint64 {
return e.Hash
}
// JWTLocation is a location where the JWT could be present in requests.
@ -546,5 +555,11 @@ func (e *JWTProviderConfigEntry) Normalize() error {
e.ClockSkewSeconds = DefaultClockSkewSeconds
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -27,8 +27,17 @@ type MeshConfigEntry struct {
Peering *PeeringMeshConfig `json:",omitempty"`
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *MeshConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *MeshConfigEntry) GetHash() uint64 {
return e.Hash
}
// TransparentProxyMeshConfig contains cluster-wide options pertaining to
@ -92,6 +101,13 @@ func (e *MeshConfigEntry) Normalize() error {
}
e.EnterpriseMeta.Normalize()
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -44,8 +44,17 @@ type HTTPRouteConfigEntry struct {
Meta map[string]string `json:",omitempty"`
// Status is the asynchronous reconciliation status which an HTTPRoute propagates to the user.
Status Status
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *HTTPRouteConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *HTTPRouteConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *HTTPRouteConfigEntry) GetKind() string { return HTTPRoute }
@ -102,6 +111,12 @@ func (e *HTTPRouteConfigEntry) Normalize() error {
e.Rules[i] = rule
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -545,8 +560,17 @@ type TCPRouteConfigEntry struct {
Meta map[string]string `json:",omitempty"`
// Status is the asynchronous reconciliation status which a TCPRoute propagates to the user.
Status Status
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (e *TCPRouteConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *TCPRouteConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *TCPRouteConfigEntry) GetKind() string { return TCPRoute }
@ -592,6 +616,11 @@ func (e *TCPRouteConfigEntry) Normalize() error {
e.Services[i] = service
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}

View File

@ -16,8 +16,17 @@ type SamenessGroupConfigEntry struct {
IncludeLocal bool `json:",omitempty" alias:"include_local"`
Members []SamenessGroupMember
Meta map[string]string `json:",omitempty"`
Hash uint64 `json:",omitempty" hash:"ignore"`
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
RaftIndex
RaftIndex `hash:"ignore"`
}
func (s *SamenessGroupConfigEntry) SetHash(h uint64) {
s.Hash = h
}
func (s *SamenessGroupConfigEntry) GetHash() uint64 {
return s.Hash
}
func (s *SamenessGroupConfigEntry) GetKind() string { return SamenessGroup }
@ -45,6 +54,12 @@ func (s *SamenessGroupConfigEntry) Normalize() error {
return fmt.Errorf("config entry is nil")
}
s.EnterpriseMeta.Normalize()
h, err := HashConfigEntry(s)
if err != nil {
return err
}
s.Hash = h
return nil
}

View File

@ -24,6 +24,20 @@ import (
"github.com/hashicorp/consul/types"
)
func TestNormalizeGenerateHash(t *testing.T) {
for _, cType := range AllConfigEntryKinds {
//this is an enterprise only config entry
if cType == RateLimitIPConfig {
continue
}
entry, err := MakeConfigEntry(cType, "global")
require.NoError(t, err)
require.NoError(t, entry.Normalize())
require.NotEmpty(t, entry.GetHash(), entry.GetKind())
}
}
func TestConfigEntries_ACLs(t *testing.T) {
type testACL = configEntryTestACL
type testcase = configEntryACLTestCase
@ -3407,6 +3421,7 @@ func testConfigEntryNormalizeAndValidate(t *testing.T, cases map[string]configEn
}
if tc.expected != nil {
tc.expected.SetHash(tc.entry.GetHash())
require.Equal(t, tc.expected, tc.entry)
}
@ -3420,6 +3435,7 @@ func testConfigEntryNormalizeAndValidate(t *testing.T, cases map[string]configEn
return a.IsSame(&b)
}),
}
beforeNormalize.(ConfigEntry).SetHash(tc.entry.GetHash())
if diff := cmp.Diff(beforeNormalize, tc.entry, opts); diff != "" {
t.Fatalf("expect unchanged after Normalize, got diff:\n%s", diff)
}

View File

@ -20,6 +20,7 @@ func APIGatewayToStructs(s *APIGateway, t *structs.APIGatewayConfigEntry) {
StatusToStructs(s.Status, &t.Status)
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func APIGatewayFromStructs(t *structs.APIGatewayConfigEntry, s *APIGateway) {
if s == nil {
@ -41,6 +42,7 @@ func APIGatewayFromStructs(t *structs.APIGatewayConfigEntry, s *APIGateway) {
s.Status = &x
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func APIGatewayListenerToStructs(s *APIGatewayListener, t *structs.APIGatewayListener) {
if s == nil {
@ -148,6 +150,7 @@ func BoundAPIGatewayToStructs(s *BoundAPIGateway, t *structs.BoundAPIGatewayConf
}
t.Services = serviceRefsToStructs(s.Services)
t.Meta = s.Meta
t.Hash = s.Hash
}
func BoundAPIGatewayFromStructs(t *structs.BoundAPIGatewayConfigEntry, s *BoundAPIGateway) {
if s == nil {
@ -165,6 +168,7 @@ func BoundAPIGatewayFromStructs(t *structs.BoundAPIGatewayConfigEntry, s *BoundA
}
s.Services = serviceRefFromStructs(t.Services)
s.Meta = t.Meta
s.Hash = t.Hash
}
func BoundAPIGatewayListenerToStructs(s *BoundAPIGatewayListener, t *structs.BoundAPIGatewayListener) {
if s == nil {
@ -633,6 +637,7 @@ func HTTPRouteToStructs(s *HTTPRoute, t *structs.HTTPRouteConfigEntry) {
if s.Status != nil {
StatusToStructs(s.Status, &t.Status)
}
t.Hash = s.Hash
}
func HTTPRouteFromStructs(t *structs.HTTPRouteConfigEntry, s *HTTPRoute) {
if s == nil {
@ -665,6 +670,7 @@ func HTTPRouteFromStructs(t *structs.HTTPRouteConfigEntry, s *HTTPRoute) {
StatusFromStructs(&t.Status, &x)
s.Status = &x
}
s.Hash = t.Hash
}
func HTTPRouteRuleToStructs(s *HTTPRouteRule, t *structs.HTTPRouteRule) {
if s == nil {
@ -809,6 +815,7 @@ func IngressGatewayToStructs(s *IngressGateway, t *structs.IngressGatewayConfigE
t.Defaults = &x
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func IngressGatewayFromStructs(t *structs.IngressGatewayConfigEntry, s *IngressGateway) {
if s == nil {
@ -835,6 +842,7 @@ func IngressGatewayFromStructs(t *structs.IngressGatewayConfigEntry, s *IngressG
s.Defaults = &x
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func IngressListenerToStructs(s *IngressListener, t *structs.IngressListener) {
if s == nil {
@ -975,6 +983,7 @@ func InlineCertificateToStructs(s *InlineCertificate, t *structs.InlineCertifica
t.Certificate = s.Certificate
t.PrivateKey = s.PrivateKey
t.Meta = s.Meta
t.Hash = s.Hash
}
func InlineCertificateFromStructs(t *structs.InlineCertificateConfigEntry, s *InlineCertificate) {
if s == nil {
@ -983,6 +992,7 @@ func InlineCertificateFromStructs(t *structs.InlineCertificateConfigEntry, s *In
s.Certificate = t.Certificate
s.PrivateKey = t.PrivateKey
s.Meta = t.Meta
s.Hash = t.Hash
}
func InstanceLevelRateLimitsToStructs(s *InstanceLevelRateLimits, t *structs.InstanceLevelRateLimits) {
if s == nil {
@ -1481,6 +1491,7 @@ func JWTProviderToStructs(s *JWTProvider, t *structs.JWTProviderConfigEntry) {
t.CacheConfig = &x
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func JWTProviderFromStructs(t *structs.JWTProviderConfigEntry, s *JWTProvider) {
if s == nil {
@ -1515,6 +1526,7 @@ func JWTProviderFromStructs(t *structs.JWTProviderConfigEntry, s *JWTProvider) {
s.CacheConfig = &x
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func LeastRequestConfigToStructs(s *LeastRequestConfig, t *structs.LeastRequestConfig) {
if s == nil {
@ -1616,6 +1628,7 @@ func MeshConfigToStructs(s *MeshConfig, t *structs.MeshConfigEntry) {
t.Peering = &x
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func MeshConfigFromStructs(t *structs.MeshConfigEntry, s *MeshConfig) {
if s == nil {
@ -1643,6 +1656,7 @@ func MeshConfigFromStructs(t *structs.MeshConfigEntry, s *MeshConfig) {
s.Peering = &x
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func MeshDirectionalTLSConfigToStructs(s *MeshDirectionalTLSConfig, t *structs.MeshDirectionalTLSConfig) {
if s == nil {
@ -1882,6 +1896,7 @@ func SamenessGroupToStructs(s *SamenessGroup, t *structs.SamenessGroupConfigEntr
}
}
t.Meta = s.Meta
t.Hash = s.Hash
t.EnterpriseMeta = enterpriseMetaToStructs(s.EnterpriseMeta)
}
func SamenessGroupFromStructs(t *structs.SamenessGroupConfigEntry, s *SamenessGroup) {
@ -1902,6 +1917,7 @@ func SamenessGroupFromStructs(t *structs.SamenessGroupConfigEntry, s *SamenessGr
}
}
s.Meta = t.Meta
s.Hash = t.Hash
s.EnterpriseMeta = enterpriseMetaFromStructs(t.EnterpriseMeta)
}
func SamenessGroupMemberToStructs(s *SamenessGroupMember, t *structs.SamenessGroupMember) {
@ -1956,6 +1972,7 @@ func ServiceDefaultsToStructs(s *ServiceDefaults, t *structs.ServiceConfigEntry)
}
t.EnvoyExtensions = EnvoyExtensionsToStructs(s.EnvoyExtensions)
t.Meta = s.Meta
t.Hash = s.Hash
}
func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefaults) {
if s == nil {
@ -2001,6 +2018,7 @@ func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefault
}
s.EnvoyExtensions = EnvoyExtensionsFromStructs(t.EnvoyExtensions)
s.Meta = t.Meta
s.Hash = t.Hash
}
func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentionsConfigEntry) {
if s == nil {
@ -2022,6 +2040,7 @@ func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentio
t.JWT = &x
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *ServiceIntentions) {
if s == nil {
@ -2043,6 +2062,7 @@ func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *Se
s.JWT = &x
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func ServiceResolverToStructs(s *ServiceResolver, t *structs.ServiceResolverConfigEntry) {
if s == nil {
@ -2087,6 +2107,7 @@ func ServiceResolverToStructs(s *ServiceResolver, t *structs.ServiceResolverConf
t.LoadBalancer = &x
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func ServiceResolverFromStructs(t *structs.ServiceResolverConfigEntry, s *ServiceResolver) {
if s == nil {
@ -2135,6 +2156,7 @@ func ServiceResolverFromStructs(t *structs.ServiceResolverConfigEntry, s *Servic
s.LoadBalancer = &x
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func ServiceResolverFailoverToStructs(s *ServiceResolverFailover, t *structs.ServiceResolverFailover) {
if s == nil {
@ -2376,6 +2398,7 @@ func TCPRouteToStructs(s *TCPRoute, t *structs.TCPRouteConfigEntry) {
if s.Status != nil {
StatusToStructs(s.Status, &t.Status)
}
t.Hash = s.Hash
}
func TCPRouteFromStructs(t *structs.TCPRouteConfigEntry, s *TCPRoute) {
if s == nil {
@ -2407,6 +2430,7 @@ func TCPRouteFromStructs(t *structs.TCPRouteConfigEntry, s *TCPRoute) {
StatusFromStructs(&t.Status, &x)
s.Status = &x
}
s.Hash = t.Hash
}
func TCPServiceToStructs(s *TCPService, t *structs.TCPService) {
if s == nil {

File diff suppressed because it is too large Load Diff

View File

@ -61,6 +61,7 @@ message MeshConfig {
map<string, string> Meta = 4;
PeeringMeshConfig Peering = 5;
bool AllowEnablingPermissiveMutualTLS = 6;
uint64 Hash = 7;
}
// mog annotation:
@ -132,6 +133,7 @@ message ServiceResolver {
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
google.protobuf.Duration RequestTimeout = 8;
ServiceResolverPrioritizeByLocality PrioritizeByLocality = 9;
uint64 Hash = 10;
}
// mog annotation:
@ -274,6 +276,7 @@ message IngressGateway {
repeated IngressListener Listeners = 2;
map<string, string> Meta = 3;
IngressServiceConfig Defaults = 4;
uint64 Hash = 5;
}
// mog annotation:
@ -377,6 +380,7 @@ message ServiceIntentions {
repeated SourceIntention Sources = 1;
map<string, string> Meta = 2;
IntentionJWTRequirement JWT = 3;
uint64 Hash = 4;
}
// mog annotation:
@ -513,6 +517,7 @@ message ServiceDefaults {
repeated hashicorp.consul.internal.common.EnvoyExtension EnvoyExtensions = 14;
// mog: func-to=mutualTLSModeToStructs func-from=mutualTLSModeFromStructs
MutualTLSMode MutualTLSMode = 15;
uint64 Hash = 17;
}
enum ProxyMode {
@ -700,6 +705,7 @@ message APIGateway {
map<string, string> Meta = 1;
repeated APIGatewayListener Listeners = 2;
Status Status = 3;
uint64 Hash = 4;
}
// mog annotation:
@ -811,6 +817,7 @@ message BoundAPIGateway {
repeated BoundAPIGatewayListener Listeners = 2;
// mog: func-to=serviceRefsToStructs func-from=serviceRefFromStructs
map<string, ListOfResourceReference> Services = 3;
uint64 Hash = 4;
}
message ListOfResourceReference {
@ -838,6 +845,7 @@ message InlineCertificate {
map<string, string> Meta = 1;
string Certificate = 2;
string PrivateKey = 3;
uint64 Hash = 4;
}
// mog annotation:
@ -852,6 +860,7 @@ message HTTPRoute {
repeated HTTPRouteRule Rules = 3;
repeated string Hostnames = 4;
Status Status = 5;
uint64 Hash = 6;
}
// mog annotation:
@ -1044,6 +1053,7 @@ message TCPRoute {
repeated ResourceReference Parents = 2;
repeated TCPService Services = 3;
Status Status = 4;
uint64 Hash = 5;
}
// mog annotation:
@ -1071,6 +1081,7 @@ message SamenessGroup {
map<string, string> Meta = 5;
// mog: func-to=enterpriseMetaToStructs func-from=enterpriseMetaFromStructs
common.EnterpriseMeta EnterpriseMeta = 6;
uint64 Hash = 7;
}
// mog annotation:
@ -1099,6 +1110,7 @@ message JWTProvider {
map<string, string> Meta = 7;
// mog: func-to=int func-from=int32
int32 ClockSkewSeconds = 8;
uint64 Hash = 9;
}
// mog annotation: