Add sameness-group configuration entry. (#16608)
This commit adds a sameness-group config entry to the API and structs packages. It includes some validation logic and a new memdb index that tracks the default sameness-group for each partition. Sameness groups will simplify the effort of managing failovers / intentions / exports for peers and partitions. Note that this change purely to introduce the configuration entry and does not include the full functionality of sameness-groups.
This commit is contained in:
parent
9872eeaffe
commit
5d17b2c90b
|
@ -494,6 +494,11 @@ func insertConfigEntryWithTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry)
|
||||||
return fmt.Errorf("failed to persist service name: %v", err)
|
return fmt.Errorf("failed to persist service name: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case structs.SamenessGroup:
|
||||||
|
err := checkSamenessGroup(tx, conf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the config entry and update the index
|
// Insert the config entry and update the index
|
||||||
|
@ -539,6 +544,7 @@ func validateProposedConfigEntryInGraph(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case structs.SamenessGroup:
|
||||||
case structs.ServiceIntentions:
|
case structs.ServiceIntentions:
|
||||||
case structs.MeshConfig:
|
case structs.MeshConfig:
|
||||||
case structs.ExportedServices:
|
case structs.ExportedServices:
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
//go:build !consulent
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/go-memdb"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SamnessGroupDefaultIndex is a placeholder for OSS. Sameness-groups are enterprise only.
|
||||||
|
type SamenessGroupDefaultIndex struct{}
|
||||||
|
|
||||||
|
var _ memdb.Indexer = (*SamenessGroupDefaultIndex)(nil)
|
||||||
|
var _ memdb.MultiIndexer = (*SamenessGroupDefaultIndex)(nil)
|
||||||
|
|
||||||
|
func (*SamenessGroupDefaultIndex) FromObject(obj interface{}) (bool, [][]byte, error) {
|
||||||
|
return false, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*SamenessGroupDefaultIndex) FromArgs(args ...interface{}) ([]byte, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSamenessGroup(tx ReadTxn, newConfig structs.ConfigEntry) error {
|
||||||
|
return fmt.Errorf("sameness-groups are an enterprise-only feature")
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
//go:build !consulent
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package state
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStore_SamenessGroup_checkSamenessGroup(t *testing.T) {
|
||||||
|
s := testStateStore(t)
|
||||||
|
err := s.EnsureConfigEntry(0, &structs.SamenessGroupConfigEntry{
|
||||||
|
Name: "sg1",
|
||||||
|
})
|
||||||
|
require.ErrorContains(t, err, "sameness-groups are an enterprise-only feature")
|
||||||
|
}
|
|
@ -11,9 +11,10 @@ import (
|
||||||
const (
|
const (
|
||||||
tableConfigEntries = "config-entries"
|
tableConfigEntries = "config-entries"
|
||||||
|
|
||||||
indexLink = "link"
|
indexLink = "link"
|
||||||
indexIntentionLegacyID = "intention-legacy-id"
|
indexIntentionLegacyID = "intention-legacy-id"
|
||||||
indexSource = "intention-source"
|
indexSource = "intention-source"
|
||||||
|
indexSamenessGroupDefault = "sameness-group-default"
|
||||||
)
|
)
|
||||||
|
|
||||||
// configTableSchema returns a new table schema used to store global
|
// configTableSchema returns a new table schema used to store global
|
||||||
|
@ -50,6 +51,12 @@ func configTableSchema() *memdb.TableSchema {
|
||||||
Unique: false,
|
Unique: false,
|
||||||
Indexer: &ServiceIntentionSourceIndex{},
|
Indexer: &ServiceIntentionSourceIndex{},
|
||||||
},
|
},
|
||||||
|
indexSamenessGroupDefault: {
|
||||||
|
Name: indexSamenessGroupDefault,
|
||||||
|
AllowMissing: true,
|
||||||
|
Unique: true,
|
||||||
|
Indexer: &SamenessGroupDefaultIndex{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +74,7 @@ type configEntryIndexable interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ configEntryIndexable = (*structs.ExportedServicesConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.ExportedServicesConfigEntry)(nil)
|
||||||
|
var _ configEntryIndexable = (*structs.SamenessGroupConfigEntry)(nil)
|
||||||
var _ configEntryIndexable = (*structs.IngressGatewayConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.IngressGatewayConfigEntry)(nil)
|
||||||
var _ configEntryIndexable = (*structs.MeshConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.MeshConfigEntry)(nil)
|
||||||
var _ configEntryIndexable = (*structs.ProxyConfigEntry)(nil)
|
var _ configEntryIndexable = (*structs.ProxyConfigEntry)(nil)
|
||||||
|
|
|
@ -357,6 +357,22 @@ var baseCases = map[string]testCase{
|
||||||
{Name: "kind", Value: "exported-services"},
|
{Name: "kind", Value: "exported-services"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=sameness-group": { // Legacy
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "sameness-group"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=sameness-group": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "sameness-group"},
|
||||||
|
},
|
||||||
|
},
|
||||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=api-gateway": { // Legacy
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=api-gateway": { // Legacy
|
||||||
Name: "consul.usage.test.consul.state.config_entries",
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
|
@ -784,6 +800,22 @@ var baseCases = map[string]testCase{
|
||||||
{Name: "kind", Value: "exported-services"},
|
{Name: "kind", Value: "exported-services"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=sameness-group": { // Legacy
|
||||||
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "sameness-group"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=sameness-group": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "sameness-group"},
|
||||||
|
},
|
||||||
|
},
|
||||||
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=api-gateway": { // Legacy
|
"consul.usage.test.consul.state.config_entries;datacenter=dc1;kind=api-gateway": { // Legacy
|
||||||
Name: "consul.usage.test.consul.state.config_entries",
|
Name: "consul.usage.test.consul.state.config_entries",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
|
|
|
@ -34,6 +34,7 @@ const (
|
||||||
ServiceIntentions string = "service-intentions"
|
ServiceIntentions string = "service-intentions"
|
||||||
MeshConfig string = "mesh"
|
MeshConfig string = "mesh"
|
||||||
ExportedServices string = "exported-services"
|
ExportedServices string = "exported-services"
|
||||||
|
SamenessGroup string = "sameness-group"
|
||||||
APIGateway string = "api-gateway"
|
APIGateway string = "api-gateway"
|
||||||
BoundAPIGateway string = "bound-api-gateway"
|
BoundAPIGateway string = "bound-api-gateway"
|
||||||
InlineCertificate string = "inline-certificate"
|
InlineCertificate string = "inline-certificate"
|
||||||
|
@ -59,6 +60,7 @@ var AllConfigEntryKinds = []string{
|
||||||
ServiceIntentions,
|
ServiceIntentions,
|
||||||
MeshConfig,
|
MeshConfig,
|
||||||
ExportedServices,
|
ExportedServices,
|
||||||
|
SamenessGroup,
|
||||||
APIGateway,
|
APIGateway,
|
||||||
BoundAPIGateway,
|
BoundAPIGateway,
|
||||||
HTTPRoute,
|
HTTPRoute,
|
||||||
|
@ -672,6 +674,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||||
return &MeshConfigEntry{}, nil
|
return &MeshConfigEntry{}, nil
|
||||||
case ExportedServices:
|
case ExportedServices:
|
||||||
return &ExportedServicesConfigEntry{Name: name}, nil
|
return &ExportedServicesConfigEntry{Name: name}, nil
|
||||||
|
case SamenessGroup:
|
||||||
|
return &SamenessGroupConfigEntry{Name: name}, nil
|
||||||
case APIGateway:
|
case APIGateway:
|
||||||
return &APIGatewayConfigEntry{Name: name}, nil
|
return &APIGatewayConfigEntry{Name: name}, nil
|
||||||
case BoundAPIGateway:
|
case BoundAPIGateway:
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SamenessGroupConfigEntry struct {
|
||||||
|
Name string
|
||||||
|
IsDefault bool `json:",omitempty" alias:"is_default"`
|
||||||
|
Members []SamenessGroupMember
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
|
RaftIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) GetKind() string { return SamenessGroup }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetName() string { return s.Name }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetMeta() map[string]string { return s.Meta }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetCreateIndex() uint64 { return s.CreateIndex }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetModifyIndex() uint64 { return s.ModifyIndex }
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) GetRaftIndex() *RaftIndex {
|
||||||
|
if s == nil {
|
||||||
|
return &RaftIndex{}
|
||||||
|
}
|
||||||
|
return &s.RaftIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &s.EnterpriseMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) Normalize() error {
|
||||||
|
if s == nil {
|
||||||
|
return fmt.Errorf("config entry is nil")
|
||||||
|
}
|
||||||
|
s.EnterpriseMeta.Normalize()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) CanWrite(authz acl.Authorizer) error {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
s.FillAuthzContext(&authzContext)
|
||||||
|
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) MarshalJSON() ([]byte, error) {
|
||||||
|
type Alias SamenessGroupConfigEntry
|
||||||
|
source := &struct {
|
||||||
|
Kind string
|
||||||
|
*Alias
|
||||||
|
}{
|
||||||
|
Kind: SamenessGroup,
|
||||||
|
Alias: (*Alias)(s),
|
||||||
|
}
|
||||||
|
return json.Marshal(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SamenessGroupMember struct {
|
||||||
|
Partition string
|
||||||
|
Peer string
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
//go:build !consulent
|
||||||
|
// +build !consulent
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) Validate() error {
|
||||||
|
return fmt.Errorf("sameness-groups are an enterprise-only feature")
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ const (
|
||||||
ServiceIntentions string = "service-intentions"
|
ServiceIntentions string = "service-intentions"
|
||||||
MeshConfig string = "mesh"
|
MeshConfig string = "mesh"
|
||||||
ExportedServices string = "exported-services"
|
ExportedServices string = "exported-services"
|
||||||
|
SamenessGroup string = "sameness-group"
|
||||||
|
|
||||||
ProxyConfigGlobal string = "global"
|
ProxyConfigGlobal string = "global"
|
||||||
MeshConfigMesh string = "mesh"
|
MeshConfigMesh string = "mesh"
|
||||||
|
@ -355,6 +356,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||||
return &MeshConfigEntry{}, nil
|
return &MeshConfigEntry{}, nil
|
||||||
case ExportedServices:
|
case ExportedServices:
|
||||||
return &ExportedServicesConfigEntry{Name: name}, nil
|
return &ExportedServicesConfigEntry{Name: name}, nil
|
||||||
|
case SamenessGroup:
|
||||||
|
return &SamenessGroupConfigEntry{Kind: kind, Name: name}, nil
|
||||||
case APIGateway:
|
case APIGateway:
|
||||||
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
|
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||||
case TCPRoute:
|
case TCPRoute:
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package api
|
||||||
|
|
||||||
|
type SamenessGroupConfigEntry struct {
|
||||||
|
Kind string
|
||||||
|
Name string
|
||||||
|
Partition string `json:",omitempty"`
|
||||||
|
IsDefault bool `json:",omitempty" alias:"is_default"`
|
||||||
|
Members []SamenessGroupMember
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
CreateIndex uint64
|
||||||
|
ModifyIndex uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type SamenessGroupMember struct {
|
||||||
|
Partition string
|
||||||
|
Peer string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SamenessGroupConfigEntry) GetKind() string { return s.Kind }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetName() string { return s.Name }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetPartition() string { return s.Partition }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetNamespace() string { return "" }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetCreateIndex() uint64 { return s.CreateIndex }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetModifyIndex() uint64 { return s.ModifyIndex }
|
||||||
|
func (s *SamenessGroupConfigEntry) GetMeta() map[string]string { return s.Meta }
|
Loading…
Reference in New Issue