blob: 2b662542954cd1bf189225a39f7babf5dc1bb514 [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package validation
import (
"fmt"
"net/url"
"strconv"
"strings"
)
import (
envoytypev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3"
"github.com/hashicorp/go-multierror"
meshconfig "istio.io/api/mesh/v1alpha1"
)
func validateExtensionProviderService(service string) error {
if service == "" {
return fmt.Errorf("service must not be empty")
}
parts := strings.Split(service, "/")
if len(parts) == 1 {
if err := ValidateFQDN(service); err != nil {
if err2 := ValidateIPAddress(service); err2 != nil {
return fmt.Errorf("invalid service fmt %s: %s", service, err2)
}
}
} else {
if err := validateNamespaceSlashWildcardHostname(service, false); err != nil {
return err
}
}
return nil
}
func validateExtensionProviderEnvoyExtAuthzStatusOnError(status string) error {
if status == "" {
return nil
}
code, err := strconv.ParseInt(status, 10, 32)
if err != nil {
return fmt.Errorf("invalid statusOnError value %s: %v", status, err)
}
if _, found := envoytypev3.StatusCode_name[int32(code)]; !found {
return fmt.Errorf("unsupported statusOnError value %s, supported values: %v", status, envoytypev3.StatusCode_name)
}
return nil
}
func ValidateExtensionProviderEnvoyExtAuthzHTTP(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationHttpProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil EnvoyExternalAuthorizationHttpProvider")
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, err)
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := validateExtensionProviderEnvoyExtAuthzStatusOnError(config.StatusOnError); err != nil {
errs = appendErrors(errs, err)
}
if config.PathPrefix != "" {
if _, err := url.Parse(config.PathPrefix); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid pathPrefix %s: %v", config.PathPrefix, err))
}
if !strings.HasPrefix(config.PathPrefix, "/") {
errs = appendErrors(errs, fmt.Errorf("pathPrefix should begin with `/` but found %q", config.PathPrefix))
}
}
return
}
func ValidateExtensionProviderEnvoyExtAuthzGRPC(config *meshconfig.MeshConfig_ExtensionProvider_EnvoyExternalAuthorizationGrpcProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil EnvoyExternalAuthorizationGrpcProvider")
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err))
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := validateExtensionProviderEnvoyExtAuthzStatusOnError(config.StatusOnError); err != nil {
errs = appendErrors(errs, err)
}
return
}
func validateExtensionProviderTracingZipkin(config *meshconfig.MeshConfig_ExtensionProvider_ZipkinTracingProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil TracingZipkinProvider")
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err))
}
return
}
func validateExtensionProviderTracingLightStep(config *meshconfig.MeshConfig_ExtensionProvider_LightstepTracingProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil TracingLightStepProvider")
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err))
}
if config.AccessToken == "" {
errs = appendErrors(errs, fmt.Errorf("access token is required"))
}
return
}
func validateExtensionProviderTracingDatadog(config *meshconfig.MeshConfig_ExtensionProvider_DatadogTracingProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil TracingDatadogProvider")
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err))
}
return
}
func validateExtensionProviderTracingOpenCensusAgent(config *meshconfig.MeshConfig_ExtensionProvider_OpenCensusAgentTracingProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil OpenCensusAgent")
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err))
}
return
}
func validateExtensionProviderTracingSkyWalking(config *meshconfig.MeshConfig_ExtensionProvider_SkyWalkingTracingProvider) (errs error) {
if config == nil {
return fmt.Errorf("nil TracingSkyWalkingProvider")
}
if err := validateExtensionProviderService(config.Service); err != nil {
errs = appendErrors(errs, err)
}
if err := ValidatePort(int(config.Port)); err != nil {
errs = appendErrors(errs, fmt.Errorf("invalid service port: %v", err))
}
return
}
func validateExtensionProviderMetricsPrometheus(_ *meshconfig.MeshConfig_ExtensionProvider_PrometheusMetricsProvider) error {
return nil
}
func validateExtensionProviderStackdriver(_ *meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider) error {
return nil
}
func validateExtensionProviderEnvoyFileAccessLog(_ *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLogProvider) error {
return nil
}
func ValidateExtensionProviderEnvoyOtelAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyOpenTelemetryLogProvider) (errs error) {
if provider == nil {
return fmt.Errorf("nil EnvoyOpenTelemetryLogProvider")
}
if err := ValidatePort(int(provider.Port)); err != nil {
errs = appendErrors(errs, err)
}
if err := validateExtensionProviderService(provider.Service); err != nil {
errs = appendErrors(errs, err)
}
return
}
func ValidateExtensionProviderEnvoyHTTPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpGrpcV3LogProvider) (errs error) {
if provider == nil {
return fmt.Errorf("nil EnvoyHttpGrpcV3LogProvider")
}
if err := ValidatePort(int(provider.Port)); err != nil {
errs = appendErrors(errs, err)
}
if err := validateExtensionProviderService(provider.Service); err != nil {
errs = appendErrors(errs, err)
}
return
}
func ValidateExtensionProviderEnvoyTCPAls(provider *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpGrpcV3LogProvider) (errs error) {
if provider == nil {
return fmt.Errorf("nil EnvoyTcpGrpcV3LogProvider")
}
if err := ValidatePort(int(provider.Port)); err != nil {
errs = appendErrors(errs, err)
}
if err := validateExtensionProviderService(provider.Service); err != nil {
errs = appendErrors(errs, err)
}
return
}
func validateExtensionProvider(config *meshconfig.MeshConfig) (errs error) {
definedProviders := map[string]struct{}{}
for _, c := range config.ExtensionProviders {
var currentErrs error
// Provider name must be unique and not empty.
if c.Name == "" {
currentErrs = appendErrors(currentErrs, fmt.Errorf("empty extension provider name"))
} else {
if _, found := definedProviders[c.Name]; found {
currentErrs = appendErrors(currentErrs, fmt.Errorf("duplicate extension provider name %s", c.Name))
}
definedProviders[c.Name] = struct{}{}
}
switch provider := c.Provider.(type) {
case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzHttp:
currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyExtAuthzHTTP(provider.EnvoyExtAuthzHttp))
case *meshconfig.MeshConfig_ExtensionProvider_EnvoyExtAuthzGrpc:
currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyExtAuthzGRPC(provider.EnvoyExtAuthzGrpc))
case *meshconfig.MeshConfig_ExtensionProvider_Zipkin:
currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingZipkin(provider.Zipkin))
case *meshconfig.MeshConfig_ExtensionProvider_Lightstep:
currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingLightStep(provider.Lightstep))
case *meshconfig.MeshConfig_ExtensionProvider_Datadog:
currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingDatadog(provider.Datadog))
case *meshconfig.MeshConfig_ExtensionProvider_Opencensus:
currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingOpenCensusAgent(provider.Opencensus))
case *meshconfig.MeshConfig_ExtensionProvider_Skywalking:
currentErrs = appendErrors(currentErrs, validateExtensionProviderTracingSkyWalking(provider.Skywalking))
case *meshconfig.MeshConfig_ExtensionProvider_Prometheus:
currentErrs = appendErrors(currentErrs, validateExtensionProviderMetricsPrometheus(provider.Prometheus))
case *meshconfig.MeshConfig_ExtensionProvider_Stackdriver:
currentErrs = appendErrors(currentErrs, validateExtensionProviderStackdriver(provider.Stackdriver))
case *meshconfig.MeshConfig_ExtensionProvider_EnvoyFileAccessLog:
currentErrs = appendErrors(currentErrs, validateExtensionProviderEnvoyFileAccessLog(provider.EnvoyFileAccessLog))
case *meshconfig.MeshConfig_ExtensionProvider_EnvoyOtelAls:
currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyOtelAls(provider.EnvoyOtelAls))
case *meshconfig.MeshConfig_ExtensionProvider_EnvoyHttpAls:
currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyHTTPAls(provider.EnvoyHttpAls))
case *meshconfig.MeshConfig_ExtensionProvider_EnvoyTcpAls:
currentErrs = appendErrors(currentErrs, ValidateExtensionProviderEnvoyTCPAls(provider.EnvoyTcpAls))
// TODO: add exhaustiveness test
default:
currentErrs = appendErrors(currentErrs, fmt.Errorf("unsupported provider: %v of type %T", provider, provider))
}
currentErrs = multierror.Prefix(currentErrs, fmt.Sprintf("invalid extension provider %s:", c.Name))
errs = appendErrors(errs, currentErrs)
}
return
}