Update 'tctl apps/db/nodes ls' to accept filter flags (#11003)

This commit is contained in:
Lisa Kim 2022-03-11 09:57:40 -08:00 committed by GitHub
parent df9c99bceb
commit 628564c801
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 133 additions and 6 deletions

View file

@ -576,6 +576,8 @@ func GetNodesWithLabels(ctx context.Context, clt nodeClient, namespace string, l
}
// GetNodes returns the list of servers registered in the cluster.
//
// DELETE IN 11.0.0, replaced by GetResourcesWithFilters
func (c *Client) GetNodes(ctx context.Context, namespace string, opts ...services.MarshalOption) ([]types.Server, error) {
if resp, err := c.APIClient.GetNodes(ctx, namespace); err != nil {
if !trace.IsNotImplemented(err) {

View file

@ -25,8 +25,12 @@ import (
"github.com/gravitational/trace"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
libclient "github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/service"
)
@ -37,6 +41,10 @@ type AppsCommand struct {
// format is the output format (text, json, or yaml)
format string
searchKeywords string
predicateExpr string
labels string
// appsList implements the "tctl apps ls" subcommand.
appsList *kingpin.CmdClause
}
@ -48,6 +56,9 @@ func (c *AppsCommand) Initialize(app *kingpin.Application, config *service.Confi
apps := app.Command("apps", "Operate on applications registered with the cluster.")
c.appsList = apps.Command("ls", "List all applications registered with the cluster.")
c.appsList.Flag("format", "Output format, 'text', 'json', or 'yaml'").Default("text").StringVar(&c.format)
c.appsList.Arg("labels", labelHelp).StringVar(&c.labels)
c.appsList.Flag("search", searchHelp).StringVar(&c.searchKeywords)
c.appsList.Flag("query", queryHelp).StringVar(&c.predicateExpr)
}
// TryRun attempts to run subcommands like "apps ls".
@ -63,11 +74,40 @@ func (c *AppsCommand) TryRun(cmd string, client auth.ClientI) (match bool, err e
// ListApps prints the list of applications that have recently sent heartbeats
// to the cluster.
func (c *AppsCommand) ListApps(client auth.ClientI) error {
servers, err := client.GetApplicationServers(context.TODO(), apidefaults.Namespace)
func (c *AppsCommand) ListApps(clt auth.ClientI) error {
ctx := context.TODO()
labels, err := libclient.ParseLabelSpec(c.labels)
if err != nil {
return trace.Wrap(err)
}
var servers []types.AppServer
resources, err := client.GetResourcesWithFilters(ctx, clt, proto.ListResourcesRequest{
ResourceType: types.KindAppServer,
Labels: labels,
PredicateExpression: c.predicateExpr,
SearchKeywords: libclient.ParseSearchKeywords(c.searchKeywords, ','),
})
switch {
// Underlying ListResources for app servers not available, use fallback.
// Using filter flags with older auth will silently do nothing.
//
// DELETE IN 11.0.0
case trace.IsNotImplemented(err):
servers, err = clt.GetApplicationServers(ctx, apidefaults.Namespace)
if err != nil {
return trace.Wrap(err)
}
case err != nil:
return trace.Wrap(err)
default:
servers, err = types.ResourcesWithLabels(resources).AsAppServers()
if err != nil {
return trace.Wrap(err)
}
}
coll := &appServerCollection{servers: servers}
switch c.format {

View file

@ -22,8 +22,12 @@ import (
"text/template"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
libclient "github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/kingpin"
@ -37,6 +41,10 @@ type DBCommand struct {
// format is the output format (text, json or yaml).
format string
searchKeywords string
predicateExpr string
labels string
// dbList implements the "tctl db ls" subcommand.
dbList *kingpin.CmdClause
}
@ -48,6 +56,9 @@ func (c *DBCommand) Initialize(app *kingpin.Application, config *service.Config)
db := app.Command("db", "Operate on databases registered with the cluster.")
c.dbList = db.Command("ls", "List all databases registered with the cluster.")
c.dbList.Flag("format", "Output format, 'text', 'json', or 'yaml'").Default("text").StringVar(&c.format)
c.dbList.Arg("labels", labelHelp).StringVar(&c.labels)
c.dbList.Flag("search", searchHelp).StringVar(&c.searchKeywords)
c.dbList.Flag("query", queryHelp).StringVar(&c.predicateExpr)
}
// TryRun attempts to run subcommands like "db ls".
@ -63,11 +74,40 @@ func (c *DBCommand) TryRun(cmd string, client auth.ClientI) (match bool, err err
// ListDatabases prints the list of database proxies that have recently sent
// heartbeats to the cluster.
func (c *DBCommand) ListDatabases(client auth.ClientI) error {
servers, err := client.GetDatabaseServers(context.TODO(), apidefaults.Namespace)
func (c *DBCommand) ListDatabases(clt auth.ClientI) error {
ctx := context.TODO()
labels, err := libclient.ParseLabelSpec(c.labels)
if err != nil {
return trace.Wrap(err)
}
var servers []types.DatabaseServer
resources, err := client.GetResourcesWithFilters(ctx, clt, proto.ListResourcesRequest{
ResourceType: types.KindDatabaseServer,
Labels: labels,
PredicateExpression: c.predicateExpr,
SearchKeywords: libclient.ParseSearchKeywords(c.searchKeywords, ','),
})
switch {
// Underlying ListResources for db servers not available, use fallback.
// Using filter flags with older auth will silently do nothing.
//
// DELETE IN 11.0.0
case trace.IsNotImplemented(err):
servers, err = clt.GetDatabaseServers(ctx, apidefaults.Namespace)
if err != nil {
return trace.Wrap(err)
}
case err != nil:
return trace.Wrap(err)
default:
servers, err = types.ResourcesWithLabels(resources).AsDatabaseServers()
if err != nil {
return trace.Wrap(err)
}
}
coll := &databaseServerCollection{servers: servers}
switch c.format {
case teleport.Text:

View file

@ -29,9 +29,12 @@ import (
"github.com/gravitational/trace"
log "github.com/sirupsen/logrus"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/lib/auth"
libclient "github.com/gravitational/teleport/lib/client"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/tlsca"
@ -53,6 +56,10 @@ type NodeCommand struct {
// if not specified, is autogenerated
token string
searchKeywords string
predicateExpr string
labels string
// CLI subcommands (clauses)
nodeAdd *kingpin.CmdClause
nodeList *kingpin.CmdClause
@ -74,6 +81,9 @@ func (c *NodeCommand) Initialize(app *kingpin.Application, config *service.Confi
c.nodeList = nodes.Command("ls", "List all active SSH nodes within the cluster")
c.nodeList.Flag("namespace", "Namespace of the nodes").Default(apidefaults.Namespace).StringVar(&c.namespace)
c.nodeList.Alias(ListNodesHelp)
c.nodeList.Arg("labels", labelHelp).StringVar(&c.labels)
c.nodeList.Flag("search", searchHelp).StringVar(&c.searchKeywords)
c.nodeList.Flag("query", queryHelp).StringVar(&c.predicateExpr)
}
// TryRun takes the CLI command as an argument (like "nodes ls") and executes it.
@ -190,12 +200,41 @@ func (c *NodeCommand) Invite(client auth.ClientI) error {
// ListActive retreives the list of nodes who recently sent heartbeats to
// to a cluster and prints it to stdout
func (c *NodeCommand) ListActive(client auth.ClientI) error {
func (c *NodeCommand) ListActive(clt auth.ClientI) error {
ctx := context.TODO()
nodes, err := client.GetNodes(ctx, c.namespace)
labels, err := libclient.ParseLabelSpec(c.labels)
if err != nil {
return trace.Wrap(err)
}
var nodes []types.Server
resources, err := client.GetResourcesWithFilters(ctx, clt, proto.ListResourcesRequest{
ResourceType: types.KindNode,
Namespace: c.namespace,
Labels: labels,
PredicateExpression: c.predicateExpr,
SearchKeywords: libclient.ParseSearchKeywords(c.searchKeywords, ','),
})
switch {
// Underlying ListResources for nodes not available, use fallback.
// Using filter flags with older auth will silently do nothing.
//
// DELETE IN 11.0.0
case trace.IsNotImplemented(err):
nodes, err = clt.GetNodes(ctx, c.namespace)
if err != nil {
return trace.Wrap(err)
}
case err != nil:
return trace.Wrap(err)
default:
nodes, err = types.ResourcesWithLabels(resources).AsServers()
if err != nil {
return trace.Wrap(err)
}
}
coll := &serverCollection{servers: nodes}
if err := coll.writeText(os.Stdout); err != nil {
return trace.Wrap(err)

View file

@ -41,6 +41,12 @@ import (
log "github.com/sirupsen/logrus"
)
const (
searchHelp = `List of comma separated search keywords or phrases enclosed in quotations (e.g. --search=foo,bar,"some phrase")`
queryHelp = `Query by predicate language enclosed in single quotes. Supports ==, !=, &&, and || (e.g. --query='labels.key1 == "value1" && labels.key2 != "value2"')`
labelHelp = "List of comma separated labels to filter by labels (e.g. key1=value1,key2=value2)"
)
// GlobalCLIFlags keeps the CLI flags that apply to all tctl commands
type GlobalCLIFlags struct {
// Debug enables verbose logging mode to the console