mirror of https://github.com/hashicorp/consul
mesh: add validation hook to proxy configuration
parent
3d1a606c3b
commit
b08d9d4b47
|
@ -100,6 +100,7 @@ func TestSortProxyConfigurations(t *testing.T) {
|
||||||
for i, ws := range c.selectors {
|
for i, ws := range c.selectors {
|
||||||
proxyCfg := &pbmesh.ProxyConfiguration{
|
proxyCfg := &pbmesh.ProxyConfiguration{
|
||||||
Workloads: ws,
|
Workloads: ws,
|
||||||
|
DynamicConfig: &pbmesh.DynamicConfig{},
|
||||||
}
|
}
|
||||||
resName := fmt.Sprintf("cfg-%d", i)
|
resName := fmt.Sprintf("cfg-%d", i)
|
||||||
proxyCfgRes := resourcetest.Resource(pbmesh.ProxyConfigurationType, resName).
|
proxyCfgRes := resourcetest.Resource(pbmesh.ProxyConfigurationType, resName).
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidPort = errors.New("port number is outside the range 1 to 65535")
|
||||||
|
errInvalidExposePathProtocol = errors.New("invalid protocol: only HTTP and HTTP2 protocols are allowed")
|
||||||
|
errMissingProxyConfigData = errors.New("at least one of \"bootstrap_config\" or \"dynamic_config\" fields must be set")
|
||||||
|
)
|
|
@ -6,6 +6,11 @@ package types
|
||||||
import (
|
import (
|
||||||
"github.com/hashicorp/go-multierror"
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/internal/catalog"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/catalog"
|
"github.com/hashicorp/consul/internal/catalog"
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
|
pbmesh "github.com/hashicorp/consul/proto-public/pbmesh/v2beta1"
|
||||||
|
@ -53,23 +58,181 @@ func MutateProxyConfiguration(res *pbresource.Resource) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateProxyConfiguration(res *pbresource.Resource) error {
|
func ValidateProxyConfiguration(res *pbresource.Resource) error {
|
||||||
var cfg pbmesh.ProxyConfiguration
|
decodedProxyCfg, decodeErr := resource.Decode[*pbmesh.ProxyConfiguration](res)
|
||||||
|
if decodeErr != nil {
|
||||||
if err := res.Data.UnmarshalTo(&cfg); err != nil {
|
return resource.NewErrDataParse(decodedProxyCfg.GetData(), decodeErr)
|
||||||
return resource.NewErrDataParse(&cfg, err)
|
|
||||||
}
|
}
|
||||||
|
proxyCfg := decodedProxyCfg.GetData()
|
||||||
|
|
||||||
var merr error
|
var err error
|
||||||
|
|
||||||
// Validate the workload selector
|
if selErr := catalog.ValidateSelector(proxyCfg.Workloads, false); selErr != nil {
|
||||||
if selErr := catalog.ValidateSelector(cfg.Workloads, false); selErr != nil {
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
merr = multierror.Append(merr, resource.ErrInvalidField{
|
|
||||||
Name: "workloads",
|
Name: "workloads",
|
||||||
Wrapped: selErr,
|
Wrapped: selErr,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rb): add more validation for proxy configuration
|
if proxyCfg.GetDynamicConfig() == nil && proxyCfg.GetBootstrapConfig() == nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidFields{
|
||||||
return merr
|
Names: []string{"dynamic_config", "bootstrap_config"},
|
||||||
|
Wrapped: errMissingProxyConfigData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:staticcheck
|
||||||
|
if proxyCfg.GetOpaqueConfig() != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "opaque_config",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if dynamicCfgErr := validateDynamicProxyConfiguration(proxyCfg.GetDynamicConfig()); dynamicCfgErr != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "dynamic_config",
|
||||||
|
Wrapped: dynamicCfgErr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDynamicProxyConfiguration(cfg *pbmesh.DynamicConfig) error {
|
||||||
|
if cfg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Error if any of the currently unsupported fields is set.
|
||||||
|
if cfg.GetMutualTlsMode() != pbmesh.MutualTLSMode_MUTUAL_TLS_MODE_DEFAULT {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "mutual_tls_mode",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.GetMeshGatewayMode() != pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_UNSPECIFIED {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "mesh_gateway_mode",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.GetAccessLogs() != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "access_logs",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.GetEnvoyExtensions() != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "envoy_extensions",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.GetPublicListenerJson() != "" {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "public_listener_json",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.GetListenerTracingJson() != "" {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "listener_tracing_json",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.GetLocalClusterJson() != "" {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "local_cluster_json",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:staticcheck
|
||||||
|
if cfg.GetLocalWorkloadAddress() != "" {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "local_workload_address",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:staticcheck
|
||||||
|
if cfg.GetLocalWorkloadPort() != 0 {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "local_workload_port",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:staticcheck
|
||||||
|
if cfg.GetLocalWorkloadSocketPath() != "" {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "local_workload_socket_path",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if tproxyCfg := cfg.GetTransparentProxy(); tproxyCfg != nil {
|
||||||
|
if tproxyCfg.DialedDirectly {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "transparent_proxy",
|
||||||
|
Wrapped: resource.ErrInvalidField{
|
||||||
|
Name: "dialed_directly",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if outboundListenerPortErr := validatePort(tproxyCfg.OutboundListenerPort, "outbound_listener_port"); outboundListenerPortErr != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "transparent_proxy",
|
||||||
|
Wrapped: outboundListenerPortErr,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if exposeCfg := cfg.GetExposeConfig(); exposeCfg != nil {
|
||||||
|
for i, path := range exposeCfg.GetExposePaths() {
|
||||||
|
if listenerPortErr := validatePort(path.ListenerPort, "listener_port"); listenerPortErr != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "expose_config",
|
||||||
|
Wrapped: resource.ErrInvalidListElement{
|
||||||
|
Name: "expose_paths",
|
||||||
|
Index: i,
|
||||||
|
Wrapped: listenerPortErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if localPathPortErr := validatePort(path.LocalPathPort, "local_path_port"); localPathPortErr != nil {
|
||||||
|
err = multierror.Append(err, resource.ErrInvalidField{
|
||||||
|
Name: "expose_config",
|
||||||
|
Wrapped: resource.ErrInvalidListElement{
|
||||||
|
Name: "expose_paths",
|
||||||
|
Index: i,
|
||||||
|
Wrapped: localPathPortErr,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePort(port uint32, fieldName string) error {
|
||||||
|
if port < 1 || port > math.MaxUint16 {
|
||||||
|
return resource.ErrInvalidField{
|
||||||
|
Name: fieldName,
|
||||||
|
Wrapped: errInvalidPort,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,12 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/internal/resource"
|
"github.com/hashicorp/consul/internal/resource"
|
||||||
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
"github.com/hashicorp/consul/internal/resource/resourcetest"
|
||||||
|
@ -86,7 +89,196 @@ func TestMutateProxyConfiguration(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateProxyConfiguration(t *testing.T) {
|
func TestValidateProxyConfiguration_MissingBothDynamicAndBootstrapConfig(t *testing.T) {
|
||||||
|
proxyCfg := &pbmesh.ProxyConfiguration{
|
||||||
|
Workloads: &pbcatalog.WorkloadSelector{Names: []string{"foo"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
res := resourcetest.Resource(pbmesh.ProxyConfigurationType, "test").
|
||||||
|
WithData(t, proxyCfg).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
err := ValidateProxyConfiguration(res)
|
||||||
|
|
||||||
|
var expError error
|
||||||
|
expError = multierror.Append(expError,
|
||||||
|
resource.ErrInvalidFields{
|
||||||
|
Names: []string{"dynamic_config", "bootstrap_config"},
|
||||||
|
Wrapped: errMissingProxyConfigData,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
require.Equal(t, err, expError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateProxyConfiguration_AllFieldsInvalid(t *testing.T) {
|
||||||
|
proxyCfg := &pbmesh.ProxyConfiguration{
|
||||||
|
// Omit workload selector.
|
||||||
|
|
||||||
|
DynamicConfig: &pbmesh.DynamicConfig{
|
||||||
|
// Set unsupported fields.
|
||||||
|
MutualTlsMode: pbmesh.MutualTLSMode_MUTUAL_TLS_MODE_PERMISSIVE,
|
||||||
|
MeshGatewayMode: pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_LOCAL,
|
||||||
|
AccessLogs: &pbmesh.AccessLogsConfig{},
|
||||||
|
EnvoyExtensions: []*pbmesh.EnvoyExtension{{Name: "foo"}},
|
||||||
|
PublicListenerJson: "listener-json",
|
||||||
|
ListenerTracingJson: "tracing-json",
|
||||||
|
LocalClusterJson: "cluster-json",
|
||||||
|
LocalWorkloadAddress: "1.1.1.1",
|
||||||
|
LocalWorkloadPort: 1234,
|
||||||
|
LocalWorkloadSocketPath: "/foo/bar",
|
||||||
|
|
||||||
|
TransparentProxy: &pbmesh.TransparentProxy{
|
||||||
|
DialedDirectly: true, // unsupported
|
||||||
|
OutboundListenerPort: math.MaxUint16 + 1, // invalid
|
||||||
|
},
|
||||||
|
|
||||||
|
// Create invalid expose paths config.
|
||||||
|
ExposeConfig: &pbmesh.ExposeConfig{
|
||||||
|
ExposePaths: []*pbmesh.ExposePath{
|
||||||
|
{
|
||||||
|
ListenerPort: 0,
|
||||||
|
LocalPathPort: math.MaxUint16 + 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
OpaqueConfig: &structpb.Struct{},
|
||||||
|
}
|
||||||
|
|
||||||
|
res := resourcetest.Resource(pbmesh.ProxyConfigurationType, "test").
|
||||||
|
WithData(t, proxyCfg).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
err := ValidateProxyConfiguration(res)
|
||||||
|
|
||||||
|
var dynamicCfgErr error
|
||||||
|
unsupportedFields := []string{
|
||||||
|
"mutual_tls_mode",
|
||||||
|
"mesh_gateway_mode",
|
||||||
|
"access_logs",
|
||||||
|
"envoy_extensions",
|
||||||
|
"public_listener_json",
|
||||||
|
"listener_tracing_json",
|
||||||
|
"local_cluster_json",
|
||||||
|
"local_workload_address",
|
||||||
|
"local_workload_port",
|
||||||
|
"local_workload_socket_path",
|
||||||
|
}
|
||||||
|
for _, f := range unsupportedFields {
|
||||||
|
dynamicCfgErr = multierror.Append(dynamicCfgErr,
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: f,
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
dynamicCfgErr = multierror.Append(dynamicCfgErr,
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "transparent_proxy",
|
||||||
|
Wrapped: resource.ErrInvalidField{
|
||||||
|
Name: "dialed_directly",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "transparent_proxy",
|
||||||
|
Wrapped: resource.ErrInvalidField{
|
||||||
|
Name: "outbound_listener_port",
|
||||||
|
Wrapped: errInvalidPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "expose_config",
|
||||||
|
Wrapped: resource.ErrInvalidListElement{
|
||||||
|
Name: "expose_paths",
|
||||||
|
Wrapped: resource.ErrInvalidField{
|
||||||
|
Name: "listener_port",
|
||||||
|
Wrapped: errInvalidPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "expose_config",
|
||||||
|
Wrapped: resource.ErrInvalidListElement{
|
||||||
|
Name: "expose_paths",
|
||||||
|
Wrapped: resource.ErrInvalidField{
|
||||||
|
Name: "local_path_port",
|
||||||
|
Wrapped: errInvalidPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
var expError error
|
||||||
|
expError = multierror.Append(expError,
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "workloads",
|
||||||
|
Wrapped: resource.ErrEmpty,
|
||||||
|
},
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "opaque_config",
|
||||||
|
Wrapped: resource.ErrUnsupported,
|
||||||
|
},
|
||||||
|
resource.ErrInvalidField{
|
||||||
|
Name: "dynamic_config",
|
||||||
|
Wrapped: dynamicCfgErr,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.Equal(t, err, expError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateProxyConfiguration_AllFieldsValid(t *testing.T) {
|
||||||
|
proxyCfg := &pbmesh.ProxyConfiguration{
|
||||||
|
Workloads: &pbcatalog.WorkloadSelector{Names: []string{"foo"}},
|
||||||
|
|
||||||
|
DynamicConfig: &pbmesh.DynamicConfig{
|
||||||
|
MutualTlsMode: pbmesh.MutualTLSMode_MUTUAL_TLS_MODE_DEFAULT,
|
||||||
|
MeshGatewayMode: pbmesh.MeshGatewayMode_MESH_GATEWAY_MODE_UNSPECIFIED,
|
||||||
|
|
||||||
|
TransparentProxy: &pbmesh.TransparentProxy{
|
||||||
|
DialedDirectly: false,
|
||||||
|
OutboundListenerPort: 15500,
|
||||||
|
},
|
||||||
|
|
||||||
|
ExposeConfig: &pbmesh.ExposeConfig{
|
||||||
|
ExposePaths: []*pbmesh.ExposePath{
|
||||||
|
{
|
||||||
|
ListenerPort: 1234,
|
||||||
|
LocalPathPort: 1235,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
BootstrapConfig: &pbmesh.BootstrapConfig{
|
||||||
|
StatsdUrl: "stats-url",
|
||||||
|
DogstatsdUrl: "dogstats-url",
|
||||||
|
StatsTags: []string{"tags"},
|
||||||
|
PrometheusBindAddr: "prom-bind-addr",
|
||||||
|
StatsBindAddr: "stats-bind-addr",
|
||||||
|
ReadyBindAddr: "ready-bind-addr",
|
||||||
|
OverrideJsonTpl: "override-json-tpl",
|
||||||
|
StaticClustersJson: "static-clusters-json",
|
||||||
|
StaticListenersJson: "static-listeners-json",
|
||||||
|
StatsSinksJson: "stats-sinks-json",
|
||||||
|
StatsConfigJson: "stats-config-json",
|
||||||
|
StatsFlushInterval: "stats-flush-interval",
|
||||||
|
TracingConfigJson: "tracing-config-json",
|
||||||
|
TelemetryCollectorBindSocketDir: "telemetry-collector-bind-socket-dir",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res := resourcetest.Resource(pbmesh.ProxyConfigurationType, "test").
|
||||||
|
WithData(t, proxyCfg).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
err := ValidateProxyConfiguration(res)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateProxyConfiguration_WorkloadSelector(t *testing.T) {
|
||||||
type testcase struct {
|
type testcase struct {
|
||||||
data *pbmesh.ProxyConfiguration
|
data *pbmesh.ProxyConfiguration
|
||||||
expectErr string
|
expectErr string
|
||||||
|
|
|
@ -5,15 +5,18 @@ package resource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/proto-public/pbresource"
|
"github.com/hashicorp/consul/proto-public/pbresource"
|
||||||
"google.golang.org/protobuf/reflect/protoreflect"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMissing = NewConstError("missing required field")
|
ErrMissing = NewConstError("missing required field")
|
||||||
ErrEmpty = NewConstError("cannot be empty")
|
ErrEmpty = NewConstError("cannot be empty")
|
||||||
ErrReferenceTenancyNotEqual = NewConstError("resource tenancy and reference tenancy differ")
|
ErrReferenceTenancyNotEqual = NewConstError("resource tenancy and reference tenancy differ")
|
||||||
|
ErrUnsupported = NewConstError("field is currently not supported")
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConstError is more or less equivalent to the stdlib errors.errorstring. However, having
|
// ConstError is more or less equivalent to the stdlib errors.errorstring. However, having
|
||||||
|
@ -147,3 +150,17 @@ type ErrInvalidReferenceType struct {
|
||||||
func (err ErrInvalidReferenceType) Error() string {
|
func (err ErrInvalidReferenceType) Error() string {
|
||||||
return fmt.Sprintf("reference must have type %s", ToGVK(err.AllowedType))
|
return fmt.Sprintf("reference must have type %s", ToGVK(err.AllowedType))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ErrInvalidFields struct {
|
||||||
|
Names []string
|
||||||
|
Wrapped error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidFields) Error() string {
|
||||||
|
allFields := strings.Join(err.Names, ",")
|
||||||
|
return fmt.Sprintf("invalid %q fields: %v", allFields, err.Wrapped)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrInvalidFields) Unwrap() error {
|
||||||
|
return err.Wrapped
|
||||||
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ type MeshGatewayMode int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// MESH_GATEWAY_MODE_UNSPECIFIED represents no specific mode and should be
|
// MESH_GATEWAY_MODE_UNSPECIFIED represents no specific mode and should be
|
||||||
// used to indicate that a the decision on the mode will be made by other
|
// used to indicate that the decision on the mode will be made by other
|
||||||
// configuration or default settings.
|
// configuration or default settings.
|
||||||
MeshGatewayMode_MESH_GATEWAY_MODE_UNSPECIFIED MeshGatewayMode = 0
|
MeshGatewayMode_MESH_GATEWAY_MODE_UNSPECIFIED MeshGatewayMode = 0
|
||||||
// MESH_GATEWAY_MODE_NONE is the mode to use when traffic should not be
|
// MESH_GATEWAY_MODE_NONE is the mode to use when traffic should not be
|
||||||
|
|
|
@ -9,7 +9,7 @@ package hashicorp.consul.mesh.v2beta1;
|
||||||
// +kubebuilder:validation:Type=string
|
// +kubebuilder:validation:Type=string
|
||||||
enum MeshGatewayMode {
|
enum MeshGatewayMode {
|
||||||
// MESH_GATEWAY_MODE_UNSPECIFIED represents no specific mode and should be
|
// MESH_GATEWAY_MODE_UNSPECIFIED represents no specific mode and should be
|
||||||
// used to indicate that a the decision on the mode will be made by other
|
// used to indicate that the decision on the mode will be made by other
|
||||||
// configuration or default settings.
|
// configuration or default settings.
|
||||||
MESH_GATEWAY_MODE_UNSPECIFIED = 0;
|
MESH_GATEWAY_MODE_UNSPECIFIED = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue