-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* EVEREST-1815 Replase AlecAivazis/survey lib with charmbracelet/bubbletea * EVEREST-1815 Adjust styles * EVEREST-1815 Test * EVEREST-1815 Logger test * EVEREST-1815 Logger test * TEST * TEST 2 * Fix Helm config * EVEREST-1815 Fix running actions in case of tty absence * Fix go.mod * Fix tests * Fix tests * Fix tests * Fix password validation regexp * Fix password validation regexp * Add tmate to minikube * Adjust docker daemon config for GH workflowswq * Adjust docker daemon config for GH workflowswq * Adjust docker daemon config for GH workflowswq * Adjust docker config for FE GH e2e workflow * Fix comments for account CLI commands * Fix comments * Fix formatting * Replace Unicode digits in post-install message * Add mark for current cursor position * EVEREST-1817 Add `namespaces list` command support to CLI * Fixes based on comments * Update go.mod * Update configure.go * Get info about installed operators from DB Engines CRs instead of OLM --------- Co-authored-by: Percona Platform Robot <61465387+percona-robot@users.noreply.github.com>
- Loading branch information
1 parent
48bbf84
commit 6d44f70
Showing
4 changed files
with
288 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// everest | ||
// Copyright (C) 2025 Percona LLC | ||
// | ||
// 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 namespaces provides the namespaces CLI command. | ||
package namespaces | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"strings" | ||
|
||
"github.com/rodaine/table" | ||
"github.com/spf13/cobra" | ||
|
||
"github.com/percona/everest/pkg/cli" | ||
"github.com/percona/everest/pkg/cli/namespaces" | ||
"github.com/percona/everest/pkg/logger" | ||
"github.com/percona/everest/pkg/output" | ||
) | ||
|
||
var ( | ||
namespacesListCmd = &cobra.Command{ | ||
Use: "list [flags]", | ||
Args: cobra.NoArgs, | ||
Long: "List namespaces managed by Everest.", | ||
Short: "List namespaces managed by Everest.", | ||
Example: `everestctl namespaces list --all`, | ||
PreRun: namespacesListPreRun, | ||
Run: namespacesListRun, | ||
} | ||
namespacesListCfg = &namespaces.NamespaceListConfig{} | ||
) | ||
|
||
func init() { | ||
// local command flags | ||
namespacesListCmd.Flags().BoolVarP(&namespacesListCfg.ListAllNamespaces, cli.FlagNamespaceAll, "a", false, "If set, returns all namespaces in kubernetes cluster (excludes system and Everest core namespaces)") | ||
} | ||
|
||
func namespacesListPreRun(cmd *cobra.Command, _ []string) { //nolint:revive | ||
// Copy global flags to config | ||
namespacesListCfg.Pretty = !(cmd.Flag(cli.FlagVerbose).Changed || cmd.Flag(cli.FlagJSON).Changed) | ||
namespacesListCfg.KubeconfigPath = cmd.Flag(cli.FlagKubeconfig).Value.String() | ||
} | ||
|
||
func namespacesListRun(cmd *cobra.Command, _ []string) { | ||
op, err := namespaces.NewNamespaceLister(*namespacesListCfg, logger.GetLogger()) | ||
if err != nil { | ||
output.PrintError(err, logger.GetLogger(), namespacesListCfg.Pretty) | ||
os.Exit(1) | ||
} | ||
|
||
if nsList, err := op.Run(cmd.Context()); err != nil { | ||
output.PrintError(err, logger.GetLogger(), namespacesListCfg.Pretty) | ||
os.Exit(1) | ||
} else { | ||
if err := printNamespacesTable(nsList); err != nil { | ||
output.PrintError(err, logger.GetLogger(), namespacesListCfg.Pretty) | ||
os.Exit(1) | ||
} | ||
} | ||
} | ||
|
||
// GetNamespacesListCmd returns the command to list a namespaces. | ||
func GetNamespacesListCmd() *cobra.Command { | ||
return namespacesListCmd | ||
} | ||
|
||
const ( | ||
// columnName is the column name for the namespace. | ||
columnName = "namespace" | ||
// columnManagedByEverest is the column name for the namespace managed by Everest. | ||
columnManagedByEverest = "managed" | ||
// columnOperators is the column name for the installed Everest operators. | ||
columnOperators = "operators" | ||
) | ||
|
||
// Print namespaces to console. | ||
func printNamespacesTable(nsList []namespaces.NamespaceInfo) error { | ||
// Prepare table headings. | ||
headings := []interface{}{columnName, columnManagedByEverest, columnOperators} | ||
// Prepare table header. | ||
tbl := table.New(headings...) | ||
tbl.WithHeaderFormatter(func(format string, vals ...interface{}) string { | ||
// Print all in caps. | ||
return strings.ToUpper(fmt.Sprintf(format, vals...)) | ||
}) | ||
|
||
// Return a table row for the given account. | ||
row := func(ns namespaces.NamespaceInfo) []any { | ||
var row []any | ||
for _, heading := range headings { | ||
switch heading { | ||
case columnName: | ||
row = append(row, ns.Name) | ||
case columnManagedByEverest: | ||
row = append(row, len(ns.InstalledOperators) != 0) | ||
case columnOperators: | ||
row = append(row, strings.Join(ns.InstalledOperators, ", ")) | ||
} | ||
} | ||
return row | ||
} | ||
|
||
for _, ns := range nsList { | ||
tbl.AddRow(row(ns)...) | ||
} | ||
|
||
tbl.Print() | ||
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
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,163 @@ | ||
// everest | ||
// Copyright (C) 2025 Percona LLC | ||
// | ||
// 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 namespaces provides the functionality to manage namespaces. | ||
package namespaces | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"slices" | ||
|
||
"go.uber.org/zap" | ||
corev1 "k8s.io/api/core/v1" | ||
v1 "k8s.io/api/core/v1" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
|
||
cliutils "github.com/percona/everest/pkg/cli/utils" | ||
"github.com/percona/everest/pkg/common" | ||
"github.com/percona/everest/pkg/kubernetes" | ||
) | ||
|
||
// skipNamespaces is a list of namespaces that cannot be added to Everest management. | ||
// It contains Kubernetes system, reserved by Everest core and cloud providers specific namespaces. | ||
// Note: this list is not exhaustive and can be extended. | ||
var skipNamespaces = []string{ | ||
// Kubernetes native system namespaces. | ||
"kube-system", | ||
"kube-public", | ||
"kube-node-lease", | ||
|
||
// Everest core namespaces. | ||
common.SystemNamespace, | ||
common.MonitoringNamespace, | ||
kubernetes.OLMNamespace, | ||
|
||
// GKE namespaces. | ||
"gke-managed-cim", | ||
"gke-managed-system", | ||
"gke-managed-volumepopulator", | ||
"gmp-public", | ||
"gmp-system", | ||
} | ||
|
||
type ( | ||
// NamespaceListConfig is the configuration for the namespace listing operation. | ||
NamespaceListConfig struct { | ||
// KubeconfigPath is a path to a kubeconfig | ||
KubeconfigPath string | ||
// Pretty if set print the output in pretty mode. | ||
Pretty bool | ||
// ListAllNamespaces if set, list all namespaces. | ||
// Note: this flag skips namespaces that cannot be added to Everest management | ||
// (i.e. Kubernetes system, specific to cloud providers and Everest Core namespaces). | ||
ListAllNamespaces bool | ||
} | ||
|
||
// NamespaceInfo contains information about a namespace. | ||
NamespaceInfo struct { | ||
// Name is the namespace name. | ||
Name string | ||
// InstalledOperators is a list of installed Percona operators in the namespace. | ||
InstalledOperators []string | ||
} | ||
|
||
// NamespaceLister is the CLI operation to list namespaces. | ||
NamespaceLister struct { | ||
cfg NamespaceListConfig | ||
kubeClient *kubernetes.Kubernetes | ||
l *zap.SugaredLogger | ||
} | ||
) | ||
|
||
// NewNamespaceLister returns a new CLI operation to list namespaces. | ||
func NewNamespaceLister(c NamespaceListConfig, l *zap.SugaredLogger) (*NamespaceLister, error) { | ||
n := &NamespaceLister{ | ||
cfg: c, | ||
l: l.With("component", "namespace-remover"), | ||
} | ||
if c.Pretty { | ||
n.l = zap.NewNop().Sugar() | ||
} | ||
|
||
k, err := cliutils.NewKubeclient(n.l, n.cfg.KubeconfigPath) | ||
if err != nil { | ||
return nil, err | ||
} | ||
n.kubeClient = k | ||
return n, nil | ||
} | ||
|
||
// Run the namespace list operation. | ||
func (nsL *NamespaceLister) Run(ctx context.Context) ([]NamespaceInfo, error) { | ||
var err error | ||
// This command expects a Helm based installation (< 1.4.0) | ||
_, err = cliutils.CheckHelmInstallation(ctx, nsL.kubeClient) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var nsList *corev1.NamespaceList | ||
var labelSelector string | ||
|
||
if !nsL.cfg.ListAllNamespaces { | ||
// show only namespaces already managed by Everest. | ||
labelSelector = fmt.Sprintf("%s=%s", common.KubernetesManagedByLabel, common.Everest) | ||
} | ||
|
||
if nsList, err = nsL.kubeClient.ListNamespaces(ctx, metav1.ListOptions{ | ||
FieldSelector: fmt.Sprintf("status.phase=%s", corev1.NamespaceActive), | ||
LabelSelector: labelSelector, | ||
}); err != nil { | ||
return nil, err | ||
} | ||
|
||
// filter out namespaces that are listed in skipNamespaces and non-active namespaces. | ||
nsList.Items = slices.DeleteFunc(nsList.Items, func(ns corev1.Namespace) bool { | ||
return slices.Contains(skipNamespaces, ns.Name) | ||
}) | ||
|
||
var toReturn []NamespaceInfo | ||
for _, ns := range nsList.Items { | ||
nsInfo := NamespaceInfo{Name: ns.Name} | ||
if nsInfo.InstalledOperators, err = nsL.getNamespaceOperators(ctx, &ns); err != nil { | ||
return nil, fmt.Errorf("cannot get namespace subscriptions: %w", err) | ||
} | ||
toReturn = append(toReturn, nsInfo) | ||
} | ||
return toReturn, nil | ||
} | ||
|
||
// getNamespaceOperators returns a list of installed operators in the namespace. | ||
// It returns an empty list if the namespace is not managed by Everest. | ||
func (nsL *NamespaceLister) getNamespaceOperators(ctx context.Context, ns *v1.Namespace) ([]string, error) { | ||
var toReturn []string | ||
if isManagedByEverest(ns) { | ||
// no need to look for installed operators from namespaces not managed by Everest. | ||
dbEngines, err := nsL.kubeClient.ListDatabaseEngines(ctx, ns.Name) | ||
if err != nil { | ||
return []string{}, fmt.Errorf("cannot list installed DB Engines: %w", err) | ||
} | ||
|
||
for _, dbE := range dbEngines.Items { | ||
// need to skip DB Engines that are not installed in this particular namespaces. | ||
if dbE.Status.State != "installed" { | ||
continue | ||
} | ||
toReturn = append(toReturn, fmt.Sprintf("%s(v%s)", dbE.Spec.Type, dbE.Status.OperatorVersion)) | ||
} | ||
} | ||
return toReturn, nil | ||
} |