Files
kubevela/pkg/webhook/core.oam.dev/v1beta1/application/validation.go
Kanchan Dhamane bc15e5b359
Some checks failed
CodeQL / Analyze (go) (push) Failing after 1m43s
Definition-Lint / definition-doc (push) Failing after 6m13s
E2E MultiCluster Test / detect-noop (push) Successful in 24s
E2E Test / detect-noop (push) Successful in 17s
Go / detect-noop (push) Successful in 21s
license / Check for unapproved licenses (push) Failing after 2m38s
Registry / publish-core-images (push) Failing after 40s
Unit-Test / detect-noop (push) Successful in 20s
E2E MultiCluster Test / e2e-multi-cluster-tests (v1.29) (push) Failing after 1m55s
E2E Test / e2e-tests (v1.29) (push) Failing after 1m18s
Go / staticcheck (push) Successful in 18m35s
Go / lint (push) Failing after 19m38s
Go / check-diff (push) Failing after 15m7s
Go / check-core-image-build (push) Failing after 3m45s
Go / check-cli-image-build (push) Failing after 2m23s
Unit-Test / unit-tests (push) Failing after 12m43s
Go / check-windows (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Failing after 48s
Feat: Semantic versioning support for Definitions (#6648)
* feature: Add Semantic versioning to KubeVela Definitions

Fixes https://github.com/kubevela/kubevela/issues/6435
Fixes https://github.com/kubevela/kubevela/issues/6534

Changes:
- Adds an optional "Version" field for all Definition Specs.
- Adds the following new validations to Webhooks for Definitions:
	- Validate the "Version" field follows Semantic versioning.
	- Dis-allow conflicting versioning fields ( Name annotation, Spec.Version)
- Adds the following new validations to Webhooks for Application:
	- Dis-allow the use of both the "publishVersion" & "autoUpdate" annotations.
- Enahnce "multiStageComponentApply" feature to support auto updates.

Boy Scout Changes:
- Fixes Plugin e2e tests broken by the fix for 6534.
- Fixes the dryRun and livediff commands to respect the "-n" namespace flag.
- Fixes the Application ValidationWebhook to respect the "-n" namespace flag.

Co-authored-by: Rahul Kumar <35751394+bugbounce@users.noreply.github.com>
Co-authored-by: Chaitanya Reddy <chaitanyareddy0702@gmail.com>
Co-authored-by: Vibhor Chinda <vibhorchinda@gmail.com>
Co-authored-by: Shivin Gopalani <gopalanishivin@gmail.com>

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

* feature: Add KEP to define the proposal

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

* fix: Rebase and fix merge conflicts

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

* Fix: Adds unit test cases

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>

---------

Signed-off-by: kanchan-dhamane <74534570+kanchan-dhamane@users.noreply.github.com>
Co-authored-by: bugbounce <35751394+bugbounce@users.noreply.github.com>
2025-02-03 11:09:28 +08:00

141 lines
5.2 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 application
import (
"context"
"fmt"
"time"
"github.com/kubevela/pkg/controller/sharding"
"github.com/kubevela/pkg/util/singleton"
"k8s.io/apimachinery/pkg/util/validation/field"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"sigs.k8s.io/controller-runtime/pkg/client"
"github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1"
"github.com/oam-dev/kubevela/pkg/appfile"
"github.com/oam-dev/kubevela/pkg/features"
"github.com/oam-dev/kubevela/pkg/oam"
)
// ValidateWorkflow validates the Application workflow
func (h *ValidatingHandler) ValidateWorkflow(_ context.Context, app *v1beta1.Application) field.ErrorList {
var errs field.ErrorList
if app.Spec.Workflow != nil {
stepName := make(map[string]interface{})
for _, step := range app.Spec.Workflow.Steps {
if _, ok := stepName[step.Name]; ok {
errs = append(errs, field.Invalid(field.NewPath("spec", "workflow", "steps"), step.Name, "duplicated step name"))
}
stepName[step.Name] = nil
if step.Timeout != "" {
errs = append(errs, h.ValidateTimeout(step.Name, step.Timeout)...)
}
for _, sub := range step.SubSteps {
if _, ok := stepName[sub.Name]; ok {
errs = append(errs, field.Invalid(field.NewPath("spec", "workflow", "steps", "subSteps"), sub.Name, "duplicated step name"))
}
stepName[sub.Name] = nil
if step.Timeout != "" {
errs = append(errs, h.ValidateTimeout(step.Name, step.Timeout)...)
}
}
}
}
return errs
}
// ValidateTimeout validates the timeout of steps
func (h *ValidatingHandler) ValidateTimeout(name, timeout string) field.ErrorList {
var errs field.ErrorList
_, err := time.ParseDuration(timeout)
if err != nil {
errs = append(errs, field.Invalid(field.NewPath("spec", "workflow", "steps", "timeout"), name, "invalid timeout, please use the format of timeout like 1s, 1m, 1h or 1d"))
}
return errs
}
// appRevBypassCacheClient
type appRevBypassCacheClient struct {
client.Client
}
// Get retrieve appRev directly from request if sharding enabled
func (in *appRevBypassCacheClient) Get(ctx context.Context, key client.ObjectKey, obj client.Object, _ ...client.GetOption) error {
if _, ok := obj.(*v1beta1.ApplicationRevision); ok && sharding.EnableSharding {
return singleton.KubeClient.Get().Get(ctx, key, obj)
}
return in.Client.Get(ctx, key, obj)
}
// ValidateComponents validates the Application components
func (h *ValidatingHandler) ValidateComponents(ctx context.Context, app *v1beta1.Application) field.ErrorList {
if sharding.EnableSharding && !utilfeature.DefaultMutableFeatureGate.Enabled(features.ValidateComponentWhenSharding) {
return nil
}
var componentErrs field.ErrorList
// try to generate an app file
cli := &appRevBypassCacheClient{Client: h.Client}
appParser := appfile.NewApplicationParser(cli)
af, err := appParser.GenerateAppFile(ctx, app)
if err != nil {
componentErrs = append(componentErrs, field.Invalid(field.NewPath("spec"), app, err.Error()))
// cannot generate appfile, no need to validate further
return componentErrs
}
if i, err := appParser.ValidateComponentNames(app); err != nil {
componentErrs = append(componentErrs, field.Invalid(field.NewPath(fmt.Sprintf("components[%d].name", i)), app, err.Error()))
}
if err := appParser.ValidateCUESchematicAppfile(af); err != nil {
componentErrs = append(componentErrs, field.Invalid(field.NewPath("schematic"), app, err.Error()))
}
return componentErrs
}
// ValidateAnnotations validates whether the application has both autoupdate and publish version annotations
func (h *ValidatingHandler) ValidateAnnotations(_ context.Context, app *v1beta1.Application) field.ErrorList {
var annotationsErrs field.ErrorList
hasPublishVersion := app.Annotations[oam.AnnotationPublishVersion]
hasAutoUpdate := app.Annotations[oam.AnnotationAutoUpdate]
if hasAutoUpdate == "true" && hasPublishVersion != "" {
annotationsErrs = append(annotationsErrs, field.Invalid(field.NewPath("metadata", "annotations"), app,
"Application has both autoUpdate and publishVersion annotations. Only one can be present"))
}
return annotationsErrs
}
// ValidateCreate validates the Application on creation
func (h *ValidatingHandler) ValidateCreate(ctx context.Context, app *v1beta1.Application) field.ErrorList {
var errs field.ErrorList
errs = append(errs, h.ValidateAnnotations(ctx, app)...)
errs = append(errs, h.ValidateWorkflow(ctx, app)...)
errs = append(errs, h.ValidateComponents(ctx, app)...)
return errs
}
// ValidateUpdate validates the Application on update
func (h *ValidatingHandler) ValidateUpdate(ctx context.Context, newApp, _ *v1beta1.Application) field.ErrorList {
// check if the newApp is valid
errs := h.ValidateCreate(ctx, newApp)
// TODO: add more validating
return errs
}