Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 3 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/auth/httpfallback.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
44 changes: 42 additions & 2 deletions tool/tctl/common/app_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)

Expand All @@ -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
}
Expand All @@ -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".
Expand All @@ -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 {
Expand Down
44 changes: 42 additions & 2 deletions tool/tctl/common/db_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
}
Expand All @@ -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".
Expand All @@ -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:
Expand Down
43 changes: 41 additions & 2 deletions tool/tctl/common/node_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand All @@ -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.
Expand Down Expand Up @@ -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)
Expand Down
6 changes: 6 additions & 0 deletions tool/tctl/common/tctl.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down