mirror of
https://github.com/kubevela/kubevela.git
synced 2026-05-18 15:27:45 +00:00
* feat: implement output resource existence validation in component, trait, and policy definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add validation tests for ComponentDefinition and TraitDefinition outputs - Implement tests for ComponentDefinition with non-existent CRDs in outputs, ensuring they are rejected. - Add tests for valid outputs in ComponentDefinition, confirming acceptance. - Include tests for mixed valid and non-K8s outputs in ComponentDefinition, verifying they pass validation. - Test handling of empty outputs in ComponentDefinition, ensuring they are accepted. - Introduce tests for invalid apiVersion formats in ComponentDefinition, confirming rejection. - Add tests for TraitDefinition with mixed valid and invalid outputs, ensuring proper rejection. - Create YAML manifests for valid and invalid ComponentDefinitions and TraitDefinitions to support e2e tests. - Ensure comprehensive coverage of edge cases in output validation logic. Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> fix: handle errors in resource validation for component, trait, and policy definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> fix: improve error handling in Go module tidy and resource validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add webhook debugging setup and validation tests for ComponentDefinition and TraitDefinition Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add VS Code launch configuration for debugging webhook validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> refactor: streamline error handling in Go module tidy and remove obsolete test manifests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add mock context support for CUE template compilation Signed-off-by: Reetika Malhotra <malhotra.reetika25@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance validation for WorkflowStepDefinition resources and improve output resource checks Signed-off-by: viskumar <viskumar@guidewire.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: implement resource validation for CUE templates and add unit tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance logging and validation for component, policy, and trait definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: improve error handling and logging in validation handlers for component, policy, trait, and workflow step definitions Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Remove testUnknownResource folder from repository Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: implement structured logging for validation handlers and remove deprecated request_logger Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance structured logging and error handling in admission validation handlers Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: improve logging messages in validating handlers for better clarity Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: refactor logging field definitions for consistency and improve error handling in resource validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> chore: add license header to invalid_resource_check.go and invalid_resource_check_test.go Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance validation tests for WorkflowStepDefinition and improve error messages Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add e2e-test-local target for k3d cluster setup and webhook validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add webhook configuration for workflow step definitions with validation rules Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: update e2e-test-local configuration and improve Ingress API version compatibility Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add installation of FluxCD CRDs in pre-hook to prevent webhook validation errors Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add ValidateResourcesExist feature gate and enhance resource validation in webhook handlers Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance resource validation in e2e tests and improve addon definition checks Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: enhance addon definition detection by using owner references for validation Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: add ValidateResourcesExist feature gate and implement webhook validation for resource existence Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: update Ingress API version to v1 and adjust service references in tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> chore: remove webhook test commands and related YAML files from makefiles and tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> chore: remove architecture section from webhook debugging guide Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> feat: update webhook setup script with k3d host gateway IP note and improve cluster creation logic Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> * Fix: Correct path in Ingress resource definition in template tests Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> * Chore: add empty line to re-trigger failing workflow Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> * Chore: remove space to re-trigger workflow Signed-off-by: Chaitanya Reddy Onteddu <co@guidewire.com> --------- Signed-off-by: Ayush Kumar <ayushshyamkumar888@gmail.com> Signed-off-by: Vaibhav Agrawal <vaibhav.agrawal0096@gmail.com> Signed-off-by: Chaitanya Reddy Onteddu <co@guidewire.com> Co-authored-by: Chaitanya Reddy Onteddu <chaitanyareddy0702@gmail.com> Co-authored-by: Amit Singh <amisingh@guidewire.com>
258 lines
6.7 KiB
Go
258 lines
6.7 KiB
Go
/*
|
|
Copyright 2021. The KubeVela 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 utils
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"cuelang.org/go/cue/ast"
|
|
"cuelang.org/go/cue/parser"
|
|
"cuelang.org/go/cue/token"
|
|
"k8s.io/apimachinery/pkg/api/meta"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
|
|
"github.com/oam-dev/kubevela/pkg/features"
|
|
)
|
|
|
|
// ExtractResourceInfo extracts apiVersion and kind from CUE template without evaluation
|
|
func ExtractResourceInfo(cueTemplate string) ([]ResourceInfo, error) {
|
|
file, err := parser.ParseFile("", cueTemplate, parser.ParseComments)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to parse CUE template: %w", err)
|
|
}
|
|
|
|
var resources []ResourceInfo
|
|
|
|
// Walk through the AST to find output and outputs fields
|
|
ast.Walk(file, func(node ast.Node) bool {
|
|
if n, ok := node.(*ast.Field); ok {
|
|
label := extractLabel(n.Label)
|
|
if label == "output" || label == "outputs" {
|
|
if label == "output" {
|
|
// Extract from single output field
|
|
if resource := extractResourceFromStruct(n.Value); resource != nil {
|
|
resources = append(resources, *resource)
|
|
}
|
|
} else if label == "outputs" {
|
|
// Extract from outputs field (multiple resources)
|
|
resources = append(resources, extractResourcesFromOutputs(n.Value)...)
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}, nil)
|
|
|
|
return resources, nil
|
|
}
|
|
|
|
type ResourceInfo struct {
|
|
APIVersion string
|
|
Kind string
|
|
Name string // optional, for better error messages
|
|
}
|
|
|
|
func extractLabel(label ast.Label) string {
|
|
switch l := label.(type) {
|
|
case *ast.Ident:
|
|
return l.Name
|
|
case *ast.BasicLit:
|
|
if l.Kind == token.STRING {
|
|
// Remove quotes
|
|
return strings.Trim(l.Value, `"`)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func extractResourceFromStruct(expr ast.Expr) *ResourceInfo {
|
|
structLit, ok := expr.(*ast.StructLit)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
|
|
resource := &ResourceInfo{}
|
|
|
|
for _, elt := range structLit.Elts {
|
|
field, ok := elt.(*ast.Field)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
label := extractLabel(field.Label)
|
|
value := extractStringValue(field.Value)
|
|
|
|
switch label {
|
|
case "apiVersion":
|
|
resource.APIVersion = value
|
|
case "kind":
|
|
resource.Kind = value
|
|
case "metadata":
|
|
// Try to extract name from metadata.name
|
|
if name := extractNameFromMetadata(field.Value); name != "" {
|
|
resource.Name = name
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only return if we have both apiVersion and kind
|
|
if resource.APIVersion != "" && resource.Kind != "" {
|
|
return resource
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func extractResourcesFromOutputs(expr ast.Expr) []ResourceInfo {
|
|
var resources []ResourceInfo
|
|
|
|
structLit, ok := expr.(*ast.StructLit)
|
|
if !ok {
|
|
return resources
|
|
}
|
|
|
|
for _, elt := range structLit.Elts {
|
|
field, ok := elt.(*ast.Field)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if resource := extractResourceFromStruct(field.Value); resource != nil {
|
|
// Use the field label as the resource name if not found in metadata
|
|
if resource.Name == "" {
|
|
resource.Name = extractLabel(field.Label)
|
|
}
|
|
resources = append(resources, *resource)
|
|
}
|
|
}
|
|
|
|
return resources
|
|
}
|
|
|
|
func extractStringValue(expr ast.Expr) string {
|
|
if e, ok := expr.(*ast.BasicLit); ok {
|
|
if e.Kind == token.STRING {
|
|
return strings.Trim(e.Value, `"`)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func extractNameFromMetadata(expr ast.Expr) string {
|
|
structLit, ok := expr.(*ast.StructLit)
|
|
if !ok {
|
|
return ""
|
|
}
|
|
|
|
for _, elt := range structLit.Elts {
|
|
field, ok := elt.(*ast.Field)
|
|
if !ok {
|
|
continue
|
|
}
|
|
|
|
if extractLabel(field.Label) == "name" {
|
|
return extractStringValue(field.Value)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// ValidateOutputResourcesExist validates that resources referenced in output/outputs fields exist on the cluster
|
|
func ValidateOutputResourcesExist(cueTemplate string, mapper meta.RESTMapper, obj client.Object) error {
|
|
// Check if feature gate is enabled FIRST before doing anything
|
|
if !utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateResourcesExist) {
|
|
return nil // Skip validation if feature is disabled
|
|
}
|
|
|
|
// Skip validation for addon definitions
|
|
// Addons often bundle CRDs with definitions that reference them
|
|
if IsAddonDefinition(obj) {
|
|
return nil
|
|
}
|
|
|
|
// Only extract and validate resources if feature is enabled and not an addon
|
|
resources, err := ExtractResourceInfo(cueTemplate)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract resource info: %w", err)
|
|
}
|
|
|
|
for _, resource := range resources {
|
|
gvk := schema.GroupVersionKind{
|
|
Version: resource.APIVersion,
|
|
Kind: resource.Kind,
|
|
}
|
|
|
|
// Parse the apiVersion to get group and version
|
|
if strings.Contains(resource.APIVersion, "/") {
|
|
parts := strings.SplitN(resource.APIVersion, "/", 2)
|
|
gvk.Group = parts[0]
|
|
gvk.Version = parts[1]
|
|
} else {
|
|
// Core API resources (like v1) don't have a group
|
|
gvk.Group = ""
|
|
gvk.Version = resource.APIVersion
|
|
}
|
|
|
|
_, err := mapper.RESTMapping(gvk.GroupKind(), gvk.Version)
|
|
if err != nil {
|
|
ref := fmt.Sprintf("%s/%s", resource.APIVersion, resource.Kind)
|
|
return fmt.Errorf("resource type not found on cluster: %s (%w)", ref, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IsAddonDefinition checks if the object is part of an addon installation
|
|
// This is a generic solution that works for ALL addons by checking owner references
|
|
func IsAddonDefinition(obj client.Object) bool {
|
|
if obj == nil {
|
|
return false
|
|
}
|
|
|
|
// Generic approach: Check if the object has an owner reference to an addon application
|
|
// All addon definitions get owner references to their addon application (pattern: addon-{name})
|
|
for _, ownerRef := range obj.GetOwnerReferences() {
|
|
if ownerRef.Kind == "Application" &&
|
|
ownerRef.APIVersion == "core.oam.dev/v1beta1" &&
|
|
strings.HasPrefix(ownerRef.Name, "addon-") {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Fallback checks for edge cases where owner references might not be set yet
|
|
labels := obj.GetLabels()
|
|
if labels != nil {
|
|
// Check if this is managed by an addon application
|
|
appName, hasApp := labels["app.oam.dev/name"]
|
|
if hasApp && strings.HasPrefix(appName, "addon-") {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// Also check annotations for addon markers
|
|
annotations := obj.GetAnnotations()
|
|
if annotations != nil {
|
|
if addonName, hasAddon := annotations["addons.oam.dev/name"]; hasAddon && addonName != "" {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|