pkg/api/validation/validator.go (194 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You 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 ( "context" "fmt" "github.com/hashicorp/go-multierror" kwhmodel "github.com/slok/kubewebhook/v2/pkg/model" kwhvalidating "github.com/slok/kubewebhook/v2/pkg/webhook/validating" "go.uber.org/zap" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" "github.com/apache/apisix-ingress-controller/pkg/apisix" v2 "github.com/apache/apisix-ingress-controller/pkg/kube/apisix/apis/config/v2" "github.com/apache/apisix-ingress-controller/pkg/log" ) var ( scheme = runtime.NewScheme() codecs = serializer.NewCodecFactory(scheme) ) var ( ApisixRouteV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixroutes", } ApisixPluginConfigV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixpluginconfigs", } ApisixConsumerV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixconsumers", } ApisixTlsV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixtlses", } ApisixClusterConfigV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixclusterconfigs", } ApisixUpstreamV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixupstreams", } ApisixGlobalRuleV2GVR = metav1.GroupVersionResource{ Group: v2.GroupVersion.Group, Version: v2.GroupVersion.Version, Resource: "apisixglobalrules", } ) var Validator = kwhvalidating.ValidatorFunc( func(ctx context.Context, review *kwhmodel.AdmissionReview, object metav1.Object) (result *kwhvalidating.ValidatorResult, err error) { log.Debugw("arrive validator webhook", zap.Any("object", object)) var ( deserializer = codecs.UniversalDeserializer() GVR = review.RequestGVR valid = true resultErr error msg string ) switch *GVR { case ApisixRouteV2GVR: ar := object.(*v2.ApisixRoute) if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixRoute _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixRoute in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, ar.Spec.IngressClassName) } if valid { valid, resultErr = ValidateApisixRouteV2(ar) } case ApisixUpstreamV2GVR: au := object.(*v2.ApisixUpstream) if au.Spec == nil { valid, msg = false, fmt.Sprintln("Spec cannot be empty") break } if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixUpstream _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixUpstream in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, au.Spec.IngressClassName) } case ApisixPluginConfigV2GVR: apc := object.(*v2.ApisixPluginConfig) if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixPluginConfig _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixPluginConfig in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, apc.Spec.IngressClassName) } if valid { valid, resultErr = ValidateApisixPluginConfigV2(apc) } case ApisixConsumerV2GVR: ac := object.(*v2.ApisixConsumer) if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixConsumer _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixConsumer in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, ac.Spec.IngressClassName) } case ApisixTlsV2GVR: atls := object.(*v2.ApisixTls) if atls.Spec == nil { valid, msg = false, fmt.Sprintln("Spec cannot be empty") break } if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixTls _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixTls in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, atls.Spec.IngressClassName) } case ApisixClusterConfigV2GVR: acc := object.(*v2.ApisixClusterConfig) if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixClusterConfig _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixClusterConfig in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, acc.Spec.IngressClassName) } case ApisixGlobalRuleV2GVR: agr := object.(*v2.ApisixGlobalRule) if review.Operation == kwhmodel.OperationUpdate { var old v2.ApisixGlobalRule _, _, err := deserializer.Decode(review.OldObjectRaw, nil, &old) if err != nil { log.Error("Failed to deserialize ApisixGlobalRule in admisson webhook") break } valid, resultErr = validateIngressClassName(old.Spec.IngressClassName, agr.Spec.IngressClassName) } default: valid = false resultErr = fmt.Errorf("{group: %s, version: %s, Resource: %s} not supported", GVR.Group, GVR.Version, GVR.Resource) } if resultErr != nil { msg = resultErr.Error() } return &kwhvalidating.ValidatorResult{ Valid: valid, Message: msg, }, nil }, ) func ValidateApisixRoutePlugins(plugins []v2.ApisixRoutePlugin) (valid bool, resultErr error) { valid = true client, err := GetSchemaClient(&apisix.ClusterOptions{}) if err != nil { msg := "failed to get the schema client" log.Errorf("%s: %s", msg, err) return false, fmt.Errorf(msg) } for _, plugin := range plugins { if plugin.Enable { pluginConfig := plugin.Config if pluginConfig == nil { pluginConfig = map[string]interface{}{} } if v, err := ValidatePlugin(client, plugin.Name, pluginConfig); !v { valid = false resultErr = multierror.Append(resultErr, err) log.Warnf("failed to validate plugin %s: %s", plugin.Name, err) } } } return }