From 3ac188124e348c2463a9e8b46bae9ab8967ce255 Mon Sep 17 00:00:00 2001 From: Ankit Chawla Date: Wed, 2 Feb 2022 12:28:41 +0530 Subject: [PATCH] [Feature] Force spec apply even if conflicting resources exist with different deployment ID (#2338) * Added allowconflicts flag to specs * Made changes for all resources * Added help for allowconflicts flag * Changed function isObjectMetaEqual --- pkg/fission-cli/cmd/spec/apply.go | 125 ++++++++++++++------------- pkg/fission-cli/cmd/spec/command.go | 4 +- pkg/fission-cli/cmd/spec/destroy.go | 2 +- pkg/fission-cli/cmd/spec/validate.go | 24 ++--- pkg/fission-cli/flag/flag.go | 1 + pkg/fission-cli/flag/key/key.go | 1 + 6 files changed, 85 insertions(+), 72 deletions(-) diff --git a/pkg/fission-cli/cmd/spec/apply.go b/pkg/fission-cli/cmd/spec/apply.go index 8bdd1779..1e9e63d9 100644 --- a/pkg/fission-cli/cmd/spec/apply.go +++ b/pkg/fission-cli/cmd/spec/apply.go @@ -130,7 +130,7 @@ func (opts *ApplySubCommand) run(input cli.Input) error { } // make changes to the cluster based on the specs - pkgMetas, as, err := applyResources(opts.Client(), specDir, fr, deleteResources) + pkgMetas, as, err := applyResources(opts.Client(), specDir, fr, deleteResources, input.Bool(flagkey.SpecAllowConflicts)) if err != nil { return errors.Wrap(err, "error applying specs") } @@ -357,7 +357,7 @@ func applyArchives(fclient client.Interface, specDir string, fr *FissionResource } // applyResources applies the given set of fission resources. -func applyResources(fclient client.Interface, specDir string, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, map[string]ResourceApplyStatus, error) { +func applyResources(fclient client.Interface, specDir string, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, map[string]ResourceApplyStatus, error) { applyStatus := make(map[string]ResourceApplyStatus) @@ -367,13 +367,13 @@ func applyResources(fclient client.Interface, specDir string, fr *FissionResourc return nil, nil, err } - _, ras, err := applyEnvironments(fclient, fr, delete) + _, ras, err := applyEnvironments(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "environment apply failed") } applyStatus["environment"] = *ras - pkgMeta, ras, err := applyPackages(fclient, fr, delete) + pkgMeta, ras, err := applyPackages(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "package apply failed") } @@ -402,31 +402,31 @@ func applyResources(fclient client.Interface, specDir string, fr *FissionResourc fr.Functions[i].Spec.Package.PackageRef.ResourceVersion = m.ResourceVersion } - _, ras, err = applyFunctions(fclient, fr, delete) + _, ras, err = applyFunctions(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "function apply failed") } applyStatus["function"] = *ras - _, ras, err = applyHTTPTriggers(fclient, fr, delete) + _, ras, err = applyHTTPTriggers(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "HTTPTrigger apply failed") } applyStatus["HTTPTrigger"] = *ras - _, ras, err = applyKubernetesWatchTriggers(fclient, fr, delete) + _, ras, err = applyKubernetesWatchTriggers(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "KubernetesWatchTrigger apply failed") } applyStatus["KubernetesWatchTrigger"] = *ras - _, ras, err = applyTimeTriggers(fclient, fr, delete) + _, ras, err = applyTimeTriggers(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "TimeTrigger apply failed") } applyStatus["TimeTrigger"] = *ras - _, ras, err = applyMessageQueueTriggers(fclient, fr, delete) + _, ras, err = applyMessageQueueTriggers(fclient, fr, delete, specAllowConflicts) if err != nil { return nil, nil, errors.Wrap(err, "MessageQueueTrigger apply failed") } @@ -576,7 +576,7 @@ func waitForPackageBuild(fclient client.Interface, pkg *fv1.Package) (*fv1.Packa } } -func applyPackages(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyPackages(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().Package().List(metav1.NamespaceAll) if err != nil { @@ -585,9 +585,13 @@ func applyPackages(fclient client.Interface, fr *FissionResources, delete bool) // filter objs := make([]fv1.Package, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -632,7 +636,6 @@ func applyPackages(fclient client.Interface, fr *FissionResources, delete bool) } else { // update o.ObjectMeta.ResourceVersion = existingObj.ObjectMeta.ResourceVersion - // We may be racing against the package builder to update the // package (a previous version might have been getting built). So, // wait for the package to have a non-running build status. @@ -687,7 +690,7 @@ func applyPackages(fclient client.Interface, fr *FissionResources, delete bool) return metadataMap, &ras, nil } -func applyFunctions(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyFunctions(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().Function().List(metav1.NamespaceAll) if err != nil { @@ -696,9 +699,13 @@ func applyFunctions(fclient client.Interface, fr *FissionResources, delete bool) // filter objs := make([]fv1.Function, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -770,7 +777,7 @@ func applyFunctions(fclient client.Interface, fr *FissionResources, delete bool) return metadataMap, &ras, nil } -func applyEnvironments(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyEnvironments(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().Environment().List(metav1.NamespaceAll) if err != nil { @@ -779,9 +786,13 @@ func applyEnvironments(fclient client.Interface, fr *FissionResources, delete bo // filter objs := make([]fv1.Environment, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -853,7 +864,7 @@ func applyEnvironments(fclient client.Interface, fr *FissionResources, delete bo return metadataMap, &ras, nil } -func applyHTTPTriggers(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyHTTPTriggers(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().HTTPTrigger().List(metav1.NamespaceAll) if err != nil { @@ -862,9 +873,13 @@ func applyHTTPTriggers(fclient client.Interface, fr *FissionResources, delete bo // filter objs := make([]fv1.HTTPTrigger, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -936,7 +951,7 @@ func applyHTTPTriggers(fclient client.Interface, fr *FissionResources, delete bo return metadataMap, &ras, nil } -func applyKubernetesWatchTriggers(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyKubernetesWatchTriggers(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().KubeWatcher().List(metav1.NamespaceAll) if err != nil { @@ -945,9 +960,13 @@ func applyKubernetesWatchTriggers(fclient client.Interface, fr *FissionResources // filter objs := make([]fv1.KubernetesWatchTrigger, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -1019,7 +1038,7 @@ func applyKubernetesWatchTriggers(fclient client.Interface, fr *FissionResources return metadataMap, &ras, nil } -func applyTimeTriggers(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyTimeTriggers(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().TimeTrigger().List(metav1.NamespaceAll) if err != nil { @@ -1028,9 +1047,13 @@ func applyTimeTriggers(fclient client.Interface, fr *FissionResources, delete bo // filter objs := make([]fv1.TimeTrigger, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -1102,7 +1125,7 @@ func applyTimeTriggers(fclient client.Interface, fr *FissionResources, delete bo return metadataMap, &ras, nil } -func applyMessageQueueTriggers(fclient client.Interface, fr *FissionResources, delete bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { +func applyMessageQueueTriggers(fclient client.Interface, fr *FissionResources, delete bool, specAllowConflicts bool) (map[string]metav1.ObjectMeta, *ResourceApplyStatus, error) { // get list allObjs, err := fclient.V1().MessageQueueTrigger().List("", metav1.NamespaceAll) if err != nil { @@ -1111,9 +1134,13 @@ func applyMessageQueueTriggers(fclient client.Interface, fr *FissionResources, d // filter objs := make([]fv1.MessageQueueTrigger, 0) - for _, o := range allObjs { - if hasDeploymentConfig(&o.ObjectMeta, fr) { - objs = append(objs, o) + if specAllowConflicts { + objs = allObjs + } else { + for _, o := range allObjs { + if hasDeploymentConfig(&o.ObjectMeta, fr) { + objs = append(objs, o) + } } } @@ -1186,25 +1213,5 @@ func applyMessageQueueTriggers(fclient client.Interface, fr *FissionResources, d } func isObjectMetaEqual(existingObj, newObj metav1.ObjectMeta) bool { - - if !reflect.DeepEqual(existingObj.Labels, newObj.Labels) { - return false - } - - existingAnnotations := make(map[string]string) - newAnnotations := make(map[string]string) - - for existingObjKey, existingObjVal := range existingObj.Annotations { - if existingObjKey != FISSION_DEPLOYMENT_NAME_KEY && existingObjKey != FISSION_DEPLOYMENT_UID_KEY { - existingAnnotations[existingObjKey] = existingObjVal - } - } - - for newObjKey, newObjVal := range newObj.Annotations { - if newObjKey != FISSION_DEPLOYMENT_NAME_KEY && newObjKey != FISSION_DEPLOYMENT_UID_KEY { - newAnnotations[newObjKey] = newObjVal - } - } - - return reflect.DeepEqual(existingAnnotations, newAnnotations) + return reflect.DeepEqual(existingObj.Labels, newObj.Labels) && reflect.DeepEqual(existingObj.Annotations, newObj.Annotations) } diff --git a/pkg/fission-cli/cmd/spec/command.go b/pkg/fission-cli/cmd/spec/command.go index b7966ab5..bb798101 100644 --- a/pkg/fission-cli/cmd/spec/command.go +++ b/pkg/fission-cli/cmd/spec/command.go @@ -39,7 +39,7 @@ func Commands() *cobra.Command { RunE: wrapper.Wrapper(Validate), } wrapper.SetFlags(validateCmd, flag.FlagSet{ - Optional: []flag.Flag{flag.SpecDir, flag.SpecIgnore}, + Optional: []flag.Flag{flag.SpecDir, flag.SpecIgnore, flag.SpecAllowConflicts}, }) applyCmd := &cobra.Command{ @@ -48,7 +48,7 @@ func Commands() *cobra.Command { RunE: wrapper.Wrapper(Apply), } wrapper.SetFlags(applyCmd, flag.FlagSet{ - Optional: []flag.Flag{flag.SpecDir, flag.SpecIgnore, flag.SpecDelete, flag.SpecWait, flag.SpecWatch, flag.SpecValidation, flag.SpecApplyCommitLabel}, + Optional: []flag.Flag{flag.SpecDir, flag.SpecIgnore, flag.SpecDelete, flag.SpecWait, flag.SpecWatch, flag.SpecValidation, flag.SpecApplyCommitLabel, flag.SpecAllowConflicts}, }) destroyCmd := &cobra.Command{ diff --git a/pkg/fission-cli/cmd/spec/destroy.go b/pkg/fission-cli/cmd/spec/destroy.go index dd394c26..85e16e54 100644 --- a/pkg/fission-cli/cmd/spec/destroy.go +++ b/pkg/fission-cli/cmd/spec/destroy.go @@ -53,7 +53,7 @@ func (opts *DestroySubCommand) run(input cli.Input) error { emptyFr.DeploymentConfig = fr.DeploymentConfig // "apply" the empty state - _, _, err = applyResources(opts.Client(), specDir, &emptyFr, true) + _, _, err = applyResources(opts.Client(), specDir, &emptyFr, true, false) if err != nil { return errors.Wrap(err, "error deleting resources") } diff --git a/pkg/fission-cli/cmd/spec/validate.go b/pkg/fission-cli/cmd/spec/validate.go index 14133f6b..b46e082b 100644 --- a/pkg/fission-cli/cmd/spec/validate.go +++ b/pkg/fission-cli/cmd/spec/validate.go @@ -31,6 +31,7 @@ import ( "github.com/fission/fission/pkg/fission-cli/cliwrapper/cli" "github.com/fission/fission/pkg/fission-cli/cmd" "github.com/fission/fission/pkg/fission-cli/console" + flagkey "github.com/fission/fission/pkg/fission-cli/flag/key" "github.com/fission/fission/pkg/fission-cli/util" "github.com/fission/fission/pkg/utils" "github.com/fission/fission/pkg/utils/gitrepo" @@ -71,7 +72,7 @@ func (opts *ValidateSubCommand) run(input cli.Input) error { return errors.Wrap(err, "error validating specs") } - err = resourceConflictCheck(opts.Client(), fr) + err = resourceConflictCheck(opts.Client(), fr, input.Bool(flagkey.SpecAllowConflicts)) if err != nil { return errors.Wrap(err, "name conflict error") } @@ -89,7 +90,7 @@ func (opts *ValidateSubCommand) run(input cli.Input) error { // the same name is already present in the same cluster namespace. // If a same name resource exists in the same namespace, a name // conflict error will be returned. -func resourceConflictCheck(c client.Interface, fr *FissionResources) error { +func resourceConflictCheck(c client.Interface, fr *FissionResources, specAllowConflicts bool) error { deployUID := fr.DeploymentConfig.UID result := utils.MultiErrorWithFormat() @@ -99,7 +100,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.Functions { for _, cObj := range fnList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -112,7 +113,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.Environments { for _, cObj := range envList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -125,7 +126,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.Packages { for _, cObj := range pkgList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -138,7 +139,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.HttpTriggers { for _, cObj := range httptriggerList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -151,7 +152,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.MessageQueueTriggers { for _, cObj := range mqtriggerList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -164,7 +165,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.TimeTriggers { for _, cObj := range timetriggerList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -177,7 +178,7 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { } for _, sObj := range fr.KubernetesWatchTriggers { for _, cObj := range kubewatchtriggerList { - if err := isResourceConflicts(deployUID, &sObj, &cObj); err != nil { + if err := isResourceConflicts(deployUID, &sObj, &cObj, specAllowConflicts); err != nil { result = multierror.Append(result, err) break } @@ -187,10 +188,13 @@ func resourceConflictCheck(c client.Interface, fr *FissionResources) error { return result.ErrorOrNil() } -func isResourceConflicts(deployUID string, specObj fv1.MetadataAccessor, clusterObj fv1.MetadataAccessor) error { +func isResourceConflicts(deployUID string, specObj fv1.MetadataAccessor, clusterObj fv1.MetadataAccessor, specAllowConflicts bool) error { if specObj.GetObjectMeta().GetName() == clusterObj.GetObjectMeta().GetName() && specObj.GetObjectMeta().GetNamespace() == clusterObj.GetObjectMeta().GetNamespace() && deployUID != clusterObj.GetObjectMeta().GetAnnotations()[FISSION_DEPLOYMENT_UID_KEY] { + if specAllowConflicts { + return nil + } return fmt.Errorf("%v: '%v/%v' with different deploy uid already exists", clusterObj.GetObjectKind().GroupVersionKind().Kind, clusterObj.GetObjectMeta().GetName(), clusterObj.GetObjectMeta().GetNamespace()) } diff --git a/pkg/fission-cli/flag/flag.go b/pkg/fission-cli/flag/flag.go index ecccb3d0..5d1ca2cd 100644 --- a/pkg/fission-cli/flag/flag.go +++ b/pkg/fission-cli/flag/flag.go @@ -210,6 +210,7 @@ var ( SpecValidation = Flag{Type: String, Name: flagkey.SpecValidate, Usage: "Turns server side validations of Fission objects on/off"} SpecIgnore = Flag{Type: String, Name: flagkey.SpecIgnore, Usage: fmt.Sprintf("File containing specs to be ingored inside --specdir, defaults to %v", util.SPEC_IGNORE_FILE)} SpecApplyCommitLabel = Flag{Type: Bool, Name: flagkey.SpecApplyCommitLabel, Usage: "Apply commit label to the resources"} + SpecAllowConflicts = Flag{Type: Bool, Name: flagkey.SpecAllowConflicts, Usage: "If true, spec apply will be forced even if conflicting resources exist", DefaultValue: false} SupportOutput = Flag{Type: String, Name: flagkey.SupportOutput, Short: "o", Usage: "Output directory to save dump archive/files", DefaultValue: flagkey.DefaultSpecOutputDir} SupportNoZip = Flag{Type: Bool, Name: flagkey.SupportNoZip, Usage: "Save dump information into multiple files instead of single zip file"} diff --git a/pkg/fission-cli/flag/key/key.go b/pkg/fission-cli/flag/key/key.go index 5365a482..42df7c43 100644 --- a/pkg/fission-cli/flag/key/key.go +++ b/pkg/fission-cli/flag/key/key.go @@ -160,6 +160,7 @@ const ( SpecValidate = "validation" SpecIgnore = "specignore" SpecApplyCommitLabel = "commitlabel" + SpecAllowConflicts = "allowconflicts" SupportOutput = Output SupportNoZip = "nozip"