agent: add a clone function for duplicating the serf lan configuration (#11443)

This commit is contained in:
R.B. Boyer 2021-10-28 16:11:26 -05:00 committed by GitHub
parent 977be77493
commit af9ffc214d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 176 additions and 0 deletions

View File

@ -1251,6 +1251,10 @@ func newConsulConfig(runtimeCfg *config.RuntimeConfig, logger hclog.Logger) (*co
cfg.ConfigEntryBootstrap = runtimeCfg.ConfigEntryBootstrap
// Duplicate our own serf config once to make sure that the duplication
// function does not drift.
cfg.SerfLANConfig = consul.CloneSerfLANConfig(cfg.SerfLANConfig)
enterpriseConsulConfig(cfg, runtimeCfg)
return cfg, nil
}

View File

@ -541,6 +541,49 @@ func DefaultConfig() *Config {
return conf
}
// CloneSerfLANConfig clones an existing serf.Config used on the LAN by
// reconstructing it from defaults and re-applying changes made in the agent
// configs.
//
// This function is tricky to keep from rotting so we enforce that it MUST work
// by cloning our own serf LAN configuration on startup and only using the
// cloned one so any configs we need to change have to be changed here for them
// to work at all.
func CloneSerfLANConfig(base *serf.Config) *serf.Config {
cfg := DefaultConfig().SerfLANConfig
// from consul.DefaultConfig()
cfg.ReconnectTimeout = base.ReconnectTimeout
cfg.MemberlistConfig.BindPort = base.MemberlistConfig.BindPort
cfg.MemberlistConfig.DeadNodeReclaimTime = base.MemberlistConfig.DeadNodeReclaimTime
// from agent.newConsulConfig()
cfg.MemberlistConfig.BindAddr = base.MemberlistConfig.BindAddr
cfg.MemberlistConfig.BindPort = base.MemberlistConfig.BindPort
cfg.MemberlistConfig.CIDRsAllowed = base.MemberlistConfig.CIDRsAllowed
cfg.MemberlistConfig.AdvertiseAddr = base.MemberlistConfig.AdvertiseAddr
cfg.MemberlistConfig.AdvertisePort = base.MemberlistConfig.AdvertisePort
cfg.MemberlistConfig.GossipVerifyIncoming = base.MemberlistConfig.GossipVerifyIncoming
cfg.MemberlistConfig.GossipVerifyOutgoing = base.MemberlistConfig.GossipVerifyOutgoing
cfg.MemberlistConfig.GossipInterval = base.MemberlistConfig.GossipInterval
cfg.MemberlistConfig.GossipNodes = base.MemberlistConfig.GossipNodes
cfg.MemberlistConfig.ProbeInterval = base.MemberlistConfig.ProbeInterval
cfg.MemberlistConfig.ProbeTimeout = base.MemberlistConfig.ProbeTimeout
cfg.MemberlistConfig.SuspicionMult = base.MemberlistConfig.SuspicionMult
cfg.MemberlistConfig.RetransmitMult = base.MemberlistConfig.RetransmitMult
// agent/keyring.go
cfg.MemberlistConfig.Keyring = base.MemberlistConfig.Keyring
// tests
cfg.KeyringFile = base.KeyringFile
cfg.ReapInterval = base.ReapInterval
cfg.TombstoneTimeout = base.TombstoneTimeout
cfg.MemberlistConfig.SecretKey = base.MemberlistConfig.SecretKey
return cfg
}
// RPCConfig settings for the RPC server
//
// TODO: move many settings to this struct.

129
agent/consul/config_test.go Normal file
View File

@ -0,0 +1,129 @@
package consul
import (
"reflect"
"testing"
"time"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
)
func TestCloneSerfLANConfig(t *testing.T) {
config := DefaultConfig().SerfLANConfig
// NOTE: ALL fields on serf.Config and memberlist.Config MUST BE
// represented either here or in the CloneSerfLANConfig function body.
// Failure to add it to the clone or ignore sections will fail the test.
memberlistIgnoreFieldNames := []string{
"Alive",
"AwarenessMaxMultiplier",
"Conflict",
"DNSConfigPath",
"Delegate",
"DelegateProtocolMax",
"DelegateProtocolMin",
"DelegateProtocolVersion",
"DisableTcpPings",
"DisableTcpPingsForNode",
"EnableCompression",
"Events",
"GossipToTheDeadTime",
"HandoffQueueDepth",
"IndirectChecks",
"LogOutput",
"Logger",
"Merge",
"Name",
"Ping",
"ProtocolVersion",
"PushPullInterval",
"RequireNodeNames",
"SuspicionMaxTimeoutMult",
"TCPTimeout",
"Transport",
"UDPBufferSize",
}
serfIgnoreFieldNames := []string{
"BroadcastTimeout",
"CoalescePeriod",
"DisableCoordinates",
"EnableNameConflictResolution",
"EventBuffer",
"EventCh",
"FlapTimeout",
"LeavePropagateDelay",
"LogOutput",
"Logger",
"MaxQueueDepth",
"MemberlistConfig",
"Merge",
"MinQueueDepth",
"NodeName",
"ProtocolVersion",
"QueryBuffer",
"QueryResponseSizeLimit",
"QuerySizeLimit",
"QueryTimeoutMult",
"QueueCheckInterval",
"QueueDepthWarning",
"QuiescentPeriod",
"RecentIntentTimeout",
"ReconnectInterval",
"ReconnectTimeoutOverride",
"RejoinAfterLeave",
"SnapshotPath",
"Tags",
"UserCoalescePeriod",
"UserEventSizeLimit",
"UserQuiescentPeriod",
"ValidateNodeNames",
}
serfFuzzed := fuzzNonIgnoredFields(config, serfIgnoreFieldNames)
t.Logf("Fuzzing serf.Config fields: %v", serfFuzzed)
memberlistFuzzed := fuzzNonIgnoredFields(config.MemberlistConfig, memberlistIgnoreFieldNames)
t.Logf("Fuzzing memberlist.Config fields: %v", memberlistFuzzed)
clone := CloneSerfLANConfig(config)
require.Equal(t, config, clone)
}
func fuzzNonIgnoredFields(value interface{}, ignoredFields []string) []string {
ignored := make(map[string]struct{})
for _, field := range ignoredFields {
ignored[field] = struct{}{}
}
var fuzzed []string
// Walk the fields of our object to fuzz and selectively only fuzz the
// fields that were not ignored.
fuzzer := fuzz.NewWithSeed(time.Now().UnixNano())
v := reflect.ValueOf(value).Elem()
for i := 0; i < v.NumField(); i++ {
field := v.Field(i)
if !field.CanInterface() {
continue // skip unexported fields
}
fieldName := v.Type().Field(i).Name
if _, ok := ignored[fieldName]; ok {
continue
}
fuzzed = append(fuzzed, fieldName)
// copy the data somewhere mutable
tmp := reflect.New(field.Type())
tmp.Elem().Set(field)
// fuzz the copy
fuzzer.Fuzz(tmp.Interface())
// and set the fuzzed copy back to the original location
field.Set(reflect.Indirect(tmp))
}
return fuzzed
}