Backport of Hash based config entry replication into release/1.15.x (#19914)

add hash based config entry replication

Co-authored-by: Dhia Ayachi <dhia@hashicorp.com>
pull/19921/head
hc-github-team-consul-core 2023-12-12 12:35:47 -06:00 committed by GitHub
parent 2c6d620439
commit d62bf2c06a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 2402 additions and 1826 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

@ -6036,6 +6036,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()
@ -6915,6 +6918,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

@ -122,6 +122,7 @@ func TestConfig_Get(t *testing.T) {
ce.CreateIndex = 12
ce.ModifyIndex = 13
ce.EnterpriseMeta = acl.EnterpriseMeta{}
ce.Hash = 0
out, err := a.srv.marshalJSON(req, obj)
require.NoError(t, err)
@ -447,6 +448,7 @@ func TestConfig_Apply_IngressGateway(t *testing.T) {
},
},
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
Hash: got.GetHash(),
}
require.Equal(t, expect, got)
}

View File

@ -18,6 +18,12 @@ func configSort(configs []structs.ConfigEntry) {
return cmpConfigLess(configs[i], configs[j])
})
}
func sameHash(e1, e2 structs.ConfigEntry) bool {
if e1.GetHash() == 0 || e2.GetHash() == 0 {
return false
}
return e1.GetHash() == e2.GetHash()
}
func diffConfigEntries(local []structs.ConfigEntry, remote []structs.ConfigEntry, lastRemoteIndex uint64) ([]structs.ConfigEntry, []structs.ConfigEntry) {
configSort(local)
@ -33,8 +39,10 @@ func diffConfigEntries(local []structs.ConfigEntry, remote []structs.ConfigEntry
if configSameID(local[localIdx], remote[remoteIdx]) {
// config is in both the local and remote state - need to check raft indices
if remote[remoteIdx].GetRaftIndex().ModifyIndex > lastRemoteIndex {
if !sameHash(local[localIdx], remote[remoteIdx]) {
updates = append(updates, remote[remoteIdx])
}
}
// increment both indices when equal
localIdx += 1
remoteIdx += 1

View File

@ -3,6 +3,8 @@ package consul
import (
"context"
"fmt"
"github.com/oklog/ulid/v2"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
@ -345,3 +347,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

@ -739,14 +739,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())
@ -780,11 +783,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

@ -547,6 +547,7 @@ func TestIntentionApply_WithoutIDs(t *testing.T) {
},
},
RaftIndex: entry.RaftIndex,
Hash: entry.GetHash(),
}
require.Equal(t, expect, entry)
@ -686,6 +687,7 @@ func TestIntentionApply_WithoutIDs(t *testing.T) {
},
},
RaftIndex: entry.RaftIndex,
Hash: entry.GetHash(),
}
require.Equal(t, expect, entry)
@ -755,6 +757,7 @@ func TestIntentionApply_WithoutIDs(t *testing.T) {
},
},
RaftIndex: entry.RaftIndex,
Hash: entry.GetHash(),
}
require.Equal(t, expect, entry)

View File

@ -2083,6 +2083,7 @@ func TestDatacenterSupportsIntentionsAsConfigEntries(t *testing.T) {
},
RaftIndex: got.RaftIndex,
Hash: got.GetHash(),
}
require.Equal(t, expect, got)

View File

@ -93,6 +93,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

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

View File

@ -85,6 +85,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
@ -137,8 +147,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 {
@ -193,6 +212,11 @@ func (e *ServiceConfigEntry) Normalize() error {
}
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return validationErr
}
@ -371,8 +395,17 @@ type ProxyConfigEntry 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 *ProxyConfigEntry) SetHash(h uint64) {
e.Hash = h
}
func (e *ProxyConfigEntry) GetHash() uint64 {
return e.Hash
}
func (e *ProxyConfigEntry) GetKind() string {
@ -411,7 +444,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

@ -75,8 +75,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 {
@ -125,6 +134,11 @@ func (e *ServiceRouterConfigEntry) Normalize() error {
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -533,8 +547,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 {
@ -577,6 +600,11 @@ func (e *ServiceSplitterConfigEntry) Normalize() error {
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -868,8 +896,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) MarshalJSON() ([]byte, error) {
@ -969,6 +1006,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

@ -17,8 +17,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
@ -107,6 +116,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

@ -42,8 +42,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 {
@ -193,6 +202,12 @@ func (e *IngressGatewayConfigEntry) Normalize() error {
e.Listeners[i] = listener
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -467,8 +482,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
@ -526,6 +550,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
}
@ -712,8 +741,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 }
@ -764,6 +802,11 @@ func (e *APIGatewayConfigEntry) Normalize() error {
}
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -971,8 +1014,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 {
@ -1064,6 +1116,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

@ -26,13 +26,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) 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 { return nil }
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

@ -20,7 +20,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)
@ -475,6 +484,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

@ -20,8 +20,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
@ -85,6 +94,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

@ -40,8 +40,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 }
@ -98,6 +107,12 @@ func (e *HTTPRouteConfigEntry) Normalize() error {
e.Rules[i] = rule
}
h, err := HashConfigEntry(e)
if err != nil {
return err
}
e.Hash = h
return nil
}
@ -481,8 +496,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 }
@ -528,6 +552,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

@ -21,6 +21,17 @@ import (
"github.com/hashicorp/consul/types"
)
func TestNormalizeGenerateHash(t *testing.T) {
for _, cType := range AllConfigEntryKinds {
//this is an enterprise only config entry
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
@ -3386,6 +3397,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)
}
@ -3399,6 +3411,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)
}

