diff --git a/cmd/controller-manager/app/server.go b/cmd/controller-manager/app/server.go index 8d5a198ca..1e0c392d4 100644 --- a/cmd/controller-manager/app/server.go +++ b/cmd/controller-manager/app/server.go @@ -115,6 +115,7 @@ func init() { runtime.Must(controller.Register(&application.AppVersionReconciler{})) // k8s application runtime.Must(controller.Register(&k8sapplication.Reconciler{})) + runtime.Must(controller.Register(&application.ReleaseWebhook{})) // kubectl runtime.Must(controller.Register(&kubectl.Reconciler{})) } diff --git a/config/ks-core/templates/webhook.yaml b/config/ks-core/templates/webhook.yaml index 1053bd76d..72dfdd00c 100644 --- a/config/ks-core/templates/webhook.yaml +++ b/config/ks-core/templates/webhook.yaml @@ -427,3 +427,39 @@ webhooks: scope: '*' sideEffects: None timeoutSeconds: 30 + +--- +{{- if eq (include "multicluster.role" .) "host" }} +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: applications.kubesphere.io +webhooks: + - admissionReviewVersions: + - v1 + clientConfig: + caBundle: {{ b64enc $ca.Cert | quote }} + service: + name: ks-controller-manager + namespace: {{ .Release.Namespace }} + path: /validate-application-kubesphere-io-v2-applicationrelease + port: 443 + failurePolicy: Fail + matchPolicy: Exact + name: applicationrelease.extensions.kubesphere.io + namespaceSelector: {} + objectSelector: {} + rules: + - apiGroups: + - application.kubesphere.io + apiVersions: + - v2 + operations: + - CREATE + - UPDATE + resources: + - applicationreleases + scope: '*' + sideEffects: None + timeoutSeconds: 30 +{{- end }} diff --git a/pkg/controller/application/apprelease_webhook.go b/pkg/controller/application/apprelease_webhook.go new file mode 100644 index 000000000..5f4b56603 --- /dev/null +++ b/pkg/controller/application/apprelease_webhook.go @@ -0,0 +1,68 @@ +package application + +import ( + "context" + "fmt" + "strings" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + + kscontroller "kubesphere.io/kubesphere/pkg/controller" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + appv2 "kubesphere.io/api/application/v2" + clusterv1alpha1 "kubesphere.io/api/cluster/v1alpha1" + + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +var _ admission.CustomValidator = &ReleaseWebhook{} +var _ kscontroller.ClusterSelector = &ReleaseWebhook{} +var _ kscontroller.Controller = &ReleaseWebhook{} + +type ReleaseWebhook struct { + cache.Cache +} + +func (a *ReleaseWebhook) Name() string { + return "applicationrelease-webhook" +} + +func (a *ReleaseWebhook) SetupWithManager(mgr *kscontroller.Manager) error { + a.Cache = mgr.GetCache() + return ctrl.NewWebhookManagedBy(mgr).WithValidator(a).For(&appv2.ApplicationRelease{}).Complete() + +} + +func (a *ReleaseWebhook) Enabled(clusterRole string) bool { + return strings.EqualFold(clusterRole, string(clusterv1alpha1.ClusterRoleHost)) +} + +func (a *ReleaseWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { + return a.validateAppVersionState(ctx, obj.(*appv2.ApplicationRelease)) +} + +func (a *ReleaseWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (warnings admission.Warnings, err error) { + return a.validateAppVersionState(ctx, newObj.(*appv2.ApplicationRelease)) +} + +func (a *ReleaseWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (warnings admission.Warnings, err error) { + return nil, nil +} + +func (a *ReleaseWebhook) validateAppVersionState(ctx context.Context, release *appv2.ApplicationRelease) (warnings admission.Warnings, err error) { + versionID := release.Spec.AppVersionID + appVersion := &appv2.ApplicationVersion{} + err = a.Get(ctx, types.NamespacedName{Name: versionID}, appVersion) + if err != nil { + return nil, err + } + if appVersion.Status.State != appv2.ReviewStatusActive && release.Status.State != appv2.ReviewStatusPassed { + + return nil, fmt.Errorf("invalid application version: %s, state: %s, for release: %s", + versionID, appVersion.Status.State, release.Name) + } + return nil, nil +}