Skip to content

Commit f3c7362

Browse files
committed
tctl resources: convert db
1 parent 363d678 commit f3c7362

File tree

8 files changed

+478
-308
lines changed

8 files changed

+478
-308
lines changed

tool/tctl/common/collection.go

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -708,41 +708,6 @@ func (c *databaseServerCollection) writeYAML(w io.Writer) error {
708708
return utils.WriteYAML(w, c.servers)
709709
}
710710

711-
type databaseCollection struct {
712-
databases []types.Database
713-
}
714-
715-
func (c *databaseCollection) Resources() (r []types.Resource) {
716-
for _, resource := range c.databases {
717-
r = append(r, resource)
718-
}
719-
return r
720-
}
721-
722-
func (c *databaseCollection) WriteText(w io.Writer, verbose bool) error {
723-
var rows [][]string
724-
for _, database := range c.databases {
725-
labels := common.FormatLabels(database.GetAllLabels(), verbose)
726-
rows = append(rows, []string{
727-
common.FormatResourceName(database, verbose),
728-
database.GetProtocol(),
729-
database.GetURI(),
730-
labels,
731-
})
732-
}
733-
headers := []string{"Name", "Protocol", "URI", "Labels"}
734-
var t asciitable.Table
735-
if verbose {
736-
t = asciitable.MakeTable(headers, rows...)
737-
} else {
738-
t = asciitable.MakeTableWithTruncatedColumn(headers, rows, "Labels")
739-
}
740-
// stable sort by name.
741-
t.SortRowsBy([]int{0}, true)
742-
_, err := t.AsBuffer().WriteTo(w)
743-
return trace.Wrap(err)
744-
}
745-
746711
type lockCollection struct {
747712
locks []types.Lock
748713
}

tool/tctl/common/collection_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ func testDatabaseCollection_writeText(t *testing.T) {
195195
rdsDiscoveredNameLabel),
196196
}
197197
test := writeTextTest{
198-
collection: &databaseCollection{databases: databases},
198+
collection: resources.NewDatabaseCollection(databases),
199199
wantNonVerboseTable: func() string {
200200
table := asciitable.MakeTableWithTruncatedColumn(
201201
[]string{"Name", "Protocol", "URI", "Labels"},

tool/tctl/common/resource_command.go

Lines changed: 9 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import (
2929
"reflect"
3030
"slices"
3131
"sort"
32-
"strings"
3332
"time"
3433

3534
"github.com/alecthomas/kingpin/v2"
@@ -151,7 +150,6 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, _ *tctlcfg.Globa
151150
types.KindNetworkRestrictions: rc.createNetworkRestrictions,
152151
types.KindApp: rc.createApp,
153152
types.KindAppServer: rc.createAppServer,
154-
types.KindDatabase: rc.createDatabase,
155153
types.KindKubernetesCluster: rc.createKubeCluster,
156154
types.KindToken: rc.createToken,
157155
types.KindInstaller: rc.createInstaller,
@@ -1322,29 +1320,6 @@ func (rc *ResourceCommand) updateUserTask(ctx context.Context, client *authclien
13221320
return nil
13231321
}
13241322

1325-
func (rc *ResourceCommand) createDatabase(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
1326-
database, err := services.UnmarshalDatabase(raw.Raw, services.DisallowUnknown())
1327-
if err != nil {
1328-
return trace.Wrap(err)
1329-
}
1330-
database.SetOrigin(types.OriginDynamic)
1331-
if err := client.CreateDatabase(ctx, database); err != nil {
1332-
if trace.IsAlreadyExists(err) {
1333-
if !rc.force {
1334-
return trace.AlreadyExists("database %q already exists", database.GetName())
1335-
}
1336-
if err := client.UpdateDatabase(ctx, database); err != nil {
1337-
return trace.Wrap(err)
1338-
}
1339-
fmt.Printf("database %q has been updated\n", database.GetName())
1340-
return nil
1341-
}
1342-
return trace.Wrap(err)
1343-
}
1344-
fmt.Printf("database %q has been created\n", database.GetName())
1345-
return nil
1346-
}
1347-
13481323
func (rc *ResourceCommand) createToken(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
13491324
token, err := services.UnmarshalProvisionToken(raw.Raw, services.DisallowUnknown())
13501325
if err != nil {
@@ -1931,8 +1906,8 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client
19311906
return trace.Wrap(err)
19321907
}
19331908
resDesc := "database server"
1934-
servers = filterByNameOrDiscoveredName(servers, rc.ref.Name)
1935-
name, err := getOneResourceNameToDelete(servers, rc.ref, resDesc)
1909+
servers = resources.FilterByNameOrDiscoveredName(servers, rc.ref.Name)
1910+
name, err := resources.GetOneResourceNameToDelete(servers, rc.ref, resDesc)
19361911
if err != nil {
19371912
return trace.Wrap(err)
19381913
}
@@ -1953,31 +1928,15 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client
19531928
return trace.Wrap(err)
19541929
}
19551930
fmt.Printf("application %q has been deleted\n", rc.ref.Name)
1956-
case types.KindDatabase:
1957-
// TODO(okraport) DELETE IN v21.0.0, replace with regular Collect
1958-
databases, err := clientutils.CollectWithFallback(ctx, client.ListDatabases, client.GetDatabases)
1959-
if err != nil {
1960-
return trace.Wrap(err)
1961-
}
1962-
resDesc := "database"
1963-
databases = filterByNameOrDiscoveredName(databases, rc.ref.Name)
1964-
name, err := getOneResourceNameToDelete(databases, rc.ref, resDesc)
1965-
if err != nil {
1966-
return trace.Wrap(err)
1967-
}
1968-
if err := client.DeleteDatabase(ctx, name); err != nil {
1969-
return trace.Wrap(err)
1970-
}
1971-
fmt.Printf("%s %q has been deleted\n", resDesc, name)
19721931
case types.KindKubernetesCluster:
19731932
// TODO(okraport) DELETE IN v21.0.0, replace with regular Collect
19741933
clusters, err := clientutils.CollectWithFallback(ctx, client.ListKubernetesClusters, client.GetKubernetesClusters)
19751934
if err != nil {
19761935
return trace.Wrap(err)
19771936
}
19781937
resDesc := "Kubernetes cluster"
1979-
clusters = filterByNameOrDiscoveredName(clusters, rc.ref.Name)
1980-
name, err := getOneResourceNameToDelete(clusters, rc.ref, resDesc)
1938+
clusters = resources.FilterByNameOrDiscoveredName(clusters, rc.ref.Name)
1939+
name, err := resources.GetOneResourceNameToDelete(clusters, rc.ref, resDesc)
19811940
if err != nil {
19821941
return trace.Wrap(err)
19831942
}
@@ -2052,8 +2011,8 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client *authclient.Client
20522011
return trace.Wrap(err)
20532012
}
20542013
resDesc := "Kubernetes server"
2055-
servers = filterByNameOrDiscoveredName(servers, rc.ref.Name)
2056-
name, err := getOneResourceNameToDelete(servers, rc.ref, resDesc)
2014+
servers = resources.FilterByNameOrDiscoveredName(servers, rc.ref.Name)
2015+
name, err := resources.GetOneResourceNameToDelete(servers, rc.ref, resDesc)
20572016
if err != nil {
20582017
return trace.Wrap(err)
20592018
}
@@ -2720,7 +2679,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
27202679
return &databaseServerCollection{servers: servers}, nil
27212680
}
27222681

2723-
servers = filterByNameOrDiscoveredName(servers, rc.ref.Name)
2682+
servers = resources.FilterByNameOrDiscoveredName(servers, rc.ref.Name)
27242683
if len(servers) == 0 {
27252684
return nil, trace.NotFound("database server %q not found", rc.ref.Name)
27262685
}
@@ -2736,7 +2695,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
27362695
altNameFn := func(r types.KubeServer) string {
27372696
return r.GetHostname()
27382697
}
2739-
servers = filterByNameOrDiscoveredName(servers, rc.ref.Name, altNameFn)
2698+
servers = resources.FilterByNameOrDiscoveredName(servers, rc.ref.Name, altNameFn)
27402699
if len(servers) == 0 {
27412700
return nil, trace.NotFound("Kubernetes server %q not found", rc.ref.Name)
27422701
}
@@ -2783,21 +2742,6 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
27832742
return nil, trace.Wrap(err)
27842743
}
27852744
return &appCollection{apps: []types.Application{app}}, nil
2786-
case types.KindDatabase:
2787-
// TODO(okraport): DELETE IN v21.0.0, replace with regular Collect
2788-
databases, err := clientutils.CollectWithFallback(ctx, client.ListDatabases, client.GetDatabases)
2789-
if err != nil {
2790-
return nil, trace.Wrap(err)
2791-
}
2792-
2793-
if rc.ref.Name == "" {
2794-
return &databaseCollection{databases: databases}, nil
2795-
}
2796-
databases = filterByNameOrDiscoveredName(databases, rc.ref.Name)
2797-
if len(databases) == 0 {
2798-
return nil, trace.NotFound("database %q not found", rc.ref.Name)
2799-
}
2800-
return &databaseCollection{databases: databases}, nil
28012745
case types.KindKubernetesCluster:
28022746
// TODO(okraport) DELETE IN v21.0.0, replace with regular Collect
28032747
clusters, err := clientutils.CollectWithFallback(ctx, client.ListKubernetesClusters, client.GetKubernetesClusters)
@@ -2807,7 +2751,7 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client *authclient
28072751
if rc.ref.Name == "" {
28082752
return &kubeClusterCollection{clusters: clusters}, nil
28092753
}
2810-
clusters = filterByNameOrDiscoveredName(clusters, rc.ref.Name)
2754+
clusters = resources.FilterByNameOrDiscoveredName(clusters, rc.ref.Name)
28112755
if len(clusters) == 0 {
28122756
return nil, trace.NotFound("Kubernetes cluster %q not found", rc.ref.Name)
28132757
}
@@ -3802,105 +3746,6 @@ func findDeviceByIDOrTag(ctx context.Context, remote devicepb.DeviceTrustService
38023746
return nil, trace.BadParameter("found multiple devices for asset tag %q, please retry using the device ID instead", idOrTag)
38033747
}
38043748

3805-
// keepFn is a predicate function that returns true if a resource should be
3806-
// retained by filterResources.
3807-
type keepFn[T types.ResourceWithLabels] func(T) bool
3808-
3809-
// filterResources takes a list of resources and returns a filtered list of
3810-
// resources for which the `keep` predicate function returns true.
3811-
func filterResources[T types.ResourceWithLabels](resources []T, keep keepFn[T]) []T {
3812-
out := make([]T, 0, len(resources))
3813-
for _, r := range resources {
3814-
if keep(r) {
3815-
out = append(out, r)
3816-
}
3817-
}
3818-
return out
3819-
}
3820-
3821-
// altNameFn is a func that returns an alternative name for a resource.
3822-
type altNameFn[T types.ResourceWithLabels] func(T) string
3823-
3824-
// filterByNameOrDiscoveredName filters resources by name or "discovered name".
3825-
// It prefers exact name filtering first - if none of the resource names match
3826-
// exactly (i.e. all of the resources are filtered out), then it retries and
3827-
// filters the resources by "discovered name" of resource name instead, which
3828-
// comes from an auto-discovery label.
3829-
func filterByNameOrDiscoveredName[T types.ResourceWithLabels](resources []T, prefixOrName string, extra ...altNameFn[T]) []T {
3830-
// prefer exact names
3831-
out := filterByName(resources, prefixOrName, extra...)
3832-
if len(out) == 0 {
3833-
// fallback to looking for discovered name label matches.
3834-
out = filterByDiscoveredName(resources, prefixOrName)
3835-
}
3836-
return out
3837-
}
3838-
3839-
// filterByName filters resources by exact name match.
3840-
func filterByName[T types.ResourceWithLabels](resources []T, name string, altNameFns ...altNameFn[T]) []T {
3841-
return filterResources(resources, func(r T) bool {
3842-
if r.GetName() == name {
3843-
return true
3844-
}
3845-
for _, altName := range altNameFns {
3846-
if altName(r) == name {
3847-
return true
3848-
}
3849-
}
3850-
return false
3851-
})
3852-
}
3853-
3854-
// filterByDiscoveredName filters resources that have a "discovered name" label
3855-
// that matches the given name.
3856-
func filterByDiscoveredName[T types.ResourceWithLabels](resources []T, name string) []T {
3857-
return filterResources(resources, func(r T) bool {
3858-
discoveredName, ok := r.GetLabel(types.DiscoveredNameLabel)
3859-
return ok && discoveredName == name
3860-
})
3861-
}
3862-
3863-
// getOneResourceNameToDelete checks a list of resources to ensure there is
3864-
// exactly one resource name among them, and returns that name or an error.
3865-
// Heartbeat resources can have the same name but different host ID, so this
3866-
// still allows a user to delete multiple heartbeats of the same name, for
3867-
// example `$ tctl rm db_server/someDB`.
3868-
func getOneResourceNameToDelete[T types.ResourceWithLabels](rs []T, ref services.Ref, resDesc string) (string, error) {
3869-
seen := make(map[string]struct{})
3870-
for _, r := range rs {
3871-
seen[r.GetName()] = struct{}{}
3872-
}
3873-
switch len(seen) {
3874-
case 1: // need exactly one.
3875-
return rs[0].GetName(), nil
3876-
case 0:
3877-
return "", trace.NotFound("%v %q not found", resDesc, ref.Name)
3878-
default:
3879-
names := make([]string, 0, len(rs))
3880-
for _, r := range rs {
3881-
names = append(names, r.GetName())
3882-
}
3883-
msg := formatAmbiguousDeleteMessage(ref, resDesc, names)
3884-
return "", trace.BadParameter("%s", msg)
3885-
}
3886-
}
3887-
3888-
// formatAmbiguousDeleteMessage returns a formatted message when a user is
3889-
// attempting to delete multiple resources by an ambiguous prefix of the
3890-
// resource names.
3891-
func formatAmbiguousDeleteMessage(ref services.Ref, resDesc string, names []string) string {
3892-
slices.Sort(names)
3893-
// choose an actual resource for the example in the error.
3894-
exampleRef := ref
3895-
exampleRef.Name = names[0]
3896-
return fmt.Sprintf(`%s matches multiple auto-discovered %vs:
3897-
%v
3898-
3899-
Use the full resource name that was generated by the Teleport Discovery service, for example:
3900-
$ tctl rm %s`,
3901-
ref.String(), resDesc, strings.Join(names, "\n"), exampleRef.String())
3902-
}
3903-
39043749
func (rc *ResourceCommand) createAuditQuery(ctx context.Context, client *authclient.Client, raw services.UnknownResource) error {
39053750
in, err := services.UnmarshalAuditQuery(raw.Raw, services.DisallowUnknown())
39063751
if err != nil {

0 commit comments

Comments
 (0)