1
go.mod
View File

@ -85,6 +85,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/mitchellh/pointerstructure v1.2.1
github.com/mitchellh/reflectwalk v1.0.2
github.com/oklog/ulid/v2 v2.1.0
github.com/olekukonko/tablewriter v0.0.4
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1

3
go.sum
View File

@ -767,6 +767,8 @@ github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
@ -787,6 +789,7 @@ github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0Mw
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=

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 {
@ -116,6 +118,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 {
@ -133,6 +136,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 {
@ -551,6 +555,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 {
@ -583,6 +588,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 {
@ -711,6 +717,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 {
@ -737,6 +744,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 {
@ -877,6 +885,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 {
@ -885,6 +894,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 IntentionHTTPHeaderPermissionToStructs(s *IntentionHTTPHeaderPermission, t *structs.IntentionHTTPHeaderPermission) {
if s == nil {
@ -1053,6 +1063,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 {
@ -1079,6 +1090,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 {
@ -1246,6 +1258,7 @@ func ServiceDefaultsToStructs(s *ServiceDefaults, t *structs.ServiceConfigEntry)
t.BalanceInboundConnections = s.BalanceInboundConnections
t.EnvoyExtensions = EnvoyExtensionsToStructs(s.EnvoyExtensions)
t.Meta = s.Meta
t.Hash = s.Hash
}
func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefaults) {
if s == nil {
@ -1285,6 +1298,7 @@ func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefault
s.BalanceInboundConnections = t.BalanceInboundConnections
s.EnvoyExtensions = EnvoyExtensionsFromStructs(t.EnvoyExtensions)
s.Meta = t.Meta
s.Hash = t.Hash
}
func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentionsConfigEntry) {
if s == nil {
@ -1301,6 +1315,7 @@ func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentio
}
}
t.Meta = s.Meta
t.Hash = s.Hash
}
func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *ServiceIntentions) {
if s == nil {
@ -1317,6 +1332,7 @@ func ServiceIntentionsFromStructs(t *structs.ServiceIntentionsConfigEntry, s *Se
}
}
s.Meta = t.Meta
s.Hash = t.Hash
}
func ServiceResolverToStructs(s *ServiceResolver, t *structs.ServiceResolverConfigEntry) {
if s == nil {
@ -1356,6 +1372,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 {
@ -1399,6 +1416,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 {
@ -1598,6 +1616,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 {
@ -1629,6 +1648,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

@ -53,6 +53,7 @@ message MeshConfig {
MeshHTTPConfig HTTP = 3;
map<string, string> Meta = 4;
PeeringMeshConfig Peering = 5;
uint64 Hash = 7;
}
// mog annotation:
@ -123,6 +124,7 @@ message ServiceResolver {
map<string, string> Meta = 7;
// mog: func-to=structs.DurationFromProto func-from=structs.DurationToProto
google.protobuf.Duration RequestTimeout = 8;
uint64 Hash = 10;
}
// mog annotation:
@ -243,6 +245,7 @@ message IngressGateway {
repeated IngressListener Listeners = 2;
map<string, string> Meta = 3;
IngressServiceConfig Defaults = 4;
uint64 Hash = 5;
}
// mog annotation:
@ -345,6 +348,7 @@ message HTTPHeaderModifiers {
message ServiceIntentions {
repeated SourceIntention Sources = 1;
map<string, string> Meta = 2;
uint64 Hash = 4;
}
// mog annotation:
@ -447,6 +451,7 @@ message ServiceDefaults {
map<string, string> Meta = 13;
// mog: func-to=EnvoyExtensionsToStructs func-from=EnvoyExtensionsFromStructs
repeated hashicorp.consul.internal.common.EnvoyExtension EnvoyExtensions = 14;
uint64 Hash = 17;
}
enum ProxyMode {
@ -591,6 +596,7 @@ message APIGateway {
map<string, string> Meta = 1;
repeated APIGatewayListener Listeners = 2;
Status Status = 3;
uint64 Hash = 4;
}
// mog annotation:
@ -676,6 +682,7 @@ message BoundAPIGateway {
repeated BoundAPIGatewayListener Listeners = 2;
// mog: func-to=serviceRefsToStructs func-from=serviceRefFromStructs
map<string, ListOfResourceReference> Services = 3;
uint64 Hash = 4;
}
message ListOfResourceReference {
@ -703,6 +710,7 @@ message InlineCertificate {
map<string, string> Meta = 1;
string Certificate = 2;
string PrivateKey = 3;
uint64 Hash = 4;
}
// mog annotation:
@ -717,6 +725,7 @@ message HTTPRoute {
repeated HTTPRouteRule Rules = 3;
repeated string Hostnames = 4;
Status Status = 5;
uint64 Hash = 6;
}
// mog annotation:
@ -866,6 +875,7 @@ message TCPRoute {
repeated ResourceReference Parents = 2;
repeated TCPService Services = 3;
Status Status = 4;
uint64 Hash = 5;
}
// mog annotation:

File diff suppressed because it is too large Load Diff