Skip to content

Commit

Permalink
Update 'tctl apps/db/nodes ls' to accept filter flags
Browse files Browse the repository at this point in the history
  • Loading branch information
kimlisa committed Mar 10, 2022
1 parent 350ea5b commit a5f2bc9
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 9 deletions.
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
50 changes: 47 additions & 3 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,44 @@ 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)
if err != nil {
func (c *AppsCommand) ListApps(clt auth.ClientI) error {
ctx := context.TODO()

var labels map[string]string
var err error
if c.labels != "" {
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
50 changes: 47 additions & 3 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,44 @@ 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)
if err != nil {
func (c *DBCommand) ListDatabases(clt auth.ClientI) error {
ctx := context.TODO()

var labels map[string]string
var err error
if c.labels != "" {
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
49 changes: 46 additions & 3 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,45 @@ 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)
if err != nil {

var labels map[string]string
var err error
if c.labels != "" {
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

0 comments on commit a5f2bc9

Please sign in to comment.