consul/agent/xds/builtinextensions/lua/lua.go

142 lines
4.4 KiB
Go

package lua
import (
"errors"
"fmt"
envoy_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
envoy_listener_v3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3"
envoy_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
envoy_lua_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/lua/v3"
envoy_http_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
envoy_resource_v3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3"
"github.com/hashicorp/go-multierror"
"github.com/mitchellh/mapstructure"
"github.com/hashicorp/consul/agent/xds/builtinextensiontemplate"
"github.com/hashicorp/consul/agent/xds/xdscommon"
"github.com/hashicorp/consul/api"
)
type lua struct {
ProxyType string
Listener string
Script string
}
var _ builtinextensiontemplate.Plugin = (*lua)(nil)
// MakeLuaExtension is a builtinextensiontemplate.PluginConstructor for a builtinextensiontemplate.EnvoyExtension.
func MakeLuaExtension(ext xdscommon.ExtensionConfiguration) (builtinextensiontemplate.Plugin, error) {
var resultErr error
var plugin lua
if name := ext.EnvoyExtension.Name; name != api.BuiltinLuaExtension {
return nil, fmt.Errorf("expected extension name 'lua' but got %q", name)
}
if err := mapstructure.Decode(ext.EnvoyExtension.Arguments, &plugin); err != nil {
return nil, fmt.Errorf("error decoding extension arguments: %v", err)
}
if plugin.Script == "" {
resultErr = multierror.Append(resultErr, fmt.Errorf("Script is required"))
}
if err := validateProxyType(plugin.ProxyType); err != nil {
resultErr = multierror.Append(resultErr, err)
}
if err := validateListener(plugin.Listener); err != nil {
resultErr = multierror.Append(resultErr, err)
}
return plugin, resultErr
}
func validateProxyType(t string) error {
if t != "connect-proxy" {
return fmt.Errorf("unexpected ProxyType %q", t)
}
return nil
}
func validateListener(t string) error {
if t != "inbound" && t != "outbound" {
return fmt.Errorf("unexpected Listener %q", t)
}
return nil
}
// CanApply determines if the extension can apply to the given extension configuration.
func (p lua) CanApply(config xdscommon.ExtensionConfiguration) bool {
return string(config.Kind) == p.ProxyType && p.matchesListenerDirection(config)
}
func (p lua) matchesListenerDirection(config xdscommon.ExtensionConfiguration) bool {
return (config.IsUpstream() && p.Listener == "outbound") || (!config.IsUpstream() && p.Listener == "inbound")
}
// PatchRoute does nothing.
func (p lua) PatchRoute(route *envoy_route_v3.RouteConfiguration) (*envoy_route_v3.RouteConfiguration, bool, error) {
return route, false, nil
}
// PatchCluster does nothing.
func (p lua) PatchCluster(c *envoy_cluster_v3.Cluster) (*envoy_cluster_v3.Cluster, bool, error) {
return c, false, nil
}
// PatchFilter inserts a lua filter directly prior to envoy.filters.http.router.
func (p lua) PatchFilter(filter *envoy_listener_v3.Filter) (*envoy_listener_v3.Filter, bool, error) {
if filter.Name != "envoy.filters.network.http_connection_manager" {
return filter, false, nil
}
if typedConfig := filter.GetTypedConfig(); typedConfig == nil {
return filter, false, errors.New("error getting typed config for http filter")
}
config := envoy_resource_v3.GetHTTPConnectionManager(filter)
if config == nil {
return filter, false, errors.New("error unmarshalling filter")
}
luaHttpFilter, err := makeEnvoyHTTPFilter(
"envoy.filters.http.lua",
&envoy_lua_v3.Lua{
InlineCode: p.Script,
},
)
if err != nil {
return filter, false, err
}
var (
changedFilters = make([]*envoy_http_v3.HttpFilter, 0, len(config.HttpFilters)+1)
changed bool
)
// We need to be careful about overwriting http filters completely because
// http filters validates intentions with the RBAC filter. This inserts the
// lua filter before envoy.filters.http.router while keeping everything
// else intact.
for _, httpFilter := range config.HttpFilters {
if httpFilter.Name == "envoy.filters.http.router" {
changedFilters = append(changedFilters, luaHttpFilter)
changed = true
}
changedFilters = append(changedFilters, httpFilter)
}
if changed {
config.HttpFilters = changedFilters
}
newFilter, err := makeFilter("envoy.filters.network.http_connection_manager", config)
if err != nil {
return filter, false, errors.New("error making new filter")
}
return newFilter, true, nil
}