Merge pull request #2755 from chaunceyjiang/resourceInterpreter
Implement ResourceInterpreterCustomization ValidatingAdmission
This commit is contained in:
commit
3f21fc1ab4
|
@ -139,6 +139,20 @@ webhooks:
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
admissionReviewVersions: ["v1"]
|
admissionReviewVersions: ["v1"]
|
||||||
timeoutSeconds: 10
|
timeoutSeconds: 10
|
||||||
|
- name: resourceinterpretercustomization.karmada.io
|
||||||
|
rules:
|
||||||
|
- operations: ["CREATE", "UPDATE"]
|
||||||
|
apiGroups: ["config.karmada.io"]
|
||||||
|
apiVersions: ["*"]
|
||||||
|
resources: ["resourceinterpretercustomizations"]
|
||||||
|
scope: "Cluster"
|
||||||
|
clientConfig:
|
||||||
|
url: https://karmada-webhook.karmada-system.svc:443/validate-resourceinterpretercustomization
|
||||||
|
caBundle: {{caBundle}}
|
||||||
|
failurePolicy: Fail
|
||||||
|
sideEffects: None
|
||||||
|
admissionReviewVersions: ["v1"]
|
||||||
|
timeoutSeconds: 10
|
||||||
- name: federatedresourcequota.karmada.io
|
- name: federatedresourcequota.karmada.io
|
||||||
rules:
|
rules:
|
||||||
- operations: ["CREATE", "UPDATE"]
|
- operations: ["CREATE", "UPDATE"]
|
||||||
|
|
|
@ -143,4 +143,18 @@ webhooks:
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
admissionReviewVersions: ["v1"]
|
admissionReviewVersions: ["v1"]
|
||||||
timeoutSeconds: 3
|
timeoutSeconds: 3
|
||||||
|
- name: resourceinterpretercustomization.karmada.io
|
||||||
|
rules:
|
||||||
|
- operations: ["CREATE", "UPDATE"]
|
||||||
|
apiGroups: ["config.karmada.io"]
|
||||||
|
apiVersions: ["*"]
|
||||||
|
resources: ["resourceinterpretercustomizations"]
|
||||||
|
scope: "Cluster"
|
||||||
|
clientConfig:
|
||||||
|
url: https://{{ $name }}-webhook.{{ $namespace }}.svc:443/validate-resourceinterpretercustomization
|
||||||
|
{{- include "karmada.webhook.caBundle" . | nindent 6 }}
|
||||||
|
failurePolicy: Fail
|
||||||
|
sideEffects: None
|
||||||
|
admissionReviewVersions: ["v1"]
|
||||||
|
timeoutSeconds: 3
|
||||||
{{- end -}}
|
{{- end -}}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/webhook/federatedresourcequota"
|
"github.com/karmada-io/karmada/pkg/webhook/federatedresourcequota"
|
||||||
"github.com/karmada-io/karmada/pkg/webhook/overridepolicy"
|
"github.com/karmada-io/karmada/pkg/webhook/overridepolicy"
|
||||||
"github.com/karmada-io/karmada/pkg/webhook/propagationpolicy"
|
"github.com/karmada-io/karmada/pkg/webhook/propagationpolicy"
|
||||||
|
"github.com/karmada-io/karmada/pkg/webhook/resourceinterpretercustomization"
|
||||||
"github.com/karmada-io/karmada/pkg/webhook/work"
|
"github.com/karmada-io/karmada/pkg/webhook/work"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -125,6 +126,7 @@ func Run(ctx context.Context, opts *options.Options) error {
|
||||||
hookServer.Register("/convert", &conversion.Webhook{})
|
hookServer.Register("/convert", &conversion.Webhook{})
|
||||||
hookServer.Register("/validate-resourceinterpreterwebhookconfiguration", &webhook.Admission{Handler: &configuration.ValidatingAdmission{}})
|
hookServer.Register("/validate-resourceinterpreterwebhookconfiguration", &webhook.Admission{Handler: &configuration.ValidatingAdmission{}})
|
||||||
hookServer.Register("/validate-federatedresourcequota", &webhook.Admission{Handler: &federatedresourcequota.ValidatingAdmission{}})
|
hookServer.Register("/validate-federatedresourcequota", &webhook.Admission{Handler: &federatedresourcequota.ValidatingAdmission{}})
|
||||||
|
hookServer.Register("/validate-resourceinterpretercustomization", &webhook.Admission{Handler: &resourceinterpretercustomization.ValidatingAdmission{Client: hookManager.GetClient()}})
|
||||||
hookServer.WebhookMux.Handle("/readyz/", http.StripPrefix("/readyz/", &healthz.Handler{}))
|
hookServer.WebhookMux.Handle("/readyz/", http.StripPrefix("/readyz/", &healthz.Handler{}))
|
||||||
|
|
||||||
// blocks until the context is done.
|
// blocks until the context is done.
|
||||||
|
|
|
@ -157,7 +157,22 @@ webhooks:
|
||||||
failurePolicy: Fail
|
failurePolicy: Fail
|
||||||
sideEffects: None
|
sideEffects: None
|
||||||
admissionReviewVersions: ["v1"]
|
admissionReviewVersions: ["v1"]
|
||||||
timeoutSeconds: 3`, systemNamespace, caBundle)
|
timeoutSeconds: 3
|
||||||
|
- name: resourceinterpretercustomization.karmada.io
|
||||||
|
rules:
|
||||||
|
- operations: ["CREATE", "UPDATE"]
|
||||||
|
apiGroups: ["config.karmada.io"]
|
||||||
|
apiVersions: ["*"]
|
||||||
|
resources: ["resourceexploringwebhookconfigurations"]
|
||||||
|
scope: "Cluster"
|
||||||
|
clientConfig:
|
||||||
|
url: https://karmada-webhook.%[1]s.svc:443/validate-resourceinterpretercustomization
|
||||||
|
caBundle: %[2]s
|
||||||
|
failurePolicy: Fail
|
||||||
|
sideEffects: None
|
||||||
|
admissionReviewVersions: ["v1"]
|
||||||
|
timeoutSeconds: 3
|
||||||
|
`, systemNamespace, caBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOrUpdateValidatingWebhookConfiguration(c kubernetes.Interface, staticYaml string) error {
|
func createOrUpdateValidatingWebhookConfiguration(c kubernetes.Interface, staticYaml string) error {
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
|
||||||
"github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource"
|
"github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource"
|
||||||
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter"
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/interpreter"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (o *Options) completeExecute(f util.Factory) []error {
|
func (o *Options) completeExecute(f util.Factory) []error {
|
||||||
|
@ -72,7 +73,7 @@ func (o *Options) runExecute() error {
|
||||||
return fmt.Errorf("fail to get status items: %v", err)
|
return fmt.Errorf("fail to get status items: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := ruleArgs{
|
args := interpreter.RuleArgs{
|
||||||
Desired: desired,
|
Desired: desired,
|
||||||
Observed: observed,
|
Observed: observed,
|
||||||
Status: status,
|
Status: status,
|
||||||
|
@ -92,7 +93,7 @@ func (o *Options) runExecute() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func printExecuteResult(w, errOut io.Writer, name string, result *ruleResult) {
|
func printExecuteResult(w, errOut io.Writer, name string, result *interpreter.RuleResult) {
|
||||||
if result.Err != nil {
|
if result.Err != nil {
|
||||||
fmt.Fprintf(errOut, "Execute %s error: %v\n", name, result.Err)
|
fmt.Fprintf(errOut, "Execute %s error: %v\n", name, result.Err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource"
|
"github.com/karmada-io/karmada/pkg/karmadactl/util/genericresource"
|
||||||
"github.com/karmada-io/karmada/pkg/util/gclient"
|
"github.com/karmada-io/karmada/pkg/util/gclient"
|
||||||
"github.com/karmada-io/karmada/pkg/util/helper"
|
"github.com/karmada-io/karmada/pkg/util/helper"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/interpreter"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -72,7 +73,7 @@ const (
|
||||||
func NewCmdInterpret(f util.Factory, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command {
|
func NewCmdInterpret(f util.Factory, parentCommand string, streams genericclioptions.IOStreams) *cobra.Command {
|
||||||
o := &Options{
|
o := &Options{
|
||||||
IOStreams: streams,
|
IOStreams: streams,
|
||||||
Rules: allRules,
|
Rules: interpreter.AllResourceInterpreterCustomizationRules,
|
||||||
}
|
}
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "interpret (-f FILENAME) (--operation OPERATION) [--ARGS VALUE]... ",
|
Use: "interpret (-f FILENAME) (--operation OPERATION) [--ARGS VALUE]... ",
|
||||||
|
@ -123,7 +124,7 @@ type Options struct {
|
||||||
ObservedResult *resource.Result
|
ObservedResult *resource.Result
|
||||||
StatusResult *genericresource.Result
|
StatusResult *genericresource.Result
|
||||||
|
|
||||||
Rules Rules
|
Rules interpreter.Rules
|
||||||
|
|
||||||
genericclioptions.IOStreams
|
genericclioptions.IOStreams
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package interpret
|
package interpreter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -11,7 +11,8 @@ import (
|
||||||
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter"
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter"
|
||||||
)
|
)
|
||||||
|
|
||||||
var allRules = []Rule{
|
// AllResourceInterpreterCustomizationRules all InterpreterOperations
|
||||||
|
var AllResourceInterpreterCustomizationRules = []Rule{
|
||||||
&retentionRule{},
|
&retentionRule{},
|
||||||
&replicaResourceRule{},
|
&replicaResourceRule{},
|
||||||
&replicaRevisionRule{},
|
&replicaRevisionRule{},
|
||||||
|
@ -46,7 +47,7 @@ func (r *retentionRule) SetScript(c *configv1alpha1.ResourceInterpreterCustomiza
|
||||||
c.Spec.Customizations.Retention.LuaScript = script
|
c.Spec.Customizations.Retention.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *retentionRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (r *retentionRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
desired, err := args.getDesiredObjectOrError()
|
desired, err := args.getDesiredObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -91,7 +92,7 @@ func (r *replicaResourceRule) SetScript(c *configv1alpha1.ResourceInterpreterCus
|
||||||
c.Spec.Customizations.ReplicaResource.LuaScript = script
|
c.Spec.Customizations.ReplicaResource.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *replicaResourceRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (r *replicaResourceRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
obj, err := args.getObjectOrError()
|
obj, err := args.getObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -132,7 +133,7 @@ func (r *replicaRevisionRule) SetScript(c *configv1alpha1.ResourceInterpreterCus
|
||||||
c.Spec.Customizations.ReplicaRevision.LuaScript = script
|
c.Spec.Customizations.ReplicaRevision.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *replicaRevisionRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (r *replicaRevisionRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
obj, err := args.getObjectOrError()
|
obj, err := args.getObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -173,7 +174,7 @@ func (s *statusReflectionRule) SetScript(c *configv1alpha1.ResourceInterpreterCu
|
||||||
c.Spec.Customizations.StatusReflection.LuaScript = script
|
c.Spec.Customizations.StatusReflection.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusReflectionRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (s *statusReflectionRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
obj, err := args.getObjectOrError()
|
obj, err := args.getObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -214,7 +215,7 @@ func (s *statusAggregationRule) SetScript(c *configv1alpha1.ResourceInterpreterC
|
||||||
c.Spec.Customizations.StatusAggregation.LuaScript = script
|
c.Spec.Customizations.StatusAggregation.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusAggregationRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (s *statusAggregationRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
obj, err := args.getObjectOrError()
|
obj, err := args.getObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -260,7 +261,7 @@ func (h *healthInterpretationRule) SetScript(c *configv1alpha1.ResourceInterpret
|
||||||
c.Spec.Customizations.HealthInterpretation.LuaScript = script
|
c.Spec.Customizations.HealthInterpretation.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *healthInterpretationRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (h *healthInterpretationRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
obj, err := args.getObjectOrError()
|
obj, err := args.getObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -301,7 +302,7 @@ func (d *dependencyInterpretationRule) SetScript(c *configv1alpha1.ResourceInter
|
||||||
c.Spec.Customizations.DependencyInterpretation.LuaScript = script
|
c.Spec.Customizations.DependencyInterpretation.LuaScript = script
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dependencyInterpretationRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args ruleArgs) *ruleResult {
|
func (d *dependencyInterpretationRule) Run(interpreter *configurableinterpreter.ConfigurableInterpreter, args RuleArgs) *RuleResult {
|
||||||
obj, err := args.getObjectOrError()
|
obj, err := args.getObjectOrError()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newRuleResultWithError(err)
|
return newRuleResultWithError(err)
|
||||||
|
@ -325,7 +326,7 @@ type Rule interface {
|
||||||
// SetScript set the script for the rule. If script is empty, disable the rule.
|
// SetScript set the script for the rule. If script is empty, disable the rule.
|
||||||
SetScript(*configv1alpha1.ResourceInterpreterCustomization, string)
|
SetScript(*configv1alpha1.ResourceInterpreterCustomization, string)
|
||||||
// Run execute the rule with given args, and return the result.
|
// Run execute the rule with given args, and return the result.
|
||||||
Run(*configurableinterpreter.ConfigurableInterpreter, ruleArgs) *ruleResult
|
Run(*configurableinterpreter.ConfigurableInterpreter, RuleArgs) *RuleResult
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules is a series of rules.
|
// Rules is a series of rules.
|
||||||
|
@ -365,28 +366,29 @@ func (r Rules) Get(name string) Rule {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ruleArgs struct {
|
// RuleArgs rule execution args.
|
||||||
|
type RuleArgs struct {
|
||||||
Desired *unstructured.Unstructured
|
Desired *unstructured.Unstructured
|
||||||
Observed *unstructured.Unstructured
|
Observed *unstructured.Unstructured
|
||||||
Status []workv1alpha2.AggregatedStatusItem
|
Status []workv1alpha2.AggregatedStatusItem
|
||||||
Replica int64
|
Replica int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ruleArgs) getDesiredObjectOrError() (*unstructured.Unstructured, error) {
|
func (r RuleArgs) getDesiredObjectOrError() (*unstructured.Unstructured, error) {
|
||||||
if r.Desired == nil {
|
if r.Desired == nil {
|
||||||
return nil, fmt.Errorf("desired, desired-file options are not set")
|
return nil, fmt.Errorf("desired, desired-file options are not set")
|
||||||
}
|
}
|
||||||
return r.Desired, nil
|
return r.Desired, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ruleArgs) getObservedObjectOrError() (*unstructured.Unstructured, error) {
|
func (r RuleArgs) getObservedObjectOrError() (*unstructured.Unstructured, error) {
|
||||||
if r.Observed == nil {
|
if r.Observed == nil {
|
||||||
return nil, fmt.Errorf("observed, observed-file options are not set")
|
return nil, fmt.Errorf("observed, observed-file options are not set")
|
||||||
}
|
}
|
||||||
return r.Observed, nil
|
return r.Observed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ruleArgs) getObjectOrError() (*unstructured.Unstructured, error) {
|
func (r RuleArgs) getObjectOrError() (*unstructured.Unstructured, error) {
|
||||||
if r.Desired == nil && r.Observed == nil {
|
if r.Desired == nil && r.Observed == nil {
|
||||||
return nil, fmt.Errorf("desired-file, observed-file options are not set")
|
return nil, fmt.Errorf("desired-file, observed-file options are not set")
|
||||||
}
|
}
|
||||||
|
@ -399,27 +401,29 @@ func (r ruleArgs) getObjectOrError() (*unstructured.Unstructured, error) {
|
||||||
return r.Observed, nil
|
return r.Observed, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type nameValue struct {
|
// NameValue name and value.
|
||||||
|
type NameValue struct {
|
||||||
Name string
|
Name string
|
||||||
Value interface{}
|
Value interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ruleResult struct {
|
// RuleResult rule execution result.
|
||||||
Results []nameValue
|
type RuleResult struct {
|
||||||
|
Results []NameValue
|
||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuleResult() *ruleResult {
|
func newRuleResult() *RuleResult {
|
||||||
return &ruleResult{}
|
return &RuleResult{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRuleResultWithError(err error) *ruleResult {
|
func newRuleResultWithError(err error) *RuleResult {
|
||||||
return &ruleResult{
|
return &RuleResult{
|
||||||
Err: err,
|
Err: err,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ruleResult) add(name string, value interface{}) *ruleResult {
|
func (r *RuleResult) add(name string, value interface{}) *RuleResult {
|
||||||
r.Results = append(r.Results, nameValue{Name: name, Value: value})
|
r.Results = append(r.Results, NameValue{Name: name, Value: value})
|
||||||
return r
|
return r
|
||||||
}
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package resourceinterpretercustomization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||||
|
"github.com/karmada-io/karmada/pkg/resourceinterpreter/configurableinterpreter/luavm"
|
||||||
|
"github.com/karmada-io/karmada/pkg/util/interpreter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateCustomizationRule(oldRules, newRules *configv1alpha1.ResourceInterpreterCustomization) error {
|
||||||
|
if oldRules.Spec.Target.APIVersion != newRules.Spec.Target.APIVersion ||
|
||||||
|
oldRules.Spec.Target.Kind != newRules.Spec.Target.Kind {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, rule := range interpreter.AllResourceInterpreterCustomizationRules {
|
||||||
|
oldScript := rule.GetScript(oldRules)
|
||||||
|
newScript := rule.GetScript(newRules)
|
||||||
|
if oldScript != "" && newScript != "" {
|
||||||
|
return fmt.Errorf("conflicting with InterpreterOperation(%s) of existing ResourceInterpreterCustomization(%s)", rule.Name(), oldRules.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkCustomizationsRule(customization *configv1alpha1.ResourceInterpreterCustomization) error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), time.Second)
|
||||||
|
defer cancel()
|
||||||
|
l, err := luavm.NewWithContext(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
for _, rule := range interpreter.AllResourceInterpreterCustomizationRules {
|
||||||
|
if script := rule.GetScript(customization); script != "" {
|
||||||
|
if _, err = l.LoadString(script); err != nil {
|
||||||
|
return fmt.Errorf("InterpreterOperation(%s) Lua script error: %v", rule.Name(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateResourceInterpreterCustomizations(newConfig *configv1alpha1.ResourceInterpreterCustomization, customizations *configv1alpha1.ResourceInterpreterCustomizationList) error {
|
||||||
|
for _, config := range customizations.Items {
|
||||||
|
// skip self verification
|
||||||
|
if config.Name == newConfig.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
oldConfig := config
|
||||||
|
if err := validateCustomizationRule(&oldConfig, newConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return checkCustomizationsRule(newConfig)
|
||||||
|
}
|
|
@ -0,0 +1,477 @@
|
||||||
|
package resourceinterpretercustomization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_validateCustomizationRule(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
oldRules *configv1alpha1.ResourceInterpreterCustomization
|
||||||
|
newRules *configv1alpha1.ResourceInterpreterCustomization
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "the different Kind of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "bar",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: " the different APIVersion of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v2",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(Retention) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
Retention: &configv1alpha1.LocalValueRetention{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
Retention: &configv1alpha1.LocalValueRetention{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(ReplicaResource) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(ReplicaRevision) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
ReplicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
ReplicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(StatusReflection) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
StatusReflection: &configv1alpha1.StatusReflection{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
StatusReflection: &configv1alpha1.StatusReflection{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(StatusAggregation) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
StatusAggregation: &configv1alpha1.StatusAggregation{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
StatusAggregation: &configv1alpha1.StatusAggregation{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(HealthInterpretation) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
HealthInterpretation: &configv1alpha1.HealthInterpretation{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
HealthInterpretation: &configv1alpha1.HealthInterpretation{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "the same InterpreterOperation(DependencyInterpretation) of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
oldRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
DependencyInterpretation: &configv1alpha1.DependencyInterpretation{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
newRules: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
},
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
DependencyInterpretation: &configv1alpha1.DependencyInterpretation{LuaScript: "LuaScript"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := validateCustomizationRule(tt.args.oldRules, tt.args.newRules); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("validateCustomizationRule() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_validateResourceInterpreterCustomizations(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
customization *configv1alpha1.ResourceInterpreterCustomization
|
||||||
|
customizations *configv1alpha1.ResourceInterpreterCustomizationList
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "the same name of ResourceInterpreterCustomization",
|
||||||
|
args: args{
|
||||||
|
customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
}, Customizations: configv1alpha1.CustomizationRules{Retention: &configv1alpha1.LocalValueRetention{LuaScript: `function Retain(desiredObj, observedObj) end`}}}},
|
||||||
|
customizations: &configv1alpha1.ResourceInterpreterCustomizationList{
|
||||||
|
Items: []configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
{ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{Target: configv1alpha1.CustomizationTarget{
|
||||||
|
APIVersion: "foo/v1",
|
||||||
|
Kind: "kind",
|
||||||
|
}, Customizations: configv1alpha1.CustomizationRules{Retention: &configv1alpha1.LocalValueRetention{LuaScript: "function Retain(desiredObj, observedObj) end"}}}}}},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := validateResourceInterpreterCustomizations(tt.args.customization, tt.args.customizations); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("validateResourceInterpreterCustomizations() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_checkCustomizationsRule(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
customization *configv1alpha1.ResourceInterpreterCustomization
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "correct lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
Retention: &configv1alpha1.LocalValueRetention{LuaScript: `
|
||||||
|
function Retain(desiredObj, observedObj)
|
||||||
|
desiredObj.spec.fieldFoo = observedObj.spec.fieldFoo
|
||||||
|
return desiredObj
|
||||||
|
end
|
||||||
|
`},
|
||||||
|
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: `
|
||||||
|
function GetReplicas(desiredObj)
|
||||||
|
nodeClaim = {}
|
||||||
|
resourceRequest = {}
|
||||||
|
result = {}
|
||||||
|
|
||||||
|
result.replica = desiredObj.spec.replicas
|
||||||
|
result.resourceRequest = desiredObj.spec.template.spec.containers[0].resources.limits
|
||||||
|
|
||||||
|
nodeClaim.nodeSelector = desiredObj.spec.template.spec.nodeSelector
|
||||||
|
nodeClaim.tolerations = desiredObj.spec.template.spec.tolerations
|
||||||
|
result.nodeClaim = nodeClaim
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
`},
|
||||||
|
ReplicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: `
|
||||||
|
function ReviseReplica(desiredObj, desiredReplica)
|
||||||
|
desiredObj.spec.replicas = desiredReplica
|
||||||
|
return desiredObj
|
||||||
|
end
|
||||||
|
`},
|
||||||
|
StatusReflection: &configv1alpha1.StatusReflection{LuaScript: `
|
||||||
|
function ReflectStatus(observedObj)
|
||||||
|
status = {}
|
||||||
|
status.readyReplicas = observedObj.status.observedObj
|
||||||
|
return status
|
||||||
|
end
|
||||||
|
|
||||||
|
`},
|
||||||
|
StatusAggregation: &configv1alpha1.StatusAggregation{LuaScript: `
|
||||||
|
function AggregateStatus(desiredObj, statusItems)
|
||||||
|
for i = 1, #items do
|
||||||
|
desiredObj.status.readyReplicas = desiredObj.status.readyReplicas + items[i].readyReplicas
|
||||||
|
end
|
||||||
|
return desiredObj
|
||||||
|
end
|
||||||
|
|
||||||
|
`},
|
||||||
|
HealthInterpretation: &configv1alpha1.HealthInterpretation{LuaScript: `
|
||||||
|
function InterpretHealth(observedObj)
|
||||||
|
if observedObj.status.readyReplicas == observedObj.spec.replicas then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
`},
|
||||||
|
DependencyInterpretation: &configv1alpha1.DependencyInterpretation{LuaScript: `
|
||||||
|
function GetDependencies(desiredObj)
|
||||||
|
dependencies = {}
|
||||||
|
if desiredObj.spec.serviceAccountName ~= "" and desiredObj.spec.serviceAccountName ~= "default" then
|
||||||
|
dependency = {}
|
||||||
|
dependency.apiVersion = "v1"
|
||||||
|
dependency.kind = "ServiceAccount"
|
||||||
|
dependency.name = desiredObj.spec.serviceAccountName
|
||||||
|
dependency.namespace = desiredObj.namespace
|
||||||
|
dependencies[0] = {}
|
||||||
|
dependencies[0] = dependency
|
||||||
|
end
|
||||||
|
return dependencies
|
||||||
|
end
|
||||||
|
|
||||||
|
`}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Retention contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
Retention: &configv1alpha1.LocalValueRetention{LuaScript: `function Retain(desiredObj, observedObj)`},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ReplicaResource contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
ReplicaResource: &configv1alpha1.ReplicaResourceRequirement{LuaScript: `function GetReplicas(desiredObj)`},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ReplicaRevision contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
ReplicaRevision: &configv1alpha1.ReplicaRevision{LuaScript: `function ReviseReplica(desiredObj, desiredReplica)`}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "StatusReflection contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
StatusReflection: &configv1alpha1.StatusReflection{LuaScript: `function ReflectStatus(observedObj)`}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "StatusAggregation contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
StatusAggregation: &configv1alpha1.StatusAggregation{LuaScript: `function AggregateStatus(desiredObj, statusItems)`}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "HealthInterpretation contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
HealthInterpretation: &configv1alpha1.HealthInterpretation{LuaScript: `function InterpretHealth(observedObj)`}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "DependencyInterpretation contains the wrong lua script",
|
||||||
|
args: args{customization: &configv1alpha1.ResourceInterpreterCustomization{
|
||||||
|
Spec: configv1alpha1.ResourceInterpreterCustomizationSpec{
|
||||||
|
Customizations: configv1alpha1.CustomizationRules{
|
||||||
|
DependencyInterpretation: &configv1alpha1.DependencyInterpretation{LuaScript: `function GetDependencies(desiredObj)`}},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := checkCustomizationsRule(tt.args.customization); (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("checkCustomizationsRule() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package resourceinterpretercustomization
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||||
|
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||||
|
|
||||||
|
configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Check if our ValidatingAdmission implements necessary interface
|
||||||
|
var _ admission.Handler = &ValidatingAdmission{}
|
||||||
|
var _ admission.DecoderInjector = &ValidatingAdmission{}
|
||||||
|
|
||||||
|
// ValidatingAdmission validates ResourceInterpreterCustomization object when creating/updating.
|
||||||
|
type ValidatingAdmission struct {
|
||||||
|
client.Client
|
||||||
|
decoder *admission.Decoder
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle implements admission.Handler interface.
|
||||||
|
// It yields a response to an AdmissionRequest.
|
||||||
|
func (v *ValidatingAdmission) Handle(ctx context.Context, req admission.Request) admission.Response {
|
||||||
|
configuration := &configv1alpha1.ResourceInterpreterCustomization{}
|
||||||
|
|
||||||
|
err := v.decoder.Decode(req, configuration)
|
||||||
|
if err != nil {
|
||||||
|
return admission.Errored(http.StatusBadRequest, err)
|
||||||
|
}
|
||||||
|
klog.V(2).Infof("Validating ResourceInterpreterCustomization(%s) for request: %s", configuration.Name, req.Operation)
|
||||||
|
configs := &configv1alpha1.ResourceInterpreterCustomizationList{}
|
||||||
|
if err = v.List(ctx, configs); err != nil {
|
||||||
|
return admission.Errored(http.StatusInternalServerError, err)
|
||||||
|
}
|
||||||
|
if err = validateResourceInterpreterCustomizations(configuration, configs); err != nil {
|
||||||
|
return admission.Denied(err.Error())
|
||||||
|
}
|
||||||
|
return admission.Allowed("")
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectDecoder implements admission.DecoderInjector interface.
|
||||||
|
// A decoder will be automatically injected.
|
||||||
|
func (v *ValidatingAdmission) InjectDecoder(decoder *admission.Decoder) error {
|
||||||
|
v.decoder = decoder
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue