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.TransparentProxy = proxyConf.TransparentProxy
|
||||||
thisReply.MeshGateway = proxyConf.MeshGateway
|
thisReply.MeshGateway = proxyConf.MeshGateway
|
||||||
thisReply.Expose = proxyConf.Expose
|
thisReply.Expose = proxyConf.Expose
|
||||||
|
thisReply.EnvoyExtensions = proxyConf.EnvoyExtensions
|
||||||
|
|
||||||
// Extract the global protocol from proxyConf for upstream configs.
|
// Extract the global protocol from proxyConf for upstream configs.
|
||||||
rawProtocol := proxyConf.Config["protocol"]
|
rawProtocol := proxyConf.Config["protocol"]
|
||||||
|
@ -102,6 +103,9 @@ func ComputeResolvedServiceConfig(
|
||||||
}
|
}
|
||||||
|
|
||||||
thisReply.Meta = serviceConf.Meta
|
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.
|
// 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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|
|
@ -168,6 +168,12 @@ type compiler struct {
|
||||||
// This is an OUTPUT field.
|
// This is an OUTPUT field.
|
||||||
serviceMeta map[string]string
|
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()
|
// startNode is computed inside of assembleChain()
|
||||||
//
|
//
|
||||||
// This is an OUTPUT field.
|
// This is an OUTPUT field.
|
||||||
|
@ -338,6 +344,7 @@ func (c *compiler) compile() (*structs.CompiledDiscoveryChain, error) {
|
||||||
CustomizationHash: customizationHash,
|
CustomizationHash: customizationHash,
|
||||||
Protocol: c.protocol,
|
Protocol: c.protocol,
|
||||||
ServiceMeta: c.serviceMeta,
|
ServiceMeta: c.serviceMeta,
|
||||||
|
EnvoyExtensions: c.envoyExtensions,
|
||||||
StartNode: c.startNode,
|
StartNode: c.startNode,
|
||||||
Nodes: c.nodes,
|
Nodes: c.nodes,
|
||||||
Targets: c.loadedTargets,
|
Targets: c.loadedTargets,
|
||||||
|
@ -555,9 +562,17 @@ func (c *compiler) assembleChain() error {
|
||||||
|
|
||||||
sid := structs.NewServiceID(c.serviceName, c.GetEnterpriseMeta())
|
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 {
|
if serviceDefault := c.entries.GetService(sid); serviceDefault != nil {
|
||||||
c.serviceMeta = serviceDefault.GetMeta()
|
c.serviceMeta = serviceDefault.GetMeta()
|
||||||
|
c.envoyExtensions = append(c.envoyExtensions, serviceDefault.EnvoyExtensions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for short circuit path.
|
// Check for short circuit path.
|
||||||
|
|
|
@ -56,6 +56,7 @@ func TestCompile(t *testing.T) {
|
||||||
"loadbalancer splitter and resolver": testcase_LBSplitterAndResolver(),
|
"loadbalancer splitter and resolver": testcase_LBSplitterAndResolver(),
|
||||||
"loadbalancer resolver": testcase_LBResolver(),
|
"loadbalancer resolver": testcase_LBResolver(),
|
||||||
"service redirect to service with default resolver is not a default chain": testcase_RedirectToDefaultResolverIsNotDefaultChain(),
|
"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": testcase_ServiceMetaProjection(),
|
||||||
"service meta projection with redirect": testcase_ServiceMetaProjectionWithRedirect(),
|
"service meta projection with redirect": testcase_ServiceMetaProjectionWithRedirect(),
|
||||||
|
|
||||||
|
@ -1671,6 +1672,59 @@ func testcase_DefaultResolver_WithProxyDefaults() compileTestCase {
|
||||||
return compileTestCase{entries: entries, expect: expect}
|
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 {
|
func testcase_ServiceMetaProjection() compileTestCase {
|
||||||
entries := newEntries()
|
entries := newEntries()
|
||||||
entries.AddServices(
|
entries.AddServices(
|
||||||
|
|
|
@ -950,11 +950,15 @@ func TestConfigSnapshotTerminatingGatewayWithLambdaService(t testing.T, extraUpd
|
||||||
CorrelationID: serviceConfigIDPrefix + web.String(),
|
CorrelationID: serviceConfigIDPrefix + web.String(),
|
||||||
Result: &structs.ServiceConfigResponse{
|
Result: &structs.ServiceConfigResponse{
|
||||||
ProxyConfig: map[string]interface{}{"protocol": "http"},
|
ProxyConfig: map[string]interface{}{"protocol": "http"},
|
||||||
Meta: map[string]string{
|
EnvoyExtensions: []structs.EnvoyExtension{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
Name: "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
Arguments: map[string]interface{}{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
"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"`
|
LocalConnectTimeoutMs int `json:",omitempty" alias:"local_connect_timeout_ms"`
|
||||||
LocalRequestTimeoutMs int `json:",omitempty" alias:"local_request_timeout_ms"`
|
LocalRequestTimeoutMs int `json:",omitempty" alias:"local_request_timeout_ms"`
|
||||||
BalanceInboundConnections string `json:",omitempty" alias:"balance_inbound_connections"`
|
BalanceInboundConnections string `json:",omitempty" alias:"balance_inbound_connections"`
|
||||||
|
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||||
|
|
||||||
Meta map[string]string `json:",omitempty"`
|
Meta map[string]string `json:",omitempty"`
|
||||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
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
|
return validationErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -282,6 +287,38 @@ func (e *ServiceConfigEntry) GetEnterpriseMeta() *acl.EnterpriseMeta {
|
||||||
return &e.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 {
|
type UpstreamConfiguration struct {
|
||||||
// Overrides is a slice of per-service configuration. The name field is
|
// Overrides is a slice of per-service configuration. The name field is
|
||||||
// required.
|
// required.
|
||||||
|
@ -343,6 +380,7 @@ type ProxyConfigEntry struct {
|
||||||
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
|
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
|
||||||
Expose ExposeConfig `json:",omitempty"`
|
Expose ExposeConfig `json:",omitempty"`
|
||||||
AccessLogs AccessLogsConfig `json:",omitempty" alias:"access_logs"`
|
AccessLogs AccessLogsConfig `json:",omitempty" alias:"access_logs"`
|
||||||
|
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||||
|
|
||||||
Meta map[string]string `json:",omitempty"`
|
Meta map[string]string `json:",omitempty"`
|
||||||
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
acl.EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||||
|
@ -427,6 +465,10 @@ func (e *ProxyConfigEntry) Validate() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := validateEnvoyExtensions(e.EnvoyExtensions); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
return e.validateEnterpriseMeta()
|
return e.validateEnterpriseMeta()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1127,6 +1169,7 @@ type ServiceConfigResponse struct {
|
||||||
Mode ProxyMode `json:",omitempty"`
|
Mode ProxyMode `json:",omitempty"`
|
||||||
Destination DestinationConfig `json:",omitempty"`
|
Destination DestinationConfig `json:",omitempty"`
|
||||||
Meta map[string]string `json:",omitempty"`
|
Meta map[string]string `json:",omitempty"`
|
||||||
|
EnvoyExtensions []EnvoyExtension `json:",omitempty"`
|
||||||
QueryMeta
|
QueryMeta
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2701,6 +2701,42 @@ func TestServiceConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
validateErr: "invalid value for balance_outbound_connections",
|
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)
|
testConfigEntryNormalizeAndValidate(t, cases)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ type CompiledDiscoveryChain struct {
|
||||||
// entry for the service named ServiceName.
|
// entry for the service named ServiceName.
|
||||||
ServiceMeta map[string]string `json:",omitempty"`
|
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
|
// StartNode is the first key into the Nodes map that should be followed
|
||||||
// when walking the discovery chain.
|
// when walking the discovery chain.
|
||||||
StartNode string `json:",omitempty"`
|
StartNode string `json:",omitempty"`
|
||||||
|
|
|
@ -67,6 +67,18 @@ func (o *CompiledDiscoveryChain) DeepCopy() *CompiledDiscoveryChain {
|
||||||
cp.ServiceMeta[k2] = v2
|
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 {
|
if o.Nodes != nil {
|
||||||
cp.Nodes = make(map[string]*DiscoveryGraphNode, len(o.Nodes))
|
cp.Nodes = make(map[string]*DiscoveryGraphNode, len(o.Nodes))
|
||||||
for k2, v2 := range o.Nodes {
|
for k2, v2 := range o.Nodes {
|
||||||
|
@ -540,6 +552,18 @@ func (o *ServiceConfigEntry) DeepCopy() *ServiceConfigEntry {
|
||||||
copy(cp.Destination.Addresses, o.Destination.Addresses)
|
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 {
|
if o.Meta != nil {
|
||||||
cp.Meta = make(map[string]string, len(o.Meta))
|
cp.Meta = make(map[string]string, len(o.Meta))
|
||||||
for k2, v2 := range o.Meta {
|
for k2, v2 := range o.Meta {
|
||||||
|
@ -597,6 +621,18 @@ func (o *ServiceConfigResponse) DeepCopy() *ServiceConfigResponse {
|
||||||
cp.Meta[k2] = v2
|
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
|
return &cp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,9 @@ func TestServerlessPluginFromSnapshot(t *testing.T) {
|
||||||
// Otherwise payload-passthrough=false and invocation-mode=synchronous.
|
// Otherwise payload-passthrough=false and invocation-mode=synchronous.
|
||||||
// This is used to test all the permutations.
|
// This is used to test all the permutations.
|
||||||
makeServiceDefaults := func(opposite bool) *structs.ServiceConfigEntry {
|
makeServiceDefaults := func(opposite bool) *structs.ServiceConfigEntry {
|
||||||
payloadPassthrough := "true"
|
payloadPassthrough := true
|
||||||
if opposite {
|
if opposite {
|
||||||
payloadPassthrough = "false"
|
payloadPassthrough = false
|
||||||
}
|
}
|
||||||
|
|
||||||
invocationMode := "synchronous"
|
invocationMode := "synchronous"
|
||||||
|
@ -43,12 +43,16 @@ func TestServerlessPluginFromSnapshot(t *testing.T) {
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "db",
|
Name: "db",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
Meta: map[string]string{
|
EnvoyExtensions: []structs.EnvoyExtension{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
Name: "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": payloadPassthrough,
|
Arguments: map[string]interface{}{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/invocation-mode": invocationMode,
|
"ARN": "lambda-arn",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
"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_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"
|
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
|
||||||
pstruct "github.com/golang/protobuf/ptypes/struct"
|
pstruct "github.com/golang/protobuf/ptypes/struct"
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
"github.com/hashicorp/consul/agent/xds/xdscommon"
|
||||||
"github.com/hashicorp/consul/api"
|
"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 {
|
type lambdaPatcher struct {
|
||||||
arn string
|
ARN string `mapstructure:"ARN"`
|
||||||
payloadPassthrough bool
|
PayloadPassthrough bool `mapstructure:"PayloadPassthrough"`
|
||||||
region string
|
Region string `mapstructure:"Region"`
|
||||||
kind api.ServiceKind
|
Kind api.ServiceKind
|
||||||
invocationMode envoy_lambda_v3.Config_InvocationMode
|
InvocationMode string `mapstructure:"InvocationMode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ patcher = (*lambdaPatcher)(nil)
|
var _ patcher = (*lambdaPatcher)(nil)
|
||||||
|
|
||||||
func makeLambdaPatcher(serviceConfig xdscommon.ServiceConfig) (patcher, bool) {
|
func makeLambdaPatcher(serviceConfig xdscommon.ServiceConfig) (patcher, bool) {
|
||||||
var patcher lambdaPatcher
|
var patcher lambdaPatcher
|
||||||
if !isStringTrue(serviceConfig.Meta[lambdaEnabledTag]) {
|
|
||||||
return patcher, true
|
// TODO this is a hack. We should iterate through the extensions outside of here
|
||||||
|
if len(serviceConfig.EnvoyExtensions) == 0 {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
arn := serviceConfig.Meta[lambdaArnTag]
|
// TODO this is a hack. We should iterate through the extensions outside of here
|
||||||
if arn == "" {
|
extension := serviceConfig.EnvoyExtensions[0]
|
||||||
return patcher, false
|
if extension.Name != "builtin/aws/lambda" {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
region := serviceConfig.Meta[lambdaRegionTag]
|
// TODO this fails when types aren't encode properly. We need to check this earlier in the Validate RPC.
|
||||||
if region == "" {
|
err := mapstructure.Decode(extension.Arguments, &patcher)
|
||||||
return patcher, false
|
if err != nil {
|
||||||
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadPassthrough := isStringTrue(serviceConfig.Meta[lambdaPayloadPassthroughTag])
|
if patcher.ARN == "" {
|
||||||
|
return nil, false
|
||||||
invocationModeStr := serviceConfig.Meta[lambdaInvocationMode]
|
|
||||||
invocationMode := envoy_lambda_v3.Config_SYNCHRONOUS
|
|
||||||
if invocationModeStr == "asynchronous" {
|
|
||||||
invocationMode = envoy_lambda_v3.Config_ASYNCHRONOUS
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return lambdaPatcher{
|
if patcher.Region == "" {
|
||||||
arn: arn,
|
return nil, false
|
||||||
payloadPassthrough: payloadPassthrough,
|
}
|
||||||
region: region,
|
|
||||||
kind: serviceConfig.Kind,
|
patcher.Kind = serviceConfig.Kind
|
||||||
invocationMode: invocationMode,
|
|
||||||
}, true
|
return patcher, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func isStringTrue(v string) bool {
|
func toEnvoyInvocationMode(s string) envoy_lambda_v3.Config_InvocationMode {
|
||||||
return v == "true"
|
m := envoy_lambda_v3.Config_SYNCHRONOUS
|
||||||
|
if s == "asynchronous" {
|
||||||
|
m = envoy_lambda_v3.Config_ASYNCHRONOUS
|
||||||
|
}
|
||||||
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p lambdaPatcher) CanPatch(kind api.ServiceKind) bool {
|
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) {
|
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
|
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{
|
||||||
Address: &envoy_core_v3.Address_SocketAddress{
|
Address: &envoy_core_v3.Address_SocketAddress{
|
||||||
SocketAddress: &envoy_core_v3.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{
|
PortSpecifier: &envoy_core_v3.SocketAddress_PortValue{
|
||||||
PortValue: 443,
|
PortValue: 443,
|
||||||
},
|
},
|
||||||
|
@ -170,9 +166,9 @@ func (p lambdaPatcher) PatchFilter(filter *envoy_listener_v3.Filter) (*envoy_lis
|
||||||
lambdaHttpFilter, err := makeEnvoyHTTPFilter(
|
lambdaHttpFilter, err := makeEnvoyHTTPFilter(
|
||||||
"envoy.filters.http.aws_lambda",
|
"envoy.filters.http.aws_lambda",
|
||||||
&envoy_lambda_v3.Config{
|
&envoy_lambda_v3.Config{
|
||||||
Arn: p.arn,
|
Arn: p.ARN,
|
||||||
PayloadPassthrough: p.payloadPassthrough,
|
PayloadPassthrough: p.PayloadPassthrough,
|
||||||
InvocationMode: p.invocationMode,
|
InvocationMode: toEnvoyInvocationMode(p.InvocationMode),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package serverlessplugin
|
package serverlessplugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -14,7 +13,6 @@ func TestMakeLambdaPatcher(t *testing.T) {
|
||||||
kind := api.ServiceKindTerminatingGateway
|
kind := api.ServiceKindTerminatingGateway
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
enabled bool
|
|
||||||
arn string
|
arn string
|
||||||
payloadPassthrough bool
|
payloadPassthrough bool
|
||||||
region string
|
region string
|
||||||
|
@ -22,37 +20,29 @@ func TestMakeLambdaPatcher(t *testing.T) {
|
||||||
ok bool
|
ok bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no meta",
|
name: "no extension",
|
||||||
ok: true,
|
ok: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "lambda disabled",
|
name: "missing arn",
|
||||||
enabled: false,
|
region: "blah",
|
||||||
ok: true,
|
ok: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing arn",
|
name: "missing region",
|
||||||
enabled: true,
|
arn: "arn",
|
||||||
region: "blah",
|
ok: false,
|
||||||
ok: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "missing region",
|
|
||||||
enabled: true,
|
|
||||||
region: "arn",
|
|
||||||
ok: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "including payload passthrough",
|
name: "including payload passthrough",
|
||||||
enabled: true,
|
|
||||||
arn: "arn",
|
arn: "arn",
|
||||||
region: "blah",
|
region: "blah",
|
||||||
payloadPassthrough: true,
|
payloadPassthrough: true,
|
||||||
expected: lambdaPatcher{
|
expected: lambdaPatcher{
|
||||||
arn: "arn",
|
ARN: "arn",
|
||||||
payloadPassthrough: true,
|
PayloadPassthrough: true,
|
||||||
region: "blah",
|
Region: "blah",
|
||||||
kind: kind,
|
Kind: kind,
|
||||||
},
|
},
|
||||||
ok: true,
|
ok: true,
|
||||||
},
|
},
|
||||||
|
@ -62,22 +52,25 @@ func TestMakeLambdaPatcher(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
config := xdscommon.ServiceConfig{
|
config := xdscommon.ServiceConfig{
|
||||||
Kind: kind,
|
Kind: kind,
|
||||||
Meta: map[string]string{
|
EnvoyExtensions: []api.EnvoyExtension{
|
||||||
lambdaEnabledTag: strconv.FormatBool(tc.enabled),
|
{
|
||||||
lambdaArnTag: tc.arn,
|
Name: "builtin/aws/lambda",
|
||||||
lambdaRegionTag: tc.region,
|
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)
|
patcher, ok := makeLambdaPatcher(config)
|
||||||
|
|
||||||
require.Equal(t, tc.ok, ok)
|
require.Equal(t, tc.ok, ok)
|
||||||
|
|
||||||
require.Equal(t, tc.expected, patcher)
|
if tc.ok {
|
||||||
|
require.Equal(t, tc.expected, patcher)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,10 @@ func TestGetPatcherBySNI(t *testing.T) {
|
||||||
sni: "lambda-sni",
|
sni: "lambda-sni",
|
||||||
kind: api.ServiceKindTerminatingGateway,
|
kind: api.ServiceKindTerminatingGateway,
|
||||||
expected: lambdaPatcher{
|
expected: lambdaPatcher{
|
||||||
arn: "arn",
|
ARN: "arn",
|
||||||
region: "region",
|
Region: "region",
|
||||||
payloadPassthrough: false,
|
PayloadPassthrough: false,
|
||||||
kind: api.ServiceKindTerminatingGateway,
|
Kind: api.ServiceKindTerminatingGateway,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -74,24 +74,27 @@ func sampleConfig() xdscommon.PluginConfiguration {
|
||||||
ServiceConfigs: map[api.CompoundServiceName]xdscommon.ServiceConfig{
|
ServiceConfigs: map[api.CompoundServiceName]xdscommon.ServiceConfig{
|
||||||
lambdaService: {
|
lambdaService: {
|
||||||
Kind: api.ServiceKindTerminatingGateway,
|
Kind: api.ServiceKindTerminatingGateway,
|
||||||
Meta: map[string]string{
|
EnvoyExtensions: []api.EnvoyExtension{
|
||||||
lambdaEnabledTag: "true",
|
{
|
||||||
lambdaArnTag: "arn",
|
Name: "builtin/aws/lambda",
|
||||||
lambdaRegionTag: "region",
|
Arguments: map[string]interface{}{
|
||||||
|
"ARN": "arn",
|
||||||
|
"Region": "region",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
disabledLambdaService: {
|
disabledLambdaService: {
|
||||||
Kind: api.ServiceKindTerminatingGateway,
|
Kind: api.ServiceKindTerminatingGateway,
|
||||||
Meta: map[string]string{
|
// No extension.
|
||||||
lambdaEnabledTag: "false",
|
|
||||||
lambdaArnTag: "arn",
|
|
||||||
lambdaRegionTag: "region",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
invalidLambdaService: {
|
invalidLambdaService: {
|
||||||
Kind: api.ServiceKindTerminatingGateway,
|
Kind: api.ServiceKindTerminatingGateway,
|
||||||
Meta: map[string]string{
|
EnvoyExtensions: []api.EnvoyExtension{
|
||||||
lambdaEnabledTag: "true",
|
{
|
||||||
|
Name: "builtin/aws/lambda",
|
||||||
|
Arguments: map[string]interface{}{}, // ARN, etc missing
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -56,8 +56,8 @@ func EmptyIndexedResources() *IndexedResources {
|
||||||
type ServiceConfig struct {
|
type ServiceConfig struct {
|
||||||
// Kind identifies the final proxy kind that will make the request to the
|
// Kind identifies the final proxy kind that will make the request to the
|
||||||
// destination service.
|
// destination service.
|
||||||
Kind api.ServiceKind
|
Kind api.ServiceKind
|
||||||
Meta map[string]string
|
EnvoyExtensions []api.EnvoyExtension
|
||||||
}
|
}
|
||||||
|
|
||||||
// PluginConfiguration is passed into Envoy plugins. It should depend on the
|
// 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{
|
serviceConfigs[upstreamIDToCompoundServiceName(uid)] = ServiceConfig{
|
||||||
Meta: dc.ServiceMeta,
|
Kind: api.ServiceKindConnectProxy,
|
||||||
Kind: api.ServiceKindConnectProxy,
|
EnvoyExtensions: convertEnvoyExtensions(dc.EnvoyExtensions),
|
||||||
}
|
}
|
||||||
|
|
||||||
compoundServiceName := upstreamIDToCompoundServiceName(uid)
|
compoundServiceName := upstreamIDToCompoundServiceName(uid)
|
||||||
|
@ -135,8 +135,8 @@ func MakePluginConfiguration(cfgSnap *proxycfg.ConfigSnapshot) PluginConfigurati
|
||||||
for svc, c := range cfgSnap.TerminatingGateway.ServiceConfigs {
|
for svc, c := range cfgSnap.TerminatingGateway.ServiceConfigs {
|
||||||
compoundServiceName := serviceNameToCompoundServiceName(svc)
|
compoundServiceName := serviceNameToCompoundServiceName(svc)
|
||||||
serviceConfigs[compoundServiceName] = ServiceConfig{
|
serviceConfigs[compoundServiceName] = ServiceConfig{
|
||||||
Meta: c.Meta,
|
EnvoyExtensions: convertEnvoyExtensions(c.EnvoyExtensions),
|
||||||
Kind: api.ServiceKindTerminatingGateway,
|
Kind: api.ServiceKindTerminatingGateway,
|
||||||
}
|
}
|
||||||
|
|
||||||
sni := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain)
|
sni := connect.ServiceSNI(svc.Name, "", svc.NamespaceOrDefault(), svc.PartitionOrDefault(), cfgSnap.Datacenter, trustDomain)
|
||||||
|
@ -178,3 +178,17 @@ func upstreamIDToCompoundServiceName(uid proxycfg.UpstreamID) api.CompoundServic
|
||||||
Namespace: uid.NamespaceOrDefault(),
|
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{
|
ServiceConfigs: map[api.CompoundServiceName]ServiceConfig{
|
||||||
webService: {
|
webService: {
|
||||||
Kind: api.ServiceKindTerminatingGateway,
|
Kind: api.ServiceKindTerminatingGateway,
|
||||||
Meta: map[string]string{
|
EnvoyExtensions: []api.EnvoyExtension{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
Name: "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
Arguments: map[string]interface{}{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
"ARN": "lambda-arn",
|
||||||
|
"PayloadPassthrough": true,
|
||||||
|
"Region": "us-east-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
apiService: {
|
apiService: {
|
||||||
|
@ -84,17 +88,22 @@ func TestMakePluginConfiguration_ConnectProxy(t *testing.T) {
|
||||||
Partition: "default",
|
Partition: "default",
|
||||||
Namespace: "default",
|
Namespace: "default",
|
||||||
}
|
}
|
||||||
lambdaMeta := map[string]string{
|
envoyExtensions := []structs.EnvoyExtension{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "lambda-arn",
|
Name: "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true",
|
Arguments: map[string]interface{}{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "us-east-1",
|
"ARN": "lambda-arn",
|
||||||
|
"PayloadPassthrough": true,
|
||||||
|
"Region": "us-east-1",
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
serviceDefaults := &structs.ServiceConfigEntry{
|
serviceDefaults := &structs.ServiceConfigEntry{
|
||||||
Kind: structs.ServiceDefaults,
|
Kind: structs.ServiceDefaults,
|
||||||
Name: "db",
|
Name: "db",
|
||||||
Protocol: "http",
|
Protocol: "http",
|
||||||
Meta: lambdaMeta,
|
EnvoyExtensions: envoyExtensions,
|
||||||
}
|
}
|
||||||
|
|
||||||
snap := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", nil, nil, serviceDefaults)
|
snap := proxycfg.TestConfigSnapshotDiscoveryChain(t, "default", nil, nil, serviceDefaults)
|
||||||
|
@ -102,8 +111,8 @@ func TestMakePluginConfiguration_ConnectProxy(t *testing.T) {
|
||||||
Kind: api.ServiceKindConnectProxy,
|
Kind: api.ServiceKindConnectProxy,
|
||||||
ServiceConfigs: map[api.CompoundServiceName]ServiceConfig{
|
ServiceConfigs: map[api.CompoundServiceName]ServiceConfig{
|
||||||
dbService: {
|
dbService: {
|
||||||
Kind: api.ServiceKindConnectProxy,
|
Kind: api.ServiceKindConnectProxy,
|
||||||
Meta: lambdaMeta,
|
EnvoyExtensions: convertEnvoyExtensions(envoyExtensions),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
SNIToServiceName: map[string]api.CompoundServiceName{
|
SNIToServiceName: map[string]api.CompoundServiceName{
|
||||||
|
|
|
@ -104,6 +104,13 @@ type ExposeConfig struct {
|
||||||
Paths []ExposePath `json:",omitempty"`
|
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 {
|
type ExposePath struct {
|
||||||
// ListenerPort defines the port of the proxy's listener for exposed paths.
|
// ListenerPort defines the port of the proxy's listener for exposed paths.
|
||||||
ListenerPort int `json:",omitempty" alias:"listener_port"`
|
ListenerPort int `json:",omitempty" alias:"listener_port"`
|
||||||
|
@ -272,6 +279,7 @@ type ServiceConfigEntry struct {
|
||||||
LocalConnectTimeoutMs int `json:",omitempty" alias:"local_connect_timeout_ms"`
|
LocalConnectTimeoutMs int `json:",omitempty" alias:"local_connect_timeout_ms"`
|
||||||
LocalRequestTimeoutMs int `json:",omitempty" alias:"local_request_timeout_ms"`
|
LocalRequestTimeoutMs int `json:",omitempty" alias:"local_request_timeout_ms"`
|
||||||
BalanceInboundConnections string `json:",omitempty" alias:"balance_inbound_connections"`
|
BalanceInboundConnections string `json:",omitempty" alias:"balance_inbound_connections"`
|
||||||
|
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||||
Meta map[string]string `json:",omitempty"`
|
Meta map[string]string `json:",omitempty"`
|
||||||
CreateIndex uint64
|
CreateIndex uint64
|
||||||
ModifyIndex uint64
|
ModifyIndex uint64
|
||||||
|
@ -296,6 +304,7 @@ type ProxyConfigEntry struct {
|
||||||
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
|
MeshGateway MeshGatewayConfig `json:",omitempty" alias:"mesh_gateway"`
|
||||||
Expose ExposeConfig `json:",omitempty"`
|
Expose ExposeConfig `json:",omitempty"`
|
||||||
AccessLogs *AccessLogsConfig `json:",omitempty"`
|
AccessLogs *AccessLogsConfig `json:",omitempty"`
|
||||||
|
EnvoyExtensions []EnvoyExtension `json:",omitempty" alias:"envoy_extensions"`
|
||||||
|
|
||||||
Meta map[string]string `json:",omitempty"`
|
Meta map[string]string `json:",omitempty"`
|
||||||
CreateIndex uint64
|
CreateIndex uint64
|
||||||
|
|
|
@ -34,6 +34,22 @@ func DestinationConfigFromStructs(t *structs.DestinationConfig, s *DestinationCo
|
||||||
s.Addresses = t.Addresses
|
s.Addresses = t.Addresses
|
||||||
s.Port = int32(t.Port)
|
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) {
|
func ExposeConfigToStructs(s *ExposeConfig, t *structs.ExposeConfig) {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return
|
return
|
||||||
|
@ -706,6 +722,14 @@ func ServiceDefaultsToStructs(s *ServiceDefaults, t *structs.ServiceConfigEntry)
|
||||||
t.LocalConnectTimeoutMs = int(s.LocalConnectTimeoutMs)
|
t.LocalConnectTimeoutMs = int(s.LocalConnectTimeoutMs)
|
||||||
t.LocalRequestTimeoutMs = int(s.LocalRequestTimeoutMs)
|
t.LocalRequestTimeoutMs = int(s.LocalRequestTimeoutMs)
|
||||||
t.BalanceInboundConnections = s.BalanceInboundConnections
|
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
|
t.Meta = s.Meta
|
||||||
}
|
}
|
||||||
func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefaults) {
|
func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefaults) {
|
||||||
|
@ -744,6 +768,16 @@ func ServiceDefaultsFromStructs(t *structs.ServiceConfigEntry, s *ServiceDefault
|
||||||
s.LocalConnectTimeoutMs = int32(t.LocalConnectTimeoutMs)
|
s.LocalConnectTimeoutMs = int32(t.LocalConnectTimeoutMs)
|
||||||
s.LocalRequestTimeoutMs = int32(t.LocalRequestTimeoutMs)
|
s.LocalRequestTimeoutMs = int32(t.LocalRequestTimeoutMs)
|
||||||
s.BalanceInboundConnections = t.BalanceInboundConnections
|
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
|
s.Meta = t.Meta
|
||||||
}
|
}
|
||||||
func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentionsConfigEntry) {
|
func ServiceIntentionsToStructs(s *ServiceIntentions, t *structs.ServiceIntentionsConfigEntry) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/timestamp"
|
"github.com/golang/protobuf/ptypes/timestamp"
|
||||||
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/acl"
|
"github.com/hashicorp/consul/acl"
|
||||||
|
@ -266,3 +267,14 @@ func meshGatewayModeToStructs(a MeshGatewayMode) structs.MeshGatewayMode {
|
||||||
return structs.MeshGatewayModeDefault
|
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)
|
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
|
// MarshalBinary implements encoding.BinaryMarshaler
|
||||||
func (msg *MeshGatewayConfig) MarshalBinary() ([]byte, error) {
|
func (msg *MeshGatewayConfig) MarshalBinary() ([]byte, error) {
|
||||||
return proto.Marshal(msg)
|
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;
|
package hashicorp.consul.internal.configentry;
|
||||||
|
|
||||||
import "google/protobuf/duration.proto";
|
import "google/protobuf/duration.proto";
|
||||||
|
import "google/protobuf/struct.proto";
|
||||||
import "google/protobuf/timestamp.proto";
|
import "google/protobuf/timestamp.proto";
|
||||||
import "proto/pbcommon/common.proto";
|
import "proto/pbcommon/common.proto";
|
||||||
|
|
||||||
|
@ -433,6 +434,7 @@ message ServiceDefaults {
|
||||||
int32 LocalRequestTimeoutMs = 11;
|
int32 LocalRequestTimeoutMs = 11;
|
||||||
string BalanceInboundConnections = 12;
|
string BalanceInboundConnections = 12;
|
||||||
map<string, string> Meta = 13;
|
map<string, string> Meta = 13;
|
||||||
|
repeated EnvoyExtension EnvoyExtensions = 14;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ProxyMode {
|
enum ProxyMode {
|
||||||
|
@ -452,6 +454,18 @@ message TransparentProxyConfig {
|
||||||
bool DialedDirectly = 2;
|
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:
|
// mog annotation:
|
||||||
//
|
//
|
||||||
// target=github.com/hashicorp/consul/agent/structs.MeshGatewayConfig
|
// target=github.com/hashicorp/consul/agent/structs.MeshGatewayConfig
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
"Kind": "service-defaults",
|
"Kind": "service-defaults",
|
||||||
"Name": "l1",
|
"Name": "l1",
|
||||||
"Protocol": "http",
|
"Protocol": "http",
|
||||||
"Meta": {
|
"EnvoyExtensions": [
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "${AWS_LAMBDA_REGION}",
|
"Name": "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "${AWS_LAMBDA_ARN}",
|
"Arguments": {
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "true"
|
"Region": "${AWS_LAMBDA_REGION}",
|
||||||
}
|
"ARN": "${AWS_LAMBDA_ARN}",
|
||||||
|
"PayloadPassthrough": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
"Kind": "service-defaults",
|
"Kind": "service-defaults",
|
||||||
"Name": "l2",
|
"Name": "l2",
|
||||||
"Protocol": "http",
|
"Protocol": "http",
|
||||||
"Meta": {
|
"EnvoyExtensions": [
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled": "true",
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region": "${AWS_LAMBDA_REGION}",
|
"Name": "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn": "${AWS_LAMBDA_ARN}",
|
"Arguments": {
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough": "false"
|
"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>
|
<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>`,
|
<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',
|
name: 'Mode',
|
||||||
type: `string: ""`,
|
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)
|
[Envoy Connection Balance config](https://cloudnative.to/envoy/api-v3/config/listener/v3/listener.proto.html#config-listener-v3-listener-connectionbalanceconfig)
|
||||||
for details.`
|
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',
|
name: 'Mode',
|
||||||
type: `string: ""`,
|
type: `string: ""`,
|
||||||
|
@ -524,10 +548,10 @@ represents a location outside the Consul cluster. They can be dialed directly wh
|
||||||
name: 'EnforcingConsecutive5xx',
|
name: 'EnforcingConsecutive5xx',
|
||||||
type: 'int: 100',
|
type: 'int: 100',
|
||||||
description: {
|
description: {
|
||||||
hcl: `The % chance that a host will be actually ejected
|
hcl: `Measured in percent (%), the probability of a host's ejection
|
||||||
when an outlier status is detected through consecutive 5xx.`,
|
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||||
yaml: `The % chance that a host will be actually ejected
|
yaml: `Measured in percent (%), the probability of a host's ejection
|
||||||
when an outlier status is detected through consecutive 5xx.`,
|
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',
|
name: 'EnforcingConsecutive5xx',
|
||||||
type: 'int: 100',
|
type: 'int: 100',
|
||||||
description: {
|
description: {
|
||||||
hcl: `The % chance that a host will be actually ejected
|
hcl: `Measured in percent (%), the probability of a host's ejection
|
||||||
when an outlier status is detected through consecutive 5xx.`,
|
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||||
yaml: `The % chance that a host will be actually ejected
|
yaml: `Measured in percent (%), the probability of a host's ejection
|
||||||
when an outlier status is detected through consecutive 5xx.`,
|
after a passive health check detects an outlier status through consecutive 5xx.`,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -7,13 +7,13 @@ description: >-
|
||||||
|
|
||||||
# Manual Lambda Function Registration
|
# Manual Lambda Function Registration
|
||||||
|
|
||||||
This topic describes how to manually register Lambda functions into Consul. Refer to [Automate Lambda Function Registration](/docs/lambda/registration/automate) for information about using the Lambda registrator to automate registration.
|
This topic describes how to manually register Lambda functions into Consul. Refer to [Automate Lambda Function Registration](/docs/lambda/registration/automate) for information about using the Lambda registrator to automate registration.
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
Verify that your environment meets the requirements specified in [Lambda Function Registration Requirements](/docs/lambda/registration).
|
Verify that your environment meets the requirements specified in [Lambda Function Registration Requirements](/docs/lambda/registration).
|
||||||
|
|
||||||
To manually register Lambda functions so that mesh services can invoke them, you must create and apply a service registration configuration for the Lambda function and write a [service defaults configuration entry](/docs/connect/config-entries/service-defaults) for the function.
|
To manually register Lambda functions so that mesh services can invoke them, you must create and apply a service registration configuration for the Lambda function and write a [service defaults configuration entry](/docs/connect/config-entries/service-defaults) for the function.
|
||||||
|
|
||||||
## Register a Lambda function
|
## Register a Lambda function
|
||||||
|
|
||||||
|
@ -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
|
$ 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">
|
<CodeBlockConfig filename="lambda-service-defaults.hcl">
|
||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
Kind = "service-defaults"
|
Kind = "service-defaults"
|
||||||
Name = "lambda"
|
Name = "<SERVICE_NAME>"
|
||||||
Protocol = "http"
|
Protocol = "http"
|
||||||
Meta = {
|
EnvoyExtensions = [
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/enabled" = "true"
|
{
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/arn" = "<INSERT ARN HERE>"
|
"Name": "builtin/aws/lambda",
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/payload-passthrough" = "true"
|
"Arguments": {
|
||||||
"serverless.consul.hashicorp.com/v1alpha1/lambda/region" = "us-east-2"
|
"Region": "us-east-2",
|
||||||
}
|
"ARN": "<INSERT ARN HERE>",
|
||||||
|
"PayloadPassthrough": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
</CodeBlockConfig>
|
</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
|
$ 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`.
|
- `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.
|
||||||
| Tag | Description |
|
- `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).
|
||||||
| `<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. |
|
|
||||||
|
|
|
@ -20,6 +20,47 @@ upgrade flow.
|
||||||
|
|
||||||
The `connect.enable_serverless_plugin` configuration option was removed. Lambda integration is now enabled by default.
|
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
|
## Consul 1.14.x
|
||||||
|
|
||||||
### Service Mesh Compatibility
|
### Service Mesh Compatibility
|
||||||
|
|
Loading…
Reference in New Issue