extensions: refactor serverless plugin to use extensions from config entry fields (#15817)
docs: update config entry docs and the Lambda manual registration docs Co-authored-by: Nitya Dhanushkodi <nitya@hashicorp.com> Co-authored-by: Eric <eric@haberkorn.co>
This commit is contained in:
parent
87485d05bd
commit
8386bf19bf
|
@ -0,0 +1,3 @@
|
|||
```release-note:breaking-change
|
||||
extensions: Refactor Lambda integration to get configured with the Envoy extensions field on service-defaults configuration entries.
|
||||
```
|
|
@ -37,6 +37,7 @@ func ComputeResolvedServiceConfig(
|
|||
thisReply.TransparentProxy = proxyConf.TransparentProxy
|
||||
thisReply.MeshGateway = proxyConf.MeshGateway
|
||||
thisReply.Expose = proxyConf.Expose
|
||||
thisReply.EnvoyExtensions = proxyConf.EnvoyExtensions
|
||||
|
||||
// Extract the global protocol from proxyConf for upstream configs.
|
||||
rawProtocol := proxyConf.Config["protocol"]
|
||||
|
@ -102,6 +103,9 @@ func ComputeResolvedServiceConfig(
|
|||
}
|
||||
|
||||
thisReply.Meta = serviceConf.Meta
|
||||
// Service defaults' envoy extensions are appended to the proxy defaults extensions so that proxy defaults
|
||||
// extensions are applied first.
|
||||
thisReply.EnvoyExtensions = append(thisReply.EnvoyExtensions, serviceConf.EnvoyExtensions...)
|
||||
}
|
||||
|
||||
// First collect all upstreams into a set of seen upstreams.
|
||||
|
|
|
@ -333,6 +333,82 @@ func Test_ComputeResolvedServiceConfig(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "servicedefaults envoy extension",
|
||||
args: args{
|
||||
scReq: &structs.ServiceConfigRequest{
|
||||
Name: "sid",
|
||||
},
|
||||
entries: &ResolvedServiceConfigSet{
|
||||
ServiceDefaults: map[structs.ServiceID]*structs.ServiceConfigEntry{
|
||||
sid: {
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "sd-ext",
|
||||
Required: false,
|
||||
Arguments: map[string]interface{}{"arg": "val"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &structs.ServiceConfigResponse{
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "sd-ext",
|
||||
Required: false,
|
||||
Arguments: map[string]interface{}{"arg": "val"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "proxydefaults envoy extension appended to servicedefaults extension",
|
||||
args: args{
|
||||
scReq: &structs.ServiceConfigRequest{
|
||||
Name: "sid",
|
||||
},
|
||||
entries: &ResolvedServiceConfigSet{
|
||||
ProxyDefaults: map[string]*structs.ProxyConfigEntry{
|
||||
acl.DefaultEnterpriseMeta().PartitionOrDefault(): {
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "pd-ext",
|
||||
Required: false,
|
||||
Arguments: map[string]interface{}{"arg": "val"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ServiceDefaults: map[structs.ServiceID]*structs.ServiceConfigEntry{
|
||||
sid: {
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "sd-ext",
|
||||
Required: false,
|
||||
Arguments: map[string]interface{}{"arg": "val"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &structs.ServiceConfigResponse{
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "pd-ext",
|
||||
Required: false,
|
||||
Arguments: map[string]interface{}{"arg": "val"},
|
||||
},
|
||||
{
|
||||
Name: "sd-ext",
|
||||
Required: false,
|
||||
Arguments: map[string]interface{}{"arg": "val"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
@ -168,6 +168,12 @@ type compiler struct {
|
|||
// This is an OUTPUT field.
|
||||
serviceMeta map[string]string
|
||||
|
||||
// envoyExtensions contains the Envoy Extensions configured through service defaults or proxy defaults config
|
||||
// entries for this discovery chain.
|
||||
//
|
||||
// This is an OUTPUT field.
|
||||
envoyExtensions []structs.EnvoyExtension
|
||||
|
||||
// startNode is computed inside of assembleChain()
|
||||
//
|
||||
// This is an OUTPUT field.
|
||||
|
@ -338,6 +344,7 @@ func (c *compiler) compile() (*structs.CompiledDiscoveryChain, error) {
|
|||
CustomizationHash: customizationHash,
|
||||
Protocol: c.protocol,
|
||||
ServiceMeta: c.serviceMeta,
|
||||
EnvoyExtensions: c.envoyExtensions,
|
||||
StartNode: c.startNode,
|
||||
Nodes: c.nodes,
|
||||
Targets: c.loadedTargets,
|
||||
|
@ -555,9 +562,17 @@ func (c *compiler) assembleChain() error {
|
|||
|
||||
sid := structs.NewServiceID(c.serviceName, c.GetEnterpriseMeta())
|
||||
|
||||
// Extract the service meta for the service named by this discovery chain.
|
||||
// Extract extensions from proxy defaults.
|
||||
proxyDefaults := c.entries.GetProxyDefaults(c.GetEnterpriseMeta().PartitionOrDefault())
|
||||
if proxyDefaults != nil {
|
||||
c.envoyExtensions = proxyDefaults.EnvoyExtensions
|
||||
}
|
||||
|
||||
// Extract the service meta for the service named by this discovery chain and add extensions from the service
|
||||
// defaults.
|
||||
if serviceDefault := c.entries.GetService(sid); serviceDefault != nil {
|
||||
c.serviceMeta = serviceDefault.GetMeta()
|
||||
c.envoyExtensions = append(c.envoyExtensions, serviceDefault.EnvoyExtensions...)
|
||||
}
|
||||
|
||||
// Check for short circuit path.
|
||||
|
|
|
@ -56,6 +56,7 @@ func TestCompile(t *testing.T) {
|
|||
"loadbalancer splitter and resolver": testcase_LBSplitterAndResolver(),
|
||||
"loadbalancer resolver": testcase_LBResolver(),
|
||||
"service redirect to service with default resolver is not a default chain": testcase_RedirectToDefaultResolverIsNotDefaultChain(),
|
||||
"extensions": testcase_Extensions(),
|
||||
"service meta projection": testcase_ServiceMetaProjection(),
|
||||
"service meta projection with redirect": testcase_ServiceMetaProjectionWithRedirect(),
|
||||
|
||||
|
@ -1671,6 +1672,59 @@ func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
|
|||
return compileTestCase{entries: entries, expect: expect}
|
||||
}
|
||||
|
||||
func testcase_Extensions() compileTestCase {
|
||||
entries := newEntries()
|
||||
entries.AddProxyDefaults(&structs.ProxyConfigEntry{
|
||||
Kind: structs.ProxyDefaults,
|
||||
Name: structs.ProxyConfigGlobal,
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "ext1",
|
||||
},
|
||||
},
|
||||
})
|
||||
entries.AddServices(
|
||||
&structs.ServiceConfigEntry{
|
||||
Kind: structs.ServiceDefaults,
|
||||
Name: "main",
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "ext2",
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
expect := &structs.CompiledDiscoveryChain{
|
||||
Protocol: "tcp",
|
||||
Default: true,
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "ext1",
|
||||
},
|
||||
{
|
||||
Name: "ext2",
|
||||
},
|
||||
},
|
||||
StartNode: "resolver:main.default.default.dc1",
|
||||
Nodes: map[string]*structs.DiscoveryGraphNode{
|
||||
"resolver:main.default.default.dc1": {
|
||||
Type: structs.DiscoveryGraphNodeTypeResolver,
|
||||
Name: "main.default.default.dc1",
|
||||
Resolver: &structs.DiscoveryResolver{
|
||||
Default: true,
|
||||
ConnectTimeout: 5 * time.Second,
|
||||
Target: "main.default.default.dc1",
|
||||
},
|
||||
},
|
||||
},
|
||||
Targets: map[string]*structs.DiscoveryTarget{
|
||||
"main.default.default.dc1": newTarget(structs.DiscoveryTargetOpts{Service: "main"}, nil),
|
||||
},
|
||||
}
|
||||
|
||||
return compileTestCase{entries: entries, expect: expect}
|
||||
}
|
||||
|
||||
func testcase_ServiceMetaProjection() compileTestCase {
|
||||
entries := newEntries()
|
||||
entries.AddServices(
|
||||
|
|
|
@ -950,11 +950,15 @@ func TestConfigSnapshotTerminatingGatewayWithLambdaService(t testing.T, extraUpd
|
|||
CorrelationID: serviceConfigIDPrefix + web.String(),
|
||||
Result: &structs.ServiceConfigResponse{
|
||||
ProxyConfig: map[string]interface{}{"protocol": "http"},
|
||||
Meta: map[string]string{
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{
|
||||
"ARN": "lambda-arn",
|
||||
"PayloadPassthrough": true,
|
||||
"Region": "us-east-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
@ -115,6 +115,7 @@ type ServiceConfigEntry struct {
|
|||
LocalConnectTimeoutMs int `json:",omitempty" alias:"local_connect_timeout_ms"`
|
||||
LocalRequestTimeoutMs int `json:",omitempty" alias:"local_request_timeout_ms"`
|
||||
BalanceInboundConnections string `json:",omitempty" alias:"balance_inbound_connections"`
|
||||
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
|
@ -235,6 +236,10 @@ func (e *ServiceConfigEntry) Validate() error {
|
|||
}
|
||||
}
|
||||
|
||||
if err := validateEnvoyExtensions(e.EnvoyExtensions); err != nil {
|
||||
validationErr = multierror.Append(validationErr, err)
|
||||
}
|
||||
|
||||
return validationErr
|
||||
}
|
||||
|
||||
|
@ -282,6 +287,38 @@ func (e *ServiceConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
|||
return &e.EnterpriseMeta
|
||||
}
|
||||
|
||||
// EnvoyExtension has configuration for an extension that patches Envoy resources.
|
||||
type EnvoyExtension struct {
|
||||
Name string
|
||||
Required bool
|
||||
Arguments map[string]interface{}
|
||||
}
|
||||
|
||||
func builtInExtension(name string) bool {
|
||||
extensions := map[string]struct{}{
|
||||
"builtin/aws/lambda": {},
|
||||
}
|
||||
|
||||
_, ok := extensions[name]
|
||||
|
||||
return ok
|
||||
}
|
||||
|
||||
func validateEnvoyExtensions(extensions []EnvoyExtension) error {
|
||||
var err error
|
||||
for i, extension := range extensions {
|
||||
if extension.Name == "" {
|
||||
err = multierror.Append(err, fmt.Errorf("invalid EnvoyExtensions[%d]: Name is required", i))
|
||||
}
|
||||
|
||||
if !builtInExtension(extension.Name) {
|
||||
err = multierror.Append(err, fmt.Errorf("invalid EnvoyExtensions[%d]: Name %q is not a built-in extension", i, extension.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
type UpstreamConfiguration struct {
|
||||
// Overrides is a slice of per-service configuration. The name field is
|
||||
// required.
|
||||
|
@ -343,6 +380,7 @@ type ProxyConfigEntry struct {
|
|||
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
|
||||
Expose ExposeConfig `json:",omitempty"`
|
||||
AccessLogs AccessLogsConfig `json:",omitempty" alias:"access_logs"`
|
||||
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
|
@ -427,6 +465,10 @@ func (e *ProxyConfigEntry) Validate() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if err := validateEnvoyExtensions(e.EnvoyExtensions); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return e.validateEnterpriseMeta()
|
||||
}
|
||||
|
||||
|
@ -1127,6 +1169,7 @@ type ServiceConfigResponse struct {
|
|||
Mode ProxyMode `json:",omitempty"`
|
||||
Destination DestinationConfig `json:",omitempty"`
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
EnvoyExtensions []EnvoyExtension `json:",omitempty"`
|
||||
QueryMeta
|
||||
}
|
||||
|
||||
|
|
|
@ -2701,6 +2701,42 @@ func TestServiceConfigEntry(t *testing.T) {
|
|||
},
|
||||
validateErr: "invalid value for balance_outbound_connections",
|
||||
},
|
||||
"validate: invalid extension": {
|
||||
entry: &ServiceConfigEntry{
|
||||
Kind: ServiceDefaults,
|
||||
Name: "external",
|
||||
Protocol: "http",
|
||||
EnvoyExtensions: []EnvoyExtension{
|
||||
{},
|
||||
},
|
||||
},
|
||||
validateErr: "invalid EnvoyExtensions[0]: Name is required",
|
||||
},
|
||||
"validate: invalid extension name": {
|
||||
entry: &ServiceConfigEntry{
|
||||
Kind: ServiceDefaults,
|
||||
Name: "external",
|
||||
Protocol: "http",
|
||||
EnvoyExtensions: []EnvoyExtension{
|
||||
{
|
||||
Name: "not-a-builtin",
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: `invalid EnvoyExtensions[0]: Name "not-a-builtin" is not a built-in extension`,
|
||||
},
|
||||
"validate: valid extension name": {
|
||||
entry: &ServiceConfigEntry{
|
||||
Kind: ServiceDefaults,
|
||||
Name: "external",
|
||||
Protocol: "http",
|
||||
EnvoyExtensions: []EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
}
|
||||
|
|
|
@ -38,6 +38,9 @@ type CompiledDiscoveryChain struct {
|
|||
// entry for the service named ServiceName.
|
||||
ServiceMeta map[string]string `json:",omitempty"`
|
||||
|
||||
// EnvoyExtensions has a list of configurations for an extension that patches Envoy resources.
|
||||
EnvoyExtensions []EnvoyExtension `json:",omitempty"`
|
||||
|
||||
// StartNode is the first key into the Nodes map that should be followed
|
||||
// when walking the discovery chain.
|
||||
StartNode string `json:",omitempty"`
|
||||
|
|
|
@ -67,6 +67,18 @@ func (o *CompiledDiscoveryChain) DeepCopy() *CompiledDiscoveryChain {
|
|||
cp.ServiceMeta[k2] = v2
|
||||
}
|
||||
}
|
||||
if o.EnvoyExtensions != nil {
|
||||
cp.EnvoyExtensions = make([]EnvoyExtension, len(o.EnvoyExtensions))
|
||||
copy(cp.EnvoyExtensions, o.EnvoyExtensions)
|
||||
for i2 := range o.EnvoyExtensions {
|
||||
if o.EnvoyExtensions[i2].Arguments != nil {
|
||||
cp.EnvoyExtensions[i2].Arguments = make(map[string]interface{}, len(o.EnvoyExtensions[i2].Arguments))
|
||||
for k4, v4 := range o.EnvoyExtensions[i2].Arguments {
|
||||
cp.EnvoyExtensions[i2].Arguments[k4] = v4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.Nodes != nil {
|
||||
cp.Nodes = make(map[string]*DiscoveryGraphNode, len(o.Nodes))
|
||||
for k2, v2 := range o.Nodes {
|
||||
|
@ -540,6 +552,18 @@ func (o *ServiceConfigEntry) DeepCopy() *ServiceConfigEntry {
|
|||
copy(cp.Destination.Addresses, o.Destination.Addresses)
|
||||
}
|
||||
}
|
||||
if o.EnvoyExtensions != nil {
|
||||
cp.EnvoyExtensions = make([]EnvoyExtension, len(o.EnvoyExtensions))
|
||||
copy(cp.EnvoyExtensions, o.EnvoyExtensions)
|
||||
for i2 := range o.EnvoyExtensions {
|
||||
if o.EnvoyExtensions[i2].Arguments != nil {
|
||||
cp.EnvoyExtensions[i2].Arguments = make(map[string]interface{}, len(o.EnvoyExtensions[i2].Arguments))
|
||||
for k4, v4 := range o.EnvoyExtensions[i2].Arguments {
|
||||
cp.EnvoyExtensions[i2].Arguments[k4] = v4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if o.Meta != nil {
|
||||
cp.Meta = make(map[string]string, len(o.Meta))
|
||||
for k2, v2 := range o.Meta {
|
||||
|
@ -597,6 +621,18 @@ func (o *ServiceConfigResponse) DeepCopy() *ServiceConfigResponse {
|
|||
cp.Meta[k2] = v2
|
||||
}
|
||||
}
|
||||
if o.EnvoyExtensions != nil {
|
||||
cp.EnvoyExtensions = make([]EnvoyExtension, len(o.EnvoyExtensions))
|
||||
copy(cp.EnvoyExtensions, o.EnvoyExtensions)
|
||||
for i2 := range o.EnvoyExtensions {
|
||||
if o.EnvoyExtensions[i2].Arguments != nil {
|
||||
cp.EnvoyExtensions[i2].Arguments = make(map[string]interface{}, len(o.EnvoyExtensions[i2].Arguments))
|
||||
for k4, v4 := range o.EnvoyExtensions[i2].Arguments {
|
||||
cp.EnvoyExtensions[i2].Arguments[k4] = v4
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return &cp
|
||||
}
|
||||
|
||||
|
|
|
@ -29,9 +29,9 @@ func TestServerlessPluginFromSnapshot(t *testing.T) {
|
|||
// Otherwise payload-passthrough=false and invocation-mode=synchronous.
|
||||
// This is used to test all the permutations.
|
||||
makeServiceDefaults := func(opposite bool) *structs.ServiceConfigEntry {
|
||||
payloadPassthrough := "true"
|
||||
payloadPassthrough := true
|
||||
if opposite {
|
||||
payloadPassthrough = "false"
|
||||
payloadPassthrough = false
|
||||
}
|
||||
|
||||
invocationMode := "synchronous"
|
||||
|
@ -43,12 +43,16 @@ func TestServerlessPluginFromSnapshot(t *testing.T) {
|
|||
Kind: structs.ServiceDefaults,
|
||||
Name: "db",
|
||||
Protocol: "http",
|
||||
Meta: map[string]string{
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": payloadPassthrough,
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/invocation-mode": invocationMode,
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
||||
EnvoyExtensions: []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{
|
||||
"ARN": "lambda-arn",
|
||||
"PayloadPassthrough": payloadPassthrough,
|
||||
"InvocationMode": invocationMode,
|
||||
"Region": "us-east-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,73 +14,69 @@ import (
|
|||
envoy_tls_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
|
||||
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
|
||||
pstruct "github.com/golang/protobuf/ptypes/struct"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
|
||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
|
||||
const (
|
||||
lambdaPrefix string = "serverless.consul.hashicorp.com/v1alpha1"
|
||||
lambdaEnabledTag string = lambdaPrefix + "/lambda/enabled"
|
||||
lambdaArnTag string = lambdaPrefix + "/lambda/arn"
|
||||
lambdaPayloadPassthroughTag string = lambdaPrefix + "/lambda/payload-passthrough"
|
||||
lambdaRegionTag string = lambdaPrefix + "/lambda/region"
|
||||
lambdaInvocationMode string = lambdaPrefix + "/lambda/invocation-mode"
|
||||
)
|
||||
|
||||
type lambdaPatcher struct {
|
||||
arn string
|
||||
payloadPassthrough bool
|
||||
region string
|
||||
kind api.ServiceKind
|
||||
invocationMode envoy_lambda_v3.Config_InvocationMode
|
||||
ARN string `mapstructure:"ARN"`
|
||||
PayloadPassthrough bool `mapstructure:"PayloadPassthrough"`
|
||||
Region string `mapstructure:"Region"`
|
||||
Kind api.ServiceKind
|
||||
InvocationMode string `mapstructure:"InvocationMode"`
|
||||
}
|
||||
|
||||
var _ patcher = (*lambdaPatcher)(nil)
|
||||
|
||||
func makeLambdaPatcher(serviceConfig xdscommon.ServiceConfig) (patcher, bool) {
|
||||
var patcher lambdaPatcher
|
||||
if !isStringTrue(serviceConfig.Meta[lambdaEnabledTag]) {
|
||||
|
||||
// TODO this is a hack. We should iterate through the extensions outside of here
|
||||
if len(serviceConfig.EnvoyExtensions) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// TODO this is a hack. We should iterate through the extensions outside of here
|
||||
extension := serviceConfig.EnvoyExtensions[0]
|
||||
if extension.Name != "builtin/aws/lambda" {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// TODO this fails when types aren't encode properly. We need to check this earlier in the Validate RPC.
|
||||
err := mapstructure.Decode(extension.Arguments, &patcher)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if patcher.ARN == "" {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if patcher.Region == "" {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
patcher.Kind = serviceConfig.Kind
|
||||
|
||||
return patcher, true
|
||||
}
|
||||
|
||||
arn := serviceConfig.Meta[lambdaArnTag]
|
||||
if arn == "" {
|
||||
return patcher, false
|
||||
func toEnvoyInvocationMode(s string) envoy_lambda_v3.Config_InvocationMode {
|
||||
m := envoy_lambda_v3.Config_SYNCHRONOUS
|
||||
if s == "asynchronous" {
|
||||
m = envoy_lambda_v3.Config_ASYNCHRONOUS
|
||||
}
|
||||
|
||||
region := serviceConfig.Meta[lambdaRegionTag]
|
||||
if region == "" {
|
||||
return patcher, false
|
||||
}
|
||||
|
||||
payloadPassthrough := isStringTrue(serviceConfig.Meta[lambdaPayloadPassthroughTag])
|
||||
|
||||
invocationModeStr := serviceConfig.Meta[lambdaInvocationMode]
|
||||
invocationMode := envoy_lambda_v3.Config_SYNCHRONOUS
|
||||
if invocationModeStr == "asynchronous" {
|
||||
invocationMode = envoy_lambda_v3.Config_ASYNCHRONOUS
|
||||
}
|
||||
|
||||
return lambdaPatcher{
|
||||
arn: arn,
|
||||
payloadPassthrough: payloadPassthrough,
|
||||
region: region,
|
||||
kind: serviceConfig.Kind,
|
||||
invocationMode: invocationMode,
|
||||
}, true
|
||||
}
|
||||
|
||||
func isStringTrue(v string) bool {
|
||||
return v == "true"
|
||||
return m
|
||||
}
|
||||
|
||||
func (p lambdaPatcher) CanPatch(kind api.ServiceKind) bool {
|
||||
return kind == p.kind
|
||||
return kind == p.Kind
|
||||
}
|
||||
|
||||
func (p lambdaPatcher) PatchRoute(route *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
|
||||
if p.kind != api.ServiceKindTerminatingGateway {
|
||||
if p.Kind != api.ServiceKindTerminatingGateway {
|
||||
return route, false, nil
|
||||
}
|
||||
|
||||
|
@ -136,7 +132,7 @@ func (p lambdaPatcher) PatchCluster(c *envoy_cluster_v3.Cluster) (*envoy_cluster
|
|||
Address: &envoy_core_v3.Address{
|
||||
Address: &envoy_core_v3.Address_SocketAddress{
|
||||
SocketAddress: &envoy_core_v3.SocketAddress{
|
||||
Address: fmt.Sprintf("lambda.%s.amazonaws.com", p.region),
|
||||
Address: fmt.Sprintf("lambda.%s.amazonaws.com", p.Region),
|
||||
PortSpecifier: &envoy_core_v3.SocketAddress_PortValue{
|
||||
PortValue: 443,
|
||||
},
|
||||
|
@ -170,9 +166,9 @@ func (p lambdaPatcher) PatchFilter(filter *envoy_listener_v3.Filter) (*envoy_lis
|
|||
lambdaHttpFilter, err := makeEnvoyHTTPFilter(
|
||||
"envoy.filters.http.aws_lambda",
|
||||
&envoy_lambda_v3.Config{
|
||||
Arn: p.arn,
|
||||
PayloadPassthrough: p.payloadPassthrough,
|
||||
InvocationMode: p.invocationMode,
|
||||
Arn: p.ARN,
|
||||
PayloadPassthrough: p.PayloadPassthrough,
|
||||
InvocationMode: toEnvoyInvocationMode(p.InvocationMode),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package serverlessplugin
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -14,7 +13,6 @@ func TestMakeLambdaPatcher(t *testing.T) {
|
|||
kind := api.ServiceKindTerminatingGateway
|
||||
cases := []struct {
|
||||
name string
|
||||
enabled bool
|
||||
arn string
|
||||
payloadPassthrough bool
|
||||
region string
|
||||
|
@ -22,37 +20,29 @@ func TestMakeLambdaPatcher(t *testing.T) {
|
|||
ok bool
|
||||
}{
|
||||
{
|
||||
name: "no meta",
|
||||
ok: true,
|
||||
},
|
||||
{
|
||||
name: "lambda disabled",
|
||||
enabled: false,
|
||||
ok: true,
|
||||
name: "no extension",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
name: "missing arn",
|
||||
enabled: true,
|
||||
region: "blah",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
name: "missing region",
|
||||
enabled: true,
|
||||
region: "arn",
|
||||
arn: "arn",
|
||||
ok: false,
|
||||
},
|
||||
{
|
||||
name: "including payload passthrough",
|
||||
enabled: true,
|
||||
arn: "arn",
|
||||
region: "blah",
|
||||
payloadPassthrough: true,
|
||||
expected: lambdaPatcher{
|
||||
arn: "arn",
|
||||
payloadPassthrough: true,
|
||||
region: "blah",
|
||||
kind: kind,
|
||||
ARN: "arn",
|
||||
PayloadPassthrough: true,
|
||||
Region: "blah",
|
||||
Kind: kind,
|
||||
},
|
||||
ok: true,
|
||||
},
|
||||
|
@ -62,22 +52,25 @@ func TestMakeLambdaPatcher(t *testing.T) {
|
|||
t.Run(tc.name, func(t *testing.T) {
|
||||
config := xdscommon.ServiceConfig{
|
||||
Kind: kind,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: strconv.FormatBool(tc.enabled),
|
||||
lambdaArnTag: tc.arn,
|
||||
lambdaRegionTag: tc.region,
|
||||
EnvoyExtensions: []api.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{
|
||||
"ARN": tc.arn,
|
||||
"Region": tc.region,
|
||||
"PayloadPassthrough": tc.payloadPassthrough,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if tc.payloadPassthrough {
|
||||
config.Meta[lambdaPayloadPassthroughTag] = strconv.FormatBool(tc.payloadPassthrough)
|
||||
}
|
||||
|
||||
patcher, ok := makeLambdaPatcher(config)
|
||||
|
||||
require.Equal(t, tc.ok, ok)
|
||||
|
||||
if tc.ok {
|
||||
require.Equal(t, tc.expected, patcher)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,10 +36,10 @@ func TestGetPatcherBySNI(t *testing.T) {
|
|||
sni: "lambda-sni",
|
||||
kind: api.ServiceKindTerminatingGateway,
|
||||
expected: lambdaPatcher{
|
||||
arn: "arn",
|
||||
region: "region",
|
||||
payloadPassthrough: false,
|
||||
kind: api.ServiceKindTerminatingGateway,
|
||||
ARN: "arn",
|
||||
Region: "region",
|
||||
PayloadPassthrough: false,
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -74,24 +74,27 @@ func sampleConfig() xdscommon.PluginConfiguration {
|
|||
ServiceConfigs: map[api.CompoundServiceName]xdscommon.ServiceConfig{
|
||||
lambdaService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: "true",
|
||||
lambdaArnTag: "arn",
|
||||
lambdaRegionTag: "region",
|
||||
EnvoyExtensions: []api.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{
|
||||
"ARN": "arn",
|
||||
"Region": "region",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
disabledLambdaService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: "false",
|
||||
lambdaArnTag: "arn",
|
||||
lambdaRegionTag: "region",
|
||||
},
|
||||
// No extension.
|
||||
},
|
||||
invalidLambdaService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
lambdaEnabledTag: "true",
|
||||
EnvoyExtensions: []api.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{}, // ARN, etc missing
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -57,7 +57,7 @@ type ServiceConfig struct {
|
|||
// Kind identifies the final proxy kind that will make the request to the
|
||||
// destination service.
|
||||
Kind api.ServiceKind
|
||||
Meta map[string]string
|
||||
EnvoyExtensions []api.EnvoyExtension
|
||||
}
|
||||
|
||||
// PluginConfiguration is passed into Envoy plugins. It should depend on the
|
||||
|
@ -121,8 +121,8 @@ func MakePluginConfiguration(cfgSnap *proxycfg.ConfigSnapshot) PluginConfigurati
|
|||
}
|
||||
|
||||
serviceConfigs[upstreamIDToCompoundServiceName(uid)] = ServiceConfig{
|
||||
Meta: dc.ServiceMeta,
|
||||
Kind: api.ServiceKindConnectProxy,
|
||||
EnvoyExtensions: convertEnvoyExtensions(dc.EnvoyExtensions),
|
||||
}
|
||||
|
||||
compoundServiceName := upstreamIDToCompoundServiceName(uid)
|
||||
|
@ -135,7 +135,7 @@ func MakePluginConfiguration(cfgSnap *proxycfg.ConfigSnapshot) PluginConfigurati
|
|||
for svc, c := range cfgSnap.TerminatingGateway.ServiceConfigs {
|
||||
compoundServiceName := serviceNameToCompoundServiceName(svc)
|
||||
serviceConfigs[compoundServiceName] = ServiceConfig{
|
||||
Meta: c.Meta,
|
||||
EnvoyExtensions: convertEnvoyExtensions(c.EnvoyExtensions),
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
}
|
||||
|
||||
|
@ -178,3 +178,17 @@ func upstreamIDToCompoundServiceName(uid proxycfg.UpstreamID) api.CompoundServic
|
|||
Namespace: uid.NamespaceOrDefault(),
|
||||
}
|
||||
}
|
||||
|
||||
func convertEnvoyExtensions(structExtensions []structs.EnvoyExtension) []api.EnvoyExtension {
|
||||
var extensions []api.EnvoyExtension
|
||||
|
||||
for _, e := range structExtensions {
|
||||
extensions = append(extensions, api.EnvoyExtension{
|
||||
Name: e.Name,
|
||||
Required: e.Required,
|
||||
Arguments: e.Arguments,
|
||||
})
|
||||
}
|
||||
|
||||
return extensions
|
||||
}
|
||||
|
|
|
@ -42,11 +42,15 @@ func TestMakePluginConfiguration_TerminatingGateway(t *testing.T) {
|
|||
ServiceConfigs: map[api.CompoundServiceName]ServiceConfig{
|
||||
webService: {
|
||||
Kind: api.ServiceKindTerminatingGateway,
|
||||
Meta: map[string]string{
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
||||
EnvoyExtensions: []api.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{
|
||||
"ARN": "lambda-arn",
|
||||
"PayloadPassthrough": true,
|
||||
"Region": "us-east-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
apiService: {
|
||||
|
@ -84,17 +88,22 @@ func TestMakePluginConfiguration_ConnectProxy(t *testing.T) {
|
|||
Partition: "default",
|
||||
Namespace: "default",
|
||||
}
|
||||
lambdaMeta := map[string]string{
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
||||
envoyExtensions := []structs.EnvoyExtension{
|
||||
{
|
||||
Name: "builtin/aws/lambda",
|
||||
Arguments: map[string]interface{}{
|
||||
"ARN": "lambda-arn",
|
||||
"PayloadPassthrough": true,
|
||||
"Region": "us-east-1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
serviceDefaults := &structs.ServiceConfigEntry{
|
||||
Kind: structs.ServiceDefaults,
|
||||
Name: "db",
|
||||
Protocol: "http",
|
||||
Meta: lambdaMeta,
|
||||
EnvoyExtensions: envoyExtensions,
|
||||
}
|
||||
|
||||
snap := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", nil, nil, serviceDefaults)
|
||||
|
@ -103,7 +112,7 @@ func TestMakePluginConfiguration_ConnectProxy(t *testing.T) {
|
|||
ServiceConfigs: map[api.CompoundServiceName]ServiceConfig{
|
||||
dbService: {
|
||||
Kind: api.ServiceKindConnectProxy,
|
||||
Meta: lambdaMeta,
|
||||
EnvoyExtensions: convertEnvoyExtensions(envoyExtensions),
|
||||
},
|
||||
},
|
||||
SNIToServiceName: map[string]api.CompoundServiceName{
|
||||
|
|
|
@ -104,6 +104,13 @@ type ExposeConfig struct {
|
|||
Paths []ExposePath `json:",omitempty"`
|
||||
}
|
||||
|
||||
// EnvoyExtension has configuration for an extension that patches Envoy resources.
|
||||
type EnvoyExtension struct {
|
||||
Name string
|
||||
Required bool
|
||||
Arguments map[string]interface{}
|
||||
}
|
||||
|
||||
type ExposePath struct {
|
||||
// ListenerPort defines the port of the proxy's listener for exposed paths.
|
||||
ListenerPort int `json:",omitempty" alias:"listener_port"`
|
||||
|
@ -272,6 +279,7 @@ type ServiceConfigEntry struct {
|
|||
LocalConnectTimeoutMs int `json:",omitempty" alias:"local_connect_timeout_ms"`
|
||||
LocalRequestTimeoutMs int `json:",omitempty" alias:"local_request_timeout_ms"`
|
||||
BalanceInboundConnections string `json:",omitempty" alias:"balance_inbound_connections"`
|
||||
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
CreateIndex uint64
|
||||
ModifyIndex uint64
|
||||
|
@ -296,6 +304,7 @@ type ProxyConfigEntry struct {
|
|||
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
|
||||
Expose ExposeConfig `json:",omitempty"`
|
||||
AccessLogs *AccessLogsConfig `json:",omitempty"`
|
||||
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
CreateIndex uint64
|
||||
|
|
|
@ -34,6 +34,22 @@ func DestinationConfigFromStructs(t *structs.DestinationConfig, s *DestinationCo
|
|||
s.Addresses = t.Addresses
|
||||
s.Port = int32(t.Port)
|
||||
}
|
||||
func EnvoyExtensionToStructs(s *EnvoyExtension, t *structs.EnvoyExtension) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
t.Name = s.Name
|
||||
t.Required = s.Required
|
||||
t.Arguments = envoyExtensionArgumentsToStructs(s.Arguments)
|
||||
}
|
||||
func EnvoyExtensionFromStructs(t *structs.EnvoyExtension, s *EnvoyExtension) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.Name = t.Name
|
||||
s.Required = t.Required
|
||||
s.Arguments = envoyExtensionArgumentsFromStructs(t.Arguments)
|
||||
}
|
||||
func ExposeConfigToStructs(s *ExposeConfig, t *structs.ExposeConfig) {
|
||||
if s == nil {
|
||||
return
|
||||
|
@ -706,6 +722,14 @@ func ServiceDefaultsToStructs(s *ServiceDefaults, t *structs.ServiceConfigEntry)
|
|||
t.LocalConnectTimeoutMs = int(s.LocalConnectTimeoutMs)
|
||||
t.LocalRequestTimeoutMs = int(s.LocalRequestTimeoutMs)
|
||||
t.BalanceInboundConnections = s.BalanceInboundConnections
|
||||
{
|
||||
t.EnvoyExtensions = make([]structs.EnvoyExtension, len(s.EnvoyExtensions))
|
||||
for i := range s.EnvoyExtensions {
|
||||
if s.EnvoyExtensions[i] != nil {
|
||||
EnvoyExtensionToStructs(s.EnvoyExtensions[i], &t.EnvoyExtensions[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Meta = s.Meta
|
||||
}
|
||||
func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefaults) {
|
||||
|
@ -744,6 +768,16 @@ func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefault
|
|||
s.LocalConnectTimeoutMs = int32(t.LocalConnectTimeoutMs)
|
||||
s.LocalRequestTimeoutMs = int32(t.LocalRequestTimeoutMs)
|
||||
s.BalanceInboundConnections = t.BalanceInboundConnections
|
||||
{
|
||||
s.EnvoyExtensions = make([]*EnvoyExtension, len(t.EnvoyExtensions))
|
||||
for i := range t.EnvoyExtensions {
|
||||
{
|
||||
var x EnvoyExtension
|
||||
EnvoyExtensionFromStructs(&t.EnvoyExtensions[i], &x)
|
||||
s.EnvoyExtensions[i] = &x
|
||||
}
|
||||
}
|
||||
}
|
||||
s.Meta = t.Meta
|
||||
}
|
||||
func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentionsConfigEntry) {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/hashicorp/consul/acl"
|
||||
|
@ -266,3 +267,14 @@ func meshGatewayModeToStructs(a MeshGatewayMode) structs.MeshGatewayMode {
|
|||
return structs.MeshGatewayModeDefault
|
||||
}
|
||||
}
|
||||
|
||||
func envoyExtensionArgumentsToStructs(args *structpb.Value) map[string]interface{} {
|
||||
return args.GetStructValue().AsMap()
|
||||
}
|
||||
|
||||
func envoyExtensionArgumentsFromStructs(args map[string]interface{}) *structpb.Value {
|
||||
if s, err := structpb.NewValue(args); err == nil {
|
||||
return s
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -327,6 +327,16 @@ func (msg *TransparentProxyConfig) UnmarshalBinary(b []byte) error {
|
|||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *EnvoyExtension) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
}
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler
|
||||
func (msg *EnvoyExtension) UnmarshalBinary(b []byte) error {
|
||||
return proto.Unmarshal(b, msg)
|
||||
}
|
||||
|
||||
// MarshalBinary implements encoding.BinaryMarshaler
|
||||
func (msg *MeshGatewayConfig) MarshalBinary() ([]byte, error) {
|
||||
return proto.Marshal(msg)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,6 +3,7 @@ syntax = "proto3";
|
|||
package hashicorp.consul.internal.configentry;
|
||||
|
||||
import "google/protobuf/duration.proto";
|
||||
import "google/protobuf/struct.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "proto/pbcommon/common.proto";
|
||||
|
||||
|
@ -433,6 +434,7 @@ message ServiceDefaults {
|
|||
int32 LocalRequestTimeoutMs = 11;
|
||||
string BalanceInboundConnections = 12;
|
||||
map<string, string> Meta = 13;
|
||||
repeated EnvoyExtension EnvoyExtensions = 14;
|
||||
}
|
||||
|
||||
enum ProxyMode {
|
||||
|
@ -452,6 +454,18 @@ message TransparentProxyConfig {
|
|||
bool DialedDirectly = 2;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.EnvoyExtension
|
||||
// output=config_entry.gen.go
|
||||
// name=Structs
|
||||
message EnvoyExtension {
|
||||
string Name = 1;
|
||||
bool Required = 2;
|
||||
// mog: func-to=envoyExtensionArgumentsToStructs func-from=envoyExtensionArgumentsFromStructs
|
||||
google.protobuf.Value Arguments = 3;
|
||||
}
|
||||
|
||||
// mog annotation:
|
||||
//
|
||||
// target=github.com/hashicorp/consul/agent/structs.MeshGatewayConfig
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
"Kind": "service-defaults",
|
||||
"Name": "l1",
|
||||
"Protocol": "http",
|
||||
"Meta": {
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "${AWS_LAMBDA_REGION}",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "${AWS_LAMBDA_ARN}",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true"
|
||||
"EnvoyExtensions": [
|
||||
{
|
||||
"Name": "builtin/aws/lambda",
|
||||
"Arguments": {
|
||||
"Region": "${AWS_LAMBDA_REGION}",
|
||||
"ARN": "${AWS_LAMBDA_ARN}",
|
||||
"PayloadPassthrough": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
"Kind": "service-defaults",
|
||||
"Name": "l2",
|
||||
"Protocol": "http",
|
||||
"Meta": {
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "${AWS_LAMBDA_REGION}",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "${AWS_LAMBDA_ARN}",
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "false"
|
||||
"EnvoyExtensions": [
|
||||
{
|
||||
"Name": "builtin/aws/lambda",
|
||||
"Arguments": {
|
||||
"Region": "${AWS_LAMBDA_REGION}",
|
||||
"ARN": "${AWS_LAMBDA_ARN}",
|
||||
"PayloadPassthrough": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -308,6 +308,30 @@ spec:
|
|||
<ul><li>[Envoy](/docs/connect/proxies/envoy#proxy-config-options)</li>
|
||||
<li>[Consul's built-in proxy](/docs/connect/proxies/built-in#proxy-config-key-reference)</li></ul>`,
|
||||
},
|
||||
{
|
||||
name: 'EnvoyExtensions',
|
||||
type: 'array<EnvoyExtension>: []',
|
||||
description: `A list of extensions to modify Envoy proxy configuration.`,
|
||||
children: [
|
||||
{
|
||||
name: 'Name',
|
||||
type: `string: ""`,
|
||||
description: `Name of the extension.`,
|
||||
},
|
||||
{
|
||||
name: 'Required',
|
||||
type: `string: ""`,
|
||||
description: `When \`Required\` is true and the extension does not update any Envoy resources, an error is
|
||||
returned. Use this parameter to ensure that extensions required for secure communication are not unintentionally
|
||||
bypassed.`,
|
||||
},
|
||||
{
|
||||
name: 'Arguments',
|
||||
type: 'map<string|Any>: nil',
|
||||
description: `Arguments to pass to the extension executable.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: `string: ""`,
|
||||
|
|
|
@ -364,6 +364,30 @@ represents a location outside the Consul cluster. They can be dialed directly wh
|
|||
[Envoy Connection Balance config](https://cloudnative.to/envoy/api-v3/config/listener/v3/listener.proto.html#config-listener-v3-listener-connectionbalanceconfig)
|
||||
for details.`
|
||||
},
|
||||
{
|
||||
name: 'EnvoyExtensions',
|
||||
type: 'array<EnvoyExtension>: []',
|
||||
description: `A list of extensions to modify Envoy proxy configuration.`,
|
||||
children: [
|
||||
{
|
||||
name: 'Name',
|
||||
type: `string: ""`,
|
||||
description: `Name of the extension.`,
|
||||
},
|
||||
{
|
||||
name: 'Required',
|
||||
type: `string: ""`,
|
||||
description: `When \`Required\` is true and the extension does not update any Envoy resources, an error is
|
||||
returned. Use this parameter to ensure that extensions required for secure communication are not unintentionally
|
||||
bypassed.`,
|
||||
},
|
||||
{
|
||||
name: 'Arguments',
|
||||
type: 'map<string|Any>: nil',
|
||||
description: `Arguments to pass to the extension executable.`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Mode',
|
||||
type: `string: ""`,
|
||||
|
@ -524,10 +548,10 @@ represents a location outside the Consul cluster. They can be dialed directly wh
|
|||
name: 'EnforcingConsecutive5xx',
|
||||
type: 'int: 100',
|
||||
description: {
|
||||
hcl: `The % chance that a host will be actually ejected
|
||||
when an outlier status is detected through consecutive 5xx.`,
|
||||
yaml: `The % chance that a host will be actually ejected
|
||||
when an outlier status is detected through consecutive 5xx.`,
|
||||
hcl: `Measured in percent (%), the probability of a host's ejection
|
||||
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||
yaml: `Measured in percent (%), the probability of a host's ejection
|
||||
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -675,10 +699,10 @@ represents a location outside the Consul cluster. They can be dialed directly wh
|
|||
name: 'EnforcingConsecutive5xx',
|
||||
type: 'int: 100',
|
||||
description: {
|
||||
hcl: `The % chance that a host will be actually ejected
|
||||
when an outlier status is detected through consecutive 5xx.`,
|
||||
yaml: `The % chance that a host will be actually ejected
|
||||
when an outlier status is detected through consecutive 5xx.`,
|
||||
hcl: `Measured in percent (%), the probability of a host's ejection
|
||||
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||
yaml: `Measured in percent (%), the probability of a host's ejection
|
||||
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -46,20 +46,26 @@ You can manually register Lambda functions if you are unable to automate the pro
|
|||
$ curl --request PUT --data @lambda.json localhost:8500/v1/catalog/register
|
||||
```
|
||||
|
||||
1. Create the `service-defaults` configuration entry and include the AWS tags used to invoke the Lambda function in the `Meta` field (refer to [Supported `Meta` fields](#supported-meta-fields). The following example creates a `service-defaults` configuration entry named `lambda`:
|
||||
1. Create the `service-defaults` configuration entry and include the AWS tags used to invoke the Lambda function in the `EnvoyExtensions` configuration. Refer to [Supported `EnvoyExtension` arguments](#supported-envoyextension-arguments) for more information.
|
||||
|
||||
The following example creates a `service-defaults` configuration entry named `lambda`:
|
||||
|
||||
<CodeBlockConfig filename="lambda-service-defaults.hcl">
|
||||
|
||||
```hcl
|
||||
Kind = "service-defaults"
|
||||
Name = "lambda"
|
||||
Name = "<SERVICE_NAME>"
|
||||
Protocol = "http"
|
||||
Meta = {
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled" = "true"
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn" = "<INSERT ARN HERE>"
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough" = "true"
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region" = "us-east-2"
|
||||
EnvoyExtensions = [
|
||||
{
|
||||
"Name": "builtin/aws/lambda",
|
||||
"Arguments": {
|
||||
"Region": "us-east-2",
|
||||
"ARN": "<INSERT ARN HERE>",
|
||||
"PayloadPassthrough": true
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
@ -69,16 +75,11 @@ You can manually register Lambda functions if you are unable to automate the pro
|
|||
$ consul config write lambda-service-defaults.hcl
|
||||
```
|
||||
|
||||
### Supported `Meta` fields
|
||||
### Supported `EnvoyExtension` arguments
|
||||
|
||||
The following tags are supported. The path prefix for all tags is `serverless.consul.hashicorp.com/v1alpha1/lambda`. For example, specify the following tag to enable Consul to configure the service as an AWS Lambda function:
|
||||
The `lambda` Envoy extension supports the following arguments:
|
||||
|
||||
`serverless.consul.hashicorp.com/v1alpha1/lambda/enabled`.
|
||||
|
||||
| Tag | Description |
|
||||
| --- | --- |
|
||||
| `<prefix-path>/enabled` | Determines if Consul configures the service as an AWS Lambda. |
|
||||
| `<prefix-path>/payload-passthrough` | Determines if the body Envoy receives is converted to JSON or directly passed to Lambda. |
|
||||
| `<prefix-path>/arn` | Specifies the [AWS ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) for the service's Lambda. |
|
||||
| `<prefix-path>/invocation-mode` | Determines if Consul configures the Lambda to be invoked using the `synchronous` or `asynchronous` [invocation mode](https://docs.aws.amazon.com/lambda/latest/operatorguide/invocation-modes.html). |
|
||||
| `<prefix-path>/region` | Specifies the AWS region the Lambda is running in. |
|
||||
- `ARN` (`string`) - Specifies the [AWS ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) for the service's Lambda. `ARN` must be set to a valid Lambda function ARN.
|
||||
- `Region` (`string`) - Specifies the AWS region the Lambda is running in. `Region` must be set to a valid AWS region where the Lambda function exists.
|
||||
- `PayloadPassthrough` (`boolean: false`) - Determines if the body Envoy receives is converted to JSON or directly passed to Lambda.
|
||||
- `InvocationMode` (`string: synchronous`) - Determines if Consul configures the Lambda to be invoked using the `synchronous` or `asynchronous` [invocation mode](https://docs.aws.amazon.com/lambda/latest/operatorguide/invocation-modes.html).
|
||||
|
|
|
@ -20,6 +20,47 @@ upgrade flow.
|
|||
|
||||
The `connect.enable_serverless_plugin` configuration option was removed. Lambda integration is now enabled by default.
|
||||
|
||||
#### Lambda Configuration
|
||||
|
||||
Instead of configuring Lambda functions in the `Meta` field of `service-defaults` configuration entries, configure them with the `EnvoyExtensions` field.
|
||||
|
||||
Before Consul v1.15:
|
||||
|
||||
<CodeBlockConfig filename="lambda-service-defaults.hcl">
|
||||
|
||||
```hcl
|
||||
Kind = "service-defaults"
|
||||
Name = "<SERVICE_NAME>"
|
||||
Protocol = "http"
|
||||
Meta = {
|
||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled" = "true"
|
||||
}
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
In Consul v1.15 and higher:
|
||||
|
||||
<CodeBlockConfig filename="lambda-service-defaults.hcl">
|
||||
|
||||
```hcl
|
||||
Kind = "service-defaults"
|
||||
Name = "<SERVICE_NAME>"
|
||||
Protocol = "http"
|
||||
EnvoyExtensions = [
|
||||
{
|
||||
"Name": "builtin/aws/lambda",
|
||||
"Arguments": {
|
||||
"Region": "us-east-2",
|
||||
"ARN": "<INSERT ARN HERE>",
|
||||
"PayloadPassthrough": true
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
</CodeBlockConfig>
|
||||
|
||||
## Consul 1.14.x
|
||||
|
||||
### Service Mesh Compatibility
|
||||
|
|
Loading…
Reference in New Issue