diff --git a/pkg/liqoctl/uninstall/handler.go b/pkg/liqoctl/uninstall/handler.go index deae3698a5..ef5a4be842 100644 --- a/pkg/liqoctl/uninstall/handler.go +++ b/pkg/liqoctl/uninstall/handler.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "slices" "strings" "time" @@ -66,10 +67,28 @@ func (o *Options) Run(ctx context.Context) error { s := o.Printer.StartSpinner("Running pre-uninstall checks") if err := utils.PreUninstall(ctx, o.CRClient); err != nil { - errMsg := fmt.Sprintf("Pre-uninstall checks failed: %s\n"+ - "You can disable the active peerings with other clusters with the 'liqoctl unpeer' command.\n"+ - "Check 'liqoctl unpeer --help' for further information.", output.PrettyErr(err)) - s.Fail(errMsg) + s.Fail("Pre-uninstall checks failed: ", output.PrettyErr(err)) + var uninstallErr utils.UninstallError + if errors.As(err, &uninstallErr) { + errorTypes := uninstallErr.GetErrorTypes() + if slices.Contains(errorTypes, utils.PendingActivePeerings) { + errMsg := fmt.Sprintf( + "You must remove all the active peerings with other clusters before uninstalling Liqo.\n" + + "You can disable the active peerings via the 'liqoctl unpeer' command.\n" + + "Check 'liqoctl unpeer --help' for further information.\n", + ) + s.Fail(errMsg) + } + + if slices.Contains(errorTypes, utils.PendingOffloadedNamespaces) { + errMsg := fmt.Sprintf( + "You must remove all the offloaded namespaces before uninstalling Liqo.\n" + + "You can disable the namespace offloading 'liqoctl unoffload' command.\n" + + "Check 'liqoctl unoffload --help' for further information.\n", + ) + s.Fail(errMsg) + } + } return err } s.Success("Pre-uninstall checks passed") diff --git a/pkg/utils/preuninstall.go b/pkg/utils/preuninstall.go index 474ee7ebaf..eb59d86467 100644 --- a/pkg/utils/preuninstall.go +++ b/pkg/utils/preuninstall.go @@ -17,6 +17,7 @@ package utils import ( "context" "fmt" + "maps" "slices" "sigs.k8s.io/controller-runtime/pkg/client" @@ -32,6 +33,18 @@ import ( ipamutils "github.com/liqotech/liqo/pkg/utils/ipam" ) +// UninstallErrorType is the type of uninstall error. +type UninstallErrorType string + +const ( + // GenericUninstallError is an error related to resources that needs to be removed. + GenericUninstallError = "generic" + // PendingActivePeerings is an error related peerings still active. + PendingActivePeerings = "pendingActivePeerings" + // PendingOffloadedNamespaces is an error related to namespaces still offloaded. + PendingOffloadedNamespaces = "pendingOffloadedNamespaces" +) + type errorMap struct { networking []string authentication []string @@ -50,15 +63,33 @@ func newErrorMap() errorMap { } } +// UninstallError is an error type returned when there are errors during the uninstall process. +type UninstallError struct { + errorTypes []UninstallErrorType + message string +} + +// GetErrorTypes returns the type of uninstall error. +func (ue UninstallError) GetErrorTypes() []UninstallErrorType { + return ue.errorTypes +} + +// Error returns the error message. +func (ue UninstallError) Error() string { + return ue.message +} + func (em *errorMap) getError() error { str := "" hasErr := false + errorTypes := map[UninstallErrorType]bool{} if len(em.networking) > 0 { str += "\ndisable networking for clusters:\n" for _, fc := range em.networking { str += fmt.Sprintf("- %s\n", fc) } + errorTypes[PendingActivePeerings] = true hasErr = true } @@ -67,6 +98,7 @@ func (em *errorMap) getError() error { for i := range em.authentication { str += fmt.Sprintf("- %s\n", em.authentication[i]) } + errorTypes[PendingActivePeerings] = true hasErr = true } @@ -75,6 +107,7 @@ func (em *errorMap) getError() error { for _, fc := range em.offloading { str += fmt.Sprintf("- %s\n", fc) } + errorTypes[PendingActivePeerings] = true hasErr = true } @@ -83,6 +116,7 @@ func (em *errorMap) getError() error { for i := range em.namespaces { str += fmt.Sprintf("- %s\n", em.namespaces[i]) } + errorTypes[PendingOffloadedNamespaces] = true hasErr = true } @@ -91,12 +125,18 @@ func (em *errorMap) getError() error { for i := range em.generic { str += fmt.Sprintf("- %s\n", em.generic[i]) } + errorTypes[GenericUninstallError] = true hasErr = true } if hasErr { - return fmt.Errorf("you should:\n%s", str) + msg := fmt.Sprintf("you should:\n%s", str) + return UninstallError{ + errorTypes: slices.Collect(maps.Keys(errorTypes)), + message: msg, + } } + return nil }