-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* application: init webhook Signed-off-by: Xieql <xieqianglong@huawei.com> * fleetmanager: add customization for deployment Signed-off-by: Xieql <xieqianglong@huawei.com> * fleetmanager: add webhook args for deployment Signed-off-by: Xieql <xieqianglong@huawei.com> --------- Signed-off-by: Xieql <xieqianglong@huawei.com>
- Loading branch information
Showing
13 changed files
with
422 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
apiVersion: v1 | ||
kind: Service | ||
metadata: | ||
name: kurator-webhook-service-fleet | ||
namespace: {{ .Release.Namespace }} | ||
spec: | ||
ports: | ||
- port: 443 | ||
targetPort: webhook-server | ||
selector: | ||
app.kubernetes.io/name: kurator-fleet-manager |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
apiVersion: admissionregistration.k8s.io/v1 | ||
kind: ValidatingWebhookConfiguration | ||
metadata: | ||
annotations: | ||
cert-manager.io/inject-ca-from: {{ .Release.Namespace }}/kurator-serving-cert | ||
creationTimestamp: null | ||
name: fleet-manager-validating-webhook-configuration | ||
webhooks: | ||
- admissionReviewVersions: | ||
- v1 | ||
- v1beta1 | ||
clientConfig: | ||
service: | ||
name: kurator-webhook-service-fleet | ||
namespace: {{ .Release.Namespace }} | ||
path: /validate-apps-kurator-dev-v1alpha1-application # do not change this | ||
failurePolicy: Fail | ||
matchPolicy: Equivalent | ||
name: validation.application.apps.kurator.dev | ||
rules: | ||
- apiGroups: | ||
- apps.kurator.dev | ||
apiVersions: | ||
- v1alpha1 | ||
operations: | ||
- CREATE | ||
- UPDATE | ||
resources: | ||
- applications | ||
sideEffects: None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,7 @@ image: | |
|
||
logging: | ||
level: 0 | ||
|
||
webhook: | ||
port: 9443 | ||
certMountPath: /tmp/k8s-webhook-server/serving-certs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
Copyright Kurator 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 webhooks | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/util/validation/field" | ||
ctrl "sigs.k8s.io/controller-runtime" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/webhook" | ||
|
||
"kurator.dev/kurator/pkg/apis/apps/v1alpha1" | ||
) | ||
|
||
var _ webhook.CustomValidator = &ApplicationWebhook{} | ||
|
||
type ApplicationWebhook struct { | ||
Client client.Reader | ||
} | ||
|
||
func (wh *ApplicationWebhook) SetupWebhookWithManager(mgr ctrl.Manager) error { | ||
return ctrl.NewWebhookManagedBy(mgr). | ||
For(&v1alpha1.Application{}). | ||
WithValidator(wh). | ||
Complete() | ||
} | ||
|
||
func (wh *ApplicationWebhook) ValidateCreate(_ context.Context, obj runtime.Object) error { | ||
in, ok := obj.(*v1alpha1.Application) | ||
if !ok { | ||
return apierrors.NewBadRequest(fmt.Sprintf("expected a Application but got a %T", obj)) | ||
} | ||
|
||
return wh.validate(in) | ||
} | ||
|
||
func (wh *ApplicationWebhook) validate(in *v1alpha1.Application) error { | ||
var allErrs field.ErrorList | ||
|
||
allErrs = append(allErrs, validateFleet(in)...) | ||
|
||
if len(allErrs) > 0 { | ||
return apierrors.NewInvalid(v1alpha1.SchemeGroupVersion.WithKind("Application").GroupKind(), in.Name, allErrs) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// validateFleet validates the fleet in the application with the following rules: | ||
// 1 if defaultFleet is set, make sure all policy fleet(if set) is same as the defaultFleet | ||
// 2 if defaultFleet is not set, every individual policies must be set and must be same as the first policy fleet | ||
func validateFleet(in *v1alpha1.Application) field.ErrorList { | ||
var allErrs field.ErrorList | ||
|
||
defaultFleet := "" | ||
if in.Spec.Destination != nil { | ||
defaultFleet = in.Spec.Destination.Fleet | ||
} | ||
|
||
// if defaultFleet is set, make sure all policy fleet(if set) is same as the defaultFleet | ||
if defaultFleet != "" { | ||
for i, policy := range in.Spec.SyncPolicies { | ||
if policy.Destination != nil && policy.Destination.Fleet != "" && defaultFleet != policy.Destination.Fleet { | ||
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "syncPolicies").Index(i).Child("destination", "fleet"), policy.Destination.Fleet, fmt.Sprintf("must be same as application.spec.destination.fleet:%v, because fleet must be consistent throughout the application", defaultFleet))) | ||
} | ||
} | ||
} | ||
|
||
// if defaultFleet is not set, every individual policies must be set and must be same as the first policy fleet | ||
if defaultFleet == "" { | ||
var ( | ||
firstPolicyFleet string | ||
isFirst = true | ||
) | ||
for i, policy := range in.Spec.SyncPolicies { | ||
// if individual policy fleet is not set, return err | ||
if policy.Destination == nil || policy.Destination.Fleet == "" { | ||
allErrs = append(allErrs, field.Required(field.NewPath("spec", "syncPolicies").Index(i).Child("destination", "fleet"), "must be set when application.spec.destination.fleet is not set")) | ||
return allErrs | ||
} | ||
if isFirst { | ||
firstPolicyFleet = policy.Destination.Fleet | ||
isFirst = false | ||
} | ||
if !isFirst && firstPolicyFleet != policy.Destination.Fleet { | ||
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "syncPolicies").Index(i).Child("destination", "fleet"), policy.Destination.Fleet, fmt.Sprintf("must be same as firstPolicyFleet:%v, because fleet must be consistent throughout the application", firstPolicyFleet))) | ||
} | ||
} | ||
} | ||
|
||
return allErrs | ||
} | ||
|
||
func (wh *ApplicationWebhook) ValidateUpdate(_ context.Context, oldObj, newObj runtime.Object) error { | ||
_, ok := oldObj.(*v1alpha1.Application) | ||
if !ok { | ||
return apierrors.NewBadRequest(fmt.Sprintf("expected a Application but got a %T", oldObj)) | ||
} | ||
|
||
newApplication, ok := newObj.(*v1alpha1.Application) | ||
if !ok { | ||
return apierrors.NewBadRequest(fmt.Sprintf("expected a Application but got a %T", newObj)) | ||
} | ||
|
||
return wh.validate(newApplication) | ||
} | ||
|
||
func (wh *ApplicationWebhook) ValidateDelete(_ context.Context, obj runtime.Object) error { | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
Copyright Kurator 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 webhooks | ||
|
||
import ( | ||
"io/fs" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"testing" | ||
|
||
. "github.com/onsi/gomega" | ||
"github.com/stretchr/testify/assert" | ||
"sigs.k8s.io/yaml" | ||
|
||
"kurator.dev/kurator/pkg/apis/apps/v1alpha1" | ||
) | ||
|
||
func TestValidApplicationValidation(t *testing.T) { | ||
// read configuration from examples directory to test valid application configuration | ||
r := path.Join("../../examples", "application") | ||
caseNames := make([]string, 0) | ||
err := filepath.WalkDir(r, func(path string, d fs.DirEntry, err error) error { | ||
if d.IsDir() { | ||
return nil | ||
} | ||
|
||
caseNames = append(caseNames, path) | ||
|
||
return nil | ||
}) | ||
assert.NoError(t, err) | ||
|
||
wh := &ApplicationWebhook{} | ||
for _, tt := range caseNames { | ||
t.Run(tt, func(t *testing.T) { | ||
g := NewWithT(t) | ||
c, err := readApplication(tt) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
|
||
err = wh.validate(c) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
}) | ||
} | ||
} | ||
|
||
func TestInvalidApplicationValidation(t *testing.T) { | ||
r := path.Join("testdata", "application") | ||
caseNames := make([]string, 0) | ||
err := filepath.WalkDir(r, func(path string, d fs.DirEntry, err error) error { | ||
if d.IsDir() { | ||
return nil | ||
} | ||
|
||
caseNames = append(caseNames, path) | ||
|
||
return nil | ||
}) | ||
assert.NoError(t, err) | ||
|
||
wh := &ApplicationWebhook{} | ||
for _, tt := range caseNames { | ||
t.Run(tt, func(t *testing.T) { | ||
g := NewWithT(t) | ||
c, err := readApplication(tt) | ||
g.Expect(err).NotTo(HaveOccurred()) | ||
|
||
err = wh.validate(c) | ||
g.Expect(err).To(HaveOccurred()) | ||
t.Logf("%v", err) | ||
}) | ||
} | ||
} | ||
|
||
func readApplication(filename string) (*v1alpha1.Application, error) { | ||
b, err := os.ReadFile(filename) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
c := &v1alpha1.Application{} | ||
if err := yaml.Unmarshal(b, c); err != nil { | ||
return nil, err | ||
} | ||
|
||
return c, nil | ||
} |
Oops, something went wrong.