Skip to content

Commit

Permalink
Merge branch 'main' into EVEREST-1647-ui-monthly-schedule-creates-an-…
Browse files Browse the repository at this point in the history
…invalid-configuration-for-psmdb
  • Loading branch information
dianabirs authored Jan 20, 2025
2 parents 00ced55 + bda2ab6 commit 9f15fa8
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 42 deletions.
29 changes: 14 additions & 15 deletions commands/namespaces/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ const forceUninstallHint = "HINT: use --force to remove the namespace and all it
// NewRemoveCommand returns a new command to remove an existing namespace.
func NewRemoveCommand(l *zap.SugaredLogger) *cobra.Command {
cmd := &cobra.Command{
Use: "remove",
Long: "Remove an existing namespace",
Short: "Remove an existing namespace",
Example: `everestctl namespaces remove [NAMESPACE] [FLAGS]`,
Use: "remove [flags] NAMESPACES",
Long: "Remove existing and managed by Everest namespaces",
Short: "Remove existing and managed by Everest namespaces",
Example: `everestctl namespaces remove --keep-namespace --force ns-1,ns-2`,
Args: cobra.ExactArgs(1),
Run: func(cmd *cobra.Command, args []string) {
initRemoveViperFlags(cmd)
c := &namespaces.NamespaceRemoveConfig{}
Expand All @@ -32,28 +33,26 @@ func NewRemoveCommand(l *zap.SugaredLogger) *cobra.Command {
l.Error(err)
return
}

if len(args) != 1 {
output.PrintError(fmt.Errorf("invalid number of arguments: expected 1, got %d", len(args)), l, true)
os.Exit(1)
}

namespace := args[0]
c.Namespaces = []string{namespace}
c.Namespaces = args[0]

enableLogging := viper.GetBool("verbose") || viper.GetBool("json")
c.Pretty = !enableLogging

if err := c.Populate(cmd.Context()); err != nil {
if errors.Is(err, namespaces.ErrNamespaceNotEmpty) {
err = fmt.Errorf("%w. %s", err, forceUninstallHint)
}
output.PrintError(err, l, !enableLogging)
os.Exit(1)
}

op, err := namespaces.NewNamespaceRemove(*c, l)
if err != nil {
output.PrintError(err, l, !enableLogging)
return
}

if err := op.Run(cmd.Context()); err != nil {
if errors.Is(err, namespaces.ErrNamespaceNotEmpty) {
err = fmt.Errorf("%w. %s", err, forceUninstallHint)
}
output.PrintError(err, l, !enableLogging)
os.Exit(1)
}
Expand Down
102 changes: 75 additions & 27 deletions pkg/cli/namespaces/remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ import (
cliutils "github.com/percona/everest/pkg/cli/utils"
"github.com/percona/everest/pkg/common"
"github.com/percona/everest/pkg/kubernetes"
"github.com/percona/everest/pkg/output"
)

const (
pollInterval = 5 * time.Second
pollTimeout = 5 * time.Minute
)

// ErrNamespaceNotEmpty is returned when the namespace is not empty.
var ErrNamespaceNotEmpty = errors.New("cannot remove namespace with running database clusters")

// NamespaceRemoveConfig is the configuration for the namespace removal operation.
type NamespaceRemoveConfig struct {
// KubeconfigPath is a path to a kubeconfig
Expand All @@ -37,8 +39,76 @@ type NamespaceRemoveConfig struct {
// If set, we will print the pretty output.
Pretty bool

// Namespaces (DB Namespaces) to remove
Namespaces []string
// Namespaces (DB Namespaces) passed by user to remove.
Namespaces string
// NamespaceList is a list of namespaces to remove.
// This is populated internally after validating the Namespaces field.:
NamespaceList []string
}

// Populate the configuration with the required values.
func (cfg *NamespaceRemoveConfig) Populate(ctx context.Context) error {
if err := cfg.populateNamespaces(); err != nil {
return err
}

for _, ns := range cfg.NamespaceList {
if err := cfg.validateNamespaceOwnership(ctx, ns); err != nil {
return fmt.Errorf("invalid namespace (%s): %w", ns, err)
}

if err := cfg.validateDatabasesAbsent(ctx, ns); err != nil {
return fmt.Errorf("invalid namespace (%s): %w", ns, err)
}
}
return nil
}

func (cfg *NamespaceRemoveConfig) populateNamespaces() error {
namespaces := cfg.Namespaces
list, err := ValidateNamespaces(namespaces)
if err != nil {
return err
}
cfg.NamespaceList = list
return nil
}

func (cfg *NamespaceRemoveConfig) validateNamespaceOwnership(ctx context.Context, namespace string) error {
k, err := cliutils.NewKubeclient(zap.NewNop().Sugar(), cfg.KubeconfigPath)
if err != nil {
return err
}

nsExists, ownedByEverest, err := namespaceExists(ctx, namespace, k)
if err != nil {
return err
}

if !nsExists {
return ErrNsDoesNotExist
}
if !ownedByEverest {
return ErrNamespaceNotManagedByEverest
}
return nil
}

func (cfg *NamespaceRemoveConfig) validateDatabasesAbsent(ctx context.Context, namespace string) error {
k, err := cliutils.NewKubeclient(zap.NewNop().Sugar(), cfg.KubeconfigPath)
if err != nil {
return err
}

dbsExist, err := k.DatabasesExist(ctx, namespace)
if err != nil {
return errors.Join(err, errors.New("failed to check if databases exist"))
}

if dbsExist && !cfg.Force {
return ErrNamespaceNotEmpty
}
return nil
}

// NamespaceRemover is the CLI operation to remove namespaces.
Expand Down Expand Up @@ -66,9 +136,6 @@ func NewNamespaceRemove(c NamespaceRemoveConfig, l *zap.SugaredLogger) (*Namespa
return n, nil
}

// ErrNamespaceNotEmpty is returned when the namespace is not empty.
var ErrNamespaceNotEmpty = errors.New("cannot remove namespace with running database clusters")

// Run the namespace removal operation.
func (r *NamespaceRemover) Run(ctx context.Context) error {
// This command expects a Helm based installation (< 1.4.0)
Expand All @@ -77,27 +144,8 @@ func (r *NamespaceRemover) Run(ctx context.Context) error {
return err
}

dbsExist, err := r.kubeClient.DatabasesExist(ctx, r.config.Namespaces...)
if err != nil {
return errors.Join(err, errors.New("failed to check if databases exist"))
}

if dbsExist && !r.config.Force {
return ErrNamespaceNotEmpty
}

removalSteps := []steps.Step{}
for _, ns := range r.config.Namespaces {
// Check that the namespace exists.
exists, managedByEverest, err := namespaceExists(ctx, ns, r.kubeClient)
if err != nil {
return errors.Join(err, errors.New("failed to check if namespace exists"))
}
if !exists || !managedByEverest {
r.l.Infof("Namespace '%s' does not exist or not managed by Everest", ns)
fmt.Fprint(os.Stdout, output.Warn("Namespace (%s) does not exist or not managed by Everest, skipping..", ns))
continue
}
var removalSteps []steps.Step
for _, ns := range r.config.NamespaceList {
removalSteps = append(removalSteps, NewRemoveNamespaceSteps(ns, r.config.KeepNamespace, r.kubeClient)...)
}

Expand Down

0 comments on commit 9f15fa8

Please sign in to comment.