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)
|
||||
}
|
||||
}
|
||||
case structs.SamenessGroup:
|
||||
err := checkSamenessGroup(tx, conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the config entry and update the index
|
||||
|
@ -539,6 +544,7 @@ func validateProposedConfigEntryInGraph(
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case structs.SamenessGroup:
|
||||
case structs.ServiceIntentions:
|
||||
case structs.MeshConfig:
|
||||
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 (
|
||||
tableConfigEntries = "config-entries"
|
||||
|
||||
indexLink = "link"
|
||||
indexIntentionLegacyID = "intention-legacy-id"
|
||||
indexSource = "intention-source"
|
||||
indexLink = "link"
|
||||
indexIntentionLegacyID = "intention-legacy-id"
|
||||
indexSource = "intention-source"
|
||||
indexSamenessGroupDefault = "sameness-group-default"
|
||||
)
|
||||
|
||||
// configTableSchema returns a new table schema used to store global
|
||||
|
@ -50,6 +51,12 @@ func configTableSchema() *memdb.TableSchema {
|
|||
Unique: false,
|
||||
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.SamenessGroupConfigEntry)(nil)
|
||||
var _ configEntryIndexable = (*structs.IngressGatewayConfigEntry)(nil)
|
||||
var _ configEntryIndexable = (*structs.MeshConfigEntry)(nil)
|
||||
var _ configEntryIndexable = (*structs.ProxyConfigEntry)(nil)
|
||||
|
|
|
@ -357,6 +357,22 @@ var baseCases = map[string]testCase{
|
|||
{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
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
|
@ -784,6 +800,22 @@ var baseCases = map[string]testCase{
|
|||
{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
|
||||
Name: "consul.usage.test.consul.state.config_entries",
|
||||
Value: 0,
|
||||
|
|
|
@ -34,6 +34,7 @@ const (
|
|||
ServiceIntentions string = "service-intentions"
|
||||
MeshConfig string = "mesh"
|
||||
ExportedServices string = "exported-services"
|
||||
SamenessGroup string = "sameness-group"
|
||||
APIGateway string = "api-gateway"
|
||||
BoundAPIGateway string = "bound-api-gateway"
|
||||
InlineCertificate string = "inline-certificate"
|
||||
|
@ -59,6 +60,7 @@ var AllConfigEntryKinds = []string{
|
|||
ServiceIntentions,
|
||||
MeshConfig,
|
||||
ExportedServices,
|
||||
SamenessGroup,
|
||||
APIGateway,
|
||||
BoundAPIGateway,
|
||||
HTTPRoute,
|
||||
|
@ -672,6 +674,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &MeshConfigEntry{}, nil
|
||||
case ExportedServices:
|
||||
return &ExportedServicesConfigEntry{Name: name}, nil
|
||||
case SamenessGroup:
|
||||
return &SamenessGroupConfigEntry{Name: name}, nil
|
||||
case APIGateway:
|
||||
return &APIGatewayConfigEntry{Name: name}, nil
|
||||
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"
|
||||
MeshConfig string = "mesh"
|
||||
ExportedServices string = "exported-services"
|
||||
SamenessGroup string = "sameness-group"
|
||||
|
||||
ProxyConfigGlobal string = "global"
|
||||
MeshConfigMesh string = "mesh"
|
||||
|
@ -355,6 +356,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
|||
return &MeshConfigEntry{}, nil
|
||||
case ExportedServices:
|
||||
return &ExportedServicesConfigEntry{Name: name}, nil
|
||||
case SamenessGroup:
|
||||
return &SamenessGroupConfigEntry{Kind: kind, Name: name}, nil
|
||||
case APIGateway:
|
||||
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||
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