Attach service virtual IP info to compiled discovery chain (#17295)
* Add v1/internal/service-virtual-ip for manually setting service VIPs * Attach service virtual IP info to compiled discovery chain * Separate auto-assigned and manual VIPs in response
This commit is contained in:
parent
b6d5d5649d
commit
73897656d5
|
@ -261,6 +261,8 @@ func TestDiscoveryChainEndpoint_Get(t *testing.T) {
|
|||
33*time.Second,
|
||||
),
|
||||
},
|
||||
AutoVirtualIPs: []string{"240.0.0.1"},
|
||||
ManualVirtualIPs: []string{},
|
||||
},
|
||||
}
|
||||
require.Equal(t, expect, resp)
|
||||
|
|
|
@ -44,6 +44,11 @@ type CompileRequest struct {
|
|||
OverrideConnectTimeout time.Duration
|
||||
|
||||
Entries *configentry.DiscoveryChainSet
|
||||
|
||||
// AutoVirtualIPs and ManualVirtualIPs are lists of IPs associated with
|
||||
// the service.
|
||||
AutoVirtualIPs []string
|
||||
ManualVirtualIPs []string
|
||||
}
|
||||
|
||||
// Compile assembles a discovery chain in the form of a graph of nodes using
|
||||
|
@ -98,6 +103,8 @@ func Compile(req CompileRequest) (*structs.CompiledDiscoveryChain, error) {
|
|||
overrideProtocol: req.OverrideProtocol,
|
||||
overrideConnectTimeout: req.OverrideConnectTimeout,
|
||||
entries: entries,
|
||||
autoVirtualIPs: req.AutoVirtualIPs,
|
||||
manualVirtualIPs: req.ManualVirtualIPs,
|
||||
|
||||
resolvers: make(map[structs.ServiceID]*structs.ServiceResolverConfigEntry),
|
||||
splitterNodes: make(map[string]*structs.DiscoveryGraphNode),
|
||||
|
@ -139,6 +146,11 @@ type compiler struct {
|
|||
// This is an INPUT field.
|
||||
entries *configentry.DiscoveryChainSet
|
||||
|
||||
// autoVirtualIPs and manualVirtualIPs are lists of IPs associated with
|
||||
// the service.
|
||||
autoVirtualIPs []string
|
||||
manualVirtualIPs []string
|
||||
|
||||
// resolvers is initially seeded by copying the provided entries.Resolvers
|
||||
// map and default resolvers are added as they are needed.
|
||||
resolvers map[structs.ServiceID]*structs.ServiceResolverConfigEntry
|
||||
|
@ -352,6 +364,8 @@ func (c *compiler) compile() (*structs.CompiledDiscoveryChain, error) {
|
|||
StartNode: c.startNode,
|
||||
Nodes: c.nodes,
|
||||
Targets: c.loadedTargets,
|
||||
AutoVirtualIPs: c.autoVirtualIPs,
|
||||
ManualVirtualIPs: c.manualVirtualIPs,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3045,11 +3045,7 @@ func (s *Store) VirtualIPForService(psn structs.PeeredServiceName) (string, erro
|
|||
return "", nil
|
||||
}
|
||||
|
||||
result, err := addIPOffset(startingVirtualIP, vip.(ServiceVirtualIP).IP)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.String(), nil
|
||||
return vip.(ServiceVirtualIP).IPWithOffset()
|
||||
}
|
||||
|
||||
func (s *Store) ServiceVirtualIPs() (uint64, []ServiceVirtualIP, error) {
|
||||
|
@ -3080,6 +3076,10 @@ func (s *Store) ServiceManualVIPs(psn structs.PeeredServiceName) (*ServiceVirtua
|
|||
tx := s.db.Txn(false)
|
||||
defer tx.Abort()
|
||||
|
||||
return serviceVIPsTxn(tx, psn)
|
||||
}
|
||||
|
||||
func serviceVIPsTxn(tx ReadTxn, psn structs.PeeredServiceName) (*ServiceVirtualIP, error) {
|
||||
vip, err := tx.First(tableServiceVirtualIPs, indexID, psn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed service virtual IP lookup: %s", err)
|
||||
|
|
|
@ -616,6 +616,14 @@ type ServiceVirtualIP struct {
|
|||
structs.RaftIndex
|
||||
}
|
||||
|
||||
func (s ServiceVirtualIP) IPWithOffset() (string, error) {
|
||||
result, err := addIPOffset(startingVirtualIP, s.IP)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return result.String(), nil
|
||||
}
|
||||
|
||||
// FreeVirtualIP is used to store a virtual IP freed up by a service deregistration.
|
||||
// It is also used to store free virtual IPs when a snapshot is created.
|
||||
type FreeVirtualIP struct {
|
||||
|
|
|
@ -1371,6 +1371,22 @@ func serviceDiscoveryChainTxn(
|
|||
}
|
||||
req.EvaluateInTrustDomain = signingID.Host()
|
||||
|
||||
psn := structs.PeeredServiceName{ServiceName: structs.NewServiceName(serviceName, entMeta)}
|
||||
serviceVIPEntry, err := serviceVIPsTxn(tx, psn)
|
||||
if err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
|
||||
if serviceVIPEntry != nil {
|
||||
assignedIP, err := serviceVIPEntry.IPWithOffset()
|
||||
if err != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
req.AutoVirtualIPs = []string{assignedIP}
|
||||
req.ManualVirtualIPs = make([]string, len(serviceVIPEntry.ManualIPs))
|
||||
copy(req.ManualVirtualIPs, serviceVIPEntry.ManualIPs)
|
||||
}
|
||||
|
||||
// Then we compile it into something useful.
|
||||
chain, err := discoverychain.Compile(req)
|
||||
if err != nil {
|
||||
|
|
|
@ -9,9 +9,12 @@ import (
|
|||
"time"
|
||||
|
||||
memdb "github.com/hashicorp/go-memdb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
"github.com/hashicorp/consul/agent/configentry"
|
||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/proto/private/pbpeering"
|
||||
"github.com/hashicorp/consul/proto/private/prototest"
|
||||
|
@ -3389,3 +3392,51 @@ func writeConfigAndBumpIndexForTest(s *Store, idx uint64, entry structs.ConfigEn
|
|||
}
|
||||
return idx, err
|
||||
}
|
||||
|
||||
func TestStateStore_DiscoveryChain_AttachVirtualIPs(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
setVirtualIPFlags(t, s)
|
||||
|
||||
ca := &structs.CAConfiguration{
|
||||
Provider: "consul",
|
||||
}
|
||||
err := s.CASetConfig(0, ca)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Attempt to assign manual virtual IPs to a service that doesn't exist - should be a no-op.
|
||||
psn := structs.PeeredServiceName{ServiceName: structs.ServiceName{Name: "foo", EnterpriseMeta: *acl.DefaultEnterpriseMeta()}}
|
||||
|
||||
// Register a service via config entry.
|
||||
s.EnsureConfigEntry(1, &structs.ServiceResolverConfigEntry{
|
||||
Kind: structs.ServiceResolver,
|
||||
Name: "foo",
|
||||
})
|
||||
|
||||
vip, err := s.VirtualIPForService(psn)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "240.0.0.1", vip)
|
||||
|
||||
// Assign manual virtual IPs for foo
|
||||
found, _, err := s.AssignManualServiceVIPs(2, psn, []string{"2.2.2.2", "3.3.3.3"})
|
||||
require.NoError(t, err)
|
||||
require.True(t, found)
|
||||
|
||||
serviceVIP, err := s.ServiceManualVIPs(psn)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(2), serviceVIP.ModifyIndex)
|
||||
require.Equal(t, "0.0.0.1", serviceVIP.IP.String())
|
||||
require.Equal(t, []string{"2.2.2.2", "3.3.3.3"}, serviceVIP.ManualIPs)
|
||||
|
||||
req := discoverychain.CompileRequest{
|
||||
ServiceName: "foo",
|
||||
EvaluateInNamespace: psn.ServiceName.NamespaceOrDefault(),
|
||||
EvaluateInPartition: psn.ServiceName.PartitionOrDefault(),
|
||||
EvaluateInDatacenter: "dc1",
|
||||
}
|
||||
idx, chain, _, err := s.ServiceDiscoveryChain(nil, "foo", structs.DefaultEnterpriseMetaInDefaultPartition(), req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, uint64(1), idx)
|
||||
require.Equal(t, []string{"240.0.0.1"}, chain.AutoVirtualIPs)
|
||||
require.Equal(t, []string{"2.2.2.2", "3.3.3.3"}, chain.ManualVirtualIPs)
|
||||
|
||||
}
|
||||
|
|
|
@ -285,6 +285,8 @@ func TestDiscoveryChainRead(t *testing.T) {
|
|||
33*time.Second,
|
||||
),
|
||||
},
|
||||
AutoVirtualIPs: []string{"240.0.0.1"},
|
||||
ManualVirtualIPs: []string{},
|
||||
}
|
||||
if !reflect.DeepEqual(expect, value.Chain) {
|
||||
r.Fatalf("should be equal: expected=%+v, got=%+v", expect, value.Chain)
|
||||
|
@ -333,6 +335,8 @@ func TestDiscoveryChainRead(t *testing.T) {
|
|||
expectTarget_DC1.ID: expectTarget_DC1,
|
||||
expectTarget_DC2.ID: expectTarget_DC2,
|
||||
},
|
||||
AutoVirtualIPs: []string{"240.0.0.1"},
|
||||
ManualVirtualIPs: []string{},
|
||||
}
|
||||
|
||||
require.True(t, t.Run("POST: read modified chain with overrides (camel case)", func(t *testing.T) {
|
||||
|
|
|
@ -57,6 +57,10 @@ type CompiledDiscoveryChain struct {
|
|||
|
||||
// Targets is a list of all targets used in this chain.
|
||||
Targets map[string]*DiscoveryTarget `json:",omitempty"`
|
||||
|
||||
// VirtualIPs is a list of virtual IPs associated with the service.
|
||||
AutoVirtualIPs []string
|
||||
ManualVirtualIPs []string
|
||||
}
|
||||
|
||||
// ID returns an ID that encodes the service, namespace, partition, and datacenter.
|
||||
|
|
|
@ -133,6 +133,14 @@ func (o *CompiledDiscoveryChain) DeepCopy() *CompiledDiscoveryChain {
|
|||
cp.Targets[k2] = cp_Targets_v2
|
||||
}
|
||||
}
|
||||
if o.AutoVirtualIPs != nil {
|
||||
cp.AutoVirtualIPs = make([]string, len(o.AutoVirtualIPs))
|
||||
copy(cp.AutoVirtualIPs, o.AutoVirtualIPs)
|
||||
}
|
||||
if o.ManualVirtualIPs != nil {
|
||||
cp.ManualVirtualIPs = make([]string, len(o.ManualVirtualIPs))
|
||||
copy(cp.ManualVirtualIPs, o.ManualVirtualIPs)
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue