mirror of https://github.com/hashicorp/consul
Merge branch 'main' into cm-bug-11457
commit
cbd6ca9f20
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:feature
|
||||||
|
gateways: api-gateway can leverage listener TLS certificates available on the gateway's local filesystem by specifying the public certificate and private key path in the new file-system-certificate configuration entry
|
||||||
|
```
|
|
@ -0,0 +1,4 @@
|
||||||
|
```release-note:improvement
|
||||||
|
dns: DNS-over-grpc when using `consul-dataplane` now accepts partition, namespace, token as metadata to default those query parameters.
|
||||||
|
`consul-dataplane` v1.5+ will send this information automatically.
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:enhancement
|
||||||
|
gateways: service defaults configuration entries can now be used to set default upstream limits for mesh-gateways
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
server: fix Ent snapshot restore on CE when CE downgrade is enabled
|
||||||
|
```
|
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note: improvement
|
||||||
|
telemetry: Add `telemetry.disable_per_tenancy_usage_metrics` in agent configuration to disable setting tenancy labels on usage metrics. This significantly decreases CPU utilization in clusters with many admin partitions or namespaces.
|
||||||
|
```
|
|
@ -947,6 +947,7 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
|
||||||
CirconusSubmissionInterval: stringVal(c.Telemetry.CirconusSubmissionInterval),
|
CirconusSubmissionInterval: stringVal(c.Telemetry.CirconusSubmissionInterval),
|
||||||
CirconusSubmissionURL: stringVal(c.Telemetry.CirconusSubmissionURL),
|
CirconusSubmissionURL: stringVal(c.Telemetry.CirconusSubmissionURL),
|
||||||
DisableHostname: boolVal(c.Telemetry.DisableHostname),
|
DisableHostname: boolVal(c.Telemetry.DisableHostname),
|
||||||
|
DisablePerTenancyUsageMetrics: boolVal(c.Telemetry.DisablePerTenancyUsageMetrics),
|
||||||
DogstatsdAddr: stringVal(c.Telemetry.DogstatsdAddr),
|
DogstatsdAddr: stringVal(c.Telemetry.DogstatsdAddr),
|
||||||
DogstatsdTags: c.Telemetry.DogstatsdTags,
|
DogstatsdTags: c.Telemetry.DogstatsdTags,
|
||||||
RetryFailedConfiguration: boolVal(c.Telemetry.RetryFailedConfiguration),
|
RetryFailedConfiguration: boolVal(c.Telemetry.RetryFailedConfiguration),
|
||||||
|
|
|
@ -697,6 +697,7 @@ type Telemetry struct {
|
||||||
CirconusSubmissionInterval *string `mapstructure:"circonus_submission_interval" json:"circonus_submission_interval,omitempty"`
|
CirconusSubmissionInterval *string `mapstructure:"circonus_submission_interval" json:"circonus_submission_interval,omitempty"`
|
||||||
CirconusSubmissionURL *string `mapstructure:"circonus_submission_url" json:"circonus_submission_url,omitempty"`
|
CirconusSubmissionURL *string `mapstructure:"circonus_submission_url" json:"circonus_submission_url,omitempty"`
|
||||||
DisableHostname *bool `mapstructure:"disable_hostname" json:"disable_hostname,omitempty"`
|
DisableHostname *bool `mapstructure:"disable_hostname" json:"disable_hostname,omitempty"`
|
||||||
|
DisablePerTenancyUsageMetrics *bool `mapstructure:"disable_per_tenancy_usage_metrics" json:"disable_per_tenancy_usage_metrics,omitempty"`
|
||||||
EnableHostMetrics *bool `mapstructure:"enable_host_metrics" json:"enable_host_metrics,omitempty"`
|
EnableHostMetrics *bool `mapstructure:"enable_host_metrics" json:"enable_host_metrics,omitempty"`
|
||||||
DogstatsdAddr *string `mapstructure:"dogstatsd_addr" json:"dogstatsd_addr,omitempty"`
|
DogstatsdAddr *string `mapstructure:"dogstatsd_addr" json:"dogstatsd_addr,omitempty"`
|
||||||
DogstatsdTags []string `mapstructure:"dogstatsd_tags" json:"dogstatsd_tags,omitempty"`
|
DogstatsdTags []string `mapstructure:"dogstatsd_tags" json:"dogstatsd_tags,omitempty"`
|
||||||
|
|
|
@ -6023,7 +6023,7 @@ func TestLoad_IntegrationWithFlags(t *testing.T) {
|
||||||
json: []string{`
|
json: []string{`
|
||||||
{
|
{
|
||||||
"experiments": ["resource-apis"]
|
"experiments": ["resource-apis"]
|
||||||
}
|
}
|
||||||
`},
|
`},
|
||||||
hcl: []string{`experiments=["resource-apis"]`},
|
hcl: []string{`experiments=["resource-apis"]`},
|
||||||
expected: func(rt *RuntimeConfig) {
|
expected: func(rt *RuntimeConfig) {
|
||||||
|
@ -6974,7 +6974,8 @@ func TestLoad_FullConfig(t *testing.T) {
|
||||||
Expiration: 15 * time.Second,
|
Expiration: 15 * time.Second,
|
||||||
Name: "ftO6DySn", // notice this is the same as the metrics prefix
|
Name: "ftO6DySn", // notice this is the same as the metrics prefix
|
||||||
},
|
},
|
||||||
EnableHostMetrics: true,
|
EnableHostMetrics: true,
|
||||||
|
DisablePerTenancyUsageMetrics: true,
|
||||||
},
|
},
|
||||||
TLS: tlsutil.Config{
|
TLS: tlsutil.Config{
|
||||||
InternalRPC: tlsutil.ProtocolConfig{
|
InternalRPC: tlsutil.ProtocolConfig{
|
||||||
|
|
|
@ -208,11 +208,11 @@
|
||||||
"ExposeMaxPort": 0,
|
"ExposeMaxPort": 0,
|
||||||
"ExposeMinPort": 0,
|
"ExposeMinPort": 0,
|
||||||
"GRPCAddrs": [],
|
"GRPCAddrs": [],
|
||||||
|
"GRPCKeepaliveInterval": "0s",
|
||||||
|
"GRPCKeepaliveTimeout": "0s",
|
||||||
"GRPCPort": 0,
|
"GRPCPort": 0,
|
||||||
"GRPCTLSAddrs": [],
|
"GRPCTLSAddrs": [],
|
||||||
"GRPCTLSPort": 0,
|
"GRPCTLSPort": 0,
|
||||||
"GRPCKeepaliveInterval": "0s",
|
|
||||||
"GRPCKeepaliveTimeout": "0s",
|
|
||||||
"GossipLANGossipInterval": "0s",
|
"GossipLANGossipInterval": "0s",
|
||||||
"GossipLANGossipNodes": 0,
|
"GossipLANGossipNodes": 0,
|
||||||
"GossipLANProbeInterval": "0s",
|
"GossipLANProbeInterval": "0s",
|
||||||
|
@ -472,6 +472,7 @@
|
||||||
"CirconusSubmissionURL": "",
|
"CirconusSubmissionURL": "",
|
||||||
"Disable": false,
|
"Disable": false,
|
||||||
"DisableHostname": false,
|
"DisableHostname": false,
|
||||||
|
"DisablePerTenancyUsageMetrics": false,
|
||||||
"DogstatsdAddr": "",
|
"DogstatsdAddr": "",
|
||||||
"DogstatsdTags": [],
|
"DogstatsdTags": [],
|
||||||
"EnableHostMetrics": false,
|
"EnableHostMetrics": false,
|
||||||
|
|
|
@ -718,6 +718,7 @@ telemetry {
|
||||||
prometheus_retention_time = "15s"
|
prometheus_retention_time = "15s"
|
||||||
statsd_address = "drce87cy"
|
statsd_address = "drce87cy"
|
||||||
statsite_address = "HpFwKB8R"
|
statsite_address = "HpFwKB8R"
|
||||||
|
disable_per_tenancy_usage_metrics = true
|
||||||
}
|
}
|
||||||
tls {
|
tls {
|
||||||
defaults {
|
defaults {
|
||||||
|
|
|
@ -841,7 +841,8 @@
|
||||||
"metrics_prefix": "ftO6DySn",
|
"metrics_prefix": "ftO6DySn",
|
||||||
"prometheus_retention_time": "15s",
|
"prometheus_retention_time": "15s",
|
||||||
"statsd_address": "drce87cy",
|
"statsd_address": "drce87cy",
|
||||||
"statsite_address": "HpFwKB8R"
|
"statsite_address": "HpFwKB8R",
|
||||||
|
"disable_per_tenancy_usage_metrics": true
|
||||||
},
|
},
|
||||||
"tls": {
|
"tls": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
|
@ -944,4 +945,4 @@
|
||||||
"xds": {
|
"xds": {
|
||||||
"update_max_per_second": 9526.2
|
"update_max_per_second": 9526.2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -404,6 +404,8 @@ type Config struct {
|
||||||
// report usage metrics to the configured go-metrics Sinks.
|
// report usage metrics to the configured go-metrics Sinks.
|
||||||
MetricsReportingInterval time.Duration
|
MetricsReportingInterval time.Duration
|
||||||
|
|
||||||
|
DisablePerTenancyUsageMetrics bool
|
||||||
|
|
||||||
// ConnectEnabled is whether to enable Connect features such as the CA.
|
// ConnectEnabled is whether to enable Connect features such as the CA.
|
||||||
ConnectEnabled bool
|
ConnectEnabled bool
|
||||||
|
|
||||||
|
|
|
@ -619,6 +619,8 @@ func MakeShadowConfigEntry(kind, name string) (structs.ConfigEntry, error) {
|
||||||
return &ShadowAPIGatewayConfigEntry{APIGatewayConfigEntry: &structs.APIGatewayConfigEntry{Name: name}}, nil
|
return &ShadowAPIGatewayConfigEntry{APIGatewayConfigEntry: &structs.APIGatewayConfigEntry{Name: name}}, nil
|
||||||
case structs.BoundAPIGateway:
|
case structs.BoundAPIGateway:
|
||||||
return &ShadowBoundAPIGatewayConfigEntry{BoundAPIGatewayConfigEntry: &structs.BoundAPIGatewayConfigEntry{Name: name}}, nil
|
return &ShadowBoundAPIGatewayConfigEntry{BoundAPIGatewayConfigEntry: &structs.BoundAPIGatewayConfigEntry{Name: name}}, nil
|
||||||
|
case structs.FileSystemCertificate:
|
||||||
|
return &ShadowFileSystemCertificateConfigEntry{FileSystemCertificateConfigEntry: &structs.FileSystemCertificateConfigEntry{Name: name}}, nil
|
||||||
case structs.InlineCertificate:
|
case structs.InlineCertificate:
|
||||||
return &ShadowInlineCertificateConfigEntry{InlineCertificateConfigEntry: &structs.InlineCertificateConfigEntry{Name: name}}, nil
|
return &ShadowInlineCertificateConfigEntry{InlineCertificateConfigEntry: &structs.InlineCertificateConfigEntry{Name: name}}, nil
|
||||||
case structs.HTTPRoute:
|
case structs.HTTPRoute:
|
||||||
|
@ -931,6 +933,15 @@ func (s ShadowBoundAPIGatewayConfigEntry) GetRealConfigEntry() structs.ConfigEnt
|
||||||
return s.BoundAPIGatewayConfigEntry
|
return s.BoundAPIGatewayConfigEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ShadowFileSystemCertificateConfigEntry struct {
|
||||||
|
ShadowBase
|
||||||
|
*structs.FileSystemCertificateConfigEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s ShadowFileSystemCertificateConfigEntry) GetRealConfigEntry() structs.ConfigEntry {
|
||||||
|
return s.FileSystemCertificateConfigEntry
|
||||||
|
}
|
||||||
|
|
||||||
type ShadowInlineCertificateConfigEntry struct {
|
type ShadowInlineCertificateConfigEntry struct {
|
||||||
ShadowBase
|
ShadowBase
|
||||||
*structs.InlineCertificateConfigEntry
|
*structs.InlineCertificateConfigEntry
|
||||||
|
|
|
@ -196,7 +196,7 @@ func (c *FSM) Apply(log *raft.Log) interface{} {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if structs.CEDowngrade && msgType >= 64 {
|
if structs.CEDowngrade && msgType >= 64 {
|
||||||
c.logger.Warn("ignoring enterprise message, for downgrading to oss", "type", msgType)
|
c.logger.Warn("ignoring enterprise message as part of downgrade to CE", "type", msgType)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
panic(fmt.Errorf("failed to apply request: %#v", buf))
|
panic(fmt.Errorf("failed to apply request: %#v", buf))
|
||||||
|
@ -268,8 +268,9 @@ func (c *FSM) Restore(old io.ReadCloser) error {
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
if structs.CEDowngrade && msg >= 64 {
|
if structs.CEDowngrade && msg >= 64 {
|
||||||
c.logger.Warn("ignoring enterprise message , for downgrading to oss", "type", msg)
|
c.logger.Warn("ignoring enterprise message as part of downgrade to CE", "type", msg)
|
||||||
return nil
|
var ignore interface{}
|
||||||
|
return dec.Decode(&ignore)
|
||||||
} else if msg >= 64 {
|
} else if msg >= 64 {
|
||||||
return fmt.Errorf("msg type <%d> is a Consul Enterprise log entry. Consul CE cannot restore it", msg)
|
return fmt.Errorf("msg type <%d> is a Consul Enterprise log entry. Consul CE cannot restore it", msg)
|
||||||
} else {
|
} else {
|
||||||
|
@ -402,6 +403,11 @@ func (c *FSM) registerStreamSnapshotHandlers() {
|
||||||
}, true)
|
}, true)
|
||||||
panicIfErr(err)
|
panicIfErr(err)
|
||||||
|
|
||||||
|
err = c.deps.Publisher.RegisterHandler(state.EventTopicFileSystemCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||||
|
return c.State().FileSystemCertificateSnapshot(req, buf)
|
||||||
|
}, true)
|
||||||
|
panicIfErr(err)
|
||||||
|
|
||||||
err = c.deps.Publisher.RegisterHandler(state.EventTopicInlineCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
err = c.deps.Publisher.RegisterHandler(state.EventTopicInlineCertificate, func(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||||
return c.State().InlineCertificateSnapshot(req, buf)
|
return c.State().InlineCertificateSnapshot(req, buf)
|
||||||
}, true)
|
}, true)
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/lib/stringslice"
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,3 +61,101 @@ func TestRestoreFromEnterprise(t *testing.T) {
|
||||||
require.EqualError(t, fsm.Restore(sink), "msg type <65> is a Consul Enterprise log entry. Consul CE cannot restore it")
|
require.EqualError(t, fsm.Restore(sink), "msg type <65> is a Consul Enterprise log entry. Consul CE cannot restore it")
|
||||||
sink.Cancel()
|
sink.Cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRestoreFromEnterprise_CEDowngrade(t *testing.T) {
|
||||||
|
logger := testutil.Logger(t)
|
||||||
|
|
||||||
|
handle := &testRaftHandle{}
|
||||||
|
storageBackend := newStorageBackend(t, handle)
|
||||||
|
handle.apply = func(buf []byte) (any, error) { return storageBackend.Apply(buf, 123), nil }
|
||||||
|
|
||||||
|
fsm := NewFromDeps(Deps{
|
||||||
|
Logger: logger,
|
||||||
|
NewStateStore: func() *state.Store {
|
||||||
|
return state.NewStateStore(nil)
|
||||||
|
},
|
||||||
|
StorageBackend: storageBackend,
|
||||||
|
})
|
||||||
|
|
||||||
|
// To verify if a proper message is displayed when Consul CE tries to
|
||||||
|
// unsuccessfully restore entries from a Consul Ent snapshot.
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
sink := &MockSink{buf, false}
|
||||||
|
|
||||||
|
type EntMock struct {
|
||||||
|
ID int
|
||||||
|
Type string
|
||||||
|
}
|
||||||
|
|
||||||
|
entMockEntry := EntMock{
|
||||||
|
ID: 65,
|
||||||
|
Type: "A Consul Ent Log Type",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create one entry to exercise the Go struct marshaller, and one to exercise the
|
||||||
|
// Binary Marshaller interface. This verifies that regardless of whether the struct gets
|
||||||
|
// encoded as a msgpack byte string (binary marshaller) or msgpack map (other struct),
|
||||||
|
// it will still be skipped over correctly.
|
||||||
|
registerEntry := structs.RegisterRequest{
|
||||||
|
Datacenter: "dc1",
|
||||||
|
Node: "foo",
|
||||||
|
Address: "127.0.0.1",
|
||||||
|
Service: &structs.NodeService{
|
||||||
|
ID: "db",
|
||||||
|
Service: "db",
|
||||||
|
Tags: []string{"primary"},
|
||||||
|
Port: 8000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
proxyDefaultsEntry := &structs.ConfigEntryRequest{
|
||||||
|
Op: structs.ConfigEntryUpsert,
|
||||||
|
Entry: &structs.ProxyConfigEntry{
|
||||||
|
Kind: structs.ProxyDefaults,
|
||||||
|
Name: "global",
|
||||||
|
Config: map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
},
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMetaInDefaultPartition(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the header and records.
|
||||||
|
header := SnapshotHeader{
|
||||||
|
LastIndex: 0,
|
||||||
|
}
|
||||||
|
encoder := codec.NewEncoder(sink, structs.MsgpackHandle)
|
||||||
|
encoder.Encode(&header)
|
||||||
|
sink.Write([]byte{byte(structs.MessageType(entMockEntry.ID))})
|
||||||
|
encoder.Encode(entMockEntry)
|
||||||
|
sink.Write([]byte{byte(structs.RegisterRequestType)})
|
||||||
|
encoder.Encode(registerEntry)
|
||||||
|
sink.Write([]byte{byte(structs.ConfigEntryRequestType)})
|
||||||
|
encoder.Encode(proxyDefaultsEntry)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
structs.CEDowngrade = false
|
||||||
|
}()
|
||||||
|
structs.CEDowngrade = true
|
||||||
|
|
||||||
|
require.NoError(t, fsm.Restore(sink), "failed to decode Ent snapshot to CE")
|
||||||
|
|
||||||
|
// Verify the register request
|
||||||
|
_, nodes, err := fsm.state.Nodes(nil, nil, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, nodes, 1, "incorrect number of nodes: %v", nodes)
|
||||||
|
require.Equal(t, "foo", nodes[0].Node)
|
||||||
|
require.Equal(t, "dc1", nodes[0].Datacenter)
|
||||||
|
require.Equal(t, "127.0.0.1", nodes[0].Address)
|
||||||
|
_, fooSrv, err := fsm.state.NodeServices(nil, "foo", nil, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, fooSrv.Services, 1)
|
||||||
|
require.Contains(t, fooSrv.Services["db"].Tags, "primary")
|
||||||
|
require.True(t, stringslice.Contains(fooSrv.Services["db"].Tags, "primary"))
|
||||||
|
require.Equal(t, 8000, fooSrv.Services["db"].Port)
|
||||||
|
|
||||||
|
// Verify the proxy defaults request
|
||||||
|
_, configEntry, err := fsm.state.ConfigEntry(nil, structs.ProxyDefaults, "global", structs.DefaultEnterpriseMetaInDefaultPartition())
|
||||||
|
require.NoError(t, err)
|
||||||
|
configEntry.SetHash(proxyDefaultsEntry.Entry.GetHash())
|
||||||
|
require.Equal(t, proxyDefaultsEntry.Entry, configEntry)
|
||||||
|
}
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (r *apiGatewayReconciler) Reconcile(ctx context.Context, req controller.Req
|
||||||
return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileHTTPRoute, r.cleanupRoute)
|
return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileHTTPRoute, r.cleanupRoute)
|
||||||
case structs.TCPRoute:
|
case structs.TCPRoute:
|
||||||
return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileTCPRoute, r.cleanupRoute)
|
return reconcileEntry(r.fsm.State(), r.logger, ctx, req, r.reconcileTCPRoute, r.cleanupRoute)
|
||||||
case structs.InlineCertificate:
|
case structs.InlineCertificate, structs.FileSystemCertificate:
|
||||||
return r.enqueueCertificateReferencedGateways(r.fsm.State(), ctx, req)
|
return r.enqueueCertificateReferencedGateways(r.fsm.State(), ctx, req)
|
||||||
case structs.JWTProvider:
|
case structs.JWTProvider:
|
||||||
return r.enqueueJWTProviderReferencedGatewaysAndHTTPRoutes(r.fsm.State(), ctx, req)
|
return r.enqueueJWTProviderReferencedGatewaysAndHTTPRoutes(r.fsm.State(), ctx, req)
|
||||||
|
|
|
@ -854,6 +854,7 @@ func NewServer(config *Config, flat Deps, externalGRPCServer *grpc.Server,
|
||||||
WithStateProvider(s.fsm).
|
WithStateProvider(s.fsm).
|
||||||
WithLogger(s.logger).
|
WithLogger(s.logger).
|
||||||
WithDatacenter(s.config.Datacenter).
|
WithDatacenter(s.config.Datacenter).
|
||||||
|
WithDisabledTenancyMetrics(s.config.DisablePerTenancyUsageMetrics).
|
||||||
WithReportingInterval(s.config.MetricsReportingInterval).
|
WithReportingInterval(s.config.MetricsReportingInterval).
|
||||||
WithGetMembersFunc(func() []serf.Member {
|
WithGetMembersFunc(func() []serf.Member {
|
||||||
members, err := s.lanPoolAllMembers()
|
members, err := s.lanPoolAllMembers()
|
||||||
|
|
|
@ -646,6 +646,7 @@ func validateProposedConfigEntryInGraph(
|
||||||
case structs.ExportedServices:
|
case structs.ExportedServices:
|
||||||
case structs.APIGateway: // TODO Consider checkGatewayClash
|
case structs.APIGateway: // TODO Consider checkGatewayClash
|
||||||
case structs.BoundAPIGateway:
|
case structs.BoundAPIGateway:
|
||||||
|
case structs.FileSystemCertificate:
|
||||||
case structs.InlineCertificate:
|
case structs.InlineCertificate:
|
||||||
case structs.HTTPRoute:
|
case structs.HTTPRoute:
|
||||||
case structs.TCPRoute:
|
case structs.TCPRoute:
|
||||||
|
|
|
@ -15,20 +15,21 @@ import (
|
||||||
|
|
||||||
// Adding events for a new config entry kind? Remember to update ConfigEntryFromStructs and ConfigEntryToStructs.
|
// Adding events for a new config entry kind? Remember to update ConfigEntryFromStructs and ConfigEntryToStructs.
|
||||||
var configEntryKindToTopic = map[string]stream.Topic{
|
var configEntryKindToTopic = map[string]stream.Topic{
|
||||||
structs.MeshConfig: EventTopicMeshConfig,
|
structs.MeshConfig: EventTopicMeshConfig,
|
||||||
structs.ServiceResolver: EventTopicServiceResolver,
|
structs.ServiceResolver: EventTopicServiceResolver,
|
||||||
structs.IngressGateway: EventTopicIngressGateway,
|
structs.IngressGateway: EventTopicIngressGateway,
|
||||||
structs.ServiceIntentions: EventTopicServiceIntentions,
|
structs.ServiceIntentions: EventTopicServiceIntentions,
|
||||||
structs.ServiceDefaults: EventTopicServiceDefaults,
|
structs.ServiceDefaults: EventTopicServiceDefaults,
|
||||||
structs.APIGateway: EventTopicAPIGateway,
|
structs.APIGateway: EventTopicAPIGateway,
|
||||||
structs.TCPRoute: EventTopicTCPRoute,
|
structs.TCPRoute: EventTopicTCPRoute,
|
||||||
structs.HTTPRoute: EventTopicHTTPRoute,
|
structs.HTTPRoute: EventTopicHTTPRoute,
|
||||||
structs.InlineCertificate: EventTopicInlineCertificate,
|
structs.FileSystemCertificate: EventTopicFileSystemCertificate,
|
||||||
structs.BoundAPIGateway: EventTopicBoundAPIGateway,
|
structs.InlineCertificate: EventTopicInlineCertificate,
|
||||||
structs.RateLimitIPConfig: EventTopicIPRateLimit,
|
structs.BoundAPIGateway: EventTopicBoundAPIGateway,
|
||||||
structs.SamenessGroup: EventTopicSamenessGroup,
|
structs.RateLimitIPConfig: EventTopicIPRateLimit,
|
||||||
structs.JWTProvider: EventTopicJWTProvider,
|
structs.SamenessGroup: EventTopicSamenessGroup,
|
||||||
structs.ExportedServices: EventTopicExportedServices,
|
structs.JWTProvider: EventTopicJWTProvider,
|
||||||
|
structs.ExportedServices: EventTopicExportedServices,
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventSubjectConfigEntry is a stream.Subject used to route and receive events
|
// EventSubjectConfigEntry is a stream.Subject used to route and receive events
|
||||||
|
@ -147,6 +148,12 @@ func (s *Store) HTTPRouteSnapshot(req stream.SubscribeRequest, buf stream.Snapsh
|
||||||
return s.configEntrySnapshot(structs.HTTPRoute, req, buf)
|
return s.configEntrySnapshot(structs.HTTPRoute, req, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileSystemCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of
|
||||||
|
// inline-certificate config entries.
|
||||||
|
func (s *Store) FileSystemCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||||
|
return s.configEntrySnapshot(structs.FileSystemCertificate, req, buf)
|
||||||
|
}
|
||||||
|
|
||||||
// InlineCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of
|
// InlineCertificateSnapshot is a stream.SnapshotFunc that returns a snapshot of
|
||||||
// inline-certificate config entries.
|
// inline-certificate config entries.
|
||||||
func (s *Store) InlineCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
func (s *Store) InlineCertificateSnapshot(req stream.SubscribeRequest, buf stream.SnapshotAppender) (uint64, error) {
|
||||||
|
|
|
@ -46,7 +46,8 @@ func PBToStreamSubscribeRequest(req *pbsubscribe.SubscribeRequest, entMeta acl.E
|
||||||
case EventTopicMeshConfig, EventTopicServiceResolver, EventTopicIngressGateway,
|
case EventTopicMeshConfig, EventTopicServiceResolver, EventTopicIngressGateway,
|
||||||
EventTopicServiceIntentions, EventTopicServiceDefaults, EventTopicAPIGateway,
|
EventTopicServiceIntentions, EventTopicServiceDefaults, EventTopicAPIGateway,
|
||||||
EventTopicTCPRoute, EventTopicHTTPRoute, EventTopicJWTProvider, EventTopicInlineCertificate,
|
EventTopicTCPRoute, EventTopicHTTPRoute, EventTopicJWTProvider, EventTopicInlineCertificate,
|
||||||
EventTopicBoundAPIGateway, EventTopicSamenessGroup, EventTopicExportedServices:
|
EventTopicBoundAPIGateway, EventTopicSamenessGroup, EventTopicExportedServices,
|
||||||
|
EventTopicFileSystemCertificate:
|
||||||
subject = EventSubjectConfigEntry{
|
subject = EventSubjectConfigEntry{
|
||||||
Name: named.Key,
|
Name: named.Key,
|
||||||
EnterpriseMeta: &entMeta,
|
EnterpriseMeta: &entMeta,
|
||||||
|
|
|
@ -196,23 +196,24 @@ func (db *readDB) ReadTxn() AbortTxn {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
EventTopicServiceHealth = pbsubscribe.Topic_ServiceHealth
|
EventTopicServiceHealth = pbsubscribe.Topic_ServiceHealth
|
||||||
EventTopicServiceHealthConnect = pbsubscribe.Topic_ServiceHealthConnect
|
EventTopicServiceHealthConnect = pbsubscribe.Topic_ServiceHealthConnect
|
||||||
EventTopicMeshConfig = pbsubscribe.Topic_MeshConfig
|
EventTopicMeshConfig = pbsubscribe.Topic_MeshConfig
|
||||||
EventTopicServiceResolver = pbsubscribe.Topic_ServiceResolver
|
EventTopicServiceResolver = pbsubscribe.Topic_ServiceResolver
|
||||||
EventTopicIngressGateway = pbsubscribe.Topic_IngressGateway
|
EventTopicIngressGateway = pbsubscribe.Topic_IngressGateway
|
||||||
EventTopicServiceIntentions = pbsubscribe.Topic_ServiceIntentions
|
EventTopicServiceIntentions = pbsubscribe.Topic_ServiceIntentions
|
||||||
EventTopicServiceDefaults = pbsubscribe.Topic_ServiceDefaults
|
EventTopicServiceDefaults = pbsubscribe.Topic_ServiceDefaults
|
||||||
EventTopicServiceList = pbsubscribe.Topic_ServiceList
|
EventTopicServiceList = pbsubscribe.Topic_ServiceList
|
||||||
EventTopicAPIGateway = pbsubscribe.Topic_APIGateway
|
EventTopicAPIGateway = pbsubscribe.Topic_APIGateway
|
||||||
EventTopicTCPRoute = pbsubscribe.Topic_TCPRoute
|
EventTopicTCPRoute = pbsubscribe.Topic_TCPRoute
|
||||||
EventTopicHTTPRoute = pbsubscribe.Topic_HTTPRoute
|
EventTopicHTTPRoute = pbsubscribe.Topic_HTTPRoute
|
||||||
EventTopicInlineCertificate = pbsubscribe.Topic_InlineCertificate
|
EventTopicFileSystemCertificate = pbsubscribe.Topic_FileSystemCertificate
|
||||||
EventTopicBoundAPIGateway = pbsubscribe.Topic_BoundAPIGateway
|
EventTopicInlineCertificate = pbsubscribe.Topic_InlineCertificate
|
||||||
EventTopicIPRateLimit = pbsubscribe.Topic_IPRateLimit
|
EventTopicBoundAPIGateway = pbsubscribe.Topic_BoundAPIGateway
|
||||||
EventTopicSamenessGroup = pbsubscribe.Topic_SamenessGroup
|
EventTopicIPRateLimit = pbsubscribe.Topic_IPRateLimit
|
||||||
EventTopicJWTProvider = pbsubscribe.Topic_JWTProvider
|
EventTopicSamenessGroup = pbsubscribe.Topic_SamenessGroup
|
||||||
EventTopicExportedServices = pbsubscribe.Topic_ExportedServices
|
EventTopicJWTProvider = pbsubscribe.Topic_JWTProvider
|
||||||
|
EventTopicExportedServices = pbsubscribe.Topic_ExportedServices
|
||||||
)
|
)
|
||||||
|
|
||||||
func processDBChanges(tx ReadTxn, changes Changes) ([]stream.Event, error) {
|
func processDBChanges(tx ReadTxn, changes Changes) ([]stream.Event, error) {
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/hashicorp/serf/serf"
|
"github.com/hashicorp/serf/serf"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/logging"
|
"github.com/hashicorp/consul/logging"
|
||||||
"github.com/hashicorp/consul/version"
|
"github.com/hashicorp/consul/version"
|
||||||
)
|
)
|
||||||
|
@ -76,6 +77,7 @@ type Config struct {
|
||||||
stateProvider StateProvider
|
stateProvider StateProvider
|
||||||
tickerInterval time.Duration
|
tickerInterval time.Duration
|
||||||
getMembersFunc getMembersFunc
|
getMembersFunc getMembersFunc
|
||||||
|
excludeTenancy bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithDatacenter adds the datacenter as a label to all metrics emitted by the
|
// WithDatacenter adds the datacenter as a label to all metrics emitted by the
|
||||||
|
@ -85,6 +87,12 @@ func (c *Config) WithDatacenter(dc string) *Config {
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithDisabledTenancyMetrics opts the user out of specifying usage metrics for each tenancy.
|
||||||
|
func (c *Config) WithDisabledTenancyMetrics(disabled bool) *Config {
|
||||||
|
c.excludeTenancy = disabled
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
// WithLogger takes a logger and creates a new, named sub-logger to use when
|
// WithLogger takes a logger and creates a new, named sub-logger to use when
|
||||||
// running
|
// running
|
||||||
func (c *Config) WithLogger(logger hclog.Logger) *Config {
|
func (c *Config) WithLogger(logger hclog.Logger) *Config {
|
||||||
|
@ -125,6 +133,9 @@ type UsageMetricsReporter struct {
|
||||||
stateProvider StateProvider
|
stateProvider StateProvider
|
||||||
tickerInterval time.Duration
|
tickerInterval time.Duration
|
||||||
getMembersFunc getMembersFunc
|
getMembersFunc getMembersFunc
|
||||||
|
excludeTenancy bool
|
||||||
|
|
||||||
|
usageReporter
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUsageMetricsReporter(cfg *Config) (*UsageMetricsReporter, error) {
|
func NewUsageMetricsReporter(cfg *Config) (*UsageMetricsReporter, error) {
|
||||||
|
@ -151,8 +162,11 @@ func NewUsageMetricsReporter(cfg *Config) (*UsageMetricsReporter, error) {
|
||||||
metricLabels: cfg.metricLabels,
|
metricLabels: cfg.metricLabels,
|
||||||
tickerInterval: cfg.tickerInterval,
|
tickerInterval: cfg.tickerInterval,
|
||||||
getMembersFunc: cfg.getMembersFunc,
|
getMembersFunc: cfg.getMembersFunc,
|
||||||
|
excludeTenancy: cfg.excludeTenancy,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u.usageReporter = newTenancyUsageReporter(u)
|
||||||
|
|
||||||
return u, nil
|
return u, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,7 +204,7 @@ func (u *UsageMetricsReporter) runOnce() {
|
||||||
|
|
||||||
u.emitPeeringUsage(peeringUsage)
|
u.emitPeeringUsage(peeringUsage)
|
||||||
|
|
||||||
_, serviceUsage, err := state.ServiceUsage(nil, true)
|
_, serviceUsage, err := state.ServiceUsage(nil, !u.excludeTenancy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
u.logger.Warn("failed to retrieve services from state store", "error", err)
|
u.logger.Warn("failed to retrieve services from state store", "error", err)
|
||||||
}
|
}
|
||||||
|
@ -259,3 +273,112 @@ func versionWithMetadata() string {
|
||||||
|
|
||||||
return vsn
|
return vsn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type usageReporter interface {
|
||||||
|
emitNodeUsage(nodeUsage state.NodeUsage)
|
||||||
|
emitPeeringUsage(peeringUsage state.PeeringUsage)
|
||||||
|
emitMemberUsage(members []serf.Member)
|
||||||
|
emitServiceUsage(serviceUsage structs.ServiceUsage)
|
||||||
|
emitKVUsage(kvUsage state.KVUsage)
|
||||||
|
emitConfigEntryUsage(configUsage state.ConfigEntryUsage)
|
||||||
|
}
|
||||||
|
|
||||||
|
type baseUsageReporter struct {
|
||||||
|
metricLabels []metrics.Label
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ usageReporter = (*baseUsageReporter)(nil)
|
||||||
|
|
||||||
|
func newBaseUsageReporter(u *UsageMetricsReporter) *baseUsageReporter {
|
||||||
|
return &baseUsageReporter{
|
||||||
|
metricLabels: u.metricLabels,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseUsageReporter) emitNodeUsage(nodeUsage state.NodeUsage) {
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "nodes"},
|
||||||
|
float32(nodeUsage.Nodes),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseUsageReporter) emitPeeringUsage(peeringUsage state.PeeringUsage) {
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "peerings"},
|
||||||
|
float32(peeringUsage.Peerings),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseUsageReporter) emitMemberUsage(members []serf.Member) {
|
||||||
|
var (
|
||||||
|
servers int
|
||||||
|
clients int
|
||||||
|
)
|
||||||
|
for _, m := range members {
|
||||||
|
switch m.Tags["role"] {
|
||||||
|
case "node":
|
||||||
|
clients++
|
||||||
|
case "consul":
|
||||||
|
servers++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"members", "clients"},
|
||||||
|
float32(clients),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"members", "servers"},
|
||||||
|
float32(servers),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseUsageReporter) emitServiceUsage(serviceUsage structs.ServiceUsage) {
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "services"},
|
||||||
|
float32(serviceUsage.Services),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "service_instances"},
|
||||||
|
float32(serviceUsage.ServiceInstances),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "billable_service_instances"},
|
||||||
|
float32(serviceUsage.BillableServiceInstances),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
|
||||||
|
for k, i := range serviceUsage.ConnectServiceInstances {
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "connect_instances"},
|
||||||
|
float32(i),
|
||||||
|
append(u.metricLabels, metrics.Label{Name: "kind", Value: k}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseUsageReporter) emitKVUsage(kvUsage state.KVUsage) {
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "kv_entries"},
|
||||||
|
float32(kvUsage.KVCount),
|
||||||
|
u.metricLabels,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *baseUsageReporter) emitConfigEntryUsage(configUsage state.ConfigEntryUsage) {
|
||||||
|
for k, i := range configUsage.ConfigByKind {
|
||||||
|
metrics.SetGaugeWithLabels(
|
||||||
|
[]string{"state", "config_entries"},
|
||||||
|
float32(i),
|
||||||
|
append(u.metricLabels, metrics.Label{Name: "kind", Value: k}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,99 +5,6 @@
|
||||||
|
|
||||||
package usagemetrics
|
package usagemetrics
|
||||||
|
|
||||||
import (
|
func newTenancyUsageReporter(u *UsageMetricsReporter) usageReporter {
|
||||||
"github.com/armon/go-metrics"
|
return newBaseUsageReporter(u)
|
||||||
|
|
||||||
"github.com/hashicorp/serf/serf"
|
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (u *UsageMetricsReporter) emitNodeUsage(nodeUsage state.NodeUsage) {
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "nodes"},
|
|
||||||
float32(nodeUsage.Nodes),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UsageMetricsReporter) emitPeeringUsage(peeringUsage state.PeeringUsage) {
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "peerings"},
|
|
||||||
float32(peeringUsage.Peerings),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UsageMetricsReporter) emitMemberUsage(members []serf.Member) {
|
|
||||||
var (
|
|
||||||
servers int
|
|
||||||
clients int
|
|
||||||
)
|
|
||||||
for _, m := range members {
|
|
||||||
switch m.Tags["role"] {
|
|
||||||
case "node":
|
|
||||||
clients++
|
|
||||||
case "consul":
|
|
||||||
servers++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"members", "clients"},
|
|
||||||
float32(clients),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"members", "servers"},
|
|
||||||
float32(servers),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UsageMetricsReporter) emitServiceUsage(serviceUsage structs.ServiceUsage) {
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "services"},
|
|
||||||
float32(serviceUsage.Services),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "service_instances"},
|
|
||||||
float32(serviceUsage.ServiceInstances),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "billable_service_instances"},
|
|
||||||
float32(serviceUsage.BillableServiceInstances),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
|
|
||||||
for k, i := range serviceUsage.ConnectServiceInstances {
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "connect_instances"},
|
|
||||||
float32(i),
|
|
||||||
append(u.metricLabels, metrics.Label{Name: "kind", Value: k}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UsageMetricsReporter) emitKVUsage(kvUsage state.KVUsage) {
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "kv_entries"},
|
|
||||||
float32(kvUsage.KVCount),
|
|
||||||
u.metricLabels,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *UsageMetricsReporter) emitConfigEntryUsage(configUsage state.ConfigEntryUsage) {
|
|
||||||
for k, i := range configUsage.ConfigByKind {
|
|
||||||
metrics.SetGaugeWithLabels(
|
|
||||||
[]string{"state", "config_entries"},
|
|
||||||
float32(i),
|
|
||||||
append(u.metricLabels, metrics.Label{Name: "kind", Value: k}),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,938 +6,46 @@
|
||||||
package usagemetrics
|
package usagemetrics
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"github.com/armon/go-metrics"
|
||||||
"github.com/hashicorp/serf/serf"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/hashicorp/go-uuid"
|
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
|
||||||
"github.com/hashicorp/consul/proto/private/pbpeering"
|
|
||||||
"github.com/hashicorp/consul/sdk/testutil"
|
"github.com/hashicorp/consul/sdk/testutil"
|
||||||
"github.com/hashicorp/consul/version"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStateStore() (*state.Store, error) {
|
func newStateStore() (*state.Store, error) {
|
||||||
return state.NewStateStore(nil), nil
|
return state.NewStateStore(nil), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type testCase struct {
|
func TestUsageReporter_CE(t *testing.T) {
|
||||||
modfiyStateStore func(t *testing.T, s *state.Store)
|
getMetricsReporter := func(tc testCase) (*UsageMetricsReporter, *metrics.InmemSink, error) {
|
||||||
getMembersFunc getMembersFunc
|
// Only have a single interval for the test
|
||||||
expectedGauges map[string]metrics.GaugeValue
|
sink := metrics.NewInmemSink(1*time.Minute, 1*time.Minute)
|
||||||
}
|
cfg := metrics.DefaultConfig("consul.usage.test")
|
||||||
|
cfg.EnableHostname = false
|
||||||
|
metrics.NewGlobal(cfg, sink)
|
||||||
|
|
||||||
var baseCases = map[string]testCase{
|
mockStateProvider := &mockStateProvider{}
|
||||||
"empty-state": {
|
s, err := newStateStore()
|
||||||
expectedGauges: map[string]metrics.GaugeValue{
|
|
||||||
// --- node ---
|
|
||||||
"consul.usage.test.state.nodes;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.nodes",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- peering ---
|
|
||||||
"consul.usage.test.state.peerings;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.peerings",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- member ---
|
|
||||||
"consul.usage.test.members.clients;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.members.clients",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
"consul.usage.test.members.servers;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.members.servers",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- service ---
|
|
||||||
"consul.usage.test.state.services;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.services",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.service_instances;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.service_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- service mesh ---
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-proxy": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "connect-proxy"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=terminating-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=ingress-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "ingress-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=api-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "api-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=mesh-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "mesh-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-native": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "connect-native"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.billable_service_instances;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.billable_service_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// --- kv ---
|
|
||||||
"consul.usage.test.state.kv_entries;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.kv_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- config entries ---
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-intentions": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-intentions"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-resolver": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-resolver"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-router": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-router"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-defaults": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-defaults"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=ingress-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "ingress-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-splitter": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-splitter"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=mesh": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "mesh"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=proxy-defaults": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "proxy-defaults"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=terminating-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=exported-services": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "exported-services"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"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.state.config_entries;datacenter=dc1;kind=api-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "api-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=bound-api-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "bound-api-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "inline-certificate"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=http-route": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "http-route"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=tcp-route": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "tcp-route"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=jwt-provider": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "jwt-provider"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=control-plane-request-limit": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "control-plane-request-limit"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// --- version ---
|
|
||||||
fmt.Sprintf("consul.usage.test.version;version=%s;pre_release=%s", versionWithMetadata(), version.VersionPrerelease): {
|
|
||||||
Name: "consul.usage.test.version",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "version", Value: versionWithMetadata()},
|
|
||||||
{Name: "pre_release", Value: version.VersionPrerelease},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
|
||||||
},
|
|
||||||
"nodes": {
|
|
||||||
modfiyStateStore: func(t *testing.T, s *state.Store) {
|
|
||||||
require.NoError(t, s.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}))
|
|
||||||
require.NoError(t, s.EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.2"}))
|
|
||||||
},
|
|
||||||
getMembersFunc: func() []serf.Member {
|
|
||||||
return []serf.Member{
|
|
||||||
{
|
|
||||||
Name: "foo",
|
|
||||||
Tags: map[string]string{"role": "consul"},
|
|
||||||
Status: serf.StatusAlive,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Tags: map[string]string{"role": "consul"},
|
|
||||||
Status: serf.StatusAlive,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expectedGauges: map[string]metrics.GaugeValue{
|
|
||||||
// --- node ---
|
|
||||||
"consul.usage.test.state.nodes;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.nodes",
|
|
||||||
Value: 2,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- peering ---
|
|
||||||
"consul.usage.test.state.peerings;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.peerings",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- member ---
|
|
||||||
"consul.usage.test.members.servers;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.members.servers",
|
|
||||||
Value: 2,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
"consul.usage.test.members.clients;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.members.clients",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- service ---
|
|
||||||
"consul.usage.test.state.services;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.services",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.service_instances;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.service_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- service mesh ---
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-proxy": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "connect-proxy"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=terminating-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=ingress-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "ingress-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=api-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "api-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=mesh-gateway": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "mesh-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-native": {
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "connect-native"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.billable_service_instances;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.billable_service_instances",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// --- kv ---
|
|
||||||
"consul.usage.test.state.kv_entries;datacenter=dc1": {
|
|
||||||
Name: "consul.usage.test.state.kv_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
},
|
|
||||||
// --- config entries ---
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-intentions": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-intentions"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-resolver": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-resolver"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-router": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-router"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-defaults": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-defaults"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=ingress-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "ingress-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-splitter": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "service-splitter"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=mesh": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "mesh"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=proxy-defaults": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "proxy-defaults"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=terminating-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=exported-services": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "exported-services"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"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.state.config_entries;datacenter=dc1;kind=api-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "api-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=bound-api-gateway": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "bound-api-gateway"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "inline-certificate"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=http-route": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "http-route"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=tcp-route": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "tcp-route"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=jwt-provider": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "jwt-provider"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"consul.usage.test.state.config_entries;datacenter=dc1;kind=control-plane-request-limit": {
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "control-plane-request-limit"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// --- version ---
|
|
||||||
fmt.Sprintf("consul.usage.test.version;version=%s;pre_release=%s", versionWithMetadata(), version.VersionPrerelease): {
|
|
||||||
Name: "consul.usage.test.version",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "version", Value: versionWithMetadata()},
|
|
||||||
{Name: "pre_release", Value: version.VersionPrerelease},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageReporter_emitNodeUsage_CE(t *testing.T) {
|
|
||||||
cases := baseCases
|
|
||||||
|
|
||||||
for name, tcase := range cases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
// Only have a single interval for the test
|
|
||||||
sink := metrics.NewInmemSink(1*time.Minute, 1*time.Minute)
|
|
||||||
cfg := metrics.DefaultConfig("consul.usage.test")
|
|
||||||
cfg.EnableHostname = false
|
|
||||||
metrics.NewGlobal(cfg, sink)
|
|
||||||
|
|
||||||
mockStateProvider := &mockStateProvider{}
|
|
||||||
s, err := newStateStore()
|
|
||||||
require.NoError(t, err)
|
|
||||||
if tcase.modfiyStateStore != nil {
|
|
||||||
tcase.modfiyStateStore(t, s)
|
|
||||||
}
|
|
||||||
mockStateProvider.On("State").Return(s)
|
|
||||||
|
|
||||||
reporter, err := NewUsageMetricsReporter(
|
|
||||||
new(Config).
|
|
||||||
WithStateProvider(mockStateProvider).
|
|
||||||
WithLogger(testutil.Logger(t)).
|
|
||||||
WithDatacenter("dc1").
|
|
||||||
WithGetMembersFunc(tcase.getMembersFunc),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reporter.runOnce()
|
|
||||||
|
|
||||||
intervals := sink.Data()
|
|
||||||
require.Len(t, intervals, 1)
|
|
||||||
intv := intervals[0]
|
|
||||||
|
|
||||||
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageReporter_emitPeeringUsage_CE(t *testing.T) {
|
|
||||||
cases := make(map[string]testCase)
|
|
||||||
for k, v := range baseCases {
|
|
||||||
eg := make(map[string]metrics.GaugeValue)
|
|
||||||
for k, v := range v.expectedGauges {
|
|
||||||
eg[k] = v
|
|
||||||
}
|
|
||||||
cases[k] = testCase{v.modfiyStateStore, v.getMembersFunc, eg}
|
|
||||||
}
|
|
||||||
peeringsCase := cases["nodes"]
|
|
||||||
peeringsCase.modfiyStateStore = func(t *testing.T, s *state.Store) {
|
|
||||||
id, err := uuid.GenerateUUID()
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.NoError(t, s.PeeringWrite(1, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{Name: "foo", ID: id}}))
|
if tc.modifyStateStore != nil {
|
||||||
id, err = uuid.GenerateUUID()
|
tc.modifyStateStore(t, s)
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, s.PeeringWrite(2, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{Name: "bar", ID: id}}))
|
|
||||||
id, err = uuid.GenerateUUID()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NoError(t, s.PeeringWrite(3, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{Name: "baz", ID: id}}))
|
|
||||||
}
|
|
||||||
peeringsCase.getMembersFunc = func() []serf.Member {
|
|
||||||
return []serf.Member{
|
|
||||||
{
|
|
||||||
Name: "foo",
|
|
||||||
Tags: map[string]string{"role": "consul"},
|
|
||||||
Status: serf.StatusAlive,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "bar",
|
|
||||||
Tags: map[string]string{"role": "consul"},
|
|
||||||
Status: serf.StatusAlive,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
mockStateProvider.On("State").Return(s)
|
||||||
|
|
||||||
|
reporter, err := NewUsageMetricsReporter(
|
||||||
|
new(Config).
|
||||||
|
WithStateProvider(mockStateProvider).
|
||||||
|
WithLogger(testutil.Logger(t)).
|
||||||
|
WithDatacenter("dc1").
|
||||||
|
WithGetMembersFunc(tc.getMembersFunc),
|
||||||
|
)
|
||||||
|
|
||||||
|
return reporter, sink, err
|
||||||
}
|
}
|
||||||
peeringsCase.expectedGauges["consul.usage.test.state.nodes;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.nodes",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
peeringsCase.expectedGauges["consul.usage.test.state.peerings;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.peerings",
|
|
||||||
Value: 3,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
peeringsCase.expectedGauges["consul.usage.test.members.clients;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.members.clients",
|
|
||||||
Value: 0,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
cases["peerings"] = peeringsCase
|
|
||||||
delete(cases, "nodes")
|
|
||||||
|
|
||||||
for name, tcase := range cases {
|
testUsageReporter_Tenantless(t, getMetricsReporter)
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
// Only have a single interval for the test
|
|
||||||
sink := metrics.NewInmemSink(1*time.Minute, 1*time.Minute)
|
|
||||||
cfg := metrics.DefaultConfig("consul.usage.test")
|
|
||||||
cfg.EnableHostname = false
|
|
||||||
metrics.NewGlobal(cfg, sink)
|
|
||||||
|
|
||||||
mockStateProvider := &mockStateProvider{}
|
|
||||||
s, err := newStateStore()
|
|
||||||
require.NoError(t, err)
|
|
||||||
if tcase.modfiyStateStore != nil {
|
|
||||||
tcase.modfiyStateStore(t, s)
|
|
||||||
}
|
|
||||||
mockStateProvider.On("State").Return(s)
|
|
||||||
|
|
||||||
reporter, err := NewUsageMetricsReporter(
|
|
||||||
new(Config).
|
|
||||||
WithStateProvider(mockStateProvider).
|
|
||||||
WithLogger(testutil.Logger(t)).
|
|
||||||
WithDatacenter("dc1").
|
|
||||||
WithGetMembersFunc(tcase.getMembersFunc),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reporter.runOnce()
|
|
||||||
|
|
||||||
intervals := sink.Data()
|
|
||||||
require.Len(t, intervals, 1)
|
|
||||||
intv := intervals[0]
|
|
||||||
|
|
||||||
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageReporter_emitServiceUsage_CE(t *testing.T) {
|
|
||||||
cases := make(map[string]testCase)
|
|
||||||
for k, v := range baseCases {
|
|
||||||
eg := make(map[string]metrics.GaugeValue)
|
|
||||||
for k, v := range v.expectedGauges {
|
|
||||||
eg[k] = v
|
|
||||||
}
|
|
||||||
cases[k] = testCase{v.modfiyStateStore, v.getMembersFunc, eg}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesAndSvcsCase := cases["nodes"]
|
|
||||||
nodesAndSvcsCase.modfiyStateStore = func(t *testing.T, s *state.Store) {
|
|
||||||
require.NoError(t, s.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}))
|
|
||||||
require.NoError(t, s.EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.2"}))
|
|
||||||
require.NoError(t, s.EnsureNode(3, &structs.Node{Node: "baz", Address: "127.0.0.2"}))
|
|
||||||
require.NoError(t, s.EnsureNode(4, &structs.Node{Node: "qux", Address: "127.0.0.3"}))
|
|
||||||
|
|
||||||
apigw := structs.TestNodeServiceAPIGateway(t)
|
|
||||||
apigw.ID = "api-gateway"
|
|
||||||
|
|
||||||
mgw := structs.TestNodeServiceMeshGateway(t)
|
|
||||||
mgw.ID = "mesh-gateway"
|
|
||||||
|
|
||||||
tgw := structs.TestNodeServiceTerminatingGateway(t, "1.1.1.1")
|
|
||||||
tgw.ID = "terminating-gateway"
|
|
||||||
// Typical services and some consul services spread across two nodes
|
|
||||||
require.NoError(t, s.EnsureService(5, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: nil, Address: "", Port: 5000}))
|
|
||||||
require.NoError(t, s.EnsureService(6, "bar", &structs.NodeService{ID: "api", Service: "api", Tags: nil, Address: "", Port: 5000}))
|
|
||||||
require.NoError(t, s.EnsureService(7, "foo", &structs.NodeService{ID: "consul", Service: "consul", Tags: nil}))
|
|
||||||
require.NoError(t, s.EnsureService(8, "bar", &structs.NodeService{ID: "consul", Service: "consul", Tags: nil}))
|
|
||||||
require.NoError(t, s.EnsureService(9, "foo", &structs.NodeService{ID: "db-connect-proxy", Service: "db-connect-proxy", Tags: nil, Address: "", Port: 5000, Kind: structs.ServiceKindConnectProxy}))
|
|
||||||
require.NoError(t, s.EnsureRegistration(10, structs.TestRegisterIngressGateway(t)))
|
|
||||||
require.NoError(t, s.EnsureService(11, "foo", mgw))
|
|
||||||
require.NoError(t, s.EnsureService(12, "foo", tgw))
|
|
||||||
require.NoError(t, s.EnsureService(13, "foo", apigw))
|
|
||||||
require.NoError(t, s.EnsureService(14, "bar", &structs.NodeService{ID: "db-native", Service: "db", Tags: nil, Address: "", Port: 5000, Connect: structs.ServiceConnect{Native: true}}))
|
|
||||||
require.NoError(t, s.EnsureConfigEntry(15, &structs.IngressGatewayConfigEntry{
|
|
||||||
Kind: structs.IngressGateway,
|
|
||||||
Name: "foo",
|
|
||||||
}))
|
|
||||||
require.NoError(t, s.EnsureConfigEntry(16, &structs.IngressGatewayConfigEntry{
|
|
||||||
Kind: structs.IngressGateway,
|
|
||||||
Name: "bar",
|
|
||||||
}))
|
|
||||||
require.NoError(t, s.EnsureConfigEntry(17, &structs.IngressGatewayConfigEntry{
|
|
||||||
Kind: structs.IngressGateway,
|
|
||||||
Name: "baz",
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
baseCaseMembers := nodesAndSvcsCase.getMembersFunc()
|
|
||||||
nodesAndSvcsCase.getMembersFunc = func() []serf.Member {
|
|
||||||
baseCaseMembers = append(baseCaseMembers, serf.Member{
|
|
||||||
Name: "baz",
|
|
||||||
Tags: map[string]string{"role": "node", "segment": "a"},
|
|
||||||
Status: serf.StatusAlive,
|
|
||||||
})
|
|
||||||
baseCaseMembers = append(baseCaseMembers, serf.Member{
|
|
||||||
Name: "qux",
|
|
||||||
Tags: map[string]string{"role": "node", "segment": "b"},
|
|
||||||
Status: serf.StatusAlive,
|
|
||||||
})
|
|
||||||
return baseCaseMembers
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.nodes;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.nodes",
|
|
||||||
Value: 4,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.members.clients;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.members.clients",
|
|
||||||
Value: 2,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.services;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.services",
|
|
||||||
Value: 8,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.service_instances;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.service_instances",
|
|
||||||
Value: 10,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-proxy"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "connect-proxy"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=terminating-gateway"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "terminating-gateway"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=ingress-gateway"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "ingress-gateway"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=api-gateway"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "api-gateway"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=mesh-gateway"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "mesh-gateway"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-native"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.connect_instances",
|
|
||||||
Value: 1,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "connect-native"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.billable_service_instances;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.billable_service_instances",
|
|
||||||
Value: 3,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.config_entries;datacenter=dc1;kind=ingress-gateway"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.config_entries",
|
|
||||||
Value: 3,
|
|
||||||
Labels: []metrics.Label{
|
|
||||||
{Name: "datacenter", Value: "dc1"},
|
|
||||||
{Name: "kind", Value: "ingress-gateway"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
cases["nodes-and-services"] = nodesAndSvcsCase
|
|
||||||
delete(cases, "nodes")
|
|
||||||
|
|
||||||
for name, tcase := range cases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
// Only have a single interval for the test
|
|
||||||
sink := metrics.NewInmemSink(1*time.Minute, 1*time.Minute)
|
|
||||||
cfg := metrics.DefaultConfig("consul.usage.test")
|
|
||||||
cfg.EnableHostname = false
|
|
||||||
metrics.NewGlobal(cfg, sink)
|
|
||||||
|
|
||||||
mockStateProvider := &mockStateProvider{}
|
|
||||||
s := state.NewStateStore(nil)
|
|
||||||
if tcase.modfiyStateStore != nil {
|
|
||||||
tcase.modfiyStateStore(t, s)
|
|
||||||
}
|
|
||||||
mockStateProvider.On("State").Return(s)
|
|
||||||
|
|
||||||
reporter, err := NewUsageMetricsReporter(
|
|
||||||
new(Config).
|
|
||||||
WithStateProvider(mockStateProvider).
|
|
||||||
WithLogger(testutil.Logger(t)).
|
|
||||||
WithDatacenter("dc1").
|
|
||||||
WithGetMembersFunc(tcase.getMembersFunc),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reporter.runOnce()
|
|
||||||
|
|
||||||
intervals := sink.Data()
|
|
||||||
require.Len(t, intervals, 1)
|
|
||||||
intv := intervals[0]
|
|
||||||
|
|
||||||
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUsageReporter_emitKVUsage_CE(t *testing.T) {
|
|
||||||
cases := make(map[string]testCase)
|
|
||||||
for k, v := range baseCases {
|
|
||||||
eg := make(map[string]metrics.GaugeValue)
|
|
||||||
for k, v := range v.expectedGauges {
|
|
||||||
eg[k] = v
|
|
||||||
}
|
|
||||||
cases[k] = testCase{v.modfiyStateStore, v.getMembersFunc, eg}
|
|
||||||
}
|
|
||||||
|
|
||||||
nodesCase := cases["nodes"]
|
|
||||||
mss := nodesCase.modfiyStateStore
|
|
||||||
nodesCase.modfiyStateStore = func(t *testing.T, s *state.Store) {
|
|
||||||
mss(t, s)
|
|
||||||
require.NoError(t, s.KVSSet(4, &structs.DirEntry{Key: "a", Value: []byte{1}}))
|
|
||||||
require.NoError(t, s.KVSSet(5, &structs.DirEntry{Key: "b", Value: []byte{1}}))
|
|
||||||
require.NoError(t, s.KVSSet(6, &structs.DirEntry{Key: "c", Value: []byte{1}}))
|
|
||||||
require.NoError(t, s.KVSSet(7, &structs.DirEntry{Key: "d", Value: []byte{1}}))
|
|
||||||
require.NoError(t, s.KVSDelete(8, "d", &acl.EnterpriseMeta{}))
|
|
||||||
require.NoError(t, s.KVSDelete(9, "c", &acl.EnterpriseMeta{}))
|
|
||||||
require.NoError(t, s.KVSSet(10, &structs.DirEntry{Key: "e", Value: []byte{1}}))
|
|
||||||
require.NoError(t, s.KVSSet(11, &structs.DirEntry{Key: "f", Value: []byte{1}}))
|
|
||||||
}
|
|
||||||
nodesCase.expectedGauges["consul.usage.test.state.kv_entries;datacenter=dc1"] = metrics.GaugeValue{
|
|
||||||
Name: "consul.usage.test.state.kv_entries",
|
|
||||||
Value: 4,
|
|
||||||
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
|
||||||
}
|
|
||||||
cases["nodes"] = nodesCase
|
|
||||||
|
|
||||||
for name, tcase := range cases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
// Only have a single interval for the test
|
|
||||||
sink := metrics.NewInmemSink(1*time.Minute, 1*time.Minute)
|
|
||||||
cfg := metrics.DefaultConfig("consul.usage.test")
|
|
||||||
cfg.EnableHostname = false
|
|
||||||
metrics.NewGlobal(cfg, sink)
|
|
||||||
|
|
||||||
mockStateProvider := &mockStateProvider{}
|
|
||||||
s, err := newStateStore()
|
|
||||||
require.NoError(t, err)
|
|
||||||
if tcase.modfiyStateStore != nil {
|
|
||||||
tcase.modfiyStateStore(t, s)
|
|
||||||
}
|
|
||||||
mockStateProvider.On("State").Return(s)
|
|
||||||
|
|
||||||
reporter, err := NewUsageMetricsReporter(
|
|
||||||
new(Config).
|
|
||||||
WithStateProvider(mockStateProvider).
|
|
||||||
WithLogger(testutil.Logger(t)).
|
|
||||||
WithDatacenter("dc1").
|
|
||||||
WithGetMembersFunc(tcase.getMembersFunc),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reporter.runOnce()
|
|
||||||
|
|
||||||
intervals := sink.Data()
|
|
||||||
require.Len(t, intervals, 1)
|
|
||||||
intv := intervals[0]
|
|
||||||
|
|
||||||
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,15 +10,19 @@ import (
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"github.com/armon/go-metrics"
|
||||||
"github.com/armon/go-metrics/prometheus"
|
"github.com/armon/go-metrics/prometheus"
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/hashicorp/go-uuid"
|
||||||
|
"github.com/hashicorp/serf/serf"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/consul/proto/private/pbpeering"
|
||||||
"github.com/hashicorp/serf/serf"
|
"github.com/hashicorp/consul/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
type mockStateProvider struct {
|
type mockStateProvider struct {
|
||||||
|
@ -119,3 +123,872 @@ type benchStateProvider func() *state.Store
|
||||||
func (b benchStateProvider) State() *state.Store {
|
func (b benchStateProvider) State() *state.Store {
|
||||||
return b()
|
return b()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type testCase struct {
|
||||||
|
modifyStateStore func(t *testing.T, s *state.Store)
|
||||||
|
getMembersFunc getMembersFunc
|
||||||
|
expectedGauges map[string]metrics.GaugeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseCases = map[string]testCase{
|
||||||
|
"empty-state": {
|
||||||
|
expectedGauges: map[string]metrics.GaugeValue{
|
||||||
|
// --- node ---
|
||||||
|
"consul.usage.test.state.nodes;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.nodes",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- peering ---
|
||||||
|
"consul.usage.test.state.peerings;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.peerings",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- member ---
|
||||||
|
"consul.usage.test.members.clients;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.members.clients",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
"consul.usage.test.members.servers;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.members.servers",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- service ---
|
||||||
|
"consul.usage.test.state.services;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.services",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.service_instances;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.service_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- service mesh ---
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-proxy": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "connect-proxy"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=terminating-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=ingress-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "ingress-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=api-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "api-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=mesh-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "mesh-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-native": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "connect-native"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.billable_service_instances;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.billable_service_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// --- kv ---
|
||||||
|
"consul.usage.test.state.kv_entries;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.kv_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- config entries ---
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-intentions": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-intentions"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-resolver": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-resolver"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-router": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-router"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-defaults": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-defaults"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=ingress-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "ingress-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-splitter": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-splitter"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=mesh": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "mesh"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=proxy-defaults": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "proxy-defaults"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=terminating-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=exported-services": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "exported-services"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"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.state.config_entries;datacenter=dc1;kind=api-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "api-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=bound-api-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "bound-api-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=file-system-certificate": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "file-system-certificate"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "inline-certificate"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=http-route": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "http-route"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=tcp-route": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "tcp-route"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=jwt-provider": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "jwt-provider"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=control-plane-request-limit": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "control-plane-request-limit"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// --- version ---
|
||||||
|
fmt.Sprintf("consul.usage.test.version;version=%s;pre_release=%s", versionWithMetadata(), version.VersionPrerelease): {
|
||||||
|
Name: "consul.usage.test.version",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "version", Value: versionWithMetadata()},
|
||||||
|
{Name: "pre_release", Value: version.VersionPrerelease},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
getMembersFunc: func() []serf.Member { return []serf.Member{} },
|
||||||
|
},
|
||||||
|
"nodes": {
|
||||||
|
modifyStateStore: func(t *testing.T, s *state.Store) {
|
||||||
|
require.NoError(t, s.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}))
|
||||||
|
require.NoError(t, s.EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.2"}))
|
||||||
|
},
|
||||||
|
getMembersFunc: func() []serf.Member {
|
||||||
|
return []serf.Member{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Tags: map[string]string{"role": "consul"},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Tags: map[string]string{"role": "consul"},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expectedGauges: map[string]metrics.GaugeValue{
|
||||||
|
// --- node ---
|
||||||
|
"consul.usage.test.state.nodes;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.nodes",
|
||||||
|
Value: 2,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- peering ---
|
||||||
|
"consul.usage.test.state.peerings;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.peerings",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- member ---
|
||||||
|
"consul.usage.test.members.servers;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.members.servers",
|
||||||
|
Value: 2,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
"consul.usage.test.members.clients;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.members.clients",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- service ---
|
||||||
|
"consul.usage.test.state.services;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.services",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.service_instances;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.service_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- service mesh ---
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-proxy": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "connect-proxy"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=terminating-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=ingress-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "ingress-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=api-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "api-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=mesh-gateway": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "mesh-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-native": {
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "connect-native"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.billable_service_instances;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.billable_service_instances",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// --- kv ---
|
||||||
|
"consul.usage.test.state.kv_entries;datacenter=dc1": {
|
||||||
|
Name: "consul.usage.test.state.kv_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
},
|
||||||
|
// --- config entries ---
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-intentions": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-intentions"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-resolver": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-resolver"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-router": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-router"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-defaults": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-defaults"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=ingress-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "ingress-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=service-splitter": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "service-splitter"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=mesh": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "mesh"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=proxy-defaults": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "proxy-defaults"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=terminating-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=exported-services": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "exported-services"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"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.state.config_entries;datacenter=dc1;kind=api-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "api-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=bound-api-gateway": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "bound-api-gateway"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=file-system-certificate": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "file-system-certificate"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=inline-certificate": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "inline-certificate"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=http-route": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "http-route"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=tcp-route": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "tcp-route"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=jwt-provider": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "jwt-provider"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"consul.usage.test.state.config_entries;datacenter=dc1;kind=control-plane-request-limit": {
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "control-plane-request-limit"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// --- version ---
|
||||||
|
fmt.Sprintf("consul.usage.test.version;version=%s;pre_release=%s", versionWithMetadata(), version.VersionPrerelease): {
|
||||||
|
Name: "consul.usage.test.version",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "version", Value: versionWithMetadata()},
|
||||||
|
{Name: "pre_release", Value: version.VersionPrerelease},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUsageReporter_Tenantless(t *testing.T, getMetricsReporter func(tc testCase) (*UsageMetricsReporter, *metrics.InmemSink, error)) {
|
||||||
|
t.Run("emitNodeUsage", func(t *testing.T) {
|
||||||
|
testUsageReporter_emitNodeUsage_CE(t, getMetricsReporter)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("emitPeeringUsage", func(t *testing.T) {
|
||||||
|
testUsageReporter_emitPeeringUsage_CE(t, getMetricsReporter)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("emitServiceUsage", func(t *testing.T) {
|
||||||
|
testUsageReporter_emitServiceUsage_CE(t, getMetricsReporter)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("emitKVUsage", func(t *testing.T) {
|
||||||
|
testUsageReporter_emitKVUsage_CE(t, getMetricsReporter)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUsageReporter_emitNodeUsage_CE(t *testing.T, getReporter func(testCase) (*UsageMetricsReporter, *metrics.InmemSink, error)) {
|
||||||
|
cases := baseCases
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
reporter, sink, err := getReporter(tcase)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reporter.runOnce()
|
||||||
|
|
||||||
|
intervals := sink.Data()
|
||||||
|
require.Len(t, intervals, 1)
|
||||||
|
intv := intervals[0]
|
||||||
|
|
||||||
|
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUsageReporter_emitPeeringUsage_CE(t *testing.T, getMetricsReporter func(testCase) (*UsageMetricsReporter, *metrics.InmemSink, error)) {
|
||||||
|
cases := make(map[string]testCase)
|
||||||
|
for k, v := range baseCases {
|
||||||
|
eg := make(map[string]metrics.GaugeValue)
|
||||||
|
for k, v := range v.expectedGauges {
|
||||||
|
eg[k] = v
|
||||||
|
}
|
||||||
|
cases[k] = testCase{v.modifyStateStore, v.getMembersFunc, eg}
|
||||||
|
}
|
||||||
|
peeringsCase := cases["nodes"]
|
||||||
|
peeringsCase.modifyStateStore = func(t *testing.T, s *state.Store) {
|
||||||
|
id, err := uuid.GenerateUUID()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, s.PeeringWrite(1, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{Name: "foo", ID: id}}))
|
||||||
|
id, err = uuid.GenerateUUID()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, s.PeeringWrite(2, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{Name: "bar", ID: id}}))
|
||||||
|
id, err = uuid.GenerateUUID()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NoError(t, s.PeeringWrite(3, &pbpeering.PeeringWriteRequest{Peering: &pbpeering.Peering{Name: "baz", ID: id}}))
|
||||||
|
}
|
||||||
|
peeringsCase.getMembersFunc = func() []serf.Member {
|
||||||
|
return []serf.Member{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Tags: map[string]string{"role": "consul"},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "bar",
|
||||||
|
Tags: map[string]string{"role": "consul"},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peeringsCase.expectedGauges["consul.usage.test.state.nodes;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.nodes",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
peeringsCase.expectedGauges["consul.usage.test.state.peerings;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.peerings",
|
||||||
|
Value: 3,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
peeringsCase.expectedGauges["consul.usage.test.members.clients;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.members.clients",
|
||||||
|
Value: 0,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
cases["peerings"] = peeringsCase
|
||||||
|
delete(cases, "nodes")
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
reporter, sink, err := getMetricsReporter(tcase)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reporter.runOnce()
|
||||||
|
|
||||||
|
intervals := sink.Data()
|
||||||
|
require.Len(t, intervals, 1)
|
||||||
|
intv := intervals[0]
|
||||||
|
|
||||||
|
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUsageReporter_emitServiceUsage_CE(t *testing.T, getMetricsReporter func(testCase) (*UsageMetricsReporter, *metrics.InmemSink, error)) {
|
||||||
|
cases := make(map[string]testCase)
|
||||||
|
for k, v := range baseCases {
|
||||||
|
eg := make(map[string]metrics.GaugeValue)
|
||||||
|
for k, v := range v.expectedGauges {
|
||||||
|
eg[k] = v
|
||||||
|
}
|
||||||
|
cases[k] = testCase{v.modifyStateStore, v.getMembersFunc, eg}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesAndSvcsCase := cases["nodes"]
|
||||||
|
nodesAndSvcsCase.modifyStateStore = func(t *testing.T, s *state.Store) {
|
||||||
|
require.NoError(t, s.EnsureNode(1, &structs.Node{Node: "foo", Address: "127.0.0.1"}))
|
||||||
|
require.NoError(t, s.EnsureNode(2, &structs.Node{Node: "bar", Address: "127.0.0.2"}))
|
||||||
|
require.NoError(t, s.EnsureNode(3, &structs.Node{Node: "baz", Address: "127.0.0.2"}))
|
||||||
|
require.NoError(t, s.EnsureNode(4, &structs.Node{Node: "qux", Address: "127.0.0.3"}))
|
||||||
|
|
||||||
|
apigw := structs.TestNodeServiceAPIGateway(t)
|
||||||
|
apigw.ID = "api-gateway"
|
||||||
|
|
||||||
|
mgw := structs.TestNodeServiceMeshGateway(t)
|
||||||
|
mgw.ID = "mesh-gateway"
|
||||||
|
|
||||||
|
tgw := structs.TestNodeServiceTerminatingGateway(t, "1.1.1.1")
|
||||||
|
tgw.ID = "terminating-gateway"
|
||||||
|
// Typical services and some consul services spread across two nodes
|
||||||
|
require.NoError(t, s.EnsureService(5, "foo", &structs.NodeService{ID: "db", Service: "db", Tags: nil, Address: "", Port: 5000}))
|
||||||
|
require.NoError(t, s.EnsureService(6, "bar", &structs.NodeService{ID: "api", Service: "api", Tags: nil, Address: "", Port: 5000}))
|
||||||
|
require.NoError(t, s.EnsureService(7, "foo", &structs.NodeService{ID: "consul", Service: "consul", Tags: nil}))
|
||||||
|
require.NoError(t, s.EnsureService(8, "bar", &structs.NodeService{ID: "consul", Service: "consul", Tags: nil}))
|
||||||
|
require.NoError(t, s.EnsureService(9, "foo", &structs.NodeService{ID: "db-connect-proxy", Service: "db-connect-proxy", Tags: nil, Address: "", Port: 5000, Kind: structs.ServiceKindConnectProxy}))
|
||||||
|
require.NoError(t, s.EnsureRegistration(10, structs.TestRegisterIngressGateway(t)))
|
||||||
|
require.NoError(t, s.EnsureService(11, "foo", mgw))
|
||||||
|
require.NoError(t, s.EnsureService(12, "foo", tgw))
|
||||||
|
require.NoError(t, s.EnsureService(13, "foo", apigw))
|
||||||
|
require.NoError(t, s.EnsureService(14, "bar", &structs.NodeService{ID: "db-native", Service: "db", Tags: nil, Address: "", Port: 5000, Connect: structs.ServiceConnect{Native: true}}))
|
||||||
|
require.NoError(t, s.EnsureConfigEntry(15, &structs.IngressGatewayConfigEntry{
|
||||||
|
Kind: structs.IngressGateway,
|
||||||
|
Name: "foo",
|
||||||
|
}))
|
||||||
|
require.NoError(t, s.EnsureConfigEntry(16, &structs.IngressGatewayConfigEntry{
|
||||||
|
Kind: structs.IngressGateway,
|
||||||
|
Name: "bar",
|
||||||
|
}))
|
||||||
|
require.NoError(t, s.EnsureConfigEntry(17, &structs.IngressGatewayConfigEntry{
|
||||||
|
Kind: structs.IngressGateway,
|
||||||
|
Name: "baz",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
baseCaseMembers := nodesAndSvcsCase.getMembersFunc()
|
||||||
|
nodesAndSvcsCase.getMembersFunc = func() []serf.Member {
|
||||||
|
baseCaseMembers = append(baseCaseMembers, serf.Member{
|
||||||
|
Name: "baz",
|
||||||
|
Tags: map[string]string{"role": "node", "segment": "a"},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
})
|
||||||
|
baseCaseMembers = append(baseCaseMembers, serf.Member{
|
||||||
|
Name: "qux",
|
||||||
|
Tags: map[string]string{"role": "node", "segment": "b"},
|
||||||
|
Status: serf.StatusAlive,
|
||||||
|
})
|
||||||
|
return baseCaseMembers
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.nodes;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.nodes",
|
||||||
|
Value: 4,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.members.clients;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.members.clients",
|
||||||
|
Value: 2,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.services;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.services",
|
||||||
|
Value: 8,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.service_instances;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.service_instances",
|
||||||
|
Value: 10,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-proxy"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "connect-proxy"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=terminating-gateway"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "terminating-gateway"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=ingress-gateway"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "ingress-gateway"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=api-gateway"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "api-gateway"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=mesh-gateway"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "mesh-gateway"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.connect_instances;datacenter=dc1;kind=connect-native"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.connect_instances",
|
||||||
|
Value: 1,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "connect-native"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.billable_service_instances;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.billable_service_instances",
|
||||||
|
Value: 3,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
nodesAndSvcsCase.expectedGauges["consul.usage.test.state.config_entries;datacenter=dc1;kind=ingress-gateway"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.config_entries",
|
||||||
|
Value: 3,
|
||||||
|
Labels: []metrics.Label{
|
||||||
|
{Name: "datacenter", Value: "dc1"},
|
||||||
|
{Name: "kind", Value: "ingress-gateway"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
cases["nodes-and-services"] = nodesAndSvcsCase
|
||||||
|
delete(cases, "nodes")
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
reporter, sink, err := getMetricsReporter(tcase)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reporter.runOnce()
|
||||||
|
|
||||||
|
intervals := sink.Data()
|
||||||
|
require.Len(t, intervals, 1)
|
||||||
|
intv := intervals[0]
|
||||||
|
|
||||||
|
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUsageReporter_emitKVUsage_CE(t *testing.T, getMetricsReporter func(testCase) (*UsageMetricsReporter, *metrics.InmemSink, error)) {
|
||||||
|
cases := make(map[string]testCase)
|
||||||
|
for k, v := range baseCases {
|
||||||
|
eg := make(map[string]metrics.GaugeValue)
|
||||||
|
for k, v := range v.expectedGauges {
|
||||||
|
eg[k] = v
|
||||||
|
}
|
||||||
|
cases[k] = testCase{v.modifyStateStore, v.getMembersFunc, eg}
|
||||||
|
}
|
||||||
|
|
||||||
|
nodesCase := cases["nodes"]
|
||||||
|
mss := nodesCase.modifyStateStore
|
||||||
|
nodesCase.modifyStateStore = func(t *testing.T, s *state.Store) {
|
||||||
|
mss(t, s)
|
||||||
|
require.NoError(t, s.KVSSet(4, &structs.DirEntry{Key: "a", Value: []byte{1}}))
|
||||||
|
require.NoError(t, s.KVSSet(5, &structs.DirEntry{Key: "b", Value: []byte{1}}))
|
||||||
|
require.NoError(t, s.KVSSet(6, &structs.DirEntry{Key: "c", Value: []byte{1}}))
|
||||||
|
require.NoError(t, s.KVSSet(7, &structs.DirEntry{Key: "d", Value: []byte{1}}))
|
||||||
|
require.NoError(t, s.KVSDelete(8, "d", &acl.EnterpriseMeta{}))
|
||||||
|
require.NoError(t, s.KVSDelete(9, "c", &acl.EnterpriseMeta{}))
|
||||||
|
require.NoError(t, s.KVSSet(10, &structs.DirEntry{Key: "e", Value: []byte{1}}))
|
||||||
|
require.NoError(t, s.KVSSet(11, &structs.DirEntry{Key: "f", Value: []byte{1}}))
|
||||||
|
}
|
||||||
|
nodesCase.expectedGauges["consul.usage.test.state.kv_entries;datacenter=dc1"] = metrics.GaugeValue{
|
||||||
|
Name: "consul.usage.test.state.kv_entries",
|
||||||
|
Value: 4,
|
||||||
|
Labels: []metrics.Label{{Name: "datacenter", Value: "dc1"}},
|
||||||
|
}
|
||||||
|
cases["nodes"] = nodesCase
|
||||||
|
|
||||||
|
for name, tcase := range cases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
reporter, sink, err := getMetricsReporter(tcase)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
reporter.runOnce()
|
||||||
|
|
||||||
|
intervals := sink.Data()
|
||||||
|
require.Len(t, intervals, 1)
|
||||||
|
intv := intervals[0]
|
||||||
|
|
||||||
|
assertEqualGaugeMaps(t, tcase.expectedGauges, intv.Gauges)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -116,11 +116,17 @@ func (f *V1DataFetcher) FetchNodes(ctx Context, req *QueryPayload) ([]*Result, e
|
||||||
// Nodes are not namespaced, so this is a name error
|
// Nodes are not namespaced, so this is a name error
|
||||||
return nil, ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := f.dynamicConfig.Load().(*V1DataFetcherDynamicConfig)
|
cfg := f.dynamicConfig.Load().(*V1DataFetcherDynamicConfig)
|
||||||
|
|
||||||
|
// If no datacenter is passed, default to our own
|
||||||
|
datacenter := cfg.Datacenter
|
||||||
|
if req.Tenancy.Datacenter != "" {
|
||||||
|
datacenter = req.Tenancy.Datacenter
|
||||||
|
}
|
||||||
|
|
||||||
// Make an RPC request
|
// Make an RPC request
|
||||||
args := &structs.NodeSpecificRequest{
|
args := &structs.NodeSpecificRequest{
|
||||||
Datacenter: req.Tenancy.Datacenter,
|
Datacenter: datacenter,
|
||||||
PeerName: req.Tenancy.Peer,
|
PeerName: req.Tenancy.Peer,
|
||||||
Node: req.Name,
|
Node: req.Name,
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
|
@ -299,9 +305,15 @@ func (f *V1DataFetcher) FetchWorkload(ctx Context, req *QueryPayload) (*Result,
|
||||||
func (f *V1DataFetcher) FetchPreparedQuery(ctx Context, req *QueryPayload) ([]*Result, error) {
|
func (f *V1DataFetcher) FetchPreparedQuery(ctx Context, req *QueryPayload) ([]*Result, error) {
|
||||||
cfg := f.dynamicConfig.Load().(*V1DataFetcherDynamicConfig)
|
cfg := f.dynamicConfig.Load().(*V1DataFetcherDynamicConfig)
|
||||||
|
|
||||||
|
// If no datacenter is passed, default to our own
|
||||||
|
datacenter := cfg.Datacenter
|
||||||
|
if req.Tenancy.Datacenter != "" {
|
||||||
|
datacenter = req.Tenancy.Datacenter
|
||||||
|
}
|
||||||
|
|
||||||
// Execute the prepared query.
|
// Execute the prepared query.
|
||||||
args := structs.PreparedQueryExecuteRequest{
|
args := structs.PreparedQueryExecuteRequest{
|
||||||
Datacenter: req.Tenancy.Datacenter,
|
Datacenter: datacenter,
|
||||||
QueryIDOrName: req.Name,
|
QueryIDOrName: req.Name,
|
||||||
QueryOptions: structs.QueryOptions{
|
QueryOptions: structs.QueryOptions{
|
||||||
Token: ctx.Token,
|
Token: ctx.Token,
|
||||||
|
@ -548,7 +560,11 @@ func (f *V1DataFetcher) fetchServiceBasedOnTenancy(ctx Context, req *QueryPayloa
|
||||||
return nil, errors.New("sameness groups are not allowed for service lookups based on tenancy")
|
return nil, errors.New("sameness groups are not allowed for service lookups based on tenancy")
|
||||||
}
|
}
|
||||||
|
|
||||||
datacenter := req.Tenancy.Datacenter
|
// If no datacenter is passed, default to our own
|
||||||
|
datacenter := cfg.Datacenter
|
||||||
|
if req.Tenancy.Datacenter != "" {
|
||||||
|
datacenter = req.Tenancy.Datacenter
|
||||||
|
}
|
||||||
if req.Tenancy.Peer != "" {
|
if req.Tenancy.Peer != "" {
|
||||||
datacenter = ""
|
datacenter = ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -128,6 +128,7 @@ func Test_FetchVirtualIP(t *testing.T) {
|
||||||
func Test_FetchEndpoints(t *testing.T) {
|
func Test_FetchEndpoints(t *testing.T) {
|
||||||
// set these to confirm that RPC call does not use them for this particular RPC
|
// set these to confirm that RPC call does not use them for this particular RPC
|
||||||
rc := &config.RuntimeConfig{
|
rc := &config.RuntimeConfig{
|
||||||
|
Datacenter: "dc2",
|
||||||
DNSAllowStale: true,
|
DNSAllowStale: true,
|
||||||
DNSMaxStale: 100,
|
DNSMaxStale: 100,
|
||||||
DNSUseCache: true,
|
DNSUseCache: true,
|
||||||
|
|
|
@ -347,6 +347,7 @@ func queryTenancyToResourceTenancy(qTenancy QueryTenancy) *pbresource.Tenancy {
|
||||||
rTenancy.Namespace = qTenancy.Namespace
|
rTenancy.Namespace = qTenancy.Namespace
|
||||||
}
|
}
|
||||||
// In the case of partition, we have the agent's partition as the fallback.
|
// In the case of partition, we have the agent's partition as the fallback.
|
||||||
|
// That is handled in NormalizeRequest.
|
||||||
if qTenancy.Partition != "" {
|
if qTenancy.Partition != "" {
|
||||||
rTenancy.Partition = qTenancy.Partition
|
rTenancy.Partition = qTenancy.Partition
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context is used augment a DNS message with Consul-specific metadata.
|
||||||
|
type Context struct {
|
||||||
|
Token string `mapstructure:"x-consul-token,omitempty"`
|
||||||
|
DefaultNamespace string `mapstructure:"x-consul-namespace,omitempty"`
|
||||||
|
DefaultPartition string `mapstructure:"x-consul-partition,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContextFromGRPCContext returns the request context using the gRPC metadata attached to the
|
||||||
|
// given context. If there is no gRPC metadata, it returns an empty context.
|
||||||
|
func NewContextFromGRPCContext(ctx context.Context) (Context, error) {
|
||||||
|
if ctx == nil {
|
||||||
|
return Context{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
reqCtx := Context{}
|
||||||
|
md, ok := metadata.FromIncomingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return reqCtx, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]string{}
|
||||||
|
for k, v := range md {
|
||||||
|
m[k] = v[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
decoderConfig := &mapstructure.DecoderConfig{
|
||||||
|
Metadata: nil,
|
||||||
|
Result: &reqCtx,
|
||||||
|
WeaklyTypedInput: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder, err := mapstructure.NewDecoder(decoderConfig)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, fmt.Errorf("error creating mapstructure decoder: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = decoder.Decode(m)
|
||||||
|
if err != nil {
|
||||||
|
return Context{}, fmt.Errorf("error decoding metadata: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reqCtx, nil
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package dns
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewContextFromGRPCContext(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
md := metadata.MD{}
|
||||||
|
testMeta := map[string]string{
|
||||||
|
"x-consul-token": "test-token",
|
||||||
|
"x-consul-namespace": "test-namespace",
|
||||||
|
"x-consul-partition": "test-partition",
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range testMeta {
|
||||||
|
md.Set(k, v)
|
||||||
|
}
|
||||||
|
testGRPCContext := metadata.NewIncomingContext(context.Background(), md)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
grpcCtx context.Context
|
||||||
|
expected *Context
|
||||||
|
error error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil grpc context",
|
||||||
|
grpcCtx: nil,
|
||||||
|
expected: &Context{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grpc context w/o metadata",
|
||||||
|
grpcCtx: context.Background(),
|
||||||
|
expected: &Context{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grpc context w/ kitchen sink",
|
||||||
|
grpcCtx: testGRPCContext,
|
||||||
|
expected: &Context{
|
||||||
|
Token: "test-token",
|
||||||
|
DefaultNamespace: "test-namespace",
|
||||||
|
DefaultPartition: "test-partition",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ctx, err := NewContextFromGRPCContext(tc.grpcCtx)
|
||||||
|
if tc.error != nil {
|
||||||
|
require.Error(t, err)
|
||||||
|
require.Equal(t, Context{}, &ctx)
|
||||||
|
require.Equal(t, tc.error, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, ctx)
|
||||||
|
require.Equal(t, tc.expected, &ctx)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -206,20 +206,24 @@ func getQueryTenancy(reqCtx Context, queryType discovery.QueryType, querySuffixe
|
||||||
return discovery.QueryTenancy{}, errNameNotFound
|
return discovery.QueryTenancy{}, errNameNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we don't have an explicit partition in the request, try the first fallback
|
// If we don't have an explicit partition/ns in the request, try the first fallback
|
||||||
// which was supplied in the request context. The agent's partition will be used as the last fallback
|
// which was supplied in the request context. The agent's partition will be used as the last fallback
|
||||||
// later in the query processor.
|
// later in the query processor.
|
||||||
if labels.Partition == "" {
|
if labels.Partition == "" {
|
||||||
labels.Partition = reqCtx.DefaultPartition
|
labels.Partition = reqCtx.DefaultPartition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if labels.Namespace == "" {
|
||||||
|
labels.Namespace = reqCtx.DefaultNamespace
|
||||||
|
}
|
||||||
|
|
||||||
// If we have a sameness group, we can return early without further data massage.
|
// If we have a sameness group, we can return early without further data massage.
|
||||||
if labels.SamenessGroup != "" {
|
if labels.SamenessGroup != "" {
|
||||||
return discovery.QueryTenancy{
|
return discovery.QueryTenancy{
|
||||||
Namespace: labels.Namespace,
|
Namespace: labels.Namespace,
|
||||||
Partition: labels.Partition,
|
Partition: labels.Partition,
|
||||||
SamenessGroup: labels.SamenessGroup,
|
SamenessGroup: labels.SamenessGroup,
|
||||||
Datacenter: reqCtx.DefaultDatacenter,
|
// Datacenter is not supported
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,19 +238,19 @@ func getQueryTenancy(reqCtx Context, queryType discovery.QueryType, querySuffixe
|
||||||
Namespace: labels.Namespace,
|
Namespace: labels.Namespace,
|
||||||
Partition: labels.Partition,
|
Partition: labels.Partition,
|
||||||
Peer: labels.Peer,
|
Peer: labels.Peer,
|
||||||
Datacenter: getEffectiveDatacenter(labels, reqCtx.DefaultDatacenter),
|
Datacenter: getEffectiveDatacenter(labels),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getEffectiveDatacenter returns the effective datacenter from the parsed labels.
|
// getEffectiveDatacenter returns the effective datacenter from the parsed labels.
|
||||||
func getEffectiveDatacenter(labels *parsedLabels, defaultDC string) string {
|
func getEffectiveDatacenter(labels *parsedLabels) string {
|
||||||
switch {
|
switch {
|
||||||
case labels.Datacenter != "":
|
case labels.Datacenter != "":
|
||||||
return labels.Datacenter
|
return labels.Datacenter
|
||||||
case labels.PeerOrDatacenter != "" && labels.Peer != labels.PeerOrDatacenter:
|
case labels.PeerOrDatacenter != "" && labels.Peer != labels.PeerOrDatacenter:
|
||||||
return labels.PeerOrDatacenter
|
return labels.PeerOrDatacenter
|
||||||
}
|
}
|
||||||
return defaultDC
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// getQueryTypePartsAndSuffixesFromDNSMessage returns the query type, the parts, and suffixes of the query name.
|
// getQueryTypePartsAndSuffixesFromDNSMessage returns the query type, the parts, and suffixes of the query name.
|
||||||
|
|
|
@ -160,8 +160,7 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
requestContext: &Context{
|
requestContext: &Context{
|
||||||
DefaultDatacenter: "default-dc",
|
DefaultPartition: "default-partition",
|
||||||
DefaultPartition: "default-partition",
|
|
||||||
},
|
},
|
||||||
expectedQuery: &discovery.Query{
|
expectedQuery: &discovery.Query{
|
||||||
QueryType: discovery.QueryTypeWorkload,
|
QueryType: discovery.QueryTypeWorkload,
|
||||||
|
@ -169,10 +168,9 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
PortName: "api",
|
PortName: "api",
|
||||||
Tenancy: discovery.QueryTenancy{
|
Tenancy: discovery.QueryTenancy{
|
||||||
Namespace: "banana",
|
Namespace: "banana",
|
||||||
Partition: "orange",
|
Partition: "orange",
|
||||||
Peer: "apple",
|
Peer: "apple",
|
||||||
Datacenter: "default-dc",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -192,8 +190,7 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
requestContext: &Context{
|
requestContext: &Context{
|
||||||
DefaultDatacenter: "default-dc",
|
DefaultPartition: "default-partition",
|
||||||
DefaultPartition: "default-partition",
|
|
||||||
},
|
},
|
||||||
expectedQuery: &discovery.Query{
|
expectedQuery: &discovery.Query{
|
||||||
QueryType: discovery.QueryTypeService,
|
QueryType: discovery.QueryTypeService,
|
||||||
|
@ -203,7 +200,6 @@ func Test_buildQueryFromDNSMessage(t *testing.T) {
|
||||||
Namespace: "banana",
|
Namespace: "banana",
|
||||||
Partition: "orange",
|
Partition: "orange",
|
||||||
SamenessGroup: "apple",
|
SamenessGroup: "apple",
|
||||||
Datacenter: "default-dc",
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,9 +14,8 @@ import (
|
||||||
|
|
||||||
"github.com/armon/go-metrics"
|
"github.com/armon/go-metrics"
|
||||||
"github.com/armon/go-radix"
|
"github.com/armon/go-radix"
|
||||||
"github.com/miekg/dns"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/config"
|
"github.com/hashicorp/consul/agent/config"
|
||||||
"github.com/hashicorp/consul/agent/discovery"
|
"github.com/hashicorp/consul/agent/discovery"
|
||||||
|
@ -46,13 +45,6 @@ var (
|
||||||
trailingSpacesRE = regexp.MustCompile(" +$")
|
trailingSpacesRE = regexp.MustCompile(" +$")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Context is used augment a DNS message with Consul-specific metadata.
|
|
||||||
type Context struct {
|
|
||||||
Token string
|
|
||||||
DefaultPartition string
|
|
||||||
DefaultDatacenter string
|
|
||||||
}
|
|
||||||
|
|
||||||
// RouterDynamicConfig is the dynamic configuration that can be hot-reloaded
|
// RouterDynamicConfig is the dynamic configuration that can be hot-reloaded
|
||||||
type RouterDynamicConfig struct {
|
type RouterDynamicConfig struct {
|
||||||
ARecordLimit int
|
ARecordLimit int
|
||||||
|
@ -114,13 +106,12 @@ type dnsRecursor interface {
|
||||||
// Router replaces miekg/dns.ServeMux with a simpler router that only checks for the 2-3 valid domains
|
// Router replaces miekg/dns.ServeMux with a simpler router that only checks for the 2-3 valid domains
|
||||||
// that Consul supports and forwards to a single DiscoveryQueryProcessor handler. If there is no match, it will recurse.
|
// that Consul supports and forwards to a single DiscoveryQueryProcessor handler. If there is no match, it will recurse.
|
||||||
type Router struct {
|
type Router struct {
|
||||||
processor DiscoveryQueryProcessor
|
processor DiscoveryQueryProcessor
|
||||||
recursor dnsRecursor
|
recursor dnsRecursor
|
||||||
domain string
|
domain string
|
||||||
altDomain string
|
altDomain string
|
||||||
datacenter string
|
nodeName string
|
||||||
nodeName string
|
logger hclog.Logger
|
||||||
logger hclog.Logger
|
|
||||||
|
|
||||||
tokenFunc func() string
|
tokenFunc func() string
|
||||||
translateAddressFunc func(dc string, addr string, taggedAddresses map[string]string, accept dnsutil.TranslateAddressAccept) string
|
translateAddressFunc func(dc string, addr string, taggedAddresses map[string]string, accept dnsutil.TranslateAddressAccept) string
|
||||||
|
@ -146,7 +137,6 @@ func NewRouter(cfg Config) (*Router, error) {
|
||||||
recursor: newRecursor(logger),
|
recursor: newRecursor(logger),
|
||||||
domain: domain,
|
domain: domain,
|
||||||
altDomain: altDomain,
|
altDomain: altDomain,
|
||||||
datacenter: cfg.AgentConfig.Datacenter,
|
|
||||||
logger: logger,
|
logger: logger,
|
||||||
nodeName: cfg.AgentConfig.NodeName,
|
nodeName: cfg.AgentConfig.NodeName,
|
||||||
tokenFunc: cfg.TokenFunc,
|
tokenFunc: cfg.TokenFunc,
|
||||||
|
@ -176,6 +166,7 @@ func (r *Router) HandleRequest(req *dns.Msg, reqCtx Context, remoteAddress net.A
|
||||||
}
|
}
|
||||||
|
|
||||||
r.logger.Trace("received request", "question", req.Question[0].Name, "type", dns.Type(req.Question[0].Qtype).String())
|
r.logger.Trace("received request", "question", req.Question[0].Name, "type", dns.Type(req.Question[0].Qtype).String())
|
||||||
|
r.normalizeContext(&reqCtx)
|
||||||
|
|
||||||
defer func(s time.Time, q dns.Question) {
|
defer func(s time.Time, q dns.Question) {
|
||||||
metrics.MeasureSinceWithLabels([]string{"dns", "query"}, s,
|
metrics.MeasureSinceWithLabels([]string{"dns", "query"}, s,
|
||||||
|
@ -319,10 +310,9 @@ func (r *Router) trimDomain(questionName string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeDNS implements the miekg/dns.Handler interface.
|
// ServeDNS implements the miekg/dns.Handler interface.
|
||||||
// This is a standard DNS listener, so we inject a default request context based on the agent's config.
|
// This is a standard DNS listener.
|
||||||
func (r *Router) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
|
func (r *Router) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
|
||||||
reqCtx := r.defaultAgentDNSRequestContext()
|
out := r.HandleRequest(req, Context{}, w.RemoteAddr())
|
||||||
out := r.HandleRequest(req, reqCtx, w.RemoteAddr())
|
|
||||||
w.WriteMsg(out)
|
w.WriteMsg(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,16 +410,6 @@ func (r *Router) GetConfig() *RouterDynamicConfig {
|
||||||
return r.dynamicConfig.Load().(*RouterDynamicConfig)
|
return r.dynamicConfig.Load().(*RouterDynamicConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// defaultAgentDNSRequestContext returns a default request context based on the agent's config.
|
|
||||||
func (r *Router) defaultAgentDNSRequestContext() Context {
|
|
||||||
return Context{
|
|
||||||
Token: r.tokenFunc(),
|
|
||||||
DefaultDatacenter: r.datacenter,
|
|
||||||
// We don't need to specify the agent's partition here because that will be handled further down the stack
|
|
||||||
// in the query processor.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// getErrorFromECSNotGlobalError returns the underlying error from an ECSNotGlobalError, if it exists.
|
// getErrorFromECSNotGlobalError returns the underlying error from an ECSNotGlobalError, if it exists.
|
||||||
func getErrorFromECSNotGlobalError(err error) error {
|
func getErrorFromECSNotGlobalError(err error) error {
|
||||||
if errors.Is(err, discovery.ErrECSNotGlobal) {
|
if errors.Is(err, discovery.ErrECSNotGlobal) {
|
||||||
|
@ -471,6 +451,16 @@ func validateAndNormalizeRequest(req *dns.Msg) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// normalizeContext makes sure context information is populated with agent defaults as needed.
|
||||||
|
// Right now this is just the ACL token. We do this in the router with the token because DNS doesn't
|
||||||
|
// allow a token to be passed in the request, and we expect ACL tokens upfront in APIs when they are enabled.
|
||||||
|
// Tenancy information is left out because it is safe/expected to assume agent defaults in the backend lookup.
|
||||||
|
func (r *Router) normalizeContext(ctx *Context) {
|
||||||
|
if ctx.Token == "" {
|
||||||
|
ctx.Token = r.tokenFunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// stripAnyFailoverSuffix strips off the suffixes that may have been added to the request name.
|
// stripAnyFailoverSuffix strips off the suffixes that may have been added to the request name.
|
||||||
func stripAnyFailoverSuffix(target string) (string, bool) {
|
func stripAnyFailoverSuffix(target string) (string, bool) {
|
||||||
enableFailover := false
|
enableFailover := false
|
||||||
|
|
|
@ -52,22 +52,6 @@ type HandleTestCase struct {
|
||||||
response *dns.Msg
|
response *dns.Msg
|
||||||
}
|
}
|
||||||
|
|
||||||
var testSOA = &dns.SOA{
|
|
||||||
Hdr: dns.RR_Header{
|
|
||||||
Name: "consul.",
|
|
||||||
Rrtype: dns.TypeSOA,
|
|
||||||
Class: dns.ClassINET,
|
|
||||||
Ttl: 4,
|
|
||||||
},
|
|
||||||
Ns: "ns.consul.",
|
|
||||||
Mbox: "hostmaster.consul.",
|
|
||||||
Serial: uint32(time.Now().Unix()),
|
|
||||||
Refresh: 1,
|
|
||||||
Retry: 2,
|
|
||||||
Expire: 3,
|
|
||||||
Minttl: 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_HandleRequest_Validation(t *testing.T) {
|
func Test_HandleRequest_Validation(t *testing.T) {
|
||||||
testCases := []HandleTestCase{
|
testCases := []HandleTestCase{
|
||||||
{
|
{
|
||||||
|
@ -93,6 +77,157 @@ func Test_HandleRequest_Validation(t *testing.T) {
|
||||||
Extra: nil,
|
Extra: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// Context Tests
|
||||||
|
{
|
||||||
|
name: "When a request context is provided, use those field in the query",
|
||||||
|
request: &dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Opcode: dns.OpcodeQuery,
|
||||||
|
},
|
||||||
|
Question: []dns.Question{
|
||||||
|
{
|
||||||
|
Name: "foo.service.consul.",
|
||||||
|
Qtype: dns.TypeA,
|
||||||
|
Qclass: dns.ClassINET,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestContext: &Context{
|
||||||
|
Token: "test-token",
|
||||||
|
DefaultNamespace: "test-namespace",
|
||||||
|
DefaultPartition: "test-partition",
|
||||||
|
},
|
||||||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||||||
|
result := []*discovery.Result{
|
||||||
|
{
|
||||||
|
Type: discovery.ResultTypeNode,
|
||||||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||||||
|
Tenancy: discovery.ResultTenancy{
|
||||||
|
Namespace: "test-namespace",
|
||||||
|
Partition: "test-partition",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||||||
|
On("FetchEndpoints", mock.Anything, mock.Anything, mock.Anything).
|
||||||
|
Return(result, nil).
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
ctx := args.Get(0).(discovery.Context)
|
||||||
|
req := args.Get(1).(*discovery.QueryPayload)
|
||||||
|
reqType := args.Get(2).(discovery.LookupType)
|
||||||
|
|
||||||
|
require.Equal(t, "test-token", ctx.Token)
|
||||||
|
|
||||||
|
require.Equal(t, "foo", req.Name)
|
||||||
|
require.Equal(t, "test-namespace", req.Tenancy.Namespace)
|
||||||
|
require.Equal(t, "test-partition", req.Tenancy.Partition)
|
||||||
|
|
||||||
|
require.Equal(t, discovery.LookupTypeService, reqType)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
validateAndNormalizeExpected: true,
|
||||||
|
response: &dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Response: true,
|
||||||
|
Authoritative: true,
|
||||||
|
},
|
||||||
|
Compress: true,
|
||||||
|
Question: []dns.Question{
|
||||||
|
{
|
||||||
|
Name: "foo.service.consul.",
|
||||||
|
Qtype: dns.TypeA,
|
||||||
|
Qclass: dns.ClassINET,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Answer: []dns.RR{
|
||||||
|
&dns.A{
|
||||||
|
Hdr: dns.RR_Header{
|
||||||
|
Name: "foo.service.consul.",
|
||||||
|
Rrtype: dns.TypeA,
|
||||||
|
Class: dns.ClassINET,
|
||||||
|
Ttl: 123,
|
||||||
|
},
|
||||||
|
A: net.ParseIP("1.2.3.4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "When a request context is provided, values do not override explicit tenancy",
|
||||||
|
request: &dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Opcode: dns.OpcodeQuery,
|
||||||
|
},
|
||||||
|
Question: []dns.Question{
|
||||||
|
{
|
||||||
|
Name: "foo.service.bar.ns.baz.ap.consul.",
|
||||||
|
Qtype: dns.TypeA,
|
||||||
|
Qclass: dns.ClassINET,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
requestContext: &Context{
|
||||||
|
Token: "test-token",
|
||||||
|
DefaultNamespace: "test-namespace",
|
||||||
|
DefaultPartition: "test-partition",
|
||||||
|
},
|
||||||
|
configureDataFetcher: func(fetcher discovery.CatalogDataFetcher) {
|
||||||
|
result := []*discovery.Result{
|
||||||
|
{
|
||||||
|
Type: discovery.ResultTypeNode,
|
||||||
|
Node: &discovery.Location{Name: "foo", Address: "1.2.3.4"},
|
||||||
|
Tenancy: discovery.ResultTenancy{
|
||||||
|
Namespace: "bar",
|
||||||
|
Partition: "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher.(*discovery.MockCatalogDataFetcher).
|
||||||
|
On("FetchEndpoints", mock.Anything, mock.Anything, mock.Anything).
|
||||||
|
Return(result, nil).
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
ctx := args.Get(0).(discovery.Context)
|
||||||
|
req := args.Get(1).(*discovery.QueryPayload)
|
||||||
|
reqType := args.Get(2).(discovery.LookupType)
|
||||||
|
|
||||||
|
require.Equal(t, "test-token", ctx.Token)
|
||||||
|
|
||||||
|
require.Equal(t, "foo", req.Name)
|
||||||
|
require.Equal(t, "bar", req.Tenancy.Namespace)
|
||||||
|
require.Equal(t, "baz", req.Tenancy.Partition)
|
||||||
|
|
||||||
|
require.Equal(t, discovery.LookupTypeService, reqType)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
validateAndNormalizeExpected: true,
|
||||||
|
response: &dns.Msg{
|
||||||
|
MsgHdr: dns.MsgHdr{
|
||||||
|
Response: true,
|
||||||
|
Authoritative: true,
|
||||||
|
},
|
||||||
|
Compress: true,
|
||||||
|
Question: []dns.Question{
|
||||||
|
{
|
||||||
|
Name: "foo.service.bar.ns.baz.ap.consul.",
|
||||||
|
Qtype: dns.TypeA,
|
||||||
|
Qclass: dns.ClassINET,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Answer: []dns.RR{
|
||||||
|
&dns.A{
|
||||||
|
Hdr: dns.RR_Header{
|
||||||
|
Name: "foo.service.bar.ns.baz.ap.consul.",
|
||||||
|
Rrtype: dns.TypeA,
|
||||||
|
Class: dns.ClassINET,
|
||||||
|
Ttl: 123,
|
||||||
|
},
|
||||||
|
A: net.ParseIP("1.2.3.4"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
|
@ -72,9 +72,10 @@ func (s *ServerV2) Query(ctx context.Context, req *pbdns.QueryRequest) (*pbdns.Q
|
||||||
return nil, status.Error(codes.Internal, fmt.Sprintf("failure decoding dns request: %s", err.Error()))
|
return nil, status.Error(codes.Internal, fmt.Sprintf("failure decoding dns request: %s", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO (v2-dns): parse token and other context metadata from the grpc request/metadata (NET-7885)
|
reqCtx, err := agentdns.NewContextFromGRPCContext(ctx)
|
||||||
reqCtx := agentdns.Context{
|
if err != nil {
|
||||||
Token: s.TokenFunc(),
|
s.Logger.Error("error parsing DNS context from grpc metadata", "err", err)
|
||||||
|
return nil, status.Error(codes.Internal, fmt.Sprintf("error parsing DNS context from grpc metadata: %s", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
resp := s.DNSRouter.HandleRequest(msg, reqCtx, remote)
|
resp := s.DNSRouter.HandleRequest(msg, reqCtx, remote)
|
||||||
|
|
|
@ -10,6 +10,8 @@ import (
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
agentdns "github.com/hashicorp/consul/agent/dns"
|
agentdns "github.com/hashicorp/consul/agent/dns"
|
||||||
"github.com/hashicorp/consul/proto-public/pbdns"
|
"github.com/hashicorp/consul/proto-public/pbdns"
|
||||||
|
@ -50,6 +52,7 @@ func (s *DNSTestSuite) TestProxy_V2Success() {
|
||||||
question string
|
question string
|
||||||
configureRouter func(router *agentdns.MockDNSRouter)
|
configureRouter func(router *agentdns.MockDNSRouter)
|
||||||
clientQuery func(qR *pbdns.QueryRequest)
|
clientQuery func(qR *pbdns.QueryRequest)
|
||||||
|
metadata map[string]string
|
||||||
expectedErr error
|
expectedErr error
|
||||||
}{
|
}{
|
||||||
|
|
||||||
|
@ -73,6 +76,28 @@ func (s *DNSTestSuite) TestProxy_V2Success() {
|
||||||
qR.Protocol = pbdns.Protocol_PROTOCOL_TCP
|
qR.Protocol = pbdns.Protocol_PROTOCOL_TCP
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"happy path with context variables set": {
|
||||||
|
question: "abc.com.",
|
||||||
|
configureRouter: func(router *agentdns.MockDNSRouter) {
|
||||||
|
router.On("HandleRequest", mock.Anything, mock.Anything, mock.Anything).
|
||||||
|
Run(func(args mock.Arguments) {
|
||||||
|
ctx, ok := args.Get(1).(agentdns.Context)
|
||||||
|
require.True(s.T(), ok, "error casting to agentdns.Context")
|
||||||
|
require.Equal(s.T(), "test-token", ctx.Token, "token not set in context")
|
||||||
|
require.Equal(s.T(), "test-namespace", ctx.DefaultNamespace, "namespace not set in context")
|
||||||
|
require.Equal(s.T(), "test-partition", ctx.DefaultPartition, "partition not set in context")
|
||||||
|
}).
|
||||||
|
Return(basicResponse(), nil)
|
||||||
|
},
|
||||||
|
clientQuery: func(qR *pbdns.QueryRequest) {
|
||||||
|
qR.Protocol = pbdns.Protocol_PROTOCOL_UDP
|
||||||
|
},
|
||||||
|
metadata: map[string]string{
|
||||||
|
"x-consul-token": "test-token",
|
||||||
|
"x-consul-namespace": "test-namespace",
|
||||||
|
"x-consul-partition": "test-partition",
|
||||||
|
},
|
||||||
|
},
|
||||||
"No protocol set": {
|
"No protocol set": {
|
||||||
question: "abc.com.",
|
question: "abc.com.",
|
||||||
clientQuery: func(qR *pbdns.QueryRequest) {},
|
clientQuery: func(qR *pbdns.QueryRequest) {},
|
||||||
|
@ -108,9 +133,18 @@ func (s *DNSTestSuite) TestProxy_V2Success() {
|
||||||
|
|
||||||
bytes, _ := req.Pack()
|
bytes, _ := req.Pack()
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if len(tc.metadata) > 0 {
|
||||||
|
md := metadata.MD{}
|
||||||
|
for k, v := range tc.metadata {
|
||||||
|
md.Set(k, v)
|
||||||
|
}
|
||||||
|
ctx = metadata.NewOutgoingContext(ctx, md)
|
||||||
|
}
|
||||||
|
|
||||||
clientReq := &pbdns.QueryRequest{Msg: bytes}
|
clientReq := &pbdns.QueryRequest{Msg: bytes}
|
||||||
tc.clientQuery(clientReq)
|
tc.clientQuery(clientReq)
|
||||||
clientResp, err := client.Query(context.Background(), clientReq)
|
clientResp, err := client.Query(ctx, clientReq)
|
||||||
if tc.expectedErr != nil {
|
if tc.expectedErr != nil {
|
||||||
s.Require().Error(err, "no errror calling gRPC endpoint")
|
s.Require().Error(err, "no errror calling gRPC endpoint")
|
||||||
s.Require().ErrorContains(err, tc.expectedErr.Error())
|
s.Require().ErrorContains(err, tc.expectedErr.Error())
|
||||||
|
|
|
@ -69,6 +69,8 @@ func newConfigEntryRequest(req *structs.ConfigEntryQuery, deps ServerDataSourceD
|
||||||
topic = pbsubscribe.Topic_HTTPRoute
|
topic = pbsubscribe.Topic_HTTPRoute
|
||||||
case structs.TCPRoute:
|
case structs.TCPRoute:
|
||||||
topic = pbsubscribe.Topic_TCPRoute
|
topic = pbsubscribe.Topic_TCPRoute
|
||||||
|
case structs.FileSystemCertificate:
|
||||||
|
topic = pbsubscribe.Topic_FileSystemCertificate
|
||||||
case structs.InlineCertificate:
|
case structs.InlineCertificate:
|
||||||
topic = pbsubscribe.Topic_InlineCertificate
|
topic = pbsubscribe.Topic_InlineCertificate
|
||||||
case structs.BoundAPIGateway:
|
case structs.BoundAPIGateway:
|
||||||
|
|
|
@ -63,7 +63,8 @@ func (h *handlerAPIGateway) initialize(ctx context.Context) (ConfigSnapshot, err
|
||||||
snap.APIGateway.BoundListeners = make(map[string]structs.BoundAPIGatewayListener)
|
snap.APIGateway.BoundListeners = make(map[string]structs.BoundAPIGatewayListener)
|
||||||
snap.APIGateway.HTTPRoutes = watch.NewMap[structs.ResourceReference, *structs.HTTPRouteConfigEntry]()
|
snap.APIGateway.HTTPRoutes = watch.NewMap[structs.ResourceReference, *structs.HTTPRouteConfigEntry]()
|
||||||
snap.APIGateway.TCPRoutes = watch.NewMap[structs.ResourceReference, *structs.TCPRouteConfigEntry]()
|
snap.APIGateway.TCPRoutes = watch.NewMap[structs.ResourceReference, *structs.TCPRouteConfigEntry]()
|
||||||
snap.APIGateway.Certificates = watch.NewMap[structs.ResourceReference, *structs.InlineCertificateConfigEntry]()
|
snap.APIGateway.InlineCertificates = watch.NewMap[structs.ResourceReference, *structs.InlineCertificateConfigEntry]()
|
||||||
|
snap.APIGateway.FileSystemCertificates = watch.NewMap[structs.ResourceReference, *structs.FileSystemCertificateConfigEntry]()
|
||||||
|
|
||||||
snap.APIGateway.Upstreams = make(listenerRouteUpstreams)
|
snap.APIGateway.Upstreams = make(listenerRouteUpstreams)
|
||||||
snap.APIGateway.UpstreamsSet = make(routeUpstreamSet)
|
snap.APIGateway.UpstreamsSet = make(routeUpstreamSet)
|
||||||
|
@ -96,7 +97,8 @@ func (h *handlerAPIGateway) subscribeToConfigEntry(ctx context.Context, kind, na
|
||||||
// handleUpdate responds to changes in the api-gateway. In general, we want
|
// handleUpdate responds to changes in the api-gateway. In general, we want
|
||||||
// to crawl the various resources related to or attached to the gateway and
|
// to crawl the various resources related to or attached to the gateway and
|
||||||
// collect the list of things need to generate xDS. This list of resources
|
// collect the list of things need to generate xDS. This list of resources
|
||||||
// includes the bound-api-gateway, http-routes, tcp-routes, and inline-certificates.
|
// includes the bound-api-gateway, http-routes, tcp-routes,
|
||||||
|
// file-system-certificates and inline-certificates.
|
||||||
func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
|
func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
|
||||||
if u.Err != nil {
|
if u.Err != nil {
|
||||||
return fmt.Errorf("error filling agent cache: %v", u.Err)
|
return fmt.Errorf("error filling agent cache: %v", u.Err)
|
||||||
|
@ -113,6 +115,11 @@ func (h *handlerAPIGateway) handleUpdate(ctx context.Context, u UpdateEvent, sna
|
||||||
if err := h.handleGatewayConfigUpdate(ctx, u, snap, u.CorrelationID); err != nil {
|
if err := h.handleGatewayConfigUpdate(ctx, u, snap, u.CorrelationID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case fileSystemCertificateConfigWatchID:
|
||||||
|
// Handle change in an attached file-system-certificate config entry
|
||||||
|
if err := h.handleFileSystemCertConfigUpdate(ctx, u, snap); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
case inlineCertificateConfigWatchID:
|
case inlineCertificateConfigWatchID:
|
||||||
// Handle change in an attached inline-certificate config entry
|
// Handle change in an attached inline-certificate config entry
|
||||||
if err := h.handleInlineCertConfigUpdate(ctx, u, snap); err != nil {
|
if err := h.handleInlineCertConfigUpdate(ctx, u, snap); err != nil {
|
||||||
|
@ -205,12 +212,21 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd
|
||||||
for _, ref := range listener.Certificates {
|
for _, ref := range listener.Certificates {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
seenRefs[ref] = struct{}{}
|
seenRefs[ref] = struct{}{}
|
||||||
snap.APIGateway.Certificates.InitWatch(ref, cancel)
|
|
||||||
|
|
||||||
err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, inlineCertificateConfigWatchID)
|
if ref.Kind == structs.FileSystemCertificate {
|
||||||
if err != nil {
|
snap.APIGateway.FileSystemCertificates.InitWatch(ref, cancel)
|
||||||
// TODO May want to continue
|
|
||||||
return err
|
err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, fileSystemCertificateConfigWatchID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
snap.APIGateway.InlineCertificates.InitWatch(ref, cancel)
|
||||||
|
|
||||||
|
err := h.subscribeToConfigEntry(ctx, ref.Kind, ref.Name, ref.EnterpriseMeta, inlineCertificateConfigWatchID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -234,9 +250,16 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
snap.APIGateway.Certificates.ForEachKey(func(ref structs.ResourceReference) bool {
|
snap.APIGateway.InlineCertificates.ForEachKey(func(ref structs.ResourceReference) bool {
|
||||||
if _, ok := seenRefs[ref]; !ok {
|
if _, ok := seenRefs[ref]; !ok {
|
||||||
snap.APIGateway.Certificates.CancelWatch(ref)
|
snap.APIGateway.InlineCertificates.CancelWatch(ref)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
snap.APIGateway.FileSystemCertificates.ForEachKey(func(ref structs.ResourceReference) bool {
|
||||||
|
if _, ok := seenRefs[ref]; !ok {
|
||||||
|
snap.APIGateway.FileSystemCertificates.CancelWatch(ref)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
@ -265,6 +288,30 @@ func (h *handlerAPIGateway) handleGatewayConfigUpdate(ctx context.Context, u Upd
|
||||||
return h.watchIngressLeafCert(ctx, snap)
|
return h.watchIngressLeafCert(ctx, snap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *handlerAPIGateway) handleFileSystemCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
|
||||||
|
resp, ok := u.Result.(*structs.ConfigEntryResponse)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid type for response: %T", u.Result)
|
||||||
|
} else if resp == nil || resp.Entry == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg, ok := resp.Entry.(*structs.FileSystemCertificateConfigEntry)
|
||||||
|
if !ok || cfg == nil {
|
||||||
|
return fmt.Errorf("invalid type for config entry: %T", resp.Entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
ref := structs.ResourceReference{
|
||||||
|
Kind: cfg.GetKind(),
|
||||||
|
Name: cfg.GetName(),
|
||||||
|
EnterpriseMeta: *cfg.GetEnterpriseMeta(),
|
||||||
|
}
|
||||||
|
|
||||||
|
snap.APIGateway.FileSystemCertificates.Set(ref, cfg)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleInlineCertConfigUpdate stores the certificate for the gateway
|
// handleInlineCertConfigUpdate stores the certificate for the gateway
|
||||||
func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
|
func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u UpdateEvent, snap *ConfigSnapshot) error {
|
||||||
resp, ok := u.Result.(*structs.ConfigEntryResponse)
|
resp, ok := u.Result.(*structs.ConfigEntryResponse)
|
||||||
|
@ -285,7 +332,7 @@ func (h *handlerAPIGateway) handleInlineCertConfigUpdate(_ context.Context, u Up
|
||||||
EnterpriseMeta: *cfg.GetEnterpriseMeta(),
|
EnterpriseMeta: *cfg.GetEnterpriseMeta(),
|
||||||
}
|
}
|
||||||
|
|
||||||
snap.APIGateway.Certificates.Set(ref, cfg)
|
snap.APIGateway.InlineCertificates.Set(ref, cfg)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,6 +127,17 @@ func (s *handlerMeshGateway) initialize(ctx context.Context) (ConfigSnapshot, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return snap, err
|
return snap, err
|
||||||
}
|
}
|
||||||
|
// Watch for service default object that matches this mesh gateway's name
|
||||||
|
err = s.dataSources.ConfigEntry.Notify(ctx, &structs.ConfigEntryQuery{
|
||||||
|
Kind: structs.ServiceDefaults,
|
||||||
|
Name: s.service,
|
||||||
|
Datacenter: s.source.Datacenter,
|
||||||
|
QueryOptions: structs.QueryOptions{Token: s.token},
|
||||||
|
EnterpriseMeta: s.proxyID.EnterpriseMeta,
|
||||||
|
}, serviceDefaultsWatchID, s.ch)
|
||||||
|
if err != nil {
|
||||||
|
return snap, err
|
||||||
|
}
|
||||||
|
|
||||||
snap.MeshGateway.WatchedServices = make(map[structs.ServiceName]context.CancelFunc)
|
snap.MeshGateway.WatchedServices = make(map[structs.ServiceName]context.CancelFunc)
|
||||||
snap.MeshGateway.WatchedGateways = make(map[string]context.CancelFunc)
|
snap.MeshGateway.WatchedGateways = make(map[string]context.CancelFunc)
|
||||||
|
@ -648,6 +659,25 @@ func (s *handlerMeshGateway) handleUpdate(ctx context.Context, u UpdateEvent, sn
|
||||||
}
|
}
|
||||||
|
|
||||||
snap.MeshGateway.PeerServers = peerServers
|
snap.MeshGateway.PeerServers = peerServers
|
||||||
|
case serviceDefaultsWatchID:
|
||||||
|
resp, ok := u.Result.(*structs.ConfigEntryResponse)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid type for config entry: %T", resp.Entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.Entry == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
serviceDefaults, ok := resp.Entry.(*structs.ServiceConfigEntry)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid type for config entry: %T", resp.Entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if serviceDefaults.UpstreamConfig != nil && serviceDefaults.UpstreamConfig.Defaults != nil {
|
||||||
|
if serviceDefaults.UpstreamConfig.Defaults.Limits != nil {
|
||||||
|
snap.MeshGateway.Limits = serviceDefaults.UpstreamConfig.Defaults.Limits
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
switch {
|
switch {
|
||||||
|
|
|
@ -421,7 +421,8 @@ func (o *configSnapshotAPIGateway) DeepCopy() *configSnapshotAPIGateway {
|
||||||
}
|
}
|
||||||
cp.HTTPRoutes = o.HTTPRoutes.DeepCopy()
|
cp.HTTPRoutes = o.HTTPRoutes.DeepCopy()
|
||||||
cp.TCPRoutes = o.TCPRoutes.DeepCopy()
|
cp.TCPRoutes = o.TCPRoutes.DeepCopy()
|
||||||
cp.Certificates = o.Certificates.DeepCopy()
|
cp.InlineCertificates = o.InlineCertificates.DeepCopy()
|
||||||
|
cp.FileSystemCertificates = o.FileSystemCertificates.DeepCopy()
|
||||||
if o.Listeners != nil {
|
if o.Listeners != nil {
|
||||||
cp.Listeners = make(map[string]structs.APIGatewayListener, len(o.Listeners))
|
cp.Listeners = make(map[string]structs.APIGatewayListener, len(o.Listeners))
|
||||||
for k2, v2 := range o.Listeners {
|
for k2, v2 := range o.Listeners {
|
||||||
|
@ -727,6 +728,22 @@ func (o *configSnapshotMeshGateway) DeepCopy() *configSnapshotMeshGateway {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if o.Limits != nil {
|
||||||
|
cp.Limits = new(structs.UpstreamLimits)
|
||||||
|
*cp.Limits = *o.Limits
|
||||||
|
if o.Limits.MaxConnections != nil {
|
||||||
|
cp.Limits.MaxConnections = new(int)
|
||||||
|
*cp.Limits.MaxConnections = *o.Limits.MaxConnections
|
||||||
|
}
|
||||||
|
if o.Limits.MaxPendingRequests != nil {
|
||||||
|
cp.Limits.MaxPendingRequests = new(int)
|
||||||
|
*cp.Limits.MaxPendingRequests = *o.Limits.MaxPendingRequests
|
||||||
|
}
|
||||||
|
if o.Limits.MaxConcurrentRequests != nil {
|
||||||
|
cp.Limits.MaxConcurrentRequests = new(int)
|
||||||
|
*cp.Limits.MaxConcurrentRequests = *o.Limits.MaxConcurrentRequests
|
||||||
|
}
|
||||||
|
}
|
||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||||
|
@ -17,7 +19,6 @@ import (
|
||||||
"github.com/hashicorp/consul/agent/xds/config"
|
"github.com/hashicorp/consul/agent/xds/config"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
"github.com/hashicorp/consul/proto/private/pbpeering"
|
"github.com/hashicorp/consul/proto/private/pbpeering"
|
||||||
"github.com/hashicorp/go-hclog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(ingress): Can we think of a better for this bag of data?
|
// TODO(ingress): Can we think of a better for this bag of data?
|
||||||
|
@ -497,6 +498,9 @@ type configSnapshotMeshGateway struct {
|
||||||
// PeeringTrustBundlesSet indicates that the watch on the peer trust
|
// PeeringTrustBundlesSet indicates that the watch on the peer trust
|
||||||
// bundles has completed at least once.
|
// bundles has completed at least once.
|
||||||
PeeringTrustBundlesSet bool
|
PeeringTrustBundlesSet bool
|
||||||
|
|
||||||
|
// Limits
|
||||||
|
Limits *structs.UpstreamLimits
|
||||||
}
|
}
|
||||||
|
|
||||||
// MeshGatewayValidExportedServices ensures that the following data is present
|
// MeshGatewayValidExportedServices ensures that the following data is present
|
||||||
|
@ -735,9 +739,11 @@ type configSnapshotAPIGateway struct {
|
||||||
// UpstreamsSet is the unique set of UpstreamID the gateway routes to.
|
// UpstreamsSet is the unique set of UpstreamID the gateway routes to.
|
||||||
UpstreamsSet routeUpstreamSet
|
UpstreamsSet routeUpstreamSet
|
||||||
|
|
||||||
HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry]
|
HTTPRoutes watch.Map[structs.ResourceReference, *structs.HTTPRouteConfigEntry]
|
||||||
TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry]
|
TCPRoutes watch.Map[structs.ResourceReference, *structs.TCPRouteConfigEntry]
|
||||||
Certificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry]
|
|
||||||
|
InlineCertificates watch.Map[structs.ResourceReference, *structs.InlineCertificateConfigEntry]
|
||||||
|
FileSystemCertificates watch.Map[structs.ResourceReference, *structs.FileSystemCertificateConfigEntry]
|
||||||
|
|
||||||
// LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's
|
// LeafCertWatchCancel is a CancelFunc to use when refreshing this gateway's
|
||||||
// leaf cert watch with different parameters.
|
// leaf cert watch with different parameters.
|
||||||
|
|
|
@ -34,10 +34,12 @@ const (
|
||||||
consulServerListWatchID = "consul-server-list"
|
consulServerListWatchID = "consul-server-list"
|
||||||
datacentersWatchID = "datacenters"
|
datacentersWatchID = "datacenters"
|
||||||
serviceResolversWatchID = "service-resolvers"
|
serviceResolversWatchID = "service-resolvers"
|
||||||
|
serviceDefaultsWatchID = "service-defaults"
|
||||||
gatewayServicesWatchID = "gateway-services"
|
gatewayServicesWatchID = "gateway-services"
|
||||||
gatewayConfigWatchID = "gateway-config"
|
gatewayConfigWatchID = "gateway-config"
|
||||||
apiGatewayConfigWatchID = "api-gateway-config"
|
apiGatewayConfigWatchID = "api-gateway-config"
|
||||||
boundGatewayConfigWatchID = "bound-gateway-config"
|
boundGatewayConfigWatchID = "bound-gateway-config"
|
||||||
|
fileSystemCertificateConfigWatchID = "file-system-certificate-config"
|
||||||
inlineCertificateConfigWatchID = "inline-certificate-config"
|
inlineCertificateConfigWatchID = "inline-certificate-config"
|
||||||
routeConfigWatchID = "route-config"
|
routeConfigWatchID = "route-config"
|
||||||
externalServiceIDPrefix = "external-service:"
|
externalServiceIDPrefix = "external-service:"
|
||||||
|
|
|
@ -264,6 +264,25 @@ func TestConfigSnapshotMeshGateway(t testing.T, variant string, nsFn func(ns *st
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
case "limits-added":
|
||||||
|
extraUpdates = append(extraUpdates, UpdateEvent{
|
||||||
|
CorrelationID: serviceDefaultsWatchID,
|
||||||
|
Result: &structs.ConfigEntryResponse{
|
||||||
|
Entry: &structs.ServiceConfigEntry{
|
||||||
|
Kind: structs.ServiceDefaults,
|
||||||
|
Name: "mesh-gateway",
|
||||||
|
UpstreamConfig: &structs.UpstreamConfiguration{
|
||||||
|
Defaults: &structs.UpstreamConfig{
|
||||||
|
Limits: &structs.UpstreamLimits{
|
||||||
|
MaxConnections: pointerTo(1),
|
||||||
|
MaxPendingRequests: pointerTo(10),
|
||||||
|
MaxConcurrentRequests: pointerTo(100),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unknown variant: %s", variant)
|
t.Fatalf("unknown variant: %s", variant)
|
||||||
return nil
|
return nil
|
||||||
|
@ -1124,3 +1143,7 @@ func TestConfigSnapshotPeeredMeshGateway(t testing.T, variant string, nsFn func(
|
||||||
},
|
},
|
||||||
}, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates))
|
}, nsFn, nil, testSpliceEvents(baseEvents, extraUpdates))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pointerTo[T any](v T) *T {
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
|
@ -27,22 +27,23 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ServiceDefaults string = "service-defaults"
|
ServiceDefaults string = "service-defaults"
|
||||||
ProxyDefaults string = "proxy-defaults"
|
ProxyDefaults string = "proxy-defaults"
|
||||||
ServiceRouter string = "service-router"
|
ServiceRouter string = "service-router"
|
||||||
ServiceSplitter string = "service-splitter"
|
ServiceSplitter string = "service-splitter"
|
||||||
ServiceResolver string = "service-resolver"
|
ServiceResolver string = "service-resolver"
|
||||||
IngressGateway string = "ingress-gateway"
|
IngressGateway string = "ingress-gateway"
|
||||||
TerminatingGateway string = "terminating-gateway"
|
TerminatingGateway string = "terminating-gateway"
|
||||||
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"
|
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"
|
FileSystemCertificate string = "file-system-certificate"
|
||||||
HTTPRoute string = "http-route"
|
InlineCertificate string = "inline-certificate"
|
||||||
TCPRoute string = "tcp-route"
|
HTTPRoute string = "http-route"
|
||||||
|
TCPRoute string = "tcp-route"
|
||||||
// TODO: decide if we want to highlight 'ip' keyword in the name of RateLimitIPConfig
|
// TODO: decide if we want to highlight 'ip' keyword in the name of RateLimitIPConfig
|
||||||
RateLimitIPConfig string = "control-plane-request-limit"
|
RateLimitIPConfig string = "control-plane-request-limit"
|
||||||
JWTProvider string = "jwt-provider"
|
JWTProvider string = "jwt-provider"
|
||||||
|
@ -71,6 +72,7 @@ var AllConfigEntryKinds = []string{
|
||||||
BoundAPIGateway,
|
BoundAPIGateway,
|
||||||
HTTPRoute,
|
HTTPRoute,
|
||||||
TCPRoute,
|
TCPRoute,
|
||||||
|
FileSystemCertificate,
|
||||||
InlineCertificate,
|
InlineCertificate,
|
||||||
RateLimitIPConfig,
|
RateLimitIPConfig,
|
||||||
JWTProvider,
|
JWTProvider,
|
||||||
|
@ -832,6 +834,8 @@ func MakeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||||
return &APIGatewayConfigEntry{Name: name}, nil
|
return &APIGatewayConfigEntry{Name: name}, nil
|
||||||
case BoundAPIGateway:
|
case BoundAPIGateway:
|
||||||
return &BoundAPIGatewayConfigEntry{Name: name}, nil
|
return &BoundAPIGatewayConfigEntry{Name: name}, nil
|
||||||
|
case FileSystemCertificate:
|
||||||
|
return &FileSystemCertificateConfigEntry{Name: name}, nil
|
||||||
case InlineCertificate:
|
case InlineCertificate:
|
||||||
return &InlineCertificateConfigEntry{Name: name}, nil
|
return &InlineCertificateConfigEntry{Name: name}, nil
|
||||||
case HTTPRoute:
|
case HTTPRoute:
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: BUSL-1.1
|
||||||
|
|
||||||
|
package structs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hashicorp/consul/acl"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileSystemCertificateConfigEntry manages the configuration for a certificate
|
||||||
|
// and private key located in the local file system.
|
||||||
|
type FileSystemCertificateConfigEntry struct {
|
||||||
|
// Kind of config entry. This will be set to structs.FileSystemCertificate.
|
||||||
|
Kind string
|
||||||
|
|
||||||
|
// Name is used to match the config entry with its associated file system certificate.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Certificate is the optional path to a client certificate to use for TLS connections.
|
||||||
|
Certificate string
|
||||||
|
|
||||||
|
// PrivateKey is the optional path to a private key to use for TLS connections.
|
||||||
|
PrivateKey string
|
||||||
|
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
Hash uint64 `json:",omitempty" hash:"ignore"`
|
||||||
|
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
|
RaftIndex `hash:"ignore"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) SetHash(h uint64) {
|
||||||
|
e.Hash = h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) GetHash() uint64 {
|
||||||
|
return e.Hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) GetKind() string { return FileSystemCertificate }
|
||||||
|
func (e *FileSystemCertificateConfigEntry) GetName() string { return e.Name }
|
||||||
|
func (e *FileSystemCertificateConfigEntry) Normalize() error {
|
||||||
|
h, err := HashConfigEntry(e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
e.Hash = h
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (e *FileSystemCertificateConfigEntry) GetMeta() map[string]string { return e.Meta }
|
||||||
|
func (e *FileSystemCertificateConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||||
|
return &e.EnterpriseMeta
|
||||||
|
}
|
||||||
|
func (e *FileSystemCertificateConfigEntry) GetRaftIndex() *RaftIndex { return &e.RaftIndex }
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) Validate() error {
|
||||||
|
return validateConfigEntryMeta(e.Meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) Hosts() ([]string, error) {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) CanRead(authz acl.Authorizer) error {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.ToAllowAuthorizer().MeshReadAllowed(&authzContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *FileSystemCertificateConfigEntry) CanWrite(authz acl.Authorizer) error {
|
||||||
|
var authzContext acl.AuthorizerContext
|
||||||
|
e.FillAuthzContext(&authzContext)
|
||||||
|
return authz.ToAllowAuthorizer().MeshWriteAllowed(&authzContext)
|
||||||
|
}
|
|
@ -870,7 +870,8 @@ func (e *APIGatewayConfigEntry) validateListeners() error {
|
||||||
ListenerProtocolTCP: true,
|
ListenerProtocolTCP: true,
|
||||||
}
|
}
|
||||||
allowedCertificateKinds := map[string]bool{
|
allowedCertificateKinds := map[string]bool{
|
||||||
InlineCertificate: true,
|
FileSystemCertificate: true,
|
||||||
|
InlineCertificate: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, listener := range e.Listeners {
|
for _, listener := range e.Listeners {
|
||||||
|
@ -889,7 +890,7 @@ func (e *APIGatewayConfigEntry) validateListeners() error {
|
||||||
}
|
}
|
||||||
for _, certificate := range listener.TLS.Certificates {
|
for _, certificate := range listener.TLS.Certificates {
|
||||||
if !allowedCertificateKinds[certificate.Kind] {
|
if !allowedCertificateKinds[certificate.Kind] {
|
||||||
return fmt.Errorf("unsupported certificate kind: %q, must be 'inline-certificate'", certificate.Kind)
|
return fmt.Errorf("unsupported certificate kind: %q, must be 'file-system-certificate' or 'inline-certificate'", certificate.Kind)
|
||||||
}
|
}
|
||||||
if certificate.Name == "" {
|
if certificate.Name == "" {
|
||||||
return fmt.Errorf("certificate reference must have a name")
|
return fmt.Errorf("certificate reference must have a name")
|
||||||
|
@ -978,6 +979,16 @@ func (a *APIGatewayTLSConfiguration) IsEmpty() bool {
|
||||||
return len(a.Certificates) == 0 && len(a.MaxVersion) == 0 && len(a.MinVersion) == 0 && len(a.CipherSuites) == 0
|
return len(a.Certificates) == 0 && len(a.MaxVersion) == 0 && len(a.MinVersion) == 0 && len(a.CipherSuites) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *APIGatewayTLSConfiguration) ToGatewayTLSConfig() GatewayTLSConfig {
|
||||||
|
return GatewayTLSConfig{
|
||||||
|
Enabled: true,
|
||||||
|
SDS: nil,
|
||||||
|
TLSMinVersion: a.MinVersion,
|
||||||
|
TLSMaxVersion: a.MaxVersion,
|
||||||
|
CipherSuites: a.CipherSuites,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ServiceRouteReferences is a map with a key of ServiceName type for a routed to service from a
|
// ServiceRouteReferences is a map with a key of ServiceName type for a routed to service from a
|
||||||
// bound gateway listener with a value being a slice of resource references of the routes that reference the service
|
// bound gateway listener with a value being a slice of resource references of the routes that reference the service
|
||||||
type ServiceRouteReferences map[ServiceName][]ResourceReference
|
type ServiceRouteReferences map[ServiceName][]ResourceReference
|
||||||
|
|
|
@ -24,6 +24,7 @@ deep-copy \
|
||||||
-type DiscoverySplit \
|
-type DiscoverySplit \
|
||||||
-type ExposeConfig \
|
-type ExposeConfig \
|
||||||
-type ExportedServicesConfigEntry \
|
-type ExportedServicesConfigEntry \
|
||||||
|
-type FileSystemCertificateConfigEntry \
|
||||||
-type GatewayService \
|
-type GatewayService \
|
||||||
-type GatewayServiceTLSConfig \
|
-type GatewayServiceTLSConfig \
|
||||||
-type HTTPHeaderModifiers \
|
-type HTTPHeaderModifiers \
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// generated by deep-copy -pointer-receiver -o ./structs.deepcopy.go -type APIGatewayListener -type BoundAPIGatewayListener -type CARoot -type CheckServiceNode -type CheckType -type CompiledDiscoveryChain -type ConnectProxyConfig -type DiscoveryFailover -type DiscoveryGraphNode -type DiscoveryResolver -type DiscoveryRoute -type DiscoverySplit -type ExposeConfig -type ExportedServicesConfigEntry -type GatewayService -type GatewayServiceTLSConfig -type HTTPHeaderModifiers -type HTTPRouteConfigEntry -type HashPolicy -type HealthCheck -type IndexedCARoots -type IngressListener -type InlineCertificateConfigEntry -type Intention -type IntentionPermission -type LoadBalancer -type MeshConfigEntry -type MeshDirectionalTLSConfig -type MeshTLSConfig -type Node -type NodeService -type PeeringServiceMeta -type ServiceConfigEntry -type ServiceConfigResponse -type ServiceConnect -type ServiceDefinition -type ServiceResolverConfigEntry -type ServiceResolverFailover -type ServiceRoute -type ServiceRouteDestination -type ServiceRouteMatch -type TCPRouteConfigEntry -type Upstream -type UpstreamConfiguration -type Status -type BoundAPIGatewayConfigEntry ./; DO NOT EDIT.
|
// generated by deep-copy -pointer-receiver -o ./structs.deepcopy.go -type APIGatewayListener -type BoundAPIGatewayListener -type CARoot -type CheckServiceNode -type CheckType -type CompiledDiscoveryChain -type ConnectProxyConfig -type DiscoveryFailover -type DiscoveryGraphNode -type DiscoveryResolver -type DiscoveryRoute -type DiscoverySplit -type ExposeConfig -type ExportedServicesConfigEntry -type FileSystemCertificateConfigEntry -type GatewayService -type GatewayServiceTLSConfig -type HTTPHeaderModifiers -type HTTPRouteConfigEntry -type HashPolicy -type HealthCheck -type IndexedCARoots -type IngressListener -type InlineCertificateConfigEntry -type Intention -type IntentionPermission -type LoadBalancer -type MeshConfigEntry -type MeshDirectionalTLSConfig -type MeshTLSConfig -type Node -type NodeService -type PeeringServiceMeta -type ServiceConfigEntry -type ServiceConfigResponse -type ServiceConnect -type ServiceDefinition -type ServiceResolverConfigEntry -type ServiceResolverFailover -type ServiceRoute -type ServiceRouteDestination -type ServiceRouteMatch -type TCPRouteConfigEntry -type Upstream -type UpstreamConfiguration -type Status -type BoundAPIGatewayConfigEntry ./; DO NOT EDIT.
|
||||||
|
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
|
@ -318,6 +318,18 @@ func (o *ExportedServicesConfigEntry) DeepCopy() *ExportedServicesConfigEntry {
|
||||||
return &cp
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeepCopy generates a deep copy of *FileSystemCertificateConfigEntry
|
||||||
|
func (o *FileSystemCertificateConfigEntry) DeepCopy() *FileSystemCertificateConfigEntry {
|
||||||
|
var cp FileSystemCertificateConfigEntry = *o
|
||||||
|
if o.Meta != nil {
|
||||||
|
cp.Meta = make(map[string]string, len(o.Meta))
|
||||||
|
for k2, v2 := range o.Meta {
|
||||||
|
cp.Meta[k2] = v2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &cp
|
||||||
|
}
|
||||||
|
|
||||||
// DeepCopy generates a deep copy of *GatewayService
|
// DeepCopy generates a deep copy of *GatewayService
|
||||||
func (o *GatewayService) DeepCopy() *GatewayService {
|
func (o *GatewayService) DeepCopy() *GatewayService {
|
||||||
var cp GatewayService = *o
|
var cp GatewayService = *o
|
||||||
|
|
|
@ -532,6 +532,7 @@ func (s *ResourceGenerator) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.Co
|
||||||
name: connect.GatewaySNI(key.Datacenter, key.Partition, cfgSnap.Roots.TrustDomain),
|
name: connect.GatewaySNI(key.Datacenter, key.Partition, cfgSnap.Roots.TrustDomain),
|
||||||
hostnameEndpoints: cfgSnap.MeshGateway.HostnameDatacenters[key.String()],
|
hostnameEndpoints: cfgSnap.MeshGateway.HostnameDatacenters[key.String()],
|
||||||
isRemote: true,
|
isRemote: true,
|
||||||
|
limits: cfgSnap.MeshGateway.Limits,
|
||||||
}
|
}
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
||||||
clusters = append(clusters, cluster)
|
clusters = append(clusters, cluster)
|
||||||
|
@ -554,6 +555,7 @@ func (s *ResourceGenerator) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.Co
|
||||||
name: cfgSnap.ServerSNIFn(key.Datacenter, ""),
|
name: cfgSnap.ServerSNIFn(key.Datacenter, ""),
|
||||||
hostnameEndpoints: hostnameEndpoints,
|
hostnameEndpoints: hostnameEndpoints,
|
||||||
isRemote: !key.Matches(cfgSnap.Datacenter, cfgSnap.ProxyID.PartitionOrDefault()),
|
isRemote: !key.Matches(cfgSnap.Datacenter, cfgSnap.ProxyID.PartitionOrDefault()),
|
||||||
|
limits: cfgSnap.MeshGateway.Limits,
|
||||||
}
|
}
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
||||||
clusters = append(clusters, cluster)
|
clusters = append(clusters, cluster)
|
||||||
|
@ -563,7 +565,8 @@ func (s *ResourceGenerator) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.Co
|
||||||
servers, _ := cfgSnap.MeshGateway.WatchedLocalServers.Get(structs.ConsulServiceName)
|
servers, _ := cfgSnap.MeshGateway.WatchedLocalServers.Get(structs.ConsulServiceName)
|
||||||
for _, srv := range servers {
|
for _, srv := range servers {
|
||||||
opts := clusterOpts{
|
opts := clusterOpts{
|
||||||
name: cfgSnap.ServerSNIFn(cfgSnap.Datacenter, srv.Node.Node),
|
name: cfgSnap.ServerSNIFn(cfgSnap.Datacenter, srv.Node.Node),
|
||||||
|
limits: cfgSnap.MeshGateway.Limits,
|
||||||
}
|
}
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
||||||
clusters = append(clusters, cluster)
|
clusters = append(clusters, cluster)
|
||||||
|
@ -579,14 +582,15 @@ func (s *ResourceGenerator) clustersFromSnapshotMeshGateway(cfgSnap *proxycfg.Co
|
||||||
// We avoid routing to read replicas since they will never be Raft voters.
|
// We avoid routing to read replicas since they will never be Raft voters.
|
||||||
if haveVoters(servers) {
|
if haveVoters(servers) {
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, clusterOpts{
|
cluster := s.makeGatewayCluster(cfgSnap, clusterOpts{
|
||||||
name: connect.PeeringServerSAN(cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain),
|
name: connect.PeeringServerSAN(cfgSnap.Datacenter, cfgSnap.Roots.TrustDomain),
|
||||||
|
limits: cfgSnap.MeshGateway.Limits,
|
||||||
})
|
})
|
||||||
clusters = append(clusters, cluster)
|
clusters = append(clusters, cluster)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate the per-service/subset clusters
|
// generate the per-service/subset clusters
|
||||||
c, err := s.makeGatewayServiceClusters(cfgSnap, cfgSnap.MeshGateway.ServiceGroups, cfgSnap.MeshGateway.ServiceResolvers)
|
c, err := s.makeGatewayServiceClusters(cfgSnap, cfgSnap.MeshGateway.ServiceGroups, cfgSnap.MeshGateway.ServiceResolvers, cfgSnap.MeshGateway.Limits)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -664,7 +668,7 @@ func (s *ResourceGenerator) makePeerServerClusters(cfgSnap *proxycfg.ConfigSnaps
|
||||||
// for a terminating gateway. This will include 1 cluster per Destination associated with this terminating gateway.
|
// for a terminating gateway. This will include 1 cluster per Destination associated with this terminating gateway.
|
||||||
func (s *ResourceGenerator) clustersFromSnapshotTerminatingGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
func (s *ResourceGenerator) clustersFromSnapshotTerminatingGateway(cfgSnap *proxycfg.ConfigSnapshot) ([]proto.Message, error) {
|
||||||
res := []proto.Message{}
|
res := []proto.Message{}
|
||||||
gwClusters, err := s.makeGatewayServiceClusters(cfgSnap, cfgSnap.TerminatingGateway.ServiceGroups, cfgSnap.TerminatingGateway.ServiceResolvers)
|
gwClusters, err := s.makeGatewayServiceClusters(cfgSnap, cfgSnap.TerminatingGateway.ServiceGroups, cfgSnap.TerminatingGateway.ServiceResolvers, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -683,6 +687,7 @@ func (s *ResourceGenerator) makeGatewayServiceClusters(
|
||||||
cfgSnap *proxycfg.ConfigSnapshot,
|
cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
services map[structs.ServiceName]structs.CheckServiceNodes,
|
services map[structs.ServiceName]structs.CheckServiceNodes,
|
||||||
resolvers map[structs.ServiceName]*structs.ServiceResolverConfigEntry,
|
resolvers map[structs.ServiceName]*structs.ServiceResolverConfigEntry,
|
||||||
|
limits *structs.UpstreamLimits,
|
||||||
) ([]proto.Message, error) {
|
) ([]proto.Message, error) {
|
||||||
var hostnameEndpoints structs.CheckServiceNodes
|
var hostnameEndpoints structs.CheckServiceNodes
|
||||||
|
|
||||||
|
@ -724,6 +729,7 @@ func (s *ResourceGenerator) makeGatewayServiceClusters(
|
||||||
hostnameEndpoints: hostnameEndpoints,
|
hostnameEndpoints: hostnameEndpoints,
|
||||||
connectTimeout: resolver.ConnectTimeout,
|
connectTimeout: resolver.ConnectTimeout,
|
||||||
isRemote: isRemote,
|
isRemote: isRemote,
|
||||||
|
limits: limits,
|
||||||
}
|
}
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
||||||
|
|
||||||
|
@ -763,6 +769,7 @@ func (s *ResourceGenerator) makeGatewayServiceClusters(
|
||||||
onlyPassing: subset.OnlyPassing,
|
onlyPassing: subset.OnlyPassing,
|
||||||
connectTimeout: resolver.ConnectTimeout,
|
connectTimeout: resolver.ConnectTimeout,
|
||||||
isRemote: isRemote,
|
isRemote: isRemote,
|
||||||
|
limits: limits,
|
||||||
}
|
}
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
||||||
|
|
||||||
|
@ -812,6 +819,7 @@ func (s *ResourceGenerator) makeGatewayOutgoingClusterPeeringServiceClusters(cfg
|
||||||
name: clusterName,
|
name: clusterName,
|
||||||
isRemote: true,
|
isRemote: true,
|
||||||
hostnameEndpoints: hostnameEndpoints,
|
hostnameEndpoints: hostnameEndpoints,
|
||||||
|
limits: cfgSnap.MeshGateway.Limits,
|
||||||
}
|
}
|
||||||
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
cluster := s.makeGatewayCluster(cfgSnap, opts)
|
||||||
|
|
||||||
|
@ -1706,6 +1714,8 @@ type clusterOpts struct {
|
||||||
// Corresponds to a valid address/port pairs to be routed externally
|
// Corresponds to a valid address/port pairs to be routed externally
|
||||||
// these addresses will be embedded in the cluster configuration and will never use EDS
|
// these addresses will be embedded in the cluster configuration and will never use EDS
|
||||||
addresses []structs.ServiceAddress
|
addresses []structs.ServiceAddress
|
||||||
|
|
||||||
|
limits *structs.UpstreamLimits
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeGatewayCluster creates an Envoy cluster for a mesh or terminating gateway
|
// makeGatewayCluster creates an Envoy cluster for a mesh or terminating gateway
|
||||||
|
@ -1768,6 +1778,12 @@ func (s *ResourceGenerator) makeGatewayCluster(snap *proxycfg.ConfigSnapshot, op
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.limits != nil {
|
||||||
|
cluster.CircuitBreakers = &envoy_cluster_v3.CircuitBreakers{
|
||||||
|
Thresholds: makeThresholdsIfNeeded(opts.limits),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return cluster
|
return cluster
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,22 +6,19 @@ package xds
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"golang.org/x/exp/maps"
|
|
||||||
|
|
||||||
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||||
envoy_http_jwt_authn_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3"
|
envoy_http_jwt_authn_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/jwt_authn/v3"
|
||||||
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
|
||||||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||||
|
"golang.org/x/exp/maps"
|
||||||
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
|
||||||
"github.com/hashicorp/consul/agent/xds/naming"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/consul/discoverychain"
|
||||||
"github.com/hashicorp/consul/agent/proxycfg"
|
"github.com/hashicorp/consul/agent/proxycfg"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/agent/xds/naming"
|
||||||
"github.com/hashicorp/consul/types"
|
"github.com/hashicorp/consul/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,13 +32,19 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro
|
||||||
listenerKey := readyListener.listenerKey
|
listenerKey := readyListener.listenerKey
|
||||||
boundListener := readyListener.boundListenerCfg
|
boundListener := readyListener.boundListenerCfg
|
||||||
|
|
||||||
var certs []structs.InlineCertificateConfigEntry
|
// Collect the referenced certificate config entries
|
||||||
|
var certs []structs.ConfigEntry
|
||||||
for _, certRef := range boundListener.Certificates {
|
for _, certRef := range boundListener.Certificates {
|
||||||
cert, ok := cfgSnap.APIGateway.Certificates.Get(certRef)
|
switch certRef.Kind {
|
||||||
if !ok {
|
case structs.InlineCertificate:
|
||||||
continue
|
if cert, ok := cfgSnap.APIGateway.InlineCertificates.Get(certRef); ok {
|
||||||
|
certs = append(certs, cert)
|
||||||
|
}
|
||||||
|
case structs.FileSystemCertificate:
|
||||||
|
if cert, ok := cfgSnap.APIGateway.FileSystemCertificates.Get(certRef); ok {
|
||||||
|
certs = append(certs, cert)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
certs = append(certs, *cert)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isAPIGatewayWithTLS := len(boundListener.Certificates) > 0
|
isAPIGatewayWithTLS := len(boundListener.Certificates) > 0
|
||||||
|
@ -111,7 +114,7 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro
|
||||||
|
|
||||||
if isAPIGatewayWithTLS {
|
if isAPIGatewayWithTLS {
|
||||||
// construct SNI filter chains
|
// construct SNI filter chains
|
||||||
l.FilterChains, err = makeInlineOverrideFilterChains(
|
l.FilterChains, err = s.makeInlineOverrideFilterChains(
|
||||||
cfgSnap,
|
cfgSnap,
|
||||||
cfgSnap.APIGateway.TLSConfig,
|
cfgSnap.APIGateway.TLSConfig,
|
||||||
listenerKey.Protocol,
|
listenerKey.Protocol,
|
||||||
|
@ -225,7 +228,7 @@ func (s *ResourceGenerator) makeAPIGatewayListeners(address string, cfgSnap *pro
|
||||||
sniFilterChains := []*envoy_listener_v3.FilterChain{}
|
sniFilterChains := []*envoy_listener_v3.FilterChain{}
|
||||||
|
|
||||||
if isAPIGatewayWithTLS {
|
if isAPIGatewayWithTLS {
|
||||||
sniFilterChains, err = makeInlineOverrideFilterChains(cfgSnap, cfgSnap.IngressGateway.TLSConfig, listenerKey.Protocol, filterOpts, certs)
|
sniFilterChains, err = s.makeInlineOverrideFilterChains(cfgSnap, cfgSnap.IngressGateway.TLSConfig, listenerKey.Protocol, filterOpts, certs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -393,11 +396,11 @@ func resolveAPIListenerTLSConfig(listenerTLSCfg structs.APIGatewayTLSConfigurati
|
||||||
|
|
||||||
// when we have multiple certificates on a single listener, we need
|
// when we have multiple certificates on a single listener, we need
|
||||||
// to duplicate the filter chains with multiple TLS contexts
|
// to duplicate the filter chains with multiple TLS contexts
|
||||||
func makeInlineOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
|
func (s *ResourceGenerator) makeInlineOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
tlsCfg structs.GatewayTLSConfig,
|
tlsCfg structs.GatewayTLSConfig,
|
||||||
protocol string,
|
protocol string,
|
||||||
filterOpts listenerFilterOpts,
|
filterOpts listenerFilterOpts,
|
||||||
certs []structs.InlineCertificateConfigEntry,
|
certs []structs.ConfigEntry,
|
||||||
) ([]*envoy_listener_v3.FilterChain, error) {
|
) ([]*envoy_listener_v3.FilterChain, error) {
|
||||||
var chains []*envoy_listener_v3.FilterChain
|
var chains []*envoy_listener_v3.FilterChain
|
||||||
|
|
||||||
|
@ -438,50 +441,72 @@ func makeInlineOverrideFilterChains(cfgSnap *proxycfg.ConfigSnapshot,
|
||||||
// we only need to prune out overlapping hosts if we have more than
|
// we only need to prune out overlapping hosts if we have more than
|
||||||
// one certificate
|
// one certificate
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
hosts, err := cert.Hosts()
|
switch tce := cert.(type) {
|
||||||
if err != nil {
|
case *structs.InlineCertificateConfigEntry:
|
||||||
return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts)
|
hosts, err := tce.Hosts()
|
||||||
}
|
if err != nil {
|
||||||
for _, host := range hosts {
|
return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts)
|
||||||
if _, ok := allCertHosts[host]; ok {
|
|
||||||
overlappingHosts[host] = struct{}{}
|
|
||||||
}
|
}
|
||||||
allCertHosts[host] = struct{}{}
|
for _, host := range hosts {
|
||||||
|
if _, ok := allCertHosts[host]; ok {
|
||||||
|
overlappingHosts[host] = struct{}{}
|
||||||
|
}
|
||||||
|
allCertHosts[host] = struct{}{}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// do nothing for FileSystemCertificates because we don't actually have the certificate available
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constructTLSContext := func(certConfig structs.ConfigEntry) (*envoy_tls_v3.CommonTlsContext, error) {
|
||||||
|
switch tce := certConfig.(type) {
|
||||||
|
case *structs.InlineCertificateConfigEntry:
|
||||||
|
return makeInlineTLSContextFromGatewayTLSConfig(tlsCfg, tce), nil
|
||||||
|
case *structs.FileSystemCertificateConfigEntry:
|
||||||
|
return makeFileSystemTLSContextFromGatewayTLSConfig(tlsCfg, tce), nil
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported config entry kind %s", tce.GetKind())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
var hosts []string
|
var hosts []string
|
||||||
|
|
||||||
// if we only have one cert, we just use it for all ingress
|
// if we only have one cert, we just use it for all ingress
|
||||||
if multipleCerts {
|
if multipleCerts {
|
||||||
// otherwise, we need an SNI per cert and to fallback to our ingress
|
switch tce := cert.(type) {
|
||||||
// gateway certificate signed by our Consul CA
|
case *structs.InlineCertificateConfigEntry:
|
||||||
certHosts, err := cert.Hosts()
|
certHosts, err := tce.Hosts()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts)
|
return nil, fmt.Errorf("unable to parse hosts from x509 certificate: %v", hosts)
|
||||||
}
|
}
|
||||||
// filter out any overlapping hosts so we don't have collisions in our filter chains
|
// filter out any overlapping hosts so we don't have collisions in our filter chains
|
||||||
for _, host := range certHosts {
|
for _, host := range certHosts {
|
||||||
if _, ok := overlappingHosts[host]; !ok {
|
if _, ok := overlappingHosts[host]; !ok {
|
||||||
hosts = append(hosts, host)
|
hosts = append(hosts, host)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if len(hosts) == 0 {
|
if len(hosts) == 0 {
|
||||||
// all of our hosts are overlapping, so we just skip this filter and it'll be
|
// all of our hosts are overlapping, so we just skip this filter and it'll be
|
||||||
// handled by the default filter chain
|
// handled by the default filter chain
|
||||||
continue
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := constructChain(cert.Name, hosts, makeInlineTLSContextFromGatewayTLSConfig(tlsCfg, cert)); err != nil {
|
tlsContext, err := constructTLSContext(cert)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := constructChain(cert.GetName(), hosts, tlsContext); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if multipleCerts {
|
if len(certs) > 1 {
|
||||||
// if we have more than one cert, add a default handler that uses the leaf cert from connect
|
// if we have more than one cert, add a default handler that uses the leaf cert from connect
|
||||||
if err := constructChain("default", nil, makeCommonTLSContext(cfgSnap.Leaf(), cfgSnap.RootPEMs(), makeTLSParametersFromGatewayTLSConfig(tlsCfg))); err != nil {
|
if err := constructChain("default", nil, makeCommonTLSContext(cfgSnap.Leaf(), cfgSnap.RootPEMs(), makeTLSParametersFromGatewayTLSConfig(tlsCfg))); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||||
"github.com/hashicorp/consul/agent/xds/naming"
|
|
||||||
|
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
"google.golang.org/protobuf/types/known/durationpb"
|
"google.golang.org/protobuf/types/known/durationpb"
|
||||||
|
@ -17,6 +16,7 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/proxycfg"
|
"github.com/hashicorp/consul/agent/proxycfg"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/agent/xds/naming"
|
||||||
"github.com/hashicorp/consul/lib"
|
"github.com/hashicorp/consul/lib"
|
||||||
"github.com/hashicorp/consul/types"
|
"github.com/hashicorp/consul/types"
|
||||||
)
|
)
|
||||||
|
@ -391,7 +391,26 @@ func makeTLSParametersFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig) *env
|
||||||
return makeTLSParametersFromTLSConfig(tlsCfg.TLSMinVersion, tlsCfg.TLSMaxVersion, tlsCfg.CipherSuites)
|
return makeTLSParametersFromTLSConfig(tlsCfg.TLSMinVersion, tlsCfg.TLSMaxVersion, tlsCfg.CipherSuites)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeInlineTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert structs.InlineCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext {
|
func makeFileSystemTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert *structs.FileSystemCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext {
|
||||||
|
return &envoy_tls_v3.CommonTlsContext{
|
||||||
|
TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
|
||||||
|
TlsCertificateSdsSecretConfigs: []*envoy_tls_v3.SdsSecretConfig{
|
||||||
|
{
|
||||||
|
// Delivering via SDS is required in order to get file system watches today.
|
||||||
|
// https://github.com/envoyproxy/envoy/issues/10387
|
||||||
|
Name: cert.Name, // Reference the secret returned in xds/secrets.go by name here
|
||||||
|
SdsConfig: &envoy_core_v3.ConfigSource{
|
||||||
|
ConfigSourceSpecifier: &envoy_core_v3.ConfigSource_Ads{
|
||||||
|
Ads: &envoy_core_v3.AggregatedConfigSource{},
|
||||||
|
},
|
||||||
|
ResourceApiVersion: envoy_core_v3.ApiVersion_V3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeInlineTLSContextFromGatewayTLSConfig(tlsCfg structs.GatewayTLSConfig, cert *structs.InlineCertificateConfigEntry) *envoy_tls_v3.CommonTlsContext {
|
||||||
return &envoy_tls_v3.CommonTlsContext{
|
return &envoy_tls_v3.CommonTlsContext{
|
||||||
TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
|
TlsParams: makeTLSParametersFromGatewayTLSConfig(tlsCfg),
|
||||||
TlsCertificates: []*envoy_tls_v3.TlsCertificate{{
|
TlsCertificates: []*envoy_tls_v3.TlsCertificate{{
|
||||||
|
|
|
@ -743,6 +743,14 @@ func getMeshGatewayGoldenTestCases() []goldenTestCase {
|
||||||
// TODO(proxystate): mesh gateway will come at a later time
|
// TODO(proxystate): mesh gateway will come at a later time
|
||||||
alsoRunTestForV2: false,
|
alsoRunTestForV2: false,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "mesh-gateway-with-limits",
|
||||||
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
return proxycfg.TestConfigSnapshotMeshGateway(t, "limits-added", nil, nil)
|
||||||
|
},
|
||||||
|
// TODO(proxystate): mesh gateway will come at a later time
|
||||||
|
alsoRunTestForV2: false,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "mesh-gateway-using-federation-states",
|
name: "mesh-gateway-using-federation-states",
|
||||||
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
create: func(t testinf.T) *proxycfg.ConfigSnapshot {
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
|
||||||
|
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/proxycfg"
|
"github.com/hashicorp/consul/agent/proxycfg"
|
||||||
|
@ -21,13 +23,45 @@ func (s *ResourceGenerator) secretsFromSnapshot(cfgSnap *proxycfg.ConfigSnapshot
|
||||||
}
|
}
|
||||||
|
|
||||||
switch cfgSnap.Kind {
|
switch cfgSnap.Kind {
|
||||||
|
case structs.ServiceKindAPIGateway:
|
||||||
|
return s.secretsFromSnapshotAPIGateway(cfgSnap), nil // return any attached certs
|
||||||
case structs.ServiceKindConnectProxy,
|
case structs.ServiceKindConnectProxy,
|
||||||
structs.ServiceKindTerminatingGateway,
|
structs.ServiceKindTerminatingGateway,
|
||||||
structs.ServiceKindMeshGateway,
|
structs.ServiceKindMeshGateway,
|
||||||
structs.ServiceKindIngressGateway,
|
structs.ServiceKindIngressGateway:
|
||||||
structs.ServiceKindAPIGateway:
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
|
return nil, fmt.Errorf("Invalid service kind: %v", cfgSnap.Kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// secretsFromSnapshotAPIGateway returns the "secrets" for an api-gateway service
|
||||||
|
func (s *ResourceGenerator) secretsFromSnapshotAPIGateway(cfgSnap *proxycfg.ConfigSnapshot) []proto.Message {
|
||||||
|
var resources []proto.Message
|
||||||
|
|
||||||
|
cfgSnap.APIGateway.FileSystemCertificates.ForEachKey(func(ref structs.ResourceReference) bool {
|
||||||
|
cert, ok := cfgSnap.APIGateway.FileSystemCertificates.Get(ref)
|
||||||
|
if !ok || cert == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
resources = append(resources, &envoy_tls_v3.Secret{
|
||||||
|
Name: ref.Name,
|
||||||
|
Type: &envoy_tls_v3.Secret_TlsCertificate{
|
||||||
|
TlsCertificate: &envoy_tls_v3.TlsCertificate{
|
||||||
|
CertificateChain: &envoy_core_v3.DataSource{
|
||||||
|
Specifier: &envoy_core_v3.DataSource_Filename{
|
||||||
|
Filename: cert.Certificate,
|
||||||
|
}},
|
||||||
|
PrivateKey: &envoy_core_v3.DataSource{
|
||||||
|
Specifier: &envoy_core_v3.DataSource_Filename{
|
||||||
|
Filename: cert.PrivateKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return resources
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
{
|
||||||
|
"nonce": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"circuitBreakers": {
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"maxConnections": 1,
|
||||||
|
"maxPendingRequests": 10,
|
||||||
|
"maxRequests": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"outlierDetection": {},
|
||||||
|
"type": "EDS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"circuitBreakers": {
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"maxConnections": 1,
|
||||||
|
"maxPendingRequests": 10,
|
||||||
|
"maxRequests": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "dc2.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"outlierDetection": {},
|
||||||
|
"type": "EDS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"circuitBreakers": {
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"maxConnections": 1,
|
||||||
|
"maxPendingRequests": 10,
|
||||||
|
"maxRequests": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"dnsLookupFamily": "V4_ONLY",
|
||||||
|
"dnsRefreshRate": "10s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "dc4.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "123.us-west-2.elb.notaws.com",
|
||||||
|
"portValue": 443
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "dc4.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"outlierDetection": {},
|
||||||
|
"type": "LOGICAL_DNS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"circuitBreakers": {
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"maxConnections": 1,
|
||||||
|
"maxPendingRequests": 10,
|
||||||
|
"maxRequests": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"dnsLookupFamily": "V4_ONLY",
|
||||||
|
"dnsRefreshRate": "10s",
|
||||||
|
"loadAssignment": {
|
||||||
|
"clusterName": "dc6.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "123.us-east-1.elb.notaws.com",
|
||||||
|
"portValue": 443
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "UNHEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "dc6.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"outlierDetection": {},
|
||||||
|
"type": "LOGICAL_DNS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"circuitBreakers": {
|
||||||
|
"thresholds": [
|
||||||
|
{
|
||||||
|
"maxConnections": 1,
|
||||||
|
"maxPendingRequests": 10,
|
||||||
|
"maxRequests": 100
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"connectTimeout": "5s",
|
||||||
|
"edsClusterConfig": {
|
||||||
|
"edsConfig": {
|
||||||
|
"ads": {},
|
||||||
|
"resourceApiVersion": "V3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "foo.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"outlierDetection": {},
|
||||||
|
"type": "EDS"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.cluster.v3.Cluster",
|
||||||
|
"versionInfo": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
{
|
||||||
|
"nonce": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "bar.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.6",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.7",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.8",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "dc2.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "198.18.1.1",
|
||||||
|
"portValue": 443
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "198.18.1.2",
|
||||||
|
"portValue": 443
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"clusterName": "foo.default.dc1.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"lbEndpoints": [
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.3",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.4",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.5",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"endpoint": {
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "172.16.1.9",
|
||||||
|
"portValue": 2222
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"healthStatus": "HEALTHY",
|
||||||
|
"loadBalancingWeight": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.endpoint.v3.ClusterLoadAssignment",
|
||||||
|
"versionInfo": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
{
|
||||||
|
"nonce": "00000001",
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"@type": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||||
|
"address": {
|
||||||
|
"socketAddress": {
|
||||||
|
"address": "1.2.3.4",
|
||||||
|
"portValue": 8443
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"filterChains": [
|
||||||
|
{
|
||||||
|
"filterChainMatch": {
|
||||||
|
"serverNames": [
|
||||||
|
"*.dc2.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.tcp_proxy",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||||
|
"cluster": "dc2.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"statPrefix": "mesh_gateway_remote.default.dc2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filterChainMatch": {
|
||||||
|
"serverNames": [
|
||||||
|
"*.dc4.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.tcp_proxy",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||||
|
"cluster": "dc4.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"statPrefix": "mesh_gateway_remote.default.dc4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filterChainMatch": {
|
||||||
|
"serverNames": [
|
||||||
|
"*.dc6.internal.11111111-2222-3333-4444-555555555555.consul"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.tcp_proxy",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||||
|
"cluster": "dc6.internal.11111111-2222-3333-4444-555555555555.consul",
|
||||||
|
"statPrefix": "mesh_gateway_remote.default.dc6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.sni_cluster",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.sni_cluster.v3.SniCluster"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.network.tcp_proxy",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy",
|
||||||
|
"cluster": "",
|
||||||
|
"statPrefix": "mesh_gateway_local.default"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"listenerFilters": [
|
||||||
|
{
|
||||||
|
"name": "envoy.filters.listener.tls_inspector",
|
||||||
|
"typedConfig": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "default:1.2.3.4:8443"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.listener.v3.Listener",
|
||||||
|
"versionInfo": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"nonce": "00000001",
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.config.route.v3.RouteConfiguration",
|
||||||
|
"versionInfo": "00000001"
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"nonce": "00000001",
|
||||||
|
"typeUrl": "type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret",
|
||||||
|
"versionInfo": "00000001"
|
||||||
|
}
|
|
@ -29,13 +29,14 @@ const (
|
||||||
SamenessGroup string = "sameness-group"
|
SamenessGroup string = "sameness-group"
|
||||||
RateLimitIPConfig string = "control-plane-request-limit"
|
RateLimitIPConfig string = "control-plane-request-limit"
|
||||||
|
|
||||||
ProxyConfigGlobal string = "global"
|
ProxyConfigGlobal string = "global"
|
||||||
MeshConfigMesh string = "mesh"
|
MeshConfigMesh string = "mesh"
|
||||||
APIGateway string = "api-gateway"
|
APIGateway string = "api-gateway"
|
||||||
TCPRoute string = "tcp-route"
|
TCPRoute string = "tcp-route"
|
||||||
InlineCertificate string = "inline-certificate"
|
FileSystemCertificate string = "file-system-certificate"
|
||||||
HTTPRoute string = "http-route"
|
InlineCertificate string = "inline-certificate"
|
||||||
JWTProvider string = "jwt-provider"
|
HTTPRoute string = "http-route"
|
||||||
|
JWTProvider string = "jwt-provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -447,6 +448,8 @@ func makeConfigEntry(kind, name string) (ConfigEntry, error) {
|
||||||
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
|
return &APIGatewayConfigEntry{Kind: kind, Name: name}, nil
|
||||||
case TCPRoute:
|
case TCPRoute:
|
||||||
return &TCPRouteConfigEntry{Kind: kind, Name: name}, nil
|
return &TCPRouteConfigEntry{Kind: kind, Name: name}, nil
|
||||||
|
case FileSystemCertificate:
|
||||||
|
return &FileSystemCertificateConfigEntry{Kind: kind, Name: name}, nil
|
||||||
case InlineCertificate:
|
case InlineCertificate:
|
||||||
return &InlineCertificateConfigEntry{Kind: kind, Name: name}, nil
|
return &InlineCertificateConfigEntry{Kind: kind, Name: name}, nil
|
||||||
case HTTPRoute:
|
case HTTPRoute:
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright (c) HashiCorp, Inc.
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
type FileSystemCertificateConfigEntry struct {
|
||||||
|
// Kind of the config entry. This should be set to api.FileSystemCertificate.
|
||||||
|
Kind string
|
||||||
|
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Certificate is the path to a client certificate to use for TLS connections.
|
||||||
|
Certificate string `json:",omitempty" alias:"certificate"`
|
||||||
|
|
||||||
|
// PrivateKey is the path to a private key to use for TLS connections.
|
||||||
|
PrivateKey string `json:",omitempty" alias:"private_key"`
|
||||||
|
|
||||||
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
|
||||||
|
// CreateIndex is the Raft index this entry was created at. This is a
|
||||||
|
// read-only field.
|
||||||
|
CreateIndex uint64
|
||||||
|
|
||||||
|
// ModifyIndex is used for the Check-And-Set operations and can also be fed
|
||||||
|
// back into the WaitIndex of the QueryOptions in order to perform blocking
|
||||||
|
// queries.
|
||||||
|
ModifyIndex uint64
|
||||||
|
|
||||||
|
// Partition is the partition the config entry is associated with.
|
||||||
|
// Partitioning is a Consul Enterprise feature.
|
||||||
|
Partition string `json:",omitempty"`
|
||||||
|
|
||||||
|
// Namespace is the namespace the config entry is associated with.
|
||||||
|
// Namespacing is a Consul Enterprise feature.
|
||||||
|
Namespace string `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetKind() string { return FileSystemCertificate }
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetName() string { return a.Name }
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetPartition() string { return a.Partition }
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetNamespace() string { return a.Namespace }
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetMeta() map[string]string { return a.Meta }
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetCreateIndex() uint64 { return a.CreateIndex }
|
||||||
|
func (a *FileSystemCertificateConfigEntry) GetModifyIndex() uint64 { return a.ModifyIndex }
|
|
@ -8,6 +8,7 @@ import (
|
||||||
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
envoy_endpoint_v3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3"
|
||||||
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
|
||||||
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||||
|
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||||
"github.com/hashicorp/go-hclog"
|
"github.com/hashicorp/go-hclog"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
@ -111,7 +112,7 @@ func IndexResources(logger hclog.Logger, resources map[string][]proto.Message) *
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetResourceName(res proto.Message) string {
|
func GetResourceName(res proto.Message) string {
|
||||||
// NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS
|
// NOTE: this only covers types that we currently care about for LDS/RDS/CDS/EDS/SDS
|
||||||
switch x := res.(type) {
|
switch x := res.(type) {
|
||||||
case *envoy_listener_v3.Listener: // LDS
|
case *envoy_listener_v3.Listener: // LDS
|
||||||
return x.Name
|
return x.Name
|
||||||
|
@ -121,6 +122,8 @@ func GetResourceName(res proto.Message) string {
|
||||||
return x.Name
|
return x.Name
|
||||||
case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS
|
case *envoy_endpoint_v3.ClusterLoadAssignment: // EDS
|
||||||
return x.ClusterName
|
return x.ClusterName
|
||||||
|
case *envoy_tls_v3.Secret: // SDS
|
||||||
|
return x.Name
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -133,6 +136,7 @@ func EmptyIndexedResources() *IndexedResources {
|
||||||
RouteType: make(map[string]proto.Message),
|
RouteType: make(map[string]proto.Message),
|
||||||
ClusterType: make(map[string]proto.Message),
|
ClusterType: make(map[string]proto.Message),
|
||||||
EndpointType: make(map[string]proto.Message),
|
EndpointType: make(map[string]proto.Message),
|
||||||
|
SecretType: make(map[string]proto.Message),
|
||||||
},
|
},
|
||||||
ChildIndex: map[string]map[string][]string{
|
ChildIndex: map[string]map[string][]string{
|
||||||
ListenerType: make(map[string][]string),
|
ListenerType: make(map[string][]string),
|
||||||
|
|
|
@ -146,9 +146,14 @@ type TelemetryConfig struct {
|
||||||
|
|
||||||
// DisableHostname will disable hostname prefixing for all metrics.
|
// DisableHostname will disable hostname prefixing for all metrics.
|
||||||
//
|
//
|
||||||
// hcl: telemetry { disable_hostname = (true|false)
|
// hcl: telemetry { disable_hostname = (true|false) }
|
||||||
DisableHostname bool `json:"disable_hostname,omitempty" mapstructure:"disable_hostname"`
|
DisableHostname bool `json:"disable_hostname,omitempty" mapstructure:"disable_hostname"`
|
||||||
|
|
||||||
|
// DisablePerTenancyUsageMetrics will disable setting tenancy labels on usage metrics.
|
||||||
|
//
|
||||||
|
// hcl: telemetry { disable_per_tenancy_usage_metrics = (true|false) }
|
||||||
|
DisablePerTenancyUsageMetrics bool `json:"disable_per_tenancy_usage_metrics,omitempty" mapstructure:"disable_per_tenancy_usage_metrics"`
|
||||||
|
|
||||||
// DogStatsdAddr is the address of a dogstatsd instance. If provided,
|
// DogStatsdAddr is the address of a dogstatsd instance. If provided,
|
||||||
// metrics will be sent to that instance
|
// metrics will be sent to that instance
|
||||||
//
|
//
|
||||||
|
|
|
@ -408,6 +408,24 @@ func ExposePathFromStructs(t *structs.ExposePath, s *ExposePath) {
|
||||||
s.Protocol = t.Protocol
|
s.Protocol = t.Protocol
|
||||||
s.ParsedFromCheck = t.ParsedFromCheck
|
s.ParsedFromCheck = t.ParsedFromCheck
|
||||||
}
|
}
|
||||||
|
func FileSystemCertificateToStructs(s *FileSystemCertificate, t *structs.FileSystemCertificateConfigEntry) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Certificate = s.Certificate
|
||||||
|
t.PrivateKey = s.PrivateKey
|
||||||
|
t.Meta = s.Meta
|
||||||
|
t.Hash = s.Hash
|
||||||
|
}
|
||||||
|
func FileSystemCertificateFromStructs(t *structs.FileSystemCertificateConfigEntry, s *FileSystemCertificate) {
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Certificate = t.Certificate
|
||||||
|
s.PrivateKey = t.PrivateKey
|
||||||
|
s.Meta = t.Meta
|
||||||
|
s.Hash = t.Hash
|
||||||
|
}
|
||||||
func GatewayServiceTLSConfigToStructs(s *GatewayServiceTLSConfig, t *structs.GatewayServiceTLSConfig) {
|
func GatewayServiceTLSConfigToStructs(s *GatewayServiceTLSConfig, t *structs.GatewayServiceTLSConfig) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -85,6 +85,14 @@ func ConfigEntryToStructs(s *ConfigEntry) structs.ConfigEntry {
|
||||||
pbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)
|
pbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)
|
||||||
pbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)
|
pbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)
|
||||||
return &target
|
return &target
|
||||||
|
case Kind_KindFileSystemCertificate:
|
||||||
|
var target structs.FileSystemCertificateConfigEntry
|
||||||
|
target.Name = s.Name
|
||||||
|
|
||||||
|
FileSystemCertificateToStructs(s.GetFileSystemCertificate(), &target)
|
||||||
|
pbcommon.RaftIndexToStructs(s.RaftIndex, &target.RaftIndex)
|
||||||
|
pbcommon.EnterpriseMetaToStructs(s.EnterpriseMeta, &target.EnterpriseMeta)
|
||||||
|
return &target
|
||||||
case Kind_KindInlineCertificate:
|
case Kind_KindInlineCertificate:
|
||||||
var target structs.InlineCertificateConfigEntry
|
var target structs.InlineCertificateConfigEntry
|
||||||
target.Name = s.Name
|
target.Name = s.Name
|
||||||
|
@ -213,6 +221,14 @@ func ConfigEntryFromStructs(s structs.ConfigEntry) *ConfigEntry {
|
||||||
configEntry.Entry = &ConfigEntry_HTTPRoute{
|
configEntry.Entry = &ConfigEntry_HTTPRoute{
|
||||||
HTTPRoute: &route,
|
HTTPRoute: &route,
|
||||||
}
|
}
|
||||||
|
case *structs.FileSystemCertificateConfigEntry:
|
||||||
|
var cert FileSystemCertificate
|
||||||
|
FileSystemCertificateFromStructs(v, &cert)
|
||||||
|
|
||||||
|
configEntry.Kind = Kind_KindFileSystemCertificate
|
||||||
|
configEntry.Entry = &ConfigEntry_FileSystemCertificate{
|
||||||
|
FileSystemCertificate: &cert,
|
||||||
|
}
|
||||||
case *structs.InlineCertificateConfigEntry:
|
case *structs.InlineCertificateConfigEntry:
|
||||||
var cert InlineCertificate
|
var cert InlineCertificate
|
||||||
InlineCertificateFromStructs(v, &cert)
|
InlineCertificateFromStructs(v, &cert)
|
||||||
|
|
|
@ -657,6 +657,16 @@ func (msg *BoundAPIGatewayListener) UnmarshalBinary(b []byte) error {
|
||||||
return proto.Unmarshal(b, msg)
|
return proto.Unmarshal(b, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
|
func (msg *FileSystemCertificate) MarshalBinary() ([]byte, error) {
|
||||||
|
return proto.Marshal(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||||
|
func (msg *FileSystemCertificate) UnmarshalBinary(b []byte) error {
|
||||||
|
return proto.Unmarshal(b, msg)
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalBinary implements encoding.BinaryMarshaler
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
func (msg *InlineCertificate) MarshalBinary() ([]byte, error) {
|
func (msg *InlineCertificate) MarshalBinary() ([]byte, error) {
|
||||||
return proto.Marshal(msg)
|
return proto.Marshal(msg)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -54,6 +54,7 @@ enum Kind {
|
||||||
KindSamenessGroup = 11;
|
KindSamenessGroup = 11;
|
||||||
KindJWTProvider = 12;
|
KindJWTProvider = 12;
|
||||||
KindExportedServices = 13;
|
KindExportedServices = 13;
|
||||||
|
KindFileSystemCertificate = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ConfigEntry {
|
message ConfigEntry {
|
||||||
|
@ -77,6 +78,7 @@ message ConfigEntry {
|
||||||
SamenessGroup SamenessGroup = 15;
|
SamenessGroup SamenessGroup = 15;
|
||||||
JWTProvider JWTProvider = 16;
|
JWTProvider JWTProvider = 16;
|
||||||
ExportedServices ExportedServices = 17;
|
ExportedServices ExportedServices = 17;
|
||||||
|
FileSystemCertificate FileSystemCertificate = 18;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -867,6 +869,19 @@ message BoundAPIGatewayListener {
|
||||||
repeated ResourceReference Routes = 3;
|
repeated ResourceReference Routes = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mog annotation:
|
||||||
|
//
|
||||||
|
// target=github.com/hashicorp/consul/agent/structs.FileSystemCertificateConfigEntry
|
||||||
|
// output=config_entry.gen.go
|
||||||
|
// name=Structs
|
||||||
|
// ignore-fields=Kind,Name,RaftIndex,EnterpriseMeta
|
||||||
|
message FileSystemCertificate {
|
||||||
|
map<string, string> Meta = 1;
|
||||||
|
string Certificate = 2;
|
||||||
|
string PrivateKey = 3;
|
||||||
|
uint64 Hash = 4;
|
||||||
|
}
|
||||||
|
|
||||||
// mog annotation:
|
// mog annotation:
|
||||||
//
|
//
|
||||||
// target=github.com/hashicorp/consul/agent/structs.InlineCertificateConfigEntry
|
// target=github.com/hashicorp/consul/agent/structs.InlineCertificateConfigEntry
|
||||||
|
|
|
@ -80,6 +80,8 @@ const (
|
||||||
Topic_JWTProvider Topic = 16
|
Topic_JWTProvider Topic = 16
|
||||||
// ExportedServices topic contains events for changes to exported-services.
|
// ExportedServices topic contains events for changes to exported-services.
|
||||||
Topic_ExportedServices Topic = 17
|
Topic_ExportedServices Topic = 17
|
||||||
|
// FileSystemCertificate topic contains events for changes to file-system-certificates.
|
||||||
|
Topic_FileSystemCertificate Topic = 18
|
||||||
)
|
)
|
||||||
|
|
||||||
// Enum value maps for Topic.
|
// Enum value maps for Topic.
|
||||||
|
@ -103,26 +105,28 @@ var (
|
||||||
15: "SamenessGroup",
|
15: "SamenessGroup",
|
||||||
16: "JWTProvider",
|
16: "JWTProvider",
|
||||||
17: "ExportedServices",
|
17: "ExportedServices",
|
||||||
|
18: "FileSystemCertificate",
|
||||||
}
|
}
|
||||||
Topic_value = map[string]int32{
|
Topic_value = map[string]int32{
|
||||||
"Unknown": 0,
|
"Unknown": 0,
|
||||||
"ServiceHealth": 1,
|
"ServiceHealth": 1,
|
||||||
"ServiceHealthConnect": 2,
|
"ServiceHealthConnect": 2,
|
||||||
"MeshConfig": 3,
|
"MeshConfig": 3,
|
||||||
"ServiceResolver": 4,
|
"ServiceResolver": 4,
|
||||||
"IngressGateway": 5,
|
"IngressGateway": 5,
|
||||||
"ServiceIntentions": 6,
|
"ServiceIntentions": 6,
|
||||||
"ServiceList": 7,
|
"ServiceList": 7,
|
||||||
"ServiceDefaults": 8,
|
"ServiceDefaults": 8,
|
||||||
"APIGateway": 9,
|
"APIGateway": 9,
|
||||||
"TCPRoute": 10,
|
"TCPRoute": 10,
|
||||||
"HTTPRoute": 11,
|
"HTTPRoute": 11,
|
||||||
"InlineCertificate": 12,
|
"InlineCertificate": 12,
|
||||||
"BoundAPIGateway": 13,
|
"BoundAPIGateway": 13,
|
||||||
"IPRateLimit": 14,
|
"IPRateLimit": 14,
|
||||||
"SamenessGroup": 15,
|
"SamenessGroup": 15,
|
||||||
"JWTProvider": 16,
|
"JWTProvider": 16,
|
||||||
"ExportedServices": 17,
|
"ExportedServices": 17,
|
||||||
|
"FileSystemCertificate": 18,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1004,7 +1008,7 @@ var file_private_pbsubscribe_subscribe_proto_rawDesc = []byte{
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45,
|
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x0e, 0x45,
|
||||||
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a,
|
0x6e, 0x74, 0x65, 0x72, 0x70, 0x72, 0x69, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a,
|
||||||
0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x2a, 0xdb, 0x02, 0x0a, 0x05, 0x54, 0x6f,
|
0x08, 0x50, 0x65, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x2a, 0xf6, 0x02, 0x0a, 0x05, 0x54, 0x6f,
|
||||||
0x70, 0x69, 0x63, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00,
|
0x70, 0x69, 0x63, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00,
|
||||||
0x12, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
0x12, 0x11, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65, 0x61, 0x6c, 0x74,
|
||||||
0x68, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65,
|
0x68, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x48, 0x65,
|
||||||
|
@ -1026,26 +1030,28 @@ var file_private_pbsubscribe_subscribe_proto_rawDesc = []byte{
|
||||||
0x0d, 0x53, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x10, 0x0f,
|
0x0d, 0x53, 0x61, 0x6d, 0x65, 0x6e, 0x65, 0x73, 0x73, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x10, 0x0f,
|
||||||
0x12, 0x0f, 0x0a, 0x0b, 0x4a, 0x57, 0x54, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x10,
|
0x12, 0x0f, 0x0a, 0x0b, 0x4a, 0x57, 0x54, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x10,
|
||||||
0x10, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72,
|
0x10, 0x12, 0x14, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72,
|
||||||
0x76, 0x69, 0x63, 0x65, 0x73, 0x10, 0x11, 0x2a, 0x29, 0x0a, 0x09, 0x43, 0x61, 0x74, 0x61, 0x6c,
|
0x76, 0x69, 0x63, 0x65, 0x73, 0x10, 0x11, 0x12, 0x19, 0x0a, 0x15, 0x46, 0x69, 0x6c, 0x65, 0x53,
|
||||||
0x6f, 0x67, 0x4f, 0x70, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
|
0x79, 0x73, 0x74, 0x65, 0x6d, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65,
|
||||||
0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
|
0x10, 0x12, 0x2a, 0x29, 0x0a, 0x09, 0x43, 0x61, 0x74, 0x61, 0x6c, 0x6f, 0x67, 0x4f, 0x70, 0x12,
|
||||||
0x10, 0x01, 0x32, 0x61, 0x0a, 0x17, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67,
|
0x0c, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x00, 0x12, 0x0e, 0x0a,
|
||||||
0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a,
|
0x0a, 0x44, 0x65, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x10, 0x01, 0x32, 0x61, 0x0a,
|
||||||
0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x75, 0x62,
|
0x17, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x53, 0x75, 0x62, 0x73,
|
||||||
0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
|
0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x46, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73,
|
||||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72,
|
0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x1b, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62,
|
||||||
0x69, 0x62, 0x65, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x22, 0x08, 0xe2, 0x86, 0x04, 0x04, 0x08,
|
0x65, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||||
0x02, 0x10, 0x09, 0x30, 0x01, 0x42, 0x9a, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75,
|
0x73, 0x74, 0x1a, 0x10, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x2e, 0x45,
|
||||||
0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x42, 0x0e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
0x76, 0x65, 0x6e, 0x74, 0x22, 0x08, 0xe2, 0x86, 0x04, 0x04, 0x08, 0x02, 0x10, 0x09, 0x30, 0x01,
|
||||||
0x62, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75,
|
0x42, 0x9a, 0x01, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
||||||
0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f,
|
0x62, 0x65, 0x42, 0x0e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x50, 0x72, 0x6f,
|
||||||
0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69,
|
0x74, 0x6f, 0x50, 0x01, 0x5a, 0x35, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
|
||||||
0x76, 0x61, 0x74, 0x65, 0x2f, 0x70, 0x62, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
|
0x2f, 0x68, 0x61, 0x73, 0x68, 0x69, 0x63, 0x6f, 0x72, 0x70, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x75,
|
||||||
0xa2, 0x02, 0x03, 0x53, 0x58, 0x58, 0xaa, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x2f,
|
||||||
0x62, 0x65, 0xca, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xe2, 0x02,
|
0x70, 0x62, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x58,
|
||||||
0x15, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65,
|
0x58, 0xaa, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xca, 0x02, 0x09,
|
||||||
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69,
|
0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0xe2, 0x02, 0x15, 0x53, 0x75, 0x62, 0x73,
|
||||||
0x62, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
0x63, 0x72, 0x69, 0x62, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||||
|
0x61, 0xea, 0x02, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x62, 0x06, 0x70,
|
||||||
|
0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -109,6 +109,9 @@ enum Topic {
|
||||||
|
|
||||||
// ExportedServices topic contains events for changes to exported-services.
|
// ExportedServices topic contains events for changes to exported-services.
|
||||||
ExportedServices = 17;
|
ExportedServices = 17;
|
||||||
|
|
||||||
|
// FileSystemCertificate topic contains events for changes to file-system-certificates.
|
||||||
|
FileSystemCertificate = 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
message NamedSubject {
|
message NamedSubject {
|
||||||
|
|
|
@ -227,7 +227,7 @@ function start_consul {
|
||||||
docker_kill_rm consul-${DC}-server
|
docker_kill_rm consul-${DC}-server
|
||||||
docker_kill_rm consul-${DC}
|
docker_kill_rm consul-${DC}
|
||||||
|
|
||||||
docker run -d --name envoy_consul-${DC}-server_1 \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name envoy_consul-${DC}-server_1 \
|
||||||
--net=envoy-tests \
|
--net=envoy-tests \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
--hostname "consul-${DC}-server" \
|
--hostname "consul-${DC}-server" \
|
||||||
|
@ -240,7 +240,7 @@ function start_consul {
|
||||||
-client "0.0.0.0" \
|
-client "0.0.0.0" \
|
||||||
-bind "0.0.0.0" >/dev/null
|
-bind "0.0.0.0" >/dev/null
|
||||||
|
|
||||||
docker run -d --name envoy_consul-${DC}_1 \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name envoy_consul-${DC}_1 \
|
||||||
--net=envoy-tests \
|
--net=envoy-tests \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
--hostname "consul-${DC}-client" \
|
--hostname "consul-${DC}-client" \
|
||||||
|
@ -258,7 +258,7 @@ function start_consul {
|
||||||
else
|
else
|
||||||
docker_kill_rm consul-${DC}
|
docker_kill_rm consul-${DC}
|
||||||
|
|
||||||
docker run -d --name envoy_consul-${DC}_1 \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name envoy_consul-${DC}_1 \
|
||||||
--net=envoy-tests \
|
--net=envoy-tests \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
--hostname "consul-${DC}" \
|
--hostname "consul-${DC}" \
|
||||||
|
@ -295,7 +295,7 @@ function start_partitioned_client {
|
||||||
# Run consul and expose some ports to the host to make debugging locally a
|
# Run consul and expose some ports to the host to make debugging locally a
|
||||||
# bit easier.
|
# bit easier.
|
||||||
#
|
#
|
||||||
docker run -d --name envoy_consul-${PARTITION}_1 \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name envoy_consul-${PARTITION}_1 \
|
||||||
--net=envoy-tests \
|
--net=envoy-tests \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
--hostname "consul-${PARTITION}-client" \
|
--hostname "consul-${PARTITION}-client" \
|
||||||
|
@ -351,7 +351,7 @@ function verify {
|
||||||
|
|
||||||
# need to tell the PID 1 inside of the container that it won't be actual PID
|
# need to tell the PID 1 inside of the container that it won't be actual PID
|
||||||
# 1 because we're using --pid=host so we use TINI_SUBREAPER
|
# 1 because we're using --pid=host so we use TINI_SUBREAPER
|
||||||
if docker run --name envoy_verify-${CLUSTER}_1 -t \
|
if docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 --name envoy_verify-${CLUSTER}_1 -t \
|
||||||
-e TINI_SUBREAPER=1 \
|
-e TINI_SUBREAPER=1 \
|
||||||
-e ENVOY_VERSION \
|
-e ENVOY_VERSION \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
|
@ -442,7 +442,7 @@ function global_setup {
|
||||||
}
|
}
|
||||||
|
|
||||||
function wipe_volumes {
|
function wipe_volumes {
|
||||||
docker run --rm -i \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 --rm -i \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
--net=none \
|
--net=none \
|
||||||
"${HASHICORP_DOCKER_PROXY}/alpine" \
|
"${HASHICORP_DOCKER_PROXY}/alpine" \
|
||||||
|
@ -556,7 +556,7 @@ function suite_setup {
|
||||||
# This is a dummy container that we use to create volume and keep it
|
# This is a dummy container that we use to create volume and keep it
|
||||||
# accessible while other containers are down.
|
# accessible while other containers are down.
|
||||||
docker volume create envoy_workdir &>/dev/null
|
docker volume create envoy_workdir &>/dev/null
|
||||||
docker run -d --name envoy_workdir_1 \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name envoy_workdir_1 \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
--net=none \
|
--net=none \
|
||||||
k8s.gcr.io/pause &>/dev/null
|
k8s.gcr.io/pause &>/dev/null
|
||||||
|
@ -567,7 +567,7 @@ function suite_setup {
|
||||||
retry_default docker build -t bats-verify -f Dockerfile-bats .
|
retry_default docker build -t bats-verify -f Dockerfile-bats .
|
||||||
|
|
||||||
echo "Checking bats image..."
|
echo "Checking bats image..."
|
||||||
docker run --rm -t bats-verify -v
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 --rm -t bats-verify -v
|
||||||
|
|
||||||
# pre-build the consul+envoy container
|
# pre-build the consul+envoy container
|
||||||
echo "Rebuilding 'consul-dev-envoy:${ENVOY_VERSION}' image..."
|
echo "Rebuilding 'consul-dev-envoy:${ENVOY_VERSION}' image..."
|
||||||
|
@ -615,7 +615,7 @@ function common_run_container_service {
|
||||||
local httpPort="$3"
|
local httpPort="$3"
|
||||||
local grpcPort="$4"
|
local grpcPort="$4"
|
||||||
|
|
||||||
docker run -d --name $(container_name_prev) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name_prev) \
|
||||||
-e "FORTIO_NAME=${service}" \
|
-e "FORTIO_NAME=${service}" \
|
||||||
$(network_snippet $CLUSTER) \
|
$(network_snippet $CLUSTER) \
|
||||||
"${HASHICORP_DOCKER_PROXY}/fortio/fortio" \
|
"${HASHICORP_DOCKER_PROXY}/fortio/fortio" \
|
||||||
|
@ -696,7 +696,7 @@ function common_run_container_sidecar_proxy {
|
||||||
# despite separate containers that don't share IPC namespace. Not quite
|
# despite separate containers that don't share IPC namespace. Not quite
|
||||||
# sure how this happens but may be due to unix socket being in some shared
|
# sure how this happens but may be due to unix socket being in some shared
|
||||||
# location?
|
# location?
|
||||||
docker run -d --name $(container_name_prev) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name_prev) \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
$(network_snippet $CLUSTER) \
|
$(network_snippet $CLUSTER) \
|
||||||
$(aws_snippet) \
|
$(aws_snippet) \
|
||||||
|
@ -717,7 +717,7 @@ function run_container_s1-ap1-sidecar-proxy {
|
||||||
}
|
}
|
||||||
|
|
||||||
function run_container_s1-sidecar-proxy-consul-exec {
|
function run_container_s1-sidecar-proxy-consul-exec {
|
||||||
docker run -d --name $(container_name) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name) \
|
||||||
$(network_snippet primary) \
|
$(network_snippet primary) \
|
||||||
consul-dev-envoy:${ENVOY_VERSION} \
|
consul-dev-envoy:${ENVOY_VERSION} \
|
||||||
consul connect envoy -sidecar-for s1 \
|
consul connect envoy -sidecar-for s1 \
|
||||||
|
@ -783,7 +783,7 @@ function common_run_container_gateway {
|
||||||
# despite separate containers that don't share IPC namespace. Not quite
|
# despite separate containers that don't share IPC namespace. Not quite
|
||||||
# sure how this happens but may be due to unix socket being in some shared
|
# sure how this happens but may be due to unix socket being in some shared
|
||||||
# location?
|
# location?
|
||||||
docker run -d --name $(container_name_prev) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name_prev) \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
$(network_snippet $DC) \
|
$(network_snippet $DC) \
|
||||||
$(aws_snippet) \
|
$(aws_snippet) \
|
||||||
|
@ -824,7 +824,7 @@ function run_container_fake-statsd {
|
||||||
# This magic SYSTEM incantation is needed since Envoy doesn't add newlines and so
|
# This magic SYSTEM incantation is needed since Envoy doesn't add newlines and so
|
||||||
# we need each packet to be passed to echo to add a new line before
|
# we need each packet to be passed to echo to add a new line before
|
||||||
# appending.
|
# appending.
|
||||||
docker run -d --name $(container_name) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name) \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
$(network_snippet primary) \
|
$(network_snippet primary) \
|
||||||
"${HASHICORP_DOCKER_PROXY}/alpine/socat:1.7.3.4-r1" \
|
"${HASHICORP_DOCKER_PROXY}/alpine/socat:1.7.3.4-r1" \
|
||||||
|
@ -833,14 +833,14 @@ function run_container_fake-statsd {
|
||||||
}
|
}
|
||||||
|
|
||||||
function run_container_zipkin {
|
function run_container_zipkin {
|
||||||
docker run -d --name $(container_name) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name) \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
$(network_snippet primary) \
|
$(network_snippet primary) \
|
||||||
"${HASHICORP_DOCKER_PROXY}/openzipkin/zipkin"
|
"${HASHICORP_DOCKER_PROXY}/openzipkin/zipkin"
|
||||||
}
|
}
|
||||||
|
|
||||||
function run_container_jaeger {
|
function run_container_jaeger {
|
||||||
docker run -d --name $(container_name) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name) \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
$(network_snippet primary) \
|
$(network_snippet primary) \
|
||||||
"${HASHICORP_DOCKER_PROXY}/jaegertracing/all-in-one:1.11" \
|
"${HASHICORP_DOCKER_PROXY}/jaegertracing/all-in-one:1.11" \
|
||||||
|
@ -848,7 +848,7 @@ function run_container_jaeger {
|
||||||
}
|
}
|
||||||
|
|
||||||
function run_container_test-sds-server {
|
function run_container_test-sds-server {
|
||||||
docker run -d --name $(container_name) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name) \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
$(network_snippet primary) \
|
$(network_snippet primary) \
|
||||||
"test-sds-server"
|
"test-sds-server"
|
||||||
|
@ -863,7 +863,7 @@ function container_name_prev {
|
||||||
|
|
||||||
# This is a debugging tool. Run via './run-tests.sh debug_dump_volumes'
|
# This is a debugging tool. Run via './run-tests.sh debug_dump_volumes'
|
||||||
function debug_dump_volumes {
|
function debug_dump_volumes {
|
||||||
docker run --rm -it \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 --rm -it \
|
||||||
$WORKDIR_SNIPPET \
|
$WORKDIR_SNIPPET \
|
||||||
-v ./:/cwd \
|
-v ./:/cwd \
|
||||||
--net=none \
|
--net=none \
|
||||||
|
@ -891,7 +891,7 @@ function common_run_container_tcpdump {
|
||||||
|
|
||||||
retry_default docker build -t envoy-tcpdump -f Dockerfile-tcpdump .
|
retry_default docker build -t envoy-tcpdump -f Dockerfile-tcpdump .
|
||||||
|
|
||||||
docker run -d --name $(container_name_prev) \
|
docker run --sysctl net.ipv6.conf.all.disable_ipv6=1 -d --name $(container_name_prev) \
|
||||||
$(network_snippet $DC) \
|
$(network_snippet $DC) \
|
||||||
-v $(pwd)/workdir/${DC}/envoy/:/data \
|
-v $(pwd)/workdir/${DC}/envoy/:/data \
|
||||||
--privileged \
|
--privileged \
|
||||||
|
|
|
@ -39,23 +39,24 @@ The corresponding CLI command is [`consul config write`](/consul/commands/config
|
||||||
|
|
||||||
The ACL required depends on the config entry being written:
|
The ACL required depends on the config entry being written:
|
||||||
|
|
||||||
| Config Entry Kind | Required ACLs |
|
| Config Entry Kind | Required ACLs |
|
||||||
| ------------------- | -------------------------------- |
|
| ----------------------- | -------------------------------- |
|
||||||
| api-gateway | `mesh:write` or `operator:write` |
|
| api-gateway | `mesh:write` or `operator:write` |
|
||||||
| bound-api-gateway | Not writable. |
|
| bound-api-gateway | Not writable. |
|
||||||
| exported-services | `mesh:write` or `operator:write` |
|
| exported-services | `mesh:write` or `operator:write` |
|
||||||
| http-route | `mesh:write` or `operator:write` |
|
| file-system-certificate | `mesh:write` or `operator:write` |
|
||||||
| ingress-gateway | `mesh:write` or `operator:write` |
|
| http-route | `mesh:write` or `operator:write` |
|
||||||
| inline-certificate | `mesh:write` or `operator:write` |
|
| ingress-gateway | `mesh:write` or `operator:write` |
|
||||||
| mesh | `mesh:write` or `operator:write` |
|
| inline-certificate | `mesh:write` or `operator:write` |
|
||||||
| proxy-defaults | `mesh:write` or `operator:write` |
|
| mesh | `mesh:write` or `operator:write` |
|
||||||
| service-defaults | `service:write` |
|
| proxy-defaults | `mesh:write` or `operator:write` |
|
||||||
| service-intentions | `intentions:write` |
|
| service-defaults | `service:write` |
|
||||||
| service-resolver | `service:write` |
|
| service-intentions | `intentions:write` |
|
||||||
| service-router | `service:write` |
|
| service-resolver | `service:write` |
|
||||||
| service-splitter | `service:write` |
|
| service-router | `service:write` |
|
||||||
| tcp-route | `mesh:write` or `operator:write` |
|
| service-splitter | `service:write` |
|
||||||
| terminating-gateway | `mesh:write` or `operator:write` |
|
| tcp-route | `mesh:write` or `operator:write` |
|
||||||
|
| terminating-gateway | `mesh:write` or `operator:write` |
|
||||||
|
|
||||||
### Query Parameters
|
### Query Parameters
|
||||||
|
|
||||||
|
@ -114,23 +115,24 @@ The corresponding CLI command is [`consul config read`](/consul/commands/config/
|
||||||
|
|
||||||
The ACL required depends on the config entry kind being read:
|
The ACL required depends on the config entry kind being read:
|
||||||
|
|
||||||
| Config Entry Kind | Required ACLs |
|
| Config Entry Kind | Required ACLs |
|
||||||
| ------------------- | -------------------------------- |
|
| ----------------------- | -------------------------------- |
|
||||||
| api-gateway | `service:read` |
|
| api-gateway | `service:read` |
|
||||||
| bound-api-gateway | `service:read` |
|
| bound-api-gateway | `service:read` |
|
||||||
| exported-services | `mesh:read` or `operator:read` |
|
| exported-services | `mesh:read` or `operator:read` |
|
||||||
| http-route | `mesh:read` or `operator:read` |
|
| file-system-certificate | `mesh:read` or `operator:read` |
|
||||||
| ingress-gateway | `service:read` |
|
| http-route | `mesh:read` or `operator:read` |
|
||||||
| inline-certificate | `mesh:read` or `operator:read` |
|
| ingress-gateway | `service:read` |
|
||||||
| mesh | No ACL required |
|
| inline-certificate | `mesh:read` or `operator:read` |
|
||||||
| proxy-defaults | No ACL required |
|
| mesh | No ACL required |
|
||||||
| service-defaults | `service:read` |
|
| proxy-defaults | No ACL required |
|
||||||
| service-intentions | `intentions:read` |
|
| service-defaults | `service:read` |
|
||||||
| service-resolver | `service:read` |
|
| service-intentions | `intentions:read` |
|
||||||
| service-router | `service:read` |
|
| service-resolver | `service:read` |
|
||||||
| service-splitter | `service:read` |
|
| service-router | `service:read` |
|
||||||
| tcp-route | `mesh:read` or `operator:read` |
|
| service-splitter | `service:read` |
|
||||||
| terminating-gateway | `service:read` |
|
| tcp-route | `mesh:read` or `operator:read` |
|
||||||
|
| terminating-gateway | `service:read` |
|
||||||
|
|
||||||
### Path Parameters
|
### Path Parameters
|
||||||
|
|
||||||
|
@ -192,23 +194,24 @@ The table below shows this endpoint's support for
|
||||||
|
|
||||||
The ACL required depends on the config entry kind being read:
|
The ACL required depends on the config entry kind being read:
|
||||||
|
|
||||||
| Config Entry Kind | Required ACLs |
|
| Config Entry Kind | Required ACLs |
|
||||||
| ------------------- | -------------------------------- |
|
| ----------------------- | -------------------------------- |
|
||||||
| api-gateway | `service:read` |
|
| api-gateway | `service:read` |
|
||||||
| bound-api-gateway | `service:read` |
|
| bound-api-gateway | `service:read` |
|
||||||
| exported-services | `mesh:read` or `operator:read` |
|
| exported-services | `mesh:read` or `operator:read` |
|
||||||
| http-route | `mesh:read` or `operator:read` |
|
| file-system-certificate | `mesh:read` or `operator:read` |
|
||||||
| ingress-gateway | `service:read` |
|
| http-route | `mesh:read` or `operator:read` |
|
||||||
| inline-certificate | `mesh:read` or `operator:read` |
|
| ingress-gateway | `service:read` |
|
||||||
| mesh | No ACL required |
|
| inline-certificate | `mesh:read` or `operator:read` |
|
||||||
| proxy-defaults | No ACL required |
|
| mesh | No ACL required |
|
||||||
| service-defaults | `service:read` |
|
| proxy-defaults | No ACL required |
|
||||||
| service-intentions | `intentions:read` |
|
| service-defaults | `service:read` |
|
||||||
| service-resolver | `service:read` |
|
| service-intentions | `intentions:read` |
|
||||||
| service-router | `service:read` |
|
| service-resolver | `service:read` |
|
||||||
| service-splitter | `service:read` |
|
| service-router | `service:read` |
|
||||||
| tcp-route | `mesh:read` or `operator:read` |
|
| service-splitter | `service:read` |
|
||||||
| terminating-gateway | `service:read` |
|
| tcp-route | `mesh:read` or `operator:read` |
|
||||||
|
| terminating-gateway | `service:read` |
|
||||||
|
|
||||||
The corresponding CLI command is [`consul config list`](/consul/commands/config/list).
|
The corresponding CLI command is [`consul config list`](/consul/commands/config/list).
|
||||||
|
|
||||||
|
@ -276,23 +279,24 @@ The table below shows this endpoint's support for
|
||||||
|
|
||||||
The ACL required depends on the config entry kind being deleted:
|
The ACL required depends on the config entry kind being deleted:
|
||||||
|
|
||||||
| Config Entry Kind | Required ACLs |
|
| Config Entry Kind | Required ACLs |
|
||||||
| ------------------- | -------------------------------- |
|
| ----------------------- | -------------------------------- |
|
||||||
| api-gateway | `mesh:write` or `operator:write` |
|
| api-gateway | `mesh:write` or `operator:write` |
|
||||||
| bound-api-gateway | Not writable. |
|
| bound-api-gateway | Not writable. |
|
||||||
| exported-services | `mesh:write` or `operator:write` |
|
| exported-services | `mesh:write` or `operator:write` |
|
||||||
| http-route | `mesh:write` or `operator:write` |
|
| file-system-certificate | `mesh:write` or `operator:write` |
|
||||||
| ingress-gateway | `mesh:write` or `operator:write` |
|
| http-route | `mesh:write` or `operator:write` |
|
||||||
| inline-certificate | `mesh:write` or `operator:write` |
|
| ingress-gateway | `mesh:write` or `operator:write` |
|
||||||
| mesh | `mesh:write` or `operator:write` |
|
| inline-certificate | `mesh:write` or `operator:write` |
|
||||||
| proxy-defaults | `mesh:write` or `operator:write` |
|
| mesh | `mesh:write` or `operator:write` |
|
||||||
| service-defaults | `service:write` |
|
| proxy-defaults | `mesh:write` or `operator:write` |
|
||||||
| service-intentions | `intentions:write` |
|
| service-defaults | `service:write` |
|
||||||
| service-resolver | `service:write` |
|
| service-intentions | `intentions:write` |
|
||||||
| service-router | `service:write` |
|
| service-resolver | `service:write` |
|
||||||
| service-splitter | `service:write` |
|
| service-router | `service:write` |
|
||||||
| tcp-route | `mesh:write` or `operator:write` |
|
| service-splitter | `service:write` |
|
||||||
| terminating-gateway | `mesh:write` or `operator:write` |
|
| tcp-route | `mesh:write` or `operator:write` |
|
||||||
|
| terminating-gateway | `mesh:write` or `operator:write` |
|
||||||
|
|
||||||
The corresponding CLI command is [`consul config delete`](/consul/commands/config/delete).
|
The corresponding CLI command is [`consul config delete`](/consul/commands/config/delete).
|
||||||
|
|
||||||
|
|
|
@ -1301,7 +1301,7 @@ subsystem that provides Consul's service mesh capabilities.
|
||||||
## DNS and Domain Parameters
|
## DNS and Domain Parameters
|
||||||
|
|
||||||
- `dns_config` This object allows a number of sub-keys
|
- `dns_config` This object allows a number of sub-keys
|
||||||
to be set which can tune how DNS queries are serviced. Check the tutorial on [DNS caching](/consul/tutorials/networking/dns-caching) for more detail.
|
to be set which can tune how DNS queries are serviced. Refer to [DNS caching](/consul/docs/services/discovery/dns-cache) for more information.
|
||||||
|
|
||||||
The following sub-keys are available:
|
The following sub-keys are available:
|
||||||
|
|
||||||
|
@ -1842,6 +1842,9 @@ subsystem that provides Consul's service mesh capabilities.
|
||||||
- `disable_hostname` ((#telemetry-disable_hostname))
|
- `disable_hostname` ((#telemetry-disable_hostname))
|
||||||
Set to `true` to stop prepending the machine's hostname to gauge-type metrics. Default is `false`.
|
Set to `true` to stop prepending the machine's hostname to gauge-type metrics. Default is `false`.
|
||||||
|
|
||||||
|
- `disable_per_tenancy_usage_metrics` ((#telemetry-disable_per_tenancy_usage_metrics))
|
||||||
|
Set to `true` to exclude tenancy labels from usage metrics. This significantly decreases CPU utilization in clusters with many admin partitions or namespaces.
|
||||||
|
|
||||||
- `dogstatsd_addr` ((#telemetry-dogstatsd_addr)) This provides the address
|
- `dogstatsd_addr` ((#telemetry-dogstatsd_addr)) This provides the address
|
||||||
of a DogStatsD instance in the format `host:port`. DogStatsD is a protocol-compatible
|
of a DogStatsD instance in the format `host:port`. DogStatsD is a protocol-compatible
|
||||||
flavor of statsd, with the added ability to decorate metrics with tags and event
|
flavor of statsd, with the added ability to decorate metrics with tags and event
|
||||||
|
|
|
@ -126,7 +126,7 @@ We strongly recommend using [stale consistency mode for DNS lookups](/consul/api
|
||||||
|
|
||||||
We also recommend that you do not configure [`dns_config.max_stale` to limit the staleness of DNS responses](/consul/api-docs/features/consistency#limiting-staleness-advanced-usage), as it may result in a prolonged outage if your Consul servers become overloaded. If bounded result consistency is required by a service, consider modifying the service to use consistent service discovery HTTP API queries instead of DNS lookups.
|
We also recommend that you do not configure [`dns_config.max_stale` to limit the staleness of DNS responses](/consul/api-docs/features/consistency#limiting-staleness-advanced-usage), as it may result in a prolonged outage if your Consul servers become overloaded. If bounded result consistency is required by a service, consider modifying the service to use consistent service discovery HTTP API queries instead of DNS lookups.
|
||||||
|
|
||||||
Avoid using [`dns_config.use_cache`](/consul/docs/agent/config/config-files#dns_use_cache) when operating Consul at scale. Because the Consul agent cache allocates memory for each requested route and each allocation can live up to 3 days, severe memory issues may occur. To implement DNS caching, we instead recommend that you [configure TTLs for services and nodes](/consul/tutorials/networking/dns-caching#ttl) to enable the DNS client to cache responses from Consul.
|
Avoid using [`dns_config.use_cache`](/consul/docs/agent/config/config-files#dns_use_cache) when operating Consul at scale. Because the Consul agent cache allocates memory for each requested route and each allocation can live up to 3 days, severe memory issues may occur. To implement DNS caching, we instead recommend that you [configure TTLs for services and nodes](/consul/docs/services/discovery/dns-cache#ttl) to enable the DNS client to cache responses from Consul.
|
||||||
|
|
||||||
#### HTTP API
|
#### HTTP API
|
||||||
|
|
||||||
|
|
|
@ -31,19 +31,19 @@ The following list outlines field hierarchy, language-specific data types, and r
|
||||||
- [`MaxVersion`](#listeners-tls-maxversion): string | no default
|
- [`MaxVersion`](#listeners-tls-maxversion): string | no default
|
||||||
- [`CipherSuites`](#listeners-tls-ciphersuites): list of strings | Envoy default cipher suites
|
- [`CipherSuites`](#listeners-tls-ciphersuites): list of strings | Envoy default cipher suites
|
||||||
- [`Certificates`](#listeners-tls-certificates): list of objects | no default
|
- [`Certificates`](#listeners-tls-certificates): list of objects | no default
|
||||||
- [`Kind`](#listeners-tls-certificates-kind): string | must be `"inline-certificate"`
|
- [`Kind`](#listeners-tls-certificates-kind): string | no default
|
||||||
- [`Name`](#listeners-tls-certificates-name): string | no default
|
- [`Name`](#listeners-tls-certificates-name): string | no default
|
||||||
- [`Namespace`](#listeners-tls-certificates-namespace): string | no default <EnterpriseAlert inline />
|
- [`Namespace`](#listeners-tls-certificates-namespace): string | no default <EnterpriseAlert inline />
|
||||||
- [`Partition`](#listeners-tls-certificates-partition): string | no default <EnterpriseAlert inline />
|
- [`Partition`](#listeners-tls-certificates-partition): string | no default <EnterpriseAlert inline />
|
||||||
- [`default`](#listeners-default): map
|
- [`default`](#listeners-default): map
|
||||||
- [`JWT`](#listeners-default-jwt): map
|
- [`JWT`](#listeners-default-jwt): map
|
||||||
- [`Providers`](#listeners-default-jwt-providers): list
|
- [`Providers`](#listeners-default-jwt-providers): list
|
||||||
- [`Name`](#listeners-default-jwt-providers): string
|
- [`Name`](#listeners-default-jwt-providers): string
|
||||||
- [`VerifyClaims`](#listeners-default-jwt-providers): map
|
- [`VerifyClaims`](#listeners-default-jwt-providers): map
|
||||||
- [`Path`](#listeners-default-jwt-providers): list
|
- [`Path`](#listeners-default-jwt-providers): list
|
||||||
- [`Value`](#listeners-default-jwt-providers): string
|
- [`Value`](#listeners-default-jwt-providers): string
|
||||||
- [`override`](#listeners-override): map
|
- [`override`](#listeners-override): map
|
||||||
- [`JWT`](#listeners-override-jwt): map
|
- [`JWT`](#listeners-override-jwt): map
|
||||||
- [`Providers`](#listeners-override-jwt-providers): list
|
- [`Providers`](#listeners-override-jwt-providers): list
|
||||||
- [`Name`](#listeners-override-jwt-providers): string
|
- [`Name`](#listeners-override-jwt-providers): string
|
||||||
- [`VerifyClaims`](#listeners-override-jwt-providers): map
|
- [`VerifyClaims`](#listeners-override-jwt-providers): map
|
||||||
|
@ -81,8 +81,8 @@ Listeners = [
|
||||||
]
|
]
|
||||||
Certificates = [
|
Certificates = [
|
||||||
{
|
{
|
||||||
Kind = "inline-certificate"
|
Kind = "file-system-certificate"
|
||||||
Name = "<name of inline-certificate>"
|
Name = "<name of file-system-certificate>"
|
||||||
Namespace = "<enterprise: namespace of the certificate>"
|
Namespace = "<enterprise: namespace of the certificate>"
|
||||||
Partition = "<enterprise: partition of the certificate>"
|
Partition = "<enterprise: partition of the certificate>"
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ Listeners = [
|
||||||
Value = "<value of claim>"
|
Value = "<value of claim>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
override = {
|
override = {
|
||||||
JWT = {
|
JWT = {
|
||||||
|
@ -108,7 +108,7 @@ Listeners = [
|
||||||
Value = "<value of claim>"
|
Value = "<value of claim>"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -136,8 +136,8 @@ Listeners = [
|
||||||
],
|
],
|
||||||
"Certificates": [
|
"Certificates": [
|
||||||
{
|
{
|
||||||
"Kind": "inline-certificate",
|
"Kind": "file-system-certificate",
|
||||||
"Name": "<name of inline-certificate>",
|
"Name": "<name of file-system-certificate>",
|
||||||
"Namespace": "<enterprise: namespace of the certificate>",
|
"Namespace": "<enterprise: namespace of the certificate>",
|
||||||
"Partition": "<enterprise: partition of the certificate>"
|
"Partition": "<enterprise: partition of the certificate>"
|
||||||
}
|
}
|
||||||
|
@ -349,7 +349,7 @@ Specifies a list of cipher suites that the listener supports when negotiating co
|
||||||
|
|
||||||
### `Listeners[].TLS.Certificates[]`
|
### `Listeners[].TLS.Certificates[]`
|
||||||
|
|
||||||
The list of references to inline certificates that the listener uses for TLS termination.
|
The list of references to file system or inline certificates that the listener uses for TLS termination.
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
|
@ -362,17 +362,17 @@ The list of references to inline certificates that the listener uses for TLS ter
|
||||||
|
|
||||||
### `Listeners[].TLS.Certificates[].Kind`
|
### `Listeners[].TLS.Certificates[].Kind`
|
||||||
|
|
||||||
The list of references to inline-certificates that the listener uses for TLS termination.
|
The list of references to certificates that the listener uses for TLS termination.
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
- Default: None
|
- Default: None
|
||||||
- This field is required and must be set to `"inline-certificate"`.
|
- This field is required.
|
||||||
- Data type: string
|
- The data type is one of the following string values: `"file-system-certificate"` or `"inline-certificate"`.
|
||||||
|
|
||||||
### `Listeners[].TLS.Certificates[].Name`
|
### `Listeners[].TLS.Certificates[].Name`
|
||||||
|
|
||||||
The list of references to inline certificates that the listener uses for TLS termination.
|
Specifies the name of the file system or inline certificate that the listener uses for TLS termination.
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
|
@ -400,7 +400,7 @@ Specifies the Enterprise [admin partition](/consul/docs/enterprise/admin-partiti
|
||||||
|
|
||||||
### `Listeners[].default`
|
### `Listeners[].default`
|
||||||
|
|
||||||
Specifies a block of default configurations to apply to the gateway listener. All routes attached to the listener inherit the default configurations. You can specify override configurations that have precedence over default configurations in the [`override` block](#listeners-override) as well as in the `JWT` block in the [HTTP route configuration entry](/consul/docs/connect/config-entries/http-route).
|
Specifies a block of default configurations to apply to the gateway listener. All routes attached to the listener inherit the default configurations. You can specify override configurations that have precedence over default configurations in the [`override` block](#listeners-override) as well as in the `JWT` block in the [HTTP route configuration entry](/consul/docs/connect/config-entries/http-route).
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
|
@ -409,7 +409,7 @@ Specifies a block of default configurations to apply to the gateway listener. Al
|
||||||
|
|
||||||
### `Listeners[].default{}.JWT`
|
### `Listeners[].default{}.JWT`
|
||||||
|
|
||||||
Specifies a block of default JWT verification configurations to apply to the gateway listener. Specify configurations that have precedence over the defaults in either the [`override.JWT` block](#listeners-override) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt) in the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways.
|
Specifies a block of default JWT verification configurations to apply to the gateway listener. Specify configurations that have precedence over the defaults in either the [`override.JWT` block](#listeners-override) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt) in the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways.
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
|
@ -418,7 +418,7 @@ Specifies a block of default JWT verification configurations to apply to the gat
|
||||||
|
|
||||||
### `Listeners[].default{}.JWT{}.Providers`
|
### `Listeners[].default{}.JWT{}.Providers`
|
||||||
|
|
||||||
Specifies a list of default JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. Specify configurations that have precedence over the defaults in either the [`override.JWT.Providers` block](#listeners-override-providers) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt-providers) of the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways.
|
Specifies a list of default JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. Specify configurations that have precedence over the defaults in either the [`override.JWT.Providers` block](#listeners-override-providers) or in the [`JWT` block](/consul/docs/connect/config-entries/http-route#rules-filters-jwt-providers) of the HTTP route configuration. Refer to [Use JWTs to verify requests to API gateways](/consul/docs/connect/gateways/api-gateway/secure-traffic/verify-jwts-vms) for order of precedence and other details about using JWT verification in API gateways.
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
|
@ -432,7 +432,7 @@ The following table describes the parameters you can specify in a member of the
|
||||||
| `Name` | Specifies the name of the provider. | String | None |
|
| `Name` | Specifies the name of the provider. | String | None |
|
||||||
| `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None |
|
| `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None |
|
||||||
|
|
||||||
Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration.
|
Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration.
|
||||||
|
|
||||||
### `Listeners[].override`
|
### `Listeners[].override`
|
||||||
|
|
||||||
|
@ -454,7 +454,7 @@ Specifies a block of JWT verification configurations to apply to the gateway lis
|
||||||
|
|
||||||
### `Listeners[].override{}.JWT{}.Providers`
|
### `Listeners[].override{}.JWT{}.Providers`
|
||||||
|
|
||||||
Specifies a list of JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. The override settings have precedence over `Listeners[].defaults{}.JWT{}.Providers` as well as any listener-specific configuration.
|
Specifies a list of JWT provider configurations to apply to the gateway listener. A provider configuration contains the name of the provider and claims. The override settings have precedence over `Listeners[].defaults{}.JWT{}.Providers` as well as any listener-specific configuration.
|
||||||
|
|
||||||
#### Values
|
#### Values
|
||||||
|
|
||||||
|
@ -468,7 +468,7 @@ The following table describes the parameters you can specify in a member of the
|
||||||
| `Name` | Specifies the name of the provider. | String | None |
|
| `Name` | Specifies the name of the provider. | String | None |
|
||||||
| `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None |
|
| `VerifyClaims` | Specifies a list of paths and a value that define the claim that Consul verifies when it receives a request. The `VerifyClaims` map specifies the following settings: <ul><li>`Path`: Specifies a list of one or more registered or custom claims.</li><li>`Value`: Specifies the expected value of the claim.</li></ul> | Map | None |
|
||||||
|
|
||||||
Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration.
|
Refer to [Configure JWT verification settings](#configure-jwt-verification-settings) for an example configuration.
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
||||||
|
@ -530,7 +530,7 @@ Listeners = [
|
||||||
{
|
{
|
||||||
"name": "listener-one",
|
"name": "listener-one",
|
||||||
"port": 9001,
|
"port": 9001,
|
||||||
"protocol": "http",
|
"protocol": "http",
|
||||||
"override": {
|
"override": {
|
||||||
"JWT": {
|
"JWT": {
|
||||||
"Providers": [{
|
"Providers": [{
|
||||||
|
@ -559,4 +559,4 @@ Listeners = [
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
---
|
||||||
|
layout: docs
|
||||||
|
page_title: File System Certificate Configuration Reference
|
||||||
|
description: Learn how to configure a file system certificate bound to an API Gateway on VMs.
|
||||||
|
---
|
||||||
|
|
||||||
|
# File system certificate configuration reference
|
||||||
|
|
||||||
|
This topic provides reference information for the gateway file system certificate
|
||||||
|
configuration entry. For information about certificate configuration for Kubernetes environments, refer to [Gateway Resource Configuration](/consul/docs/connect/gateways/api-gateway/configuration/gateway).
|
||||||
|
|
||||||
|
## Configuration model
|
||||||
|
|
||||||
|
The following list outlines field hierarchy, language-specific data types, and
|
||||||
|
requirements in a `file-system-certificate` configuration entry. Click on a property name
|
||||||
|
to view additional details, including default values.
|
||||||
|
|
||||||
|
- [`Kind`](#kind): string | must be `"file-system-certificate"`
|
||||||
|
- [`Name`](#name): string | no default
|
||||||
|
- [`Namespace`](#namespace): string | no default <EnterpriseAlert inline />
|
||||||
|
- [`Partition`](#partition): string | no default <EnterpriseAlert inline />
|
||||||
|
- [`Meta`](#meta): map | no default
|
||||||
|
- [`Certificate`](#certificate): string | no default
|
||||||
|
- [`PrivateKey`](#privatekey): string | no default
|
||||||
|
|
||||||
|
## Complete configuration
|
||||||
|
|
||||||
|
When every field is defined, a `file-system-certificate` configuration entry has the following form:
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
|
```HCL
|
||||||
|
Kind = "file-system-certificate"
|
||||||
|
Name = "<name of certificate>"
|
||||||
|
|
||||||
|
Meta = {
|
||||||
|
"<any key>" = "<any value>"
|
||||||
|
}
|
||||||
|
|
||||||
|
Certificate = "<filepath to public certificate>"
|
||||||
|
PrivateKey = "<filepath to private key>"
|
||||||
|
```
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"Kind": "file-system-certificate",
|
||||||
|
"Name": "<name of certificate>",
|
||||||
|
"Meta": {
|
||||||
|
"any key": "any value"
|
||||||
|
}
|
||||||
|
"Certificate": "<filepath to public certificate>",
|
||||||
|
"PrivateKey": "<filepath to private key>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
## Specification
|
||||||
|
|
||||||
|
### `Kind`
|
||||||
|
|
||||||
|
Specifies the type of configuration entry to implement.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: none
|
||||||
|
- This field is required.
|
||||||
|
- Data type: string that must equal `"file-system-certificate"`
|
||||||
|
|
||||||
|
### `Name`
|
||||||
|
|
||||||
|
Specifies a name for the configuration entry. The name is metadata that you can
|
||||||
|
use to reference the configuration entry when performing Consul operations, such
|
||||||
|
as applying a configuration entry to a specific cluster.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: none
|
||||||
|
- This field is required.
|
||||||
|
- Data type: string
|
||||||
|
|
||||||
|
### `Namespace` <EnterpriseAlert inline />
|
||||||
|
|
||||||
|
Specifies the Enterprise [namespace](/consul/docs/enterprise/namespaces) to apply to the configuration entry.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: `"default"` in Enterprise
|
||||||
|
- Data type: string
|
||||||
|
|
||||||
|
### `Partition` <EnterpriseAlert inline />
|
||||||
|
|
||||||
|
Specifies the Enterprise [admin partition](/consul/docs/enterprise/admin-partitions) to apply to the configuration entry.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: `"default"` in Enterprise
|
||||||
|
- Data type: string
|
||||||
|
|
||||||
|
### `Meta`
|
||||||
|
|
||||||
|
Specifies an arbitrary set of key-value pairs to associate with the gateway.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: none
|
||||||
|
- Data type: map containing one or more keys and string values.
|
||||||
|
|
||||||
|
### `Certificate`
|
||||||
|
|
||||||
|
Specifies the filepath to a public certificate to use for TLS. This filepath must be accessible to the API gateway proxy at runtime.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: none
|
||||||
|
- This field is required.
|
||||||
|
- Data type: string value of the filepath to a public certificate
|
||||||
|
|
||||||
|
### `PrivateKey`
|
||||||
|
|
||||||
|
Specifies the filepath to a private key to use for TLS. This filepath must be accessible to the API gateway proxy at runtime.
|
||||||
|
|
||||||
|
#### Values
|
||||||
|
|
||||||
|
- Default: none
|
||||||
|
- This field is required.
|
||||||
|
- Data type: string value of the filepath to a private key
|
|
@ -7,7 +7,7 @@ description: >-
|
||||||
|
|
||||||
# Consul API gateway configuration overview
|
# Consul API gateway configuration overview
|
||||||
|
|
||||||
This topic provides an overview of the configuration items you can use to create API gateways, configure listeners, define routes, and apply additional resources that may be necessary to operate Consul API gateways in your environment.
|
This topic provides an overview of the configuration items you can use to create API gateways, configure listeners, define routes, and apply additional resources that may be necessary to operate Consul API gateways in your environment.
|
||||||
|
|
||||||
## Configurations for virtual machines
|
## Configurations for virtual machines
|
||||||
|
|
||||||
|
@ -18,9 +18,10 @@ Apply the following configuration items if your network runs on virtual machines
|
||||||
| [`api-gateway`](/consul/docs/connect/config-entries/api-gateway) | Defines the main infrastructure resource for declaring an API gateway and listeners on the gateway. | [Deploy API gateway listeners on virtual machines](/consul/docs/connect/gateways/api-gateway/deploy/listeners-vms) |
|
| [`api-gateway`](/consul/docs/connect/config-entries/api-gateway) | Defines the main infrastructure resource for declaring an API gateway and listeners on the gateway. | [Deploy API gateway listeners on virtual machines](/consul/docs/connect/gateways/api-gateway/deploy/listeners-vms) |
|
||||||
| [`http-route`](/consul/docs/connect/config-entries/http-route) | Enables HTTP traffic to reach services in the mesh from a listener on the gateway.| <nobr>[Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms)</nobr> |
|
| [`http-route`](/consul/docs/connect/config-entries/http-route) | Enables HTTP traffic to reach services in the mesh from a listener on the gateway.| <nobr>[Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms)</nobr> |
|
||||||
| [`tcp-route`](/consul/docs/connect/config-entries/tcp-route) | Enables TCP traffic to reach services in the mesh from a listener on the gateway.| [Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms) |
|
| [`tcp-route`](/consul/docs/connect/config-entries/tcp-route) | Enables TCP traffic to reach services in the mesh from a listener on the gateway.| [Define routes on virtual machines](/consul/docs/connect/gateways/api-gateway/define-routes/routes-vms) |
|
||||||
| <nobr>[`inline-certificate`](/consul/docs/connect/config-entries/inline-certificate)</nobr> | Provides gateway artificats with a CA certificate so that requests between the user and the gateway endpoint are encrypted. | [Encrypt API gateway traffic on virtual machines](/consul/docs/connect/gateways/api-gateway/secure-traffic/encrypt-vms) |
|
| <nobr>[`file-system-certificate`](/consul/docs/connect/config-entries/file-system-certificate)</nobr> | Provides gateway with a CA certificate so that requests between the user and the gateway endpoint are encrypted. | [Encrypt API gateway traffic on virtual machines](/consul/docs/connect/gateways/api-gateway/secure-traffic/encrypt-vms) |
|
||||||
|
| <nobr>[`inline-certificate`](/consul/docs/connect/config-entries/inline-certificate)</nobr> | Provides gateway with a CA certificate so that requests between the user and the gateway endpoint are encrypted. | [Encrypt API gateway traffic on virtual machines](/consul/docs/connect/gateways/api-gateway/secure-traffic/encrypt-vms) |
|
||||||
| [`service-intentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh |
|
| [`service-intentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh |
|
||||||
|
|
||||||
## Configurations for Kubernetes
|
## Configurations for Kubernetes
|
||||||
|
|
||||||
Apply the following configuration items if your network runs on Kubernetes:
|
Apply the following configuration items if your network runs on Kubernetes:
|
||||||
|
@ -32,7 +33,7 @@ Apply the following configuration items if your network runs on Kubernetes:
|
||||||
| [`GatewayClassConfig`](/consul/docs/connect/gateways/api-gateway/configuration/gatewayclassconfig) | Describes additional gateway-related configuration parameters for the `GatewayClass` resource. | [Deploy listeners on Kubernetes](/consul/docs/connect/gateways/api-gateway/deploy/listeners-k8s) |
|
| [`GatewayClassConfig`](/consul/docs/connect/gateways/api-gateway/configuration/gatewayclassconfig) | Describes additional gateway-related configuration parameters for the `GatewayClass` resource. | [Deploy listeners on Kubernetes](/consul/docs/connect/gateways/api-gateway/deploy/listeners-k8s) |
|
||||||
| [`Routes`](/consul/docs/connect/gateways/api-gateway/configuration/routes) | Specifies paths from the gateway listener to backend services. | <nobr>[Define routes on Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/routes-k8s)</nobr><p>[Reroute traffic in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/reroute-http-requests)</p><p>[Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services)</p> |
|
| [`Routes`](/consul/docs/connect/gateways/api-gateway/configuration/routes) | Specifies paths from the gateway listener to backend services. | <nobr>[Define routes on Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/routes-k8s)</nobr><p>[Reroute traffic in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/reroute-http-requests)</p><p>[Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services)</p> |
|
||||||
| [`MeshServices`](/consul/docs/connect/gateways/api-gateway/configuration/meshservices) | Enables routes to reference services in Consul. | [Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services) |
|
| [`MeshServices`](/consul/docs/connect/gateways/api-gateway/configuration/meshservices) | Enables routes to reference services in Consul. | [Route traffic to peered services in Kubernetes](/consul/docs/connect/gateways/api-gateway/define-routes/route-to-peered-services) |
|
||||||
| [`ServiceIntentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh |
|
| [`ServiceIntentions`](/consul/docs/connect/config-entries/service-intentions) | Specifies traffic communication rules between services in the mesh. Intentions also enforce rules for service-to-service traffic routed through a Consul API gateway. | General configuration for securing a service mesh |
|
||||||
|
|
||||||
<!-- Reuse later for a topic about creating custom api gateway classes
|
<!-- Reuse later for a topic about creating custom api gateway classes
|
||||||
You can create a basic Gateway object using the default [`gatewayClassName`](/consul/docs/connect/gateways/api-gateway/configuration/gateway#gatewayclassname) (`consul`). If you want to create custom Gateways suitable for your environment, complete the following steps:
|
You can create a basic Gateway object using the default [`gatewayClassName`](/consul/docs/connect/gateways/api-gateway/configuration/gateway#gatewayclassname) (`consul`). If you want to create custom Gateways suitable for your environment, complete the following steps:
|
||||||
|
|
|
@ -300,10 +300,10 @@ The following example creates a route named `example-route` in namespace `gatewa
|
||||||
- group: gateway.networking.k8s.io
|
- group: gateway.networking.k8s.io
|
||||||
kind: HTTPRoute
|
kind: HTTPRoute
|
||||||
namespace: gateway-namespace
|
namespace: gateway-namespace
|
||||||
to:
|
to:
|
||||||
- group: ""
|
- group: ""
|
||||||
kind: Service
|
kind: Service
|
||||||
name: echo
|
name: echo
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeBlockConfig>
|
</CodeBlockConfig>
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
---
|
||||||
|
layout: docs
|
||||||
|
page_title: Store and access key/value data
|
||||||
|
description: >-
|
||||||
|
Consul includes a key/value store for that you can use to dynamically configure apps. Learn how to add and manage data in the Consul KV store using the Consul
|
||||||
|
command-line interface.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Store and access key/value data
|
||||||
|
|
||||||
|
This page describes the processes for interacting with Consul's KV store. You can interact with the KV store using the [`consul kv` CLI command](/consul/commands/kv) or the [`/kv` endpoint](/consul/api-docs/kv).
|
||||||
|
|
||||||
|
## Add data to KV store
|
||||||
|
|
||||||
|
To insert values into the KV store or update an existing value, use the `consul kv put` command. The first entry after the command is the key and the second entry is the value.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv put redis/config/minconns 1
|
||||||
|
Success! Data written to: redis/config/minconns
|
||||||
|
```
|
||||||
|
|
||||||
|
In the following example, the key is `redis/config/maxconns` and the value is set to `25`.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv put redis/config/maxconns 25
|
||||||
|
Success! Data written to: redis/config/maxconns
|
||||||
|
```
|
||||||
|
|
||||||
|
In the following example, the command includes a `flags` value of 42. Keys support setting a 64-bit integer flag value that is not used internally by Consul but can be used by clients to add metadata to a KV pair.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv put -flags=42 redis/config/users/admin zaphod
|
||||||
|
Success! Data written to: redis/config/users/admin
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query data from KV store
|
||||||
|
|
||||||
|
To query for the value of one of the keys in the KV store, use the `consul kv get` command.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv get redis/config/minconns
|
||||||
|
1
|
||||||
|
```
|
||||||
|
|
||||||
|
To retrieve metadata you included as `flags`, using the `-detailed` command line flag.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv get -detailed redis/config/users/admin
|
||||||
|
```
|
||||||
|
|
||||||
|
```plaintext hideClipboard
|
||||||
|
CreateIndex 14
|
||||||
|
Flags 42
|
||||||
|
Key redis/config/users/admin
|
||||||
|
LockIndex 0
|
||||||
|
ModifyIndex 14
|
||||||
|
Session -
|
||||||
|
Value zaphod
|
||||||
|
```
|
||||||
|
|
||||||
|
To list all the keys in the store, use the `recurse` option. Results return in lexicographical order.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv get -recurse
|
||||||
|
```
|
||||||
|
|
||||||
|
```plaintext hideClipboard
|
||||||
|
redis/config/maxconns:25
|
||||||
|
redis/config/minconns:1
|
||||||
|
redis/config/users/admin:zaphod
|
||||||
|
```
|
||||||
|
|
||||||
|
## Delete data
|
||||||
|
|
||||||
|
To delete the value of one of the keys in the KV store, use the `consul kv delete` command.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv delete redis/config/minconns
|
||||||
|
Success! Deleted key: redis/config/minconns
|
||||||
|
```
|
||||||
|
|
||||||
|
Although the keys in the KV store are stored in a flat structure, you can manipulate keys that share a prefix as a group, as if they were in folders or subfolders, by using the `-recurse` flag.
|
||||||
|
|
||||||
|
The following example deletes all the keys with the `redis` prefix using the `-recurse` option.
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul kv delete -recurse redis
|
||||||
|
Success! Deleted keys with prefix: redis
|
||||||
|
```
|
|
@ -110,9 +110,9 @@ Here are some general recommendations:
|
||||||
- For DNS-heavy workloads, configuring all Consul agents in a cluster with the
|
- For DNS-heavy workloads, configuring all Consul agents in a cluster with the
|
||||||
[`allow_stale`](/consul/docs/agent/config/config-files#allow_stale) configuration option will allow reads to
|
[`allow_stale`](/consul/docs/agent/config/config-files#allow_stale) configuration option will allow reads to
|
||||||
scale across all Consul servers, not just the leader. Consul 0.7 and later enables stale reads
|
scale across all Consul servers, not just the leader. Consul 0.7 and later enables stale reads
|
||||||
for DNS by default. See [Stale Reads](/consul/tutorials/networking/dns-caching#stale-reads) in the
|
for DNS by default. See [Stale Reads](/consul/docs/services/discovery/dns-cache#stale-reads) in the
|
||||||
[DNS Caching](/consul/tutorials/networking/dns-caching) guide for more details. It's also good to set
|
[DNS Caching](/consul/docs/services/discovery/dns-cache) guide for more details. It's also good to set
|
||||||
reasonable, non-zero [DNS TTL values](/consul/tutorials/networking/dns-caching#ttl-values) if your clients will
|
reasonable, non-zero [DNS TTL values](/consul/docs/services/discovery/dns-cache#ttl-values) if your clients will
|
||||||
respect them.
|
respect them.
|
||||||
|
|
||||||
- In other applications that perform high volumes of reads against Consul, consider using the
|
- In other applications that perform high volumes of reads against Consul, consider using the
|
||||||
|
|
|
@ -400,5 +400,5 @@ The task object is used by the Task APIs as part of a request or response. It re
|
||||||
| `providers` | list[string] | Optional | The list of provider names that the task's module uses. | none |
|
| `providers` | list[string] | Optional | The list of provider names that the task's module uses. | none |
|
||||||
| `variables` | map[string] | Optional | The map of variables that are provided to the task's module. | none |
|
| `variables` | map[string] | Optional | The map of variables that are provided to the task's module. | none |
|
||||||
| `version` | string | Optional | The version of the configured module that the task uses. | The latest version. |
|
| `version` | string | Optional | The version of the configured module that the task uses. | The latest version. |
|
||||||
| `terraform_version` | string | Optional | <EnterpriseAlert inline /> **Deprecated in CTS 0.6.0 and will be removed in 0.8.0. Review `terraform_version` in `terraform_cloud_workspace` instead.** The version of Terraform to use for the Terraform Cloud workspace associated with the task. This is only available when used with the [Terraform Cloud driver](/consul/docs/nia/configuration#terraform-cloud-driver). | The latest compatible version supported by the organization. |
|
| `terraform_version` | string | Optional | <EnterpriseAlert inline /> **Deprecated in CTS 0.6.0 and will be removed in 0.8.0. Review `terraform_version` in `terraform_cloud_workspace` instead.** The version of Terraform to use for the HCP Terraform workspace associated with the task. This is only available when used with the [HCP Terraform driver](/consul/docs/nia/configuration#hcp-terraform-driver). | The latest compatible version supported by the organization. |
|
||||||
| `terraform_cloud_workspace` | object | Optional | <EnterpriseAlert inline /> The [configurable attributes of the Terraform Cloud workspace](/consul/docs/nia/configuration#terraform_cloud_workspace) associated with the task. This option is only available when used with the [Terraform Cloud driver](/consul/docs/nia/configuration#terraform-cloud-driver).| none |
|
| `terraform_cloud_workspace` | object | Optional | <EnterpriseAlert inline /> The [configurable attributes of the HCP Terraform workspace](/consul/docs/nia/configuration#terraform_cloud_workspace) associated with the task. This option is only available when used with the [HCP Terraform driver](/consul/docs/nia/configuration#hcp-terraform-driver).| none |
|
||||||
|
|
|
@ -41,7 +41,7 @@ A driver encapsulates the resources required to communicate the updates to the
|
||||||
network infrastructure. The following [drivers](/consul/docs/nia/network-drivers#terraform) are supported:
|
network infrastructure. The following [drivers](/consul/docs/nia/network-drivers#terraform) are supported:
|
||||||
|
|
||||||
- Terraform driver
|
- Terraform driver
|
||||||
- Terraform Cloud driver<EnterpriseAlert inline />
|
- HCP Terraform driver<EnterpriseAlert inline />
|
||||||
|
|
||||||
Each driver includes a set of providers that [enables support](/consul/docs/nia/terraform-modules) for a wide variety of infrastructure applications.
|
Each driver includes a set of providers that [enables support](/consul/docs/nia/terraform-modules) for a wide variety of infrastructure applications.
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ Below are CTS versions with supported Consul versions. The latest CTS binary sup
|
||||||
|
|
||||||
CTS integration with Terraform is supported for the following:
|
CTS integration with Terraform is supported for the following:
|
||||||
|
|
||||||
| CTS Version | Terraform CLI Version | Terraform Cloud Version | Terraform Enterprise Version |
|
| CTS Version | Terraform CLI Version | HCP Terraform Version | Terraform Enterprise Version |
|
||||||
| :------------------ | :-------------------- | :---------------------- | :--------------------------- |
|
| :------------------ | :-------------------- | :---------------------- | :--------------------------- |
|
||||||
| CTS Enterprise 0.7 | 0.13-1.2 | Latest | v202010-2 - Latest |
|
| CTS Enterprise 0.7 | 0.13-1.2 | Latest | v202010-2 - Latest |
|
||||||
| CTS Enterprise 0.4+ | 0.13 - 1.1 | Latest | v202010-2 - Latest |
|
| CTS Enterprise 0.4+ | 0.13 - 1.1 | Latest | v202010-2 - Latest |
|
||||||
|
|
|
@ -302,7 +302,7 @@ task {
|
||||||
- `module` - (string: required) Module is the location the driver uses to discover the Terraform module used for automation. The module's source can be local or remote on the [Terraform Registry](https://registry.terraform.io/) or private module registry. Read more on [Terraform module source and other supported types here](/terraform/language/modules/sources).
|
- `module` - (string: required) Module is the location the driver uses to discover the Terraform module used for automation. The module's source can be local or remote on the [Terraform Registry](https://registry.terraform.io/) or private module registry. Read more on [Terraform module source and other supported types here](/terraform/language/modules/sources).
|
||||||
|
|
||||||
- To use a private module with the [`terraform` driver](#terraform-driver), run the command [`terraform login [hostname]`](/terraform/tutorials/cloud/cloud-login?utm_source=docs) to authenticate the local Terraform CLI prior to starting CTS.
|
- To use a private module with the [`terraform` driver](#terraform-driver), run the command [`terraform login [hostname]`](/terraform/tutorials/cloud/cloud-login?utm_source=docs) to authenticate the local Terraform CLI prior to starting CTS.
|
||||||
- To use a private module with the [`terraform_cloud` driver](#terraform-cloud-driver), no extra steps are needed.
|
- To use a private module with the [`terraform_cloud` driver](#hcp-terraform-driver), no extra steps are needed.
|
||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
// local module example: "./terraform-cts-hello"
|
// local module example: "./terraform-cts-hello"
|
||||||
|
@ -339,12 +339,12 @@ task {
|
||||||
- `condition` - (obj: required) The requirement that, when met, triggers CTS to execute the task. Only one `condition` may be configured per task. CTS supports different types of conditions, which each have their own configuration options. See [Task Condition](#task-condition) configuration for full details on configuration options for each condition type.
|
- `condition` - (obj: required) The requirement that, when met, triggers CTS to execute the task. Only one `condition` may be configured per task. CTS supports different types of conditions, which each have their own configuration options. See [Task Condition](#task-condition) configuration for full details on configuration options for each condition type.
|
||||||
- `source_input` - (obj) **Deprecated in CTS 0.5.0 and will be removed in 0.8.0. See the `module_input` block instead.**
|
- `source_input` - (obj) **Deprecated in CTS 0.5.0 and will be removed in 0.8.0. See the `module_input` block instead.**
|
||||||
- `module_input` - (obj) Specifies a Consul object containing values or metadata to be provided to the Terraform Module. The `module_input` block defines any extra module inputs needed for task execution. This is in addition to any module input provided by the `condition` block or `services` field (deprecated). Multiple `module_input` blocks can be configured per task. [Task Module Input](#task-module-input) configuration for full details on usage and restrictions.
|
- `module_input` - (obj) Specifies a Consul object containing values or metadata to be provided to the Terraform Module. The `module_input` block defines any extra module inputs needed for task execution. This is in addition to any module input provided by the `condition` block or `services` field (deprecated). Multiple `module_input` blocks can be configured per task. [Task Module Input](#task-module-input) configuration for full details on usage and restrictions.
|
||||||
- `terraform_version` - (string) <EnterpriseAlert inline /> **Deprecated in CTS 0.6.0 and will be removed in 0.8.0. Review `terraform_cloud_workspace.terraform_version` instead.** The version of Terraform to use for the Terraform Cloud workspace associated with the task. Defaults to the latest compatible version supported by the organization. This option is only available when used with the [Terraform Cloud driver](#terraform-cloud-driver); otherwise, set the version within the [Terraform driver](#terraform-driver).
|
- `terraform_version` - (string) <EnterpriseAlert inline /> **Deprecated in CTS 0.6.0 and will be removed in 0.8.0. Review `terraform_cloud_workspace.terraform_version` instead.** The version of Terraform to use for the HCP Terraform workspace associated with the task. Defaults to the latest compatible version supported by the organization. This option is only available when used with the [HCP Terraform driver](#hcp-terraform-driver); otherwise, set the version within the [Terraform driver](#terraform-driver).
|
||||||
- `terraform_cloud_workspace` - (obj) <EnterpriseAlert inline /> Configures attributes of the Terraform Cloud workspace associated with the task. This option is only available when used with the [Terraform Cloud driver](#terraform-cloud-driver). For global configurations of all workspaces, review [`driver.workspaces`](#workspaces).
|
- `terraform_cloud_workspace` - (obj) <EnterpriseAlert inline /> Configures attributes of the HCP Terraform workspace associated with the task. This option is only available when used with the [HCP Terraform driver](#hcp-terraform-driver). For global configurations of all workspaces, review [`driver.workspaces`](#workspaces).
|
||||||
- `execution_mode` - (string: "remote") The execution mode that determines whether to use Terraform Cloud as the Terraform execution platform. Only supports "remote" or "agent".
|
- `execution_mode` - (string: "remote") The execution mode that determines whether to use HCP Terraform as the Terraform execution platform. Only supports "remote" or "agent".
|
||||||
- `agent_pool_id` - (string) Only supported if `execution_mode` is set to "agent". The ID of the agent pool that should run the Terraform workloads. Either `agent_pool_id` or `agent_pool_name` are required if `execution_mode` is set to "agent". `agent_pool_id` takes precedence over `agent_pool_name` if both are provided.
|
- `agent_pool_id` - (string) Only supported if `execution_mode` is set to "agent". The ID of the agent pool that should run the Terraform workloads. Either `agent_pool_id` or `agent_pool_name` are required if `execution_mode` is set to "agent". `agent_pool_id` takes precedence over `agent_pool_name` if both are provided.
|
||||||
- `agent_pool_name` - (string) Only supported if `execution_mode` is set to "agent". The name of the agent pool that should run the Terraform workloads. Only supported if `execution_mode` is set to "agent". Either `agent_pool_id` or `agent_pool_name` are required. `agent_pool_id` takes precedence over `agent_pool_name` if both are provided.
|
- `agent_pool_name` - (string) Only supported if `execution_mode` is set to "agent". The name of the agent pool that should run the Terraform workloads. Only supported if `execution_mode` is set to "agent". Either `agent_pool_id` or `agent_pool_name` are required. `agent_pool_id` takes precedence over `agent_pool_name` if both are provided.
|
||||||
- `terraform_version` - (string) The version of Terraform to use for the Terraform Cloud workspace associated with the task. Defaults to the latest compatible version supported by the organization.
|
- `terraform_version` - (string) The version of Terraform to use for the HCP Terraform workspace associated with the task. Defaults to the latest compatible version supported by the organization.
|
||||||
|
|
||||||
### Task Condition
|
### Task Condition
|
||||||
|
|
||||||
|
@ -653,7 +653,7 @@ driver "terraform" {
|
||||||
- `backend` - (obj) The backend stores [Terraform state files](/terraform/language/state) for each task. This option is similar to the [Terraform backend configuration](/terraform/language/settings/backends/configuration). CTS supports Terraform backends used as a state store.
|
- `backend` - (obj) The backend stores [Terraform state files](/terraform/language/state) for each task. This option is similar to the [Terraform backend configuration](/terraform/language/settings/backends/configuration). CTS supports Terraform backends used as a state store.
|
||||||
- Supported backend options: [azurerm](/terraform/language/settings/backends/azurerm), [consul](/terraform/language/settings/backends/consul), [cos](/terraform/language/settings/backends/cos), [gcs](/terraform/language/settings/backends/gcs), [kubernetes](/terraform/language/settings/backends/kubernetes), [local](/terraform/language/settings/backends/local), [manta](/terraform/language/v1.2.x/settings/backends/manta), [pg](/terraform/language/settings/backends/pg) (Terraform v0.14+), [s3](/terraform/language/settings/backends/s3). Visit the Terraform documentation links for details on backend configuration options.
|
- Supported backend options: [azurerm](/terraform/language/settings/backends/azurerm), [consul](/terraform/language/settings/backends/consul), [cos](/terraform/language/settings/backends/cos), [gcs](/terraform/language/settings/backends/gcs), [kubernetes](/terraform/language/settings/backends/kubernetes), [local](/terraform/language/settings/backends/local), [manta](/terraform/language/v1.2.x/settings/backends/manta), [pg](/terraform/language/settings/backends/pg) (Terraform v0.14+), [s3](/terraform/language/settings/backends/s3). Visit the Terraform documentation links for details on backend configuration options.
|
||||||
- If omitted, CTS will generate default values and use configurations from the [`consul` block](#consul) to configure [Consul as the backend](/terraform/language/settings/backends/consul), which stores Terraform statefiles in the Consul KV. The [ACL token provided for Consul authentication](#consul) is used to read and write to the KV store and requires [Consul KV privileges](/consul/tutorials/network-infrastructure-automation/consul-terraform-sync-secure?utm_source=docs#configure-acl-privileges-for-consul-terraform-sync). The Consul KV path is the base path to store state files for tasks. The full path of each state file will have the task identifier appended to the end of the path, e.g. `consul-terraform-sync/terraform-env:task-name`.
|
- If omitted, CTS will generate default values and use configurations from the [`consul` block](#consul) to configure [Consul as the backend](/terraform/language/settings/backends/consul), which stores Terraform statefiles in the Consul KV. The [ACL token provided for Consul authentication](#consul) is used to read and write to the KV store and requires [Consul KV privileges](/consul/tutorials/network-infrastructure-automation/consul-terraform-sync-secure?utm_source=docs#configure-acl-privileges-for-consul-terraform-sync). The Consul KV path is the base path to store state files for tasks. The full path of each state file will have the task identifier appended to the end of the path, e.g. `consul-terraform-sync/terraform-env:task-name`.
|
||||||
- The remote enhanced backend is not supported with the Terraform driver to run operations in Terraform Cloud. Use the [Terraform Cloud driver](#terraform-cloud-driver) to integrate CTS with Terraform Cloud for remote workspaces and remote operations.
|
- The remote enhanced backend is not supported with the Terraform driver to run operations in HCP Terraform. Use the [HCP Terraform driver](#hcp-terraform-driver) to integrate CTS with HCP Terraform for remote workspaces and remote operations.
|
||||||
- The `local` backend type is not supported with CTS instances configured for high availability. If high availability is configured and the Terraform backend type is `local`, CTS logs an error and exits.
|
- The `local` backend type is not supported with CTS instances configured for high availability. If high availability is configured and the Terraform backend type is `local`, CTS logs an error and exits.
|
||||||
- `log` - (bool) Enable all Terraform output (stderr and stdout) to be included in the CTS log. This is useful for debugging and development purposes. It may be difficult to work with log aggregators that expect uniform log format.
|
- `log` - (bool) Enable all Terraform output (stderr and stdout) to be included in the CTS log. This is useful for debugging and development purposes. It may be difficult to work with log aggregators that expect uniform log format.
|
||||||
- `path` - (string) The file path to install Terraform or discover an existing Terraform binary. If omitted, Terraform will be installed in the same directory as the CTS daemon. To resolve an incompatible Terraform version or to change versions will require removing the existing binary or change to a different path.
|
- `path` - (string) The file path to install Terraform or discover an existing Terraform binary. If omitted, Terraform will be installed in the same directory as the CTS daemon. To resolve an incompatible Terraform version or to change versions will require removing the existing binary or change to a different path.
|
||||||
|
@ -661,7 +661,7 @@ driver "terraform" {
|
||||||
- `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](/terraform/language/providers/requirements#requiring-providers) field to specify the source and version for each provider. CTS will process these requirements when preparing each task that uses the provider.
|
- `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](/terraform/language/providers/requirements#requiring-providers) field to specify the source and version for each provider. CTS will process these requirements when preparing each task that uses the provider.
|
||||||
- `version` - (string) The Terraform version to install and run in automation for task execution. If omitted, the driver will install the latest [compatible release of Terraform](/consul/docs/nia/compatibility#terraform). To change versions, remove the existing binary or change the path to install the desired version. Verify that the desired Terraform version is compatible across all Terraform modules used for CTS automation.
|
- `version` - (string) The Terraform version to install and run in automation for task execution. If omitted, the driver will install the latest [compatible release of Terraform](/consul/docs/nia/compatibility#terraform). To change versions, remove the existing binary or change the path to install the desired version. Verify that the desired Terraform version is compatible across all Terraform modules used for CTS automation.
|
||||||
|
|
||||||
## Terraform Cloud Driver
|
## HCP Terraform Driver
|
||||||
|
|
||||||
<EnterpriseAlert>
|
<EnterpriseAlert>
|
||||||
This feature requires{' '}
|
This feature requires{' '}
|
||||||
|
@ -671,9 +671,9 @@ driver "terraform" {
|
||||||
which is available with <strong>Consul Enterprise</strong>.
|
which is available with <strong>Consul Enterprise</strong>.
|
||||||
</EnterpriseAlert>
|
</EnterpriseAlert>
|
||||||
|
|
||||||
The Terraform Cloud driver enables CTS Enterprise to integrate with **Terraform Cloud**, including both the [self-hosted distribution](https://www.hashicorp.com/products/terraform/editions/enterprise) and the [managed service](https://www.hashicorp.com/products/terraform/editions/cloud). With this driver, CTS automates Terraform runs and remote operations for workspaces.
|
The HCP Terraform driver enables CTS Enterprise to integrate with HCP Terraform, including both the [self-hosted distribution](https://www.hashicorp.com/products/terraform/editions/enterprise) and the [managed service](https://www.hashicorp.com/products/terraform/editions/cloud). With this driver, CTS automates Terraform runs and remote operations for workspaces.
|
||||||
|
|
||||||
An overview of features enabled with Terraform Cloud can be viewed within the [Network Drivers](/consul/docs/nia/network-drivers) documentation.
|
An overview of features enabled with HCP Terraform can be viewed within the [Network Drivers](/consul/docs/nia/network-drivers) documentation.
|
||||||
|
|
||||||
Only one network driver can be configured per deployment of CTS.
|
Only one network driver can be configured per deployment of CTS.
|
||||||
|
|
||||||
|
@ -701,16 +701,16 @@ driver "terraform-cloud" {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `hostname` - (string) The Terraform Cloud hostname to connect to. Can be overridden with the `TFC_HOSTNAME` environment variable.
|
- `hostname` - (string) The HCP Terraform hostname to connect to. Can be overridden with the `TFC_HOSTNAME` environment variable.
|
||||||
- `organization` - (string) The Terraform Cloud organization that hosts the managed workspaces by CTS. Can be overridden with the `TFC_ORGANIZATION` environment variable.
|
- `organization` - (string) The HCP Terraform organization that hosts the managed workspaces by CTS. Can be overridden with the `TFC_ORGANIZATION` environment variable.
|
||||||
- `token` - (string) Required [Team API token](/terraform/cloud-docs/users-teams-organizations/api-tokens#team-api-tokens) used for authentication with Terraform Cloud and workspace management. Only workspace permissions are needed for CTS. The token can also be provided using the `TFC_TOKEN` environment variable.
|
- `token` - (string) Required [Team API token](/terraform/cloud-docs/users-teams-organizations/api-tokens#team-api-tokens) used for authentication with HCP Terraform and workspace management. Only workspace permissions are needed for CTS. The token can also be provided using the `TFC_TOKEN` environment variable.
|
||||||
- We recommend creating a dedicated team and team API token to isolate automation by CTS from other Terraform Cloud operations.
|
- We recommend creating a dedicated team and team API token to isolate automation by CTS from other HCP Terraform operations.
|
||||||
- `workspace_prefix` - (string) **Deprecated in CTS 0.5.0**, use the [`workspaces.prefix`](#prefix) option instead. Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be `<workspace_prefix value>-<task name>`, with the character '-' between the workspace prefix and task name. For example, if you configure the prefix as "cts", then a task with the name "task-firewall" will have the workspace name "cts-task-firewall".
|
- `workspace_prefix` - (string) **Deprecated in CTS 0.5.0**, use the [`workspaces.prefix`](#prefix) option instead. Specifies a prefix to prepend to the automatically-generated workspace names used for automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be `<workspace_prefix value>-<task name>`, with the character '-' between the workspace prefix and task name. For example, if you configure the prefix as "cts", then a task with the name "task-firewall" will have the workspace name "cts-task-firewall".
|
||||||
- `workspaces` - Configure CTS management of Terraform Cloud workspaces.
|
- `workspaces` - Configure CTS management of HCP Terraform workspaces.
|
||||||
- `prefix` - (string) Specifies a prefix to prepend to the workspace names used for CTS task automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be `<prefix><task name>`. For example, if you configure the prefix as "cts_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall".
|
- `prefix` - (string) Specifies a prefix to prepend to the workspace names used for CTS task automation. This prefix will be used by all tasks that use this driver. By default, when no prefix is configured, the workspace name will be the task name. When a prefix is configured, the workspace name will be `<prefix><task name>`. For example, if you configure the prefix as "cts_", then a task with the name "task_firewall" will have the workspace name "cts_task_firewall".
|
||||||
- `tags` - (list[string]) Tags for CTS to add to all automated workspaces when the workspace is first created or discovered. Tags are added to discovered workspaces only if the workspace meets [automation requirements](/consul/docs/nia/network-drivers/terraform-cloud#remote-workspaces) and satisfies the allowlist and denylist tag options. This option will not affect existing tags. Tags that were manually removed during runtime will be re-tagged when CTS restarts. Compatible with Terraform Cloud and Terraform Enterprise v202108-1+
|
- `tags` - (list[string]) Tags for CTS to add to all automated workspaces when the workspace is first created or discovered. Tags are added to discovered workspaces only if the workspace meets [automation requirements](/consul/docs/nia/network-drivers/hcp-terraform#remote-workspaces) and satisfies the allowlist and denylist tag options. This option will not affect existing tags. Tags that were manually removed during runtime will be re-tagged when CTS restarts. Compatible with HCP Terraform and Terraform Enterprise v202108-1+
|
||||||
- `tags_allowlist` - (list[string]) Tag requirement to use as a provision check for CTS automation of workspaces. When configured, Terraform Cloud workspaces must have at least one tag from the allow list for CTS to automate the workspace and runs. Compatible with Terraform Cloud and Terraform Enterprise v202108-1+.
|
- `tags_allowlist` - (list[string]) Tag requirement to use as a provision check for CTS automation of workspaces. When configured, HCP Terraform workspaces must have at least one tag from the allow list for CTS to automate the workspace and runs. Compatible with HCP Terraform and Terraform Enterprise v202108-1+.
|
||||||
- `tags_denylist` - (list[string]) Tag restriction to use as a provision check for CTS automation of workspaces. When configured, Terraform Cloud workspaces must not have any tag from the deny list for CTS to automate the workspace and runs. Denied tags have higher priority than tags set in the `tags_allowlist` option. Compatible with Terraform Cloud and Terraform Enterprise v202108-1+.
|
- `tags_denylist` - (list[string]) Tag restriction to use as a provision check for CTS automation of workspaces. When configured, HCP Terraform workspaces must not have any tag from the deny list for CTS to automate the workspace and runs. Denied tags have higher priority than tags set in the `tags_allowlist` option. Compatible with HCP Terraform and Terraform Enterprise v202108-1+.
|
||||||
- `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](/terraform/language/providers/requirements#requiring-providers) field to specify the source and version for each provider. CTS will process these requirements when preparing each task that uses the provider.
|
- `required_providers` - (obj: required) Declare each Terraform provider used across all tasks. This can be configured the same as how you would configure [Terraform `terraform.required_providers`](/terraform/language/providers/requirements#requiring-providers) field to specify the source and version for each provider. CTS will process these requirements when preparing each task that uses the provider.
|
||||||
- `tls` - Configure TLS to allow HTTPS connections to [Terraform Enterprise](/terraform/enterprise/install/interactive/installer#tls-key-amp-cert).
|
- `tls` - Configure TLS to allow HTTPS connections to [Terraform Enterprise](/terraform/enterprise/install/interactive/installer#tls-key-amp-cert).
|
||||||
- `enabled` - (bool) Enable TLS. Providing a value for any of the TLS options will enable this parameter implicitly.
|
- `enabled` - (bool) Enable TLS. Providing a value for any of the TLS options will enable this parameter implicitly.
|
||||||
|
@ -727,7 +727,7 @@ driver "terraform-cloud" {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
CTS generates local artifacts to prepare configuration versions used for workspace runs. The location of the files created can be set with the [`working_dir`](/consul/docs/nia/configuration#working_dir) option or configured per task. When a task is configured with a local module and is run with the Terraform Cloud driver, the local module is copied and uploaded as a part of the configuration version.
|
CTS generates local artifacts to prepare configuration versions used for workspace runs. The location of the files created can be set with the [`working_dir`](/consul/docs/nia/configuration#working_dir) option or configured per task. When a task is configured with a local module and is run with the HCP Terraform driver, the local module is copied and uploaded as a part of the configuration version.
|
||||||
|
|
||||||
The version of Terraform to use for each workspace can also be set within the [task](#task) configuration.
|
The version of Terraform to use for each workspace can also be set within the [task](#task) configuration.
|
||||||
|
|
||||||
|
|
|
@ -9,22 +9,22 @@ description: >-
|
||||||
|
|
||||||
Consul-Terraform-Sync (CTS) Enterprise is available with [Consul Enterprise](https://www.hashicorp.com/products/consul) and requires a Consul [license](/consul/docs/nia/enterprise/license) to be applied.
|
Consul-Terraform-Sync (CTS) Enterprise is available with [Consul Enterprise](https://www.hashicorp.com/products/consul) and requires a Consul [license](/consul/docs/nia/enterprise/license) to be applied.
|
||||||
|
|
||||||
Enterprise features of CTS address organization complexities of collaboration, operations, scale, and governance. CTS Enterprise supports an official integration with [Terraform Cloud](https://cloud.hashicorp.com/products/terraform) and [Terraform Enterprise](/terraform/enterprise), the self-hosted distribution, to extend insight into dynamic updates of your network infrastructure.
|
Enterprise features of CTS address organization complexities of collaboration, operations, scale, and governance. CTS Enterprise supports an official integration with [HCP Terraform](https://cloud.hashicorp.com/products/terraform) and [Terraform Enterprise](/terraform/enterprise), the self-hosted distribution, to extend insight into dynamic updates of your network infrastructure.
|
||||||
|
|
||||||
| Features | Community Edition | Enterprise |
|
| Features | Community Edition | Enterprise |
|
||||||
|----------|-------------|------------|
|
|----------|-------------|------------|
|
||||||
| Consul Namespace | Default namespace only | Filter task triggers by any namespace |
|
| Consul Namespace | Default namespace only | Filter task triggers by any namespace |
|
||||||
| Automation Driver | Terraform Community Edition | Terraform Community Edition, Terraform Cloud, or Terraform Enterprise |
|
| Automation Driver | Terraform Community Edition | Terraform Community Edition, HCP Terraform, or Terraform Enterprise |
|
||||||
| Terraform Workspaces | Local | Local workspaces with the Terraform driver or [remote workspaces](/terraform/cloud-docs/workspaces) with the Terraform Cloud driver |
|
| Terraform Workspaces | Local | Local workspaces with the Terraform driver or [remote workspaces](/terraform/cloud-docs/workspaces) with the HCP Terraform driver |
|
||||||
| Terraform Backend Options | [azurerm](/terraform/language/settings/backends/azurerm), [consul](/terraform/language/settings/backends/consul), [cos](/terraform/language/settings/backends/cos), [gcs](/terraform/language/settings/backends/gcs), [kubernetes](/terraform/language/settings/backends/kubernetes), [local](/terraform/language/settings/backends/local), [manta](/terraform/language/v1.2.x/settings/backends/manta), [pg](/terraform/language/settings/backends/pg), and [s3](/terraform/language/settings/backends/s3) with the Terraform driver | The supported backends for CTS with the Terraform driver or Terraform Cloud with the Terraform Cloud driver |
|
| Terraform Backend Options | [azurerm](/terraform/language/settings/backends/azurerm), [consul](/terraform/language/settings/backends/consul), [cos](/terraform/language/settings/backends/cos), [gcs](/terraform/language/settings/backends/gcs), [kubernetes](/terraform/language/settings/backends/kubernetes), [local](/terraform/language/settings/backends/local), [manta](/terraform/language/v1.2.x/settings/backends/manta), [pg](/terraform/language/settings/backends/pg), and [s3](/terraform/language/settings/backends/s3) with the Terraform driver | The supported backends for CTS with the Terraform driver or HCP Terraform with the HCP Terraform driver |
|
||||||
| Terraform Version | One Terraform version for all tasks | Optional Terraform version per task when using the Terraform Cloud driver |
|
| Terraform Version | One Terraform version for all tasks | Optional Terraform version per task when using the HCP Terraform driver |
|
||||||
| Terraform Run Output | CTS logs | CTS logs or Terraform output organized by Terraform Cloud remote workspaces |
|
| Terraform Run Output | CTS logs | CTS logs or Terraform output organized by HCP Terraform remote workspaces |
|
||||||
| Credentials and secrets | On disk as `.tfvars` files or in shell environment | Secured variables stored in remote workspace |
|
| Credentials and secrets | On disk as `.tfvars` files or in shell environment | Secured variables stored in remote workspace |
|
||||||
| Audit | | Terraform audit logs ([Terraform Cloud](/terraform/cloud-docs/api-docs/audit-trails) or [Terraform Enterprise](/terraform/enterprise/admin/infrastructure/logging)) |
|
| Audit | | Terraform audit logs ([HCP Terraform](/terraform/cloud-docs/api-docs/audit-trails) or [Terraform Enterprise](/terraform/enterprise/admin/infrastructure/logging)) |
|
||||||
| Collaboration | | Run [history](/terraform/cloud-docs/run/manage), [triggers](/terraform/cloud-docs/workspaces/settings/run-triggers), and [notifications](/terraform/cloud-docs/workspaces/settings/notifications) supported on Terraform Cloud |
|
| Collaboration | | Run [history](/terraform/cloud-docs/run/manage), [triggers](/terraform/cloud-docs/workspaces/settings/run-triggers), and [notifications](/terraform/cloud-docs/workspaces/settings/notifications) supported on HCP Terraform |
|
||||||
| Governance | | [Sentinel](/terraform/cloud-docs/policy-enforcement) to enforce governance policies as code |
|
| Governance | | [Sentinel](/terraform/cloud-docs/policy-enforcement) to enforce governance policies as code |
|
||||||
|
|
||||||
The [Terraform Cloud driver](/consul/docs/nia/configuration#terraform-cloud-driver) enables CTS Enterprise to integrate with Terraform Cloud or Terraform Enterprise. The [Terraform Cloud driver](/consul/docs/nia/network-drivers/terraform-cloud) page provides an overview of how the integration works within CTS.
|
The [HCP Terraform driver](/consul/docs/nia/configuration#terraform-cloud-driver) enables CTS Enterprise to integrate with HCP Terraform or Terraform Enterprise. The [HCP Terraform driver](/consul/docs/nia/network-drivers/terraform-cloud) page provides an overview of how the integration works within CTS.
|
||||||
|
|
||||||
## Consul Admin Partition Support
|
## Consul Admin Partition Support
|
||||||
CTS subscribes to a Consul agent. Depending on the admin partition the Consul agent is a part of and the services within the admin partition, CTS will be able to subscribe to those services and support the automation workflow. As such, admin partitions are not relevant to the CTS workflow. We recommend deploying a single CTS instance that subscribes to services/KV within a single partition and using a different CTS instance (or instances) to subscribe to services/KV in another partition.
|
CTS subscribes to a Consul agent. Depending on the admin partition the Consul agent is a part of and the services within the admin partition, CTS will be able to subscribe to those services and support the automation workflow. As such, admin partitions are not relevant to the CTS workflow. We recommend deploying a single CTS instance that subscribes to services/KV within a single partition and using a different CTS instance (or instances) to subscribe to services/KV in another partition.
|
||||||
|
|
|
@ -53,7 +53,7 @@ CTS is available as an open source and enterprise distribution. Follow the [Auto
|
||||||
|
|
||||||
- `Tasks` - A task is the translation of dynamic service information from the Consul Catalog into network infrastructure changes downstream.
|
- `Tasks` - A task is the translation of dynamic service information from the Consul Catalog into network infrastructure changes downstream.
|
||||||
|
|
||||||
- `Terraform Cloud` - Per the [Terraform documentation](/terraform/cloud-docs), "Terraform Cloud" describes both Terraform Cloud and Terraform Enterprise, which are different distributions of the same application. Documentation will apply to both distributions unless specifically stated otherwise.
|
- `HCP Terraform` - Per the [Terraform documentation](/terraform/cloud-docs), "HCP Terraform" describes both HCP Terraform and Terraform Enterprise, which are different distributions of the same application. Documentation will apply to both distributions unless specifically stated otherwise.
|
||||||
|
|
||||||
- `Terraform Module` - A [Terraform module](/terraform/language/modules) is a container for multiple Terraform resources that are used together.
|
- `Terraform Module` - A [Terraform module](/terraform/language/modules) is a container for multiple Terraform resources that are used together.
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
---
|
---
|
||||||
layout: docs
|
layout: docs
|
||||||
page_title: Terraform Cloud Driver
|
page_title: HCP Terraform Driver
|
||||||
description: >-
|
description: >-
|
||||||
Consul-Terraform-Sync Network Drivers with Terraform Cloud
|
Consul-Terraform-Sync Network Drivers with HCP Terraform
|
||||||
---
|
---
|
||||||
|
|
||||||
# Terraform Cloud Driver
|
# HCP Terraform Driver
|
||||||
<EnterpriseAlert>
|
<EnterpriseAlert>
|
||||||
This feature requires{' '}
|
This feature requires{' '}
|
||||||
<a href="https://www.hashicorp.com/products/consul/features">Consul-Terraform-Sync Enterprise</a>{' '}
|
<a href="https://www.hashicorp.com/products/consul/features">Consul-Terraform-Sync Enterprise</a>{' '}
|
||||||
which is available with <strong>Consul Enterprise</strong>.
|
which is available with <strong>Consul Enterprise</strong>.
|
||||||
</EnterpriseAlert>
|
</EnterpriseAlert>
|
||||||
|
|
||||||
Consul-Terraform-Sync (CTS) is more powerful when you integrate it with [Terraform Cloud](https://cloud.hashicorp.com/products/terraform). Integrating with Terraform Cloud provides features, such as enhanced workspaces and insight into Terraform operations as CTS dynamically updates your network infrastructure. CTS is compatible with both the [self-hosted](https://www.hashicorp.com/products/terraform/editions/enterprise) and [managed service](https://www.hashicorp.com/products/terraform/editions/cloud) versions of Terraform Cloud. It also supports all [tiers](https://www.hashicorp.com/products/terraform/pricing) of the Terraform Cloud managed service.
|
Consul-Terraform-Sync (CTS) is more powerful when you integrate it with [HCP Terraform](https://cloud.hashicorp.com/products/terraform). Integrating with HCP Terraform provides features, such as enhanced workspaces and insight into Terraform operations as CTS dynamically updates your network infrastructure. CTS is compatible with both the [self-hosted](https://www.hashicorp.com/products/terraform/editions/enterprise) and [managed service](https://www.hashicorp.com/products/terraform/editions/cloud) versions of HCP Terraform. It also supports all [tiers](https://www.hashicorp.com/products/terraform/pricing) of the HCP Terraform managed service.
|
||||||
|
|
||||||
This page describes how the Terraform Cloud driver operates within CTS.
|
This page describes how the HCP Terraform driver operates within CTS.
|
||||||
|
|
||||||
## Terraform Workspace Automation
|
## Terraform Workspace Automation
|
||||||
|
|
||||||
CTS manages Terraform runs following the [API-driven run workflow](/terraform/cloud-docs/run/api) for workspaces in Terraform Cloud.
|
CTS manages Terraform runs following the [API-driven run workflow](/terraform/cloud-docs/run/api) for workspaces in HCP Terraform.
|
||||||
|
|
||||||
On startup, CTS:
|
On startup, CTS:
|
||||||
1. Creates or discovers Terraform Cloud workspaces corresponding to the configured tasks.
|
1. Creates or discovers HCP Terraform workspaces corresponding to the configured tasks.
|
||||||
2. Prepares the local environment and generates Terraform configuration files that make up the root module for each task.
|
2. Prepares the local environment and generates Terraform configuration files that make up the root module for each task.
|
||||||
3. Packages the generated files and uploads them as a configuration version for the task's workspace on Terraform Cloud.
|
3. Packages the generated files and uploads them as a configuration version for the task's workspace on HCP Terraform.
|
||||||
|
|
||||||
Once all workspaces are set up, CTS monitors the Consul catalog for service changes. When relevant changes are detected, the Terraform Cloud driver dynamically updates input variables for that task directly as [workspace variables](/terraform/cloud-docs/workspaces/variables) using the Terraform Cloud API. The driver then queues a run on the workspace, with auto-apply enabled, to update your network infrastructure.
|
Once all workspaces are set up, CTS monitors the Consul catalog for service changes. When relevant changes are detected, the HCP Terraform driver dynamically updates input variables for that task directly as [workspace variables](/terraform/cloud-docs/workspaces/variables) using the HCP Terraform API. The driver then queues a run on the workspace, with auto-apply enabled, to update your network infrastructure.
|
||||||
|
|
||||||
~> **Note:** Although workspaces for tasks are executed in isolated environments, this does not guarantee the infrastructure changes from concurrent task executions are independent. Ensure that modules across all tasks are not modifying the same resource objects or have overlapping changes that may result in race conditions during automation.
|
~> **Note:** Although workspaces for tasks are executed in isolated environments, this does not guarantee the infrastructure changes from concurrent task executions are independent. Ensure that modules across all tasks are not modifying the same resource objects or have overlapping changes that may result in race conditions during automation.
|
||||||
|
|
||||||
|
@ -61,8 +61,8 @@ CTS can automate remote workspaces with either auto apply or manual apply config
|
||||||
When CTS detects new changes for a workspace that already has a run pending on approval, CTS will discard the stale run and queue a new run with the latest values. The new run will go through plan and then again wait on an operator to approve it. Only once the run is approved will the infrastructure be updated with the latest Consul changes.
|
When CTS detects new changes for a workspace that already has a run pending on approval, CTS will discard the stale run and queue a new run with the latest values. The new run will go through plan and then again wait on an operator to approve it. Only once the run is approved will the infrastructure be updated with the latest Consul changes.
|
||||||
|
|
||||||
There are two approaches to setup manual apply for a workspace managed by CTS based on how the workspace is created.
|
There are two approaches to setup manual apply for a workspace managed by CTS based on how the workspace is created.
|
||||||
* For CTS created workspaces, update the apply method from auto to manual via the Terraform Cloud web application or API.
|
* For CTS created workspaces, update the apply method from auto to manual via the HCP Terraform web application or API.
|
||||||
* For pre-configured workspaces, create the workspace prior to CTS task automation via the Terraform Cloud web application or API.
|
* For pre-configured workspaces, create the workspace prior to CTS task automation via the HCP Terraform web application or API.
|
||||||
1. Create a workspace with the same name as the desired task.
|
1. Create a workspace with the same name as the desired task.
|
||||||
1. Set the workspace to [API-driven run workflow](/terraform/cloud-docs/run/api) and the execution mode to remote.
|
1. Set the workspace to [API-driven run workflow](/terraform/cloud-docs/run/api) and the execution mode to remote.
|
||||||
1. Ensure that the apply method for the workspace is set to manual apply.
|
1. Ensure that the apply method for the workspace is set to manual apply.
|
||||||
|
@ -72,7 +72,7 @@ There are two approaches to setup manual apply for a workspace managed by CTS ba
|
||||||
|
|
||||||
## Configuration Version
|
## Configuration Version
|
||||||
|
|
||||||
An example configuration version for a task named "cts-example" would have the folder structure below when running with the Terraform Cloud driver and using the default working directory.
|
An example configuration version for a task named "cts-example" would have the folder structure below when running with the HCP Terraform driver and using the default working directory.
|
||||||
|
|
||||||
```shell-session
|
```shell-session
|
||||||
$ tree sync-tasks/
|
$ tree sync-tasks/
|
||||||
|
@ -103,23 +103,23 @@ You can view the latest service information in the Terraform UI by navigating to
|
||||||
|
|
||||||
~> **Caution:** Dynamic variables maintained by CTS are formatted for automation. Unexpected manual changes to these variables may result in automation errors.
|
~> **Caution:** Dynamic variables maintained by CTS are formatted for automation. Unexpected manual changes to these variables may result in automation errors.
|
||||||
|
|
||||||
## Setting Up Terraform Cloud Driver
|
## Setting Up HCP Terraform Driver
|
||||||
|
|
||||||
### Deployment
|
### Deployment
|
||||||
|
|
||||||
Because a CTS instance can only be configured with one driver, an instance can only be associated with either a Terraform driver or a Terraform Cloud driver. If there is a need to run both types of drivers, users will need to deploy a separate CTS instance for each type of driver. Relatedly, if there is a need to run CTS across multiple Terraform Cloud organizations, users will need to deploy a separate instance for each organization.
|
Because a CTS instance can only be configured with one driver, an instance can only be associated with either a Terraform driver or a HCP Terraform driver. If there is a need to run both types of drivers, users will need to deploy a separate CTS instance for each type of driver. Relatedly, if there is a need to run CTS across multiple HCP Terraform organizations, users will need to deploy a separate instance for each organization.
|
||||||
|
|
||||||
### Required Setup
|
### Required Setup
|
||||||
|
|
||||||
This section captures requirements for setting up CTS to integrate with your [Terraform Cloud](https://cloud.hashicorp.com/products/terraform) solution.
|
This section captures requirements for setting up CTS to integrate with your [HCP Terraform](https://cloud.hashicorp.com/products/terraform) solution.
|
||||||
|
|
||||||
1. Hostname of your Terraform Cloud, self-hosted distribution
|
1. Hostname of your HCP Terraform, self-hosted distribution
|
||||||
1. Name of your organization
|
1. Name of your organization
|
||||||
1. [Team API token](/terraform/cloud-docs/users-teams-organizations/api-tokens) used for authentication with Terraform Cloud
|
1. [Team API token](/terraform/cloud-docs/users-teams-organizations/api-tokens) used for authentication with HCP Terraform
|
||||||
|
|
||||||
Prior to running CTS with a Terraform Cloud driver, you will need an account and organization set up, as well as a dedicated token. We recommend using a team token that is restricted to [Manage Workspaces](/terraform/cloud-docs/users-teams-organizations/teams#managing-workspace-access)-level permissions. Below are the steps for the recommended setup.
|
Prior to running CTS with an HCP Terraform driver, you will need an account and organization set up, as well as a dedicated token. We recommend using a team token that is restricted to [Manage Workspaces](/terraform/cloud-docs/users-teams-organizations/teams#managing-workspace-access)-level permissions. Below are the steps for the recommended setup.
|
||||||
|
|
||||||
The first step is to create an account with your Terraform Cloud service. After creating an account, create a new [organization](/terraform/cloud-docs/users-teams-organizations/organizations#creating-organizations) or select an existing organization. The address of your Terraform Cloud service will be used to configure the [`hostname`](/consul/docs/nia/configuration#hostname), and the organization name will be used to configure the [`organization`](/consul/docs/nia/configuration#organization) on the Terraform Cloud driver.
|
The first step is to create an account with your HCP Terraform service. After creating an account, create a new [organization](/terraform/cloud-docs/users-teams-organizations/organizations#creating-organizations) or select an existing organization. The address of your HCP Terraform service will be used to configure the [`hostname`](/consul/docs/nia/configuration#hostname), and the organization name will be used to configure the [`organization`](/consul/docs/nia/configuration#organization) on the HCP Terraform driver.
|
||||||
|
|
||||||
Once you have an account and organization, the next step is to [create a team](/terraform/cloud-docs/users-teams-organizations/teams). We recommend using a dedicated team and team token to run and authenticate CTS. Using a team token has the benefits of restricting organization permissions as well as associating CTS automated actions with the team rather than an individual.
|
Once you have an account and organization, the next step is to [create a team](/terraform/cloud-docs/users-teams-organizations/teams). We recommend using a dedicated team and team token to run and authenticate CTS. Using a team token has the benefits of restricting organization permissions as well as associating CTS automated actions with the team rather than an individual.
|
||||||
|
|
||||||
|
@ -127,12 +127,12 @@ After creating a dedicated team, update the team's permissions with "Manage Work
|
||||||
|
|
||||||
[![CTS Terraform Team Setup](/img/nia/cts-tfc-team-setup.png)](/img/nia/cts-tfc-team-setup.png)
|
[![CTS Terraform Team Setup](/img/nia/cts-tfc-team-setup.png)](/img/nia/cts-tfc-team-setup.png)
|
||||||
|
|
||||||
After setting the team's permissions, the final setup step is to [generate the associated team token](/terraform/cloud-docs/users-teams-organizations/api-tokens), which can be done on the same team management page. This token will be used by CTS for API authentication and will be used to configure the [`token`](/consul/docs/nia/configuration#token) on the Terraform Cloud driver.
|
After setting the team's permissions, the final setup step is to [generate the associated team token](/terraform/cloud-docs/users-teams-organizations/api-tokens), which can be done on the same team management page. This token will be used by CTS for API authentication and will be used to configure the [`token`](/consul/docs/nia/configuration#token) on the HCP Terraform driver.
|
||||||
|
|
||||||
### Recommendations
|
### Recommendations
|
||||||
|
|
||||||
We recommend configuring workspaces managed by CTS with [run notifications](/terraform/cloud-docs/workspaces/settings/notifications) through the Terraform web application. Run notifications notify external systems about the progress of runs and could help notify users of CTS events, particularly errored runs.
|
We recommend configuring workspaces managed by CTS with [run notifications](/terraform/cloud-docs/workspaces/settings/notifications) through the Terraform web application. Run notifications notify external systems about the progress of runs and could help notify users of CTS events, particularly errored runs.
|
||||||
|
|
||||||
[![CTS Terraform Cloud Run Notifications](/img/nia/cts-tfc-run-notifications.png)](/img/nia/cts-tfc-run-notifications.png)
|
[![CTS HCP Terraform Run Notifications](/img/nia/cts-tfc-run-notifications.png)](/img/nia/cts-tfc-run-notifications.png)
|
||||||
|
|
||||||
In order to configure a run notification, users can [manually create a notification configuration](/terraform/cloud-docs/workspaces/settings/notifications#creating-a-notification-configuration) for workspaces automated by CTS. A workspace may already exist for a task if the workspace name is identical to the configured task's [`name`](/consul/docs/nia/configuration#name-2). This may occur if CTS has already already run and created the workspace for the task. This may also occur if the workspace is manually created for the task prior to CTS running.
|
In order to configure a run notification, users can [manually create a notification configuration](/terraform/cloud-docs/workspaces/settings/notifications#creating-a-notification-configuration) for workspaces automated by CTS. A workspace may already exist for a task if the workspace name is identical to the configured task's [`name`](/consul/docs/nia/configuration#name-2). This may occur if CTS has already already run and created the workspace for the task. This may also occur if the workspace is manually created for the task prior to CTS running.
|
|
@ -2,7 +2,7 @@
|
||||||
layout: docs
|
layout: docs
|
||||||
page_title: Network Drivers
|
page_title: Network Drivers
|
||||||
description: >-
|
description: >-
|
||||||
Consul-Terraform-Sync Network Drivers with Terraform and Terraform Cloud
|
Consul-Terraform-Sync Network Drivers with Terraform and HCP Terraform
|
||||||
---
|
---
|
||||||
|
|
||||||
# Network Drivers
|
# Network Drivers
|
||||||
|
@ -11,18 +11,18 @@ Consul-Terraform-Sync (CTS) uses network drivers to execute and update network i
|
||||||
|
|
||||||
CTS is a HashiCorp solution to Network Infrastructure Automation. It bridges Consul's networking features and Terraform infrastructure management capabilities. The solution seamlessly embeds Terraform as network drivers to manage automation of Terraform modules. This expands the Consul ecosystem and taps into the rich features and community of Terraform and Terraform providers.
|
CTS is a HashiCorp solution to Network Infrastructure Automation. It bridges Consul's networking features and Terraform infrastructure management capabilities. The solution seamlessly embeds Terraform as network drivers to manage automation of Terraform modules. This expands the Consul ecosystem and taps into the rich features and community of Terraform and Terraform providers.
|
||||||
|
|
||||||
The following table highlights some of the additional features Terraform and Terraform Cloud offer when used as a network driver for CTS. Visit the [Terraform product page](https://www.hashicorp.com/products/terraform) or [contact our sales team](https://www.hashicorp.com/contact-sales) for a comprehensive list of features.
|
The following table highlights some of the additional features Terraform and HCP Terraform offer when used as a network driver for CTS. Visit the [Terraform product page](https://www.hashicorp.com/products/terraform) or [contact our sales team](https://www.hashicorp.com/contact-sales) for a comprehensive list of features.
|
||||||
|
|
||||||
| Network Driver | Description | Features |
|
| Network Driver | Description | Features |
|
||||||
| -------------- | ----------- | -------- |
|
| -------------- | ----------- | -------- |
|
||||||
| [Terraform driver](/consul/docs/nia/network-drivers/terraform) | CTS automates a local installation of the [Terraform CLI](https://www.terraform.io/) | - Local Terraform execution <br/> - Local workspace directories <br/> - [Backend options](/consul/docs/nia/configuration#backend) available for state storage <br/> |
|
| [Terraform driver](/consul/docs/nia/network-drivers/terraform) | CTS automates a local installation of the [Terraform CLI](https://www.terraform.io/) | - Local Terraform execution <br/> - Local workspace directories <br/> - [Backend options](/consul/docs/nia/configuration#backend) available for state storage <br/> |
|
||||||
| [Terraform Cloud driver](/consul/docs/nia/network-drivers/terraform-cloud) | CTS Enterprise automates remote workspaces on [Terraform Cloud](/terraform/cloud-docs) | - [Remote Terraform execution](/terraform/cloud-docs/run/remote-operations) <br/> - Concurrent runs <br/> - [Secured variables](/terraform/cloud-docs/workspaces/variables) <br/> - [State versions](/terraform/cloud-docs/workspaces/state) <br/> - [Sentinel](/terraform/cloud-docs/policy-enforcement) to enforce governance policies as code <br/> - Audit [logs](/terraform/enterprise/admin/infrastructure/logging) and [trails](/terraform/cloud-docs/api-docs/audit-trails) <br/> - Run [history](/terraform/cloud-docs/run/manage), [triggers](/terraform/cloud-docs/workspaces/settings/run-triggers), and [notifications](/terraform/cloud-docs/workspaces/settings/notifications) <br/> - [Terraform Cloud Agents](/terraform/cloud-docs/agents) |
|
| [HCP Terraform driver](/consul/docs/nia/network-drivers/terraform-cloud) | CTS Enterprise automates remote workspaces on [HCP Terraform](/terraform/cloud-docs) | - [Remote Terraform execution](/terraform/cloud-docs/run/remote-operations) <br/> - Concurrent runs <br/> - [Secured variables](/terraform/cloud-docs/workspaces/variables) <br/> - [State versions](/terraform/cloud-docs/workspaces/state) <br/> - [Sentinel](/terraform/cloud-docs/policy-enforcement) to enforce governance policies as code <br/> - Audit [logs](/terraform/enterprise/admin/infrastructure/logging) and [trails](/terraform/cloud-docs/api-docs/audit-trails) <br/> - Run [history](/terraform/cloud-docs/run/manage), [triggers](/terraform/cloud-docs/workspaces/settings/run-triggers), and [notifications](/terraform/cloud-docs/workspaces/settings/notifications) <br/> - [Terraform Cloud Agents](/terraform/cloud-docs/agents) |
|
||||||
|
|
||||||
## Understanding Terraform Automation
|
## Understanding Terraform Automation
|
||||||
|
|
||||||
CTS automates Terraform execution using a templated configuration to carry out infrastructure changes. The auto-generated configuration leverages input variables sourced from Consul and builds on top of reusable Terraform modules published and maintained by HashiCorp partners and the community. CTS can also run your custom built modules that suit your team's specific network automation needs.
|
CTS automates Terraform execution using a templated configuration to carry out infrastructure changes. The auto-generated configuration leverages input variables sourced from Consul and builds on top of reusable Terraform modules published and maintained by HashiCorp partners and the community. CTS can also run your custom built modules that suit your team's specific network automation needs.
|
||||||
|
|
||||||
The network driver for CTS determines how the Terraform automation operates. Visit the driver pages to read more about the [Terraform driver](/consul/docs/nia/network-drivers/terraform) and the [Terraform Cloud driver](/consul/docs/nia/network-drivers/terraform-cloud).
|
The network driver for CTS determines how the Terraform automation operates. Visit the driver pages to read more about the [Terraform driver](/consul/docs/nia/network-drivers/terraform) and the [HCP Terraform driver](/consul/docs/nia/network-drivers/terraform-cloud).
|
||||||
|
|
||||||
### Upgrading Terraform
|
### Upgrading Terraform
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ The following components are required to run Consul-Terraform-Sync (CTS):
|
||||||
|
|
||||||
You can add support for your network infrastructure through Terraform providers so that you can apply Terraform modules to implement network integrations.
|
You can add support for your network infrastructure through Terraform providers so that you can apply Terraform modules to implement network integrations.
|
||||||
|
|
||||||
The following guidance is for running CTS using the Terraform driver. The Terraform Cloud driver<EnterpriseAlert inline /> has [additional prerequisites](/consul/docs/nia/network-drivers/terraform-cloud#setting-up-terraform-cloud-driver).
|
The following guidance is for running CTS using the Terraform driver. The HCP Terraform driver<EnterpriseAlert inline /> has [additional prerequisites](/consul/docs/nia/network-drivers/terraform-cloud#setting-up-terraform-cloud-driver).
|
||||||
|
|
||||||
## Run a Consul cluster
|
## Run a Consul cluster
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ The following diagram shows the CTS cluster state after the leader stops. CTS In
|
||||||
|
|
||||||
- The time it takes for a new leader to be elected is determined by the `high_availability.cluster.storage.session_ttl` configuration. The minimum failover time is equal to the `session_ttl` value. The maximum failover time is double the `session_ttl` value.
|
- The time it takes for a new leader to be elected is determined by the `high_availability.cluster.storage.session_ttl` configuration. The minimum failover time is equal to the `session_ttl` value. The maximum failover time is double the `session_ttl` value.
|
||||||
- If failover occurs during task execution, a new leader is elected. The new leader will attempt to run all tasks once before continuing to monitor for changes.
|
- If failover occurs during task execution, a new leader is elected. The new leader will attempt to run all tasks once before continuing to monitor for changes.
|
||||||
- If using the [Terraform Cloud (TFC) driver](/consul/docs/nia/network-drivers/terraform-cloud), the task finishes and CTS starts a new leader that attempts to queue a run for each task in TFC in once-mode.
|
- If using the [HCP Terraform driver](/consul/docs/nia/network-drivers/terraform-cloud), the task finishes and CTS starts a new leader that attempts to queue a run for each task in HCP Terraform in once-mode.
|
||||||
- If using [Terraform driver](/consul/docs/nia/network-drivers/terraform), the task may complete depending on the cause of the failover. The new leader starts and attempts to run each task in [once-mode](/consul/docs/nia/cli/start#modes). Depending on the module and provider, the task may require manual intervention to fix any inconsistencies between the infrastructure and Terraform state.
|
- If using [Terraform driver](/consul/docs/nia/network-drivers/terraform), the task may complete depending on the cause of the failover. The new leader starts and attempts to run each task in [once-mode](/consul/docs/nia/cli/start#modes). Depending on the module and provider, the task may require manual intervention to fix any inconsistencies between the infrastructure and Terraform state.
|
||||||
- If failover occurs when no task is executing, CTS elects a new leader that attempts to run all tasks in once-mode.
|
- If failover occurs when no task is executing, CTS elects a new leader that attempts to run all tasks in once-mode.
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ Verify that you have met the [basic requirements](/consul/docs/nia/usage/require
|
||||||
|
|
||||||
You must configure appropriate ACL permissions for your cluster. Refer to [ACL permissions](#) for details.
|
You must configure appropriate ACL permissions for your cluster. Refer to [ACL permissions](#) for details.
|
||||||
|
|
||||||
We recommend specifying the [TFC driver](/consul/docs/nia/network-drivers/terraform-cloud) in your CTS configuration if you want to run in high availability mode.
|
We recommend specifying the [HCP Terraform driver](/consul/docs/nia/network-drivers/terraform-cloud) in your CTS configuration if you want to run in high availability mode.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ description: >-
|
||||||
|
|
||||||
We have implemented the following features in this release:
|
We have implemented the following features in this release:
|
||||||
|
|
||||||
### Per-task Execution Mode with Support for Terraform Cloud Agent <EnterpriseAlert inline />
|
### Per-task Execution Mode with Support for HCP Terraform Agent <EnterpriseAlert inline />
|
||||||
|
|
||||||
You can now execute Terraform tasks in `remote` or `cloud agent` mode. For more information, refer to the [per-task execution mode documentation](/consul/docs/nia/network-drivers/terraform-cloud#remote-workspaces).
|
You can now execute Terraform tasks in `remote` or `cloud agent` mode. For more information, refer to the [per-task execution mode documentation](/consul/docs/nia/network-drivers/terraform-cloud#remote-workspaces).
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ CTS includes a new [API endpoint](/consul/docs/nia/api/health#health) that provi
|
||||||
- Consul: v1.9+
|
- Consul: v1.9+
|
||||||
- <EnterpriseAlert inline /> HCP Consul: Latest
|
- <EnterpriseAlert inline /> HCP Consul: Latest
|
||||||
- Terraform CLI: v0.13 - v1.1
|
- Terraform CLI: v0.13 - v1.1
|
||||||
- <EnterpriseAlert inline /> Terraform Cloud: Latest
|
- <EnterpriseAlert inline /> HCP Terraform: Latest
|
||||||
- <EnterpriseAlert inline /> Terraform Enterprise: v202010-2 - Latest
|
- <EnterpriseAlert inline /> Terraform Enterprise: v202010-2 - Latest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
---
|
||||||
|
layout: docs
|
||||||
|
page_title: Enable dynamic DNS queries
|
||||||
|
description: ->
|
||||||
|
You tune Consul DNS query handling to balance between current information and reducing request response time. Learn how to enable caching by modifying TTL values, how to return stale results from the DNS cache, and how to configure Consul for negative response caching.
|
||||||
|
---
|
||||||
|
|
||||||
|
# DNS caching
|
||||||
|
|
||||||
|
This page describes the process to return cached results in response to DNS lookups. Consul agents can use DNS caching to reduce response time, but might provide stale information in the process.
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
By default, Consul serves all DNS results with a `0` TTL value, which prevents any
|
||||||
|
caching. This configuration returns the most recent information because each DNS lookup
|
||||||
|
runs every time. However, this configuration adds latency to each lookup and can potentially
|
||||||
|
exhaust the query throughput of a datacenter.
|
||||||
|
|
||||||
|
There are several ways you can modify to fine-tune Consul DNS lookup behavior to best suit your network's requirements.
|
||||||
|
|
||||||
|
## TTL values ((#ttl))
|
||||||
|
|
||||||
|
You can configure TTL values in the [agent configuration file](/consul/docs/agent/config/config-files) to allow DNS results to be cached downstream of Consul.
|
||||||
|
Higher TTL values reduce the number of lookups on the Consul servers and speed
|
||||||
|
lookups for clients, at the cost of increasingly stale results. By default, all
|
||||||
|
TTLs are zero, preventing any caching.
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
dns_config {
|
||||||
|
service_ttl {
|
||||||
|
"*" = "0s"
|
||||||
|
}
|
||||||
|
node_ttl = "0s"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns_config": {
|
||||||
|
"service_ttl": {
|
||||||
|
"*": "0s"
|
||||||
|
},
|
||||||
|
"node_ttl": "0s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
### Enable caching
|
||||||
|
|
||||||
|
To enable caching of node lookups, set the
|
||||||
|
[`dns_config.node_ttl`](/consul/docs/agent/config/config-files#node_ttl)
|
||||||
|
value. This can be set to `10s` for example, and all node lookups will serve
|
||||||
|
results with a 10 second TTL.
|
||||||
|
|
||||||
|
Service TTLs can be specified in a more granular fashion. You can set TTLs
|
||||||
|
per-service, with a wildcard TTL as the default. This is specified using the
|
||||||
|
[`dns_config.service_ttl`](/consul/docs/agent/config/config-files#service_ttl)
|
||||||
|
map. The `*` is supported at the end of any prefix and has a lower precedence
|
||||||
|
than strict match, so `my-service-x` has precedence over `my-service-*`. When
|
||||||
|
performing wildcard match, the longest path is taken into account, thus
|
||||||
|
`my-service-*` TTL will be used instead of `my-*` or `*`. With the same rule,
|
||||||
|
`*` is the default value when nothing else matches. If no match is found the TTL
|
||||||
|
defaults to 0.
|
||||||
|
|
||||||
|
For example, a [`dns_config`](/consul/docs/agent/config/config-files#dns_config)
|
||||||
|
that provides a wildcard TTL and a specific TTL for a service might look like this:
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
dns_config {
|
||||||
|
service_ttl {
|
||||||
|
"*" = "5s"
|
||||||
|
"web" = "30s"
|
||||||
|
"db*" = "10s"
|
||||||
|
"db-master" = "3s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns_config": {
|
||||||
|
"service_ttl": {
|
||||||
|
"*": "5s",
|
||||||
|
"web": "30s",
|
||||||
|
"db*": "10s",
|
||||||
|
"db-master": "3s"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
This sets all lookups to "web.service.consul" to use a 30 second TTL
|
||||||
|
while lookups to "api.service.consul" will use the 5 second TTL from the wildcard.
|
||||||
|
All lookups matching "db\*" would get a 10 seconds TTL except "db-master" that
|
||||||
|
would have a 3 seconds TTL.
|
||||||
|
|
||||||
|
### Prepared queries
|
||||||
|
|
||||||
|
[Prepared Queries](/consul/api-docs/query) provide an additional
|
||||||
|
level of control over TTL. They allow for the TTL to be defined along with
|
||||||
|
the query, and they can be changed on the fly by updating the query definition.
|
||||||
|
If a TTL is not configured for a prepared query, then it will fall back to the
|
||||||
|
service-specific configuration defined in the Consul agent as described above,
|
||||||
|
and ultimately to 0 if no TTL is configured for the service in the Consul agent.
|
||||||
|
|
||||||
|
<a name="stale"></a>
|
||||||
|
|
||||||
|
## Stale reads
|
||||||
|
|
||||||
|
Stale reads can be used to reduce latency and increase the throughput of DNS
|
||||||
|
queries. The [settings](/consul/docs/agent/config/config-files) used to
|
||||||
|
control stale reads of DNS queries are:
|
||||||
|
|
||||||
|
- [`dns_config.allow_stale`](/consul/docs/agent/config/config-files#allow_stale) must be
|
||||||
|
set to true to enable stale reads.
|
||||||
|
- [`dns_config.max_stale`](/consul/docs/agent/config/config-files#max_stale) limits how stale results
|
||||||
|
are allowed to be when querying DNS.
|
||||||
|
|
||||||
|
With these two settings you can allow or prevent stale reads. Below we will
|
||||||
|
discuss the advantages and disadvantages of both.
|
||||||
|
|
||||||
|
### Allow stale reads
|
||||||
|
|
||||||
|
Since Consul 0.7.1, `allow_stale` is enabled by default and uses a `max_stale`
|
||||||
|
value that defaults to a near-indefinite threshold (10 years). This allows DNS
|
||||||
|
queries to continue to be served in the event of a long outage with no leader. A
|
||||||
|
new telemetry counter has also been added at `consul.dns.stale_queries` to track
|
||||||
|
when agents serve DNS queries that are stale by more than 5 seconds.
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
dns_config {
|
||||||
|
allow_stale = true
|
||||||
|
max_stale = "87600h"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns_config": {
|
||||||
|
"allow_stale": true,
|
||||||
|
"max_stale": "87600h"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
|
||||||
|
The above example is the default setting. You do not need to set it explicitly.
|
||||||
|
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
Doing a stale read allows any Consul server to service a query, but non-leader
|
||||||
|
nodes may return data that is out-of-date. By allowing data to be slightly
|
||||||
|
stale, you get horizontal read scalability. Now any Consul server can service
|
||||||
|
the request, so you increase throughput by the number of servers in a datacenter.
|
||||||
|
|
||||||
|
### Prevent stale reads
|
||||||
|
|
||||||
|
If you want to prevent stale reads or limit how stale they can be, you can set
|
||||||
|
`allow_stale` to false or use a lower value for `max_stale`. Doing the first
|
||||||
|
will ensure that all reads are serviced by a
|
||||||
|
[single leader node](/consul/docs/architecture/consensus).
|
||||||
|
The reads will then be strongly consistent but will be limited by the throughput
|
||||||
|
of a single node.
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
dns_config {
|
||||||
|
allow_stale = false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns_config": {
|
||||||
|
"allow_stale": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
## Negative response caching
|
||||||
|
|
||||||
|
Although DNS clients cache negative responses, Consul returns a "not
|
||||||
|
found" style response when a service exists but there are no healthy
|
||||||
|
endpoints. When using DNS for service discovery, cached negative responses may
|
||||||
|
cause a service to appear down for longer than it is actually unavailable.
|
||||||
|
|
||||||
|
### Configure SOA
|
||||||
|
|
||||||
|
In Consul v1.3.0 and newer, it is now possible to tune SOA responses and modify
|
||||||
|
the negative TTL cache for some resolvers. It can be achieved using the
|
||||||
|
[`soa.min_ttl`](/consul/docs/agent/config/config-files#soa_min_ttl)
|
||||||
|
configuration within the [`soa`](/consul/docs/agent/config/config-files#soa) configuration.
|
||||||
|
|
||||||
|
<CodeTabs>
|
||||||
|
|
||||||
|
```hcl
|
||||||
|
dns_config {
|
||||||
|
soa {
|
||||||
|
min_ttl = 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"dns_config": {
|
||||||
|
"soa": {
|
||||||
|
"min_ttl": 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
</CodeTabs>
|
||||||
|
|
||||||
|
One common example is that Windows will default to caching negative responses
|
||||||
|
for 15 minutes. DNS forwarders may also cache negative responses, with the same
|
||||||
|
effect. To avoid this problem, check the negative response cache defaults for
|
||||||
|
your client operating system and any DNS forwarder on the path between the
|
||||||
|
client and Consul and set the cache values appropriately. In many cases
|
||||||
|
"appropriately" means turning negative response caching off to get the best
|
||||||
|
recovery time when a service becomes available again.
|
|
@ -65,12 +65,5 @@ machine.node.dc1.test-domain. 0 IN TXT "consul-network-segment="
|
||||||
Responses to pointer record (PTR) queries, such as `<ip>.in-addr.arpa.`, always use the [primary domain](/consul/docs/agent/config/config-files#domain) and not the alternative domain.
|
Responses to pointer record (PTR) queries, such as `<ip>.in-addr.arpa.`, always use the [primary domain](/consul/docs/agent/config/config-files#domain) and not the alternative domain.
|
||||||
|
|
||||||
### Caching
|
### Caching
|
||||||
By default, DNS results served by Consul are not cached. Refer to the [DNS Caching tutorial](/consul/tutorials/networking/dns-caching) for instructions on how to enable caching.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
By default, DNS results served by Consul are not cached. Refer to [DNS caching](/consul/docs/services/discovery/dns-cache) for instructions on how to enable caching.
|
|
@ -8,9 +8,7 @@ description: >-
|
||||||
|
|
||||||
# General Upgrade Process
|
# General Upgrade Process
|
||||||
|
|
||||||
## Introduction
|
This pages describes the overall process and best practices that you should follow when
|
||||||
|
|
||||||
This document describes some best practices that you should follow when
|
|
||||||
upgrading Consul. Some versions also have steps that are specific to that
|
upgrading Consul. Some versions also have steps that are specific to that
|
||||||
version, so make sure you also review the [upgrade instructions](/consul/docs/upgrading/instructions)
|
version, so make sure you also review the [upgrade instructions](/consul/docs/upgrading/instructions)
|
||||||
for the version you are on.
|
for the version you are on.
|
||||||
|
@ -46,117 +44,124 @@ If you are using Kubernetes, then please review our documentation for
|
||||||
|
|
||||||
## Prepare for the Upgrade
|
## Prepare for the Upgrade
|
||||||
|
|
||||||
**1.** Take a snapshot:
|
1. Take a snapshot to ensure you have a safe fallback option in case something goes wrong.
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul snapshot save backup.snap
|
$ consul snapshot save backup.snap
|
||||||
```
|
```
|
||||||
|
|
||||||
You can inspect the snapshot to ensure if was successful with:
|
You can inspect the snapshot to ensure your cluster's Raft index was successfully captured.
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul snapshot inspect backup.snap
|
$ consul snapshot inspect backup.snap
|
||||||
```
|
```
|
||||||
|
|
||||||
Example output:
|
Example output:
|
||||||
|
|
||||||
```
|
```shell-session hideClipboard
|
||||||
ID 2-1182-1542056499724
|
ID 2-1182-1542056499724
|
||||||
Size 4115
|
Size 4115
|
||||||
Index 1182
|
Index 1182
|
||||||
Term 2
|
Term 2
|
||||||
Version 1
|
Version 1
|
||||||
```
|
```
|
||||||
|
|
||||||
This will ensure you have a safe fallback option in case something goes wrong. Store
|
Store this snapshot somewhere safe. For more information on snapshots, refer to the following:
|
||||||
this snapshot somewhere safe. More documentation on snapshot usage is available here:
|
|
||||||
|
|
||||||
- [consul.io/commands/snapshot](/consul/commands/snapshot)
|
- [`consul snapshot` CLI command](/consul/commands/snapshot)
|
||||||
- [Backup Consul Data and State tutorial](/consul/tutorials/production-deploy/backup-and-restore)
|
- [Backup Consul Data and State tutorial](/consul/tutorials/production-deploy/backup-and-restore)
|
||||||
|
|
||||||
**2.** Temporarily modify your Consul configuration so that its [log_level](/consul/docs/agent/config/cli-flags#_log_level)
|
2. Temporarily modify your Consul configuration to set the agent's [`log_level`](/consul/docs/agent/config/cli-flags#_log_level) is set to `debug`. Then issue the following command on your servers to
|
||||||
is set to `debug`. After doing this, issue the following command on your servers to
|
|
||||||
reload the configuration:
|
reload the configuration:
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul reload
|
$ consul reload
|
||||||
```
|
```
|
||||||
|
|
||||||
This change will give you more information to work with in the event something goes wrong.
|
When you change the cluster's log level, Consul gives you more information to work with in the event that something goes wrong.
|
||||||
|
|
||||||
|
### Enterprise upgrades
|
||||||
|
|
||||||
|
<Note>
|
||||||
|
To experience a smoother upgrade process on Consul Enterprise, we recommend that you disable the upgrade migration feature.
|
||||||
|
</Note>
|
||||||
|
|
||||||
|
Consul Enterprise supports [automated upgrades](/consul/docs/enterprise/upgrades), but the autopilot feature may cause a node running an updated Consul version to elect a new leader before the version is updated on the existing cluster leader.
|
||||||
|
|
||||||
|
If your datacenter runs Consul Enterprise, update your server agent configuration file to disable autopilot's upgrade migration or run the following CLI command:
|
||||||
|
|
||||||
|
```shell-session
|
||||||
|
$ consul operator autopilot set-config -disable-upgrade-migration=true
|
||||||
|
```
|
||||||
|
|
||||||
## Perform the Upgrade
|
## Perform the Upgrade
|
||||||
|
|
||||||
**1.** Issue the following command to discover which server is currently the leader:
|
1. Issue the following command to discover which server is currently the leader:
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul operator raft list-peers
|
$ consul operator raft list-peers
|
||||||
```
|
```
|
||||||
|
|
||||||
You should receive output similar to this (exact formatting and content may differ based on version):
|
You should receive output similar to this (exact formatting and content may differ based on version):
|
||||||
|
|
||||||
```
|
```shell-session hideClipboard
|
||||||
Node ID Address State Voter RaftProtocol
|
Node ID Address State Voter RaftProtocol
|
||||||
dc1-node1 ae15858f-7f5f-4dcb-b7d5-710fdcdd2745 10.11.0.2:8300 leader true 3
|
dc1-node1 ae15858f-7f5f-4dcb-b7d5-710fdcdd2745 10.11.0.2:8300 leader true 3
|
||||||
dc1-node2 20e6be1b-f1cb-4aab-929f-f7d2d43d9a96 10.11.0.3:8300 follower true 3
|
dc1-node2 20e6be1b-f1cb-4aab-929f-f7d2d43d9a96 10.11.0.3:8300 follower true 3
|
||||||
dc1-node3 658c343b-8769-431f-a71a-236f9dbb17b3 10.11.0.4:8300 follower true 3
|
dc1-node3 658c343b-8769-431f-a71a-236f9dbb17b3 10.11.0.4:8300 follower true 3
|
||||||
```
|
```
|
||||||
|
|
||||||
Take note of which agent is the leader.
|
Take note of which agent is the leader.
|
||||||
|
|
||||||
**2.** Copy the new `consul` binary onto your servers and replace the existing
|
2. Copy the new `consul` binary onto your servers and replace the existing binary with the new one.
|
||||||
binary with the new one.
|
|
||||||
|
|
||||||
**3.** The following steps must be done in order on the server agents, leaving the leader
|
3. Use a service management system such as systemd or upstart to restart the Consul service on each server. You must restart follower server agents first, leaving the leader agent for last. If you are not using a service management system, you must restart the agent manually.
|
||||||
agent for last. First, use a service management system (e.g., systemd, upstart, etc.) to restart the Consul service. If
|
|
||||||
you are not using a service management system, you must restart the agent manually.
|
|
||||||
|
|
||||||
To validate that the agent has rejoined the cluster and is in sync with the leader, issue the
|
To validate that the agent has rejoined the cluster and is in sync with the leader after you restart it, issue the following command to the agent:
|
||||||
following command:
|
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul info
|
$ consul info
|
||||||
```
|
```
|
||||||
|
|
||||||
Check whether the `commit_index` and `last_log_index` fields have the same value. If done properly,
|
Check whether the `commit_index` and `last_log_index` fields have the same value. If done properly,
|
||||||
this should avoid an unexpected leadership election due to loss of quorum.
|
this should avoid an unexpected leadership election due to loss of quorum.
|
||||||
|
|
||||||
**4.** Double-check that all servers are showing up in the cluster as expected and are on
|
4. Double-check that all servers joined the cluster as expected and run the correct version. Issue the following command:
|
||||||
the correct version by issuing:
|
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul members
|
$ consul members
|
||||||
```
|
```
|
||||||
|
|
||||||
You should receive output similar to this:
|
You should receive output that lists the servers as `alive` and running the same updated Consul version.
|
||||||
|
|
||||||
```
|
```shell-session hideClipboard
|
||||||
Node Address Status Type Build Protocol DC
|
Node Address Status Type Build Protocol DC
|
||||||
dc1-node1 10.11.0.2:8301 alive server 1.8.3 2 dc1
|
dc1-node1 10.11.0.2:8301 alive server 1.8.3 2 dc1
|
||||||
dc1-node2 10.11.0.3:8301 alive server 1.8.3 2 dc1
|
dc1-node2 10.11.0.3:8301 alive server 1.8.3 2 dc1
|
||||||
dc1-node3 10.11.0.4:8301 alive server 1.8.3 2 dc1
|
dc1-node3 10.11.0.4:8301 alive server 1.8.3 2 dc1
|
||||||
```
|
```
|
||||||
|
|
||||||
Also double-check the raft state to make sure there is a leader and sufficient voters:
|
Also double-check the Raft state to make sure there is a leader and sufficient voters:
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul operator raft list-peers
|
$ consul operator raft list-peers
|
||||||
```
|
```
|
||||||
|
|
||||||
You should receive output similar to this:
|
You should receive output that lists one server as the `leader` and the rest as `follower`:
|
||||||
|
|
||||||
```
|
```shell-session hideClipboard
|
||||||
Node ID Address State Voter RaftProtocol
|
Node ID Address State Voter RaftProtocol
|
||||||
dc1-node1 ae15858f-7f5f-4dcb-b7d5-710fdcdd2745 10.11.0.2:8300 leader true 3
|
dc1-node1 ae15858f-7f5f-4dcb-b7d5-710fdcdd2745 10.11.0.2:8300 leader true 3
|
||||||
dc1-node2 20e6be1b-f1cb-4aab-929f-f7d2d43d9a96 10.11.0.3:8300 follower true 3
|
dc1-node2 20e6be1b-f1cb-4aab-929f-f7d2d43d9a96 10.11.0.3:8300 follower true 3
|
||||||
dc1-node3 658c343b-8769-431f-a71a-236f9dbb17b3 10.11.0.4:8300 follower true 3
|
dc1-node3 658c343b-8769-431f-a71a-236f9dbb17b3 10.11.0.4:8300 follower true 3
|
||||||
```
|
```
|
||||||
|
|
||||||
**5.** Set your `log_level` back to its original value and issue the following command
|
5. Set your `log_level` back to its original value and issue the following command
|
||||||
on your servers to reload the configuration:
|
on your servers to reload the configuration:
|
||||||
|
|
||||||
```
|
```shell-session
|
||||||
consul reload
|
$ consul reload
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
@ -164,7 +169,7 @@ Most problems with upgrading occur due to either failing to upgrade the leader a
|
||||||
or failing to wait for a follower agent to fully rejoin a cluster before moving
|
or failing to wait for a follower agent to fully rejoin a cluster before moving
|
||||||
on to another server. This can cause a loss of quorum and occasionally can result in
|
on to another server. This can cause a loss of quorum and occasionally can result in
|
||||||
all of your servers attempting to kick off leadership elections endlessly without ever
|
all of your servers attempting to kick off leadership elections endlessly without ever
|
||||||
reaching a quorum and electing a leader.
|
reaching a quorum and electing a leader. Consul Enterprise users should [disable upgrade migration](#enterprise-upgrades) to prevent autopilot from prematurely electing a new cluster leader.
|
||||||
|
|
||||||
Most of these problems can be solved by following the steps outlined in our
|
Most of these problems can be solved by following the steps outlined in our
|
||||||
[Disaster recovery for Consul clusters](/consul/tutorials/datacenter-operations/recovery-outage) document.
|
[Disaster recovery for Consul clusters](/consul/tutorials/datacenter-operations/recovery-outage) document.
|
||||||
|
|
|
@ -403,6 +403,10 @@
|
||||||
"path": "services/discovery/dns-static-lookups"
|
"path": "services/discovery/dns-static-lookups"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"title": "Cache DNS lookups",
|
||||||
|
"path": "services/discovery/dns-cache"
|
||||||
|
},
|
||||||
|
{
|
||||||
"title": "Enable dynamic DNS lookups",
|
"title": "Enable dynamic DNS lookups",
|
||||||
"path": "services/discovery/dns-dynamic-lookups"
|
"path": "services/discovery/dns-dynamic-lookups"
|
||||||
}
|
}
|
||||||
|
@ -465,6 +469,10 @@
|
||||||
"title": "TCP route",
|
"title": "TCP route",
|
||||||
"path": "connect/config-entries/tcp-route"
|
"path": "connect/config-entries/tcp-route"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "File system certificate",
|
||||||
|
"path": "connect/config-entries/file-system-certificate"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Inline certificate",
|
"title": "Inline certificate",
|
||||||
"path": "connect/config-entries/inline-certificate"
|
"path": "connect/config-entries/inline-certificate"
|
||||||
|
@ -1021,7 +1029,16 @@
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
"title": "Consul KV",
|
"title": "Consul KV",
|
||||||
"path": "dynamic-app-config/kv"
|
"routes": [
|
||||||
|
{
|
||||||
|
"title": "Overview",
|
||||||
|
"path": "dynamic-app-config/kv"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Store keys and values",
|
||||||
|
"path": "dynamic-app-config/kv/store"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Sessions",
|
"title": "Sessions",
|
||||||
|
@ -2043,7 +2060,7 @@
|
||||||
"path": "nia/enterprise/license"
|
"path": "nia/enterprise/license"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Terraform Cloud Driver",
|
"title": "HCP Terraform Driver",
|
||||||
"href": "/docs/nia/network-drivers/terraform-cloud"
|
"href": "/docs/nia/network-drivers/terraform-cloud"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -2060,8 +2077,8 @@
|
||||||
"path": "nia/network-drivers/terraform"
|
"path": "nia/network-drivers/terraform"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Terraform Cloud",
|
"title": "HCP Terraform",
|
||||||
"path": "nia/network-drivers/terraform-cloud"
|
"path": "nia/network-drivers/hcp-terraform"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -242,4 +242,14 @@ module.exports = [
|
||||||
destination: '/consul/docs/:version/architecture/catalog/v2/:slug',
|
destination: '/consul/docs/:version/architecture/catalog/v2/:slug',
|
||||||
permanent: true
|
permanent: true
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
source: '/consul/docs/nia/network-drivers/terraform-cloud',
|
||||||
|
destination: '/consul/docs/nia/network-drivers/hcp-terraform',
|
||||||
|
permanent: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
source: '/consul/docs/:version(v1\.(?:8|9|10|11|12|13|14|15|16|17)\.x)/nia/network-drivers/hcp-terraform',
|
||||||
|
destination: '/consul/docs/:version/nia/network-drivers/terraform-cloud',
|
||||||
|
permanent: true
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue