Skip to content
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
6 changes: 6 additions & 0 deletions api/types/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,12 @@ const (
// See also TeleportNamespace and TeleportInternalLabelPrefix.
TeleportHiddenLabelPrefix = "teleport.hidden/"

// DiscoveredNameLabel is a resource metadata label name used to identify
// the discovered name of a resource, i.e. the name of a resource before a
// uniquely distinguishing suffix is added by the discovery service.
// See: RFD 129 - Avoid Discovery Resource Name Collisions.
DiscoveredNameLabel = TeleportInternalLabelPrefix + "discovered-name"

// BotLabel is a label used to identify a resource used by a certificate renewal bot.
BotLabel = TeleportInternalLabelPrefix + "bot"

Expand Down
1 change: 1 addition & 0 deletions docs/pages/reference/predicate-language.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ The language also supports the following functions:
| `exists(labels["env"])` | resources with a label key `env`; label value unchecked |
| `!exists(labels["env"])` | resources without a label key `env`; label value unchecked |
| `search("foo", "bar", "some phrase")` | fuzzy match against common resource fields |
| `hasPrefix(name, "foo")` | resources with a name that starts with the prefix `foo` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What kind of backwards compatibility consequences does adding this new function have?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if the tsh version is newer than auth/proxy version, then tsh will get a predicate error when it tries to use it. For that reason, I have logic in tsh that detects this scenario and falls back to just filtering resources client side without using hasPrefix predicate to make the API list resources request.


See some [examples](cli.mdx#filter-examples) of the different ways you can filter resources.

Expand Down
2 changes: 1 addition & 1 deletion lib/client/db/dbcmd/dbcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ func (c *CLICommandBuilder) getMySQLOracleCommand() (*exec.Cmd, error) {
// We save configuration to ~/.my.cnf, but on Windows that file is not read,
// see tables 4.1 and 4.2 on https://dev.mysql.com/doc/refman/8.0/en/option-files.html.
// We instruct mysql client to use use that file with --defaults-extra-file.
configPath, err := mysql.DefaultConfigPath()
configPath, err := mysql.DefaultConfigPath(c.tc.HomePath)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
13 changes: 9 additions & 4 deletions lib/client/db/mysql/optionfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ type OptionFile struct {
path string
}

func DefaultConfigPath() (string, error) {
// Default location is .my.cnf file in the user's home directory.
// DefaultConfigPath returns the default config path, which is .my.cnf file in
// the user's home directory. Home dir is determined by environment if not
// supplied as an argument.
func DefaultConfigPath(home string) (string, error) {
if home != "" {
return filepath.Join(home, mysqlOptionFile), nil
}
home, err := os.UserHomeDir()
if err != nil || home == "" {
usr, err := utils.CurrentUser()
Expand All @@ -58,8 +63,8 @@ func DefaultConfigPath() (string, error) {
}

// Load loads MySQL option file from the default location.
func Load() (*OptionFile, error) {
cnfPath, err := DefaultConfigPath()
func Load(home string) (*OptionFile, error) {
cnfPath, err := DefaultConfigPath(home)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
28 changes: 21 additions & 7 deletions lib/client/db/postgres/servicefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,37 @@ type ServiceFile struct {
path string
}

// Load loads Postgres connection service file from the default location.
func Load() (*ServiceFile, error) {
// DefaultConfigPath returns the default config path, which is .pg_service.conf
// file in the user's home directory. Home dir is determined by environment if
// not supplied as an argument.
func defaultConfigPath(home string) (string, error) {
// Default location is .pg_service.conf file in the user's home directory.
// TODO(r0mant): Check PGSERVICEFILE and PGSYSCONFDIR env vars as well.
if home != "" {
return filepath.Join(home, pgServiceFile), nil
}
home, err := os.UserHomeDir()
if err != nil || home == "" {
user, err := utils.CurrentUser()
usr, err := utils.CurrentUser()
if err != nil {
return nil, trace.ConvertSystemError(err)
return "", trace.ConvertSystemError(err)
}
home = user.HomeDir
home = usr.HomeDir
}

return LoadFromPath(filepath.Join(home, pgServiceFile))
return filepath.Join(home, pgServiceFile), nil
}

// Load loads Postgres connection service file from the default location.
func Load(home string) (*ServiceFile, error) {
cnfPath, err := defaultConfigPath(home)
if err != nil {
return nil, trace.Wrap(err)
}
return LoadFromPath(cnfPath)
}

// LoadFromPath loads Posrtgres connection service file from the specified path.
// LoadFromPath loads Postgres connection service file from the specified path.
func LoadFromPath(path string) (*ServiceFile, error) {
// Loose load will ignore file not found error.
iniFile, err := ini.LooseLoad(path)
Expand Down
12 changes: 6 additions & 6 deletions lib/client/db/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func Add(ctx context.Context, tc *client.TeleportClient, db tlsca.RouteToDatabas
if !IsSupported(db) {
return nil
}
profileFile, err := load(db)
profileFile, err := load(tc, db)
if err != nil {
return trace.Wrap(err)
}
Expand Down Expand Up @@ -98,7 +98,7 @@ func New(tc *client.TeleportClient, db tlsca.RouteToDatabase, clientProfile clie

// Env returns environment variables for the specified database profile.
func Env(tc *client.TeleportClient, db tlsca.RouteToDatabase) (map[string]string, error) {
profileFile, err := load(db)
profileFile, err := load(tc, db)
if err != nil {
return nil, trace.Wrap(err)
}
Expand All @@ -114,7 +114,7 @@ func Delete(tc *client.TeleportClient, db tlsca.RouteToDatabase) error {
if !IsSupported(db) {
return nil
}
profileFile, err := load(db)
profileFile, err := load(tc, db)
if err != nil {
return trace.Wrap(err)
}
Expand All @@ -138,12 +138,12 @@ func IsSupported(db tlsca.RouteToDatabase) bool {
}

// load loads the appropriate database connection profile.
func load(db tlsca.RouteToDatabase) (profile.ConnectProfileFile, error) {
func load(tc *client.TeleportClient, db tlsca.RouteToDatabase) (profile.ConnectProfileFile, error) {
switch db.Protocol {
case defaults.ProtocolPostgres:
return postgres.Load()
return postgres.Load(tc.HomePath)
case defaults.ProtocolMySQL:
return mysql.Load()
return mysql.Load(tc.HomePath)
}
return nil, trace.BadParameter("unsupported database protocol %q",
db.Protocol)
Expand Down
19 changes: 18 additions & 1 deletion lib/services/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,22 @@ func NewResourceParser(resource types.ResourceWithLabels) (BoolPredicateParser,
return predicate.Equals(a, b)
}
}
predPrefix := func(a interface{}, prefix string) predicate.BoolPredicate {
switch aval := a.(type) {
case label:
return func() bool {
return strings.HasPrefix(aval.value, prefix)
}
case string:
return func() bool {
return strings.HasPrefix(aval, prefix)
}
default:
return func() bool {
return false
}
}
}

p, err := predicate.NewParser(predicate.Def{
Operators: predicate.Operators{
Expand All @@ -753,7 +769,8 @@ func NewResourceParser(resource types.ResourceWithLabels) (BoolPredicateParser,
},
},
Functions: map[string]interface{}{
"equals": predEquals,
"hasPrefix": predPrefix,
"equals": predEquals,
// search allows fuzzy matching against select field values.
"search": func(searchVals ...string) predicate.BoolPredicate {
return func() bool {
Expand Down
11 changes: 11 additions & 0 deletions lib/services/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ func TestNewResourceParser(t *testing.T) {
`search("os", "mac", "prod")`,
`search()`,
`!search("_")`,
// Test hasPrefix.
`hasPrefix(name, "")`,
`hasPrefix(name, "test-h")`,
`!hasPrefix(name, "foo")`,
`hasPrefix(resource.metadata.labels["env"], "pro")`,
// Test exists.
`exists(labels.env)`,
`!exists(labels.undefined)`,
Expand All @@ -206,6 +211,7 @@ func TestNewResourceParser(t *testing.T) {
`labels.os == "mac" && name == "test-hostname" && search("v8")`,
`exists(labels.env) && labels["env"] != "qa"`,
`search("does", "not", "exist") || resource.spec.addr == "_" || labels.version == "v8"`,
`hasPrefix(labels.os, "m") && !hasPrefix(labels.env, "dev") && name == "test-hostname"`,
// Test operator precedence
`exists(labels.env) || (exists(labels.os) && labels.os != "mac")`,
`exists(labels.env) || exists(labels.os) && labels.os != "mac"`,
Expand Down Expand Up @@ -233,6 +239,7 @@ func TestNewResourceParser(t *testing.T) {
`equals(resource.metadata.labels["env"], "wrong-value")`,
`equals(resource.spec.hostname, "wrong-value")`,
`search("mac", "not-found")`,
`hasPrefix(name, "x")`,
}
for _, expr := range exprs {
t.Run(expr, func(t *testing.T) {
Expand Down Expand Up @@ -269,6 +276,10 @@ func TestNewResourceParser(t *testing.T) {
`exists(labels.env, "too", "many")`,
`search(1,2)`,
`"just-string"`,
`hasPrefix(1, 2)`,
`hasPrefix(name)`,
`hasPrefix(name, 1)`,
`hasPrefix(name, "too", "many")`,
"",
}
for _, expr := range exprs {
Expand Down
39 changes: 39 additions & 0 deletions tool/teleport/testenv/test_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/api/utils/keys"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/backend"
"github.com/gravitational/teleport/lib/cloud"
"github.com/gravitational/teleport/lib/defaults"
Expand All @@ -60,6 +61,44 @@ func init() {
modules.SetModules(&cliModules{})
}

// WithInsecureDevMode is a test helper that sets insecure dev mode and resets
// it in test cleanup.
// It is NOT SAFE to use in parallel tests, because it modifies a global.
// To run insecure dev mode tests in parallel, group them together under a
// parent test and then run them as parallel subtests.
// and call WithInsecureDevMode before running all the tests in parallel.
func WithInsecureDevMode(t *testing.T, mode bool) {
originalValue := lib.IsInsecureDevMode()
lib.SetInsecureDevMode(mode)
// To detect tests that run in parallel incorrectly, call t.Setenv with a
// dummy env var - that function detects tests with parallel ancestors
// and panics, preventing improper use of this helper.
t.Setenv("WithInsecureDevMode", "1")
t.Cleanup(func() {
lib.SetInsecureDevMode(originalValue)
})
}

// WithResyncInterval is a test helper that sets the tunnel resync interval and
// resets it in test cleanup.
// Useful to substantially speedup test cluster setup - passing 0 for the
// interval selects a reasonably fast default of 100ms.
// It is NOT SAFE to use in parallel tests, because it modifies a global.
func WithResyncInterval(t *testing.T, interval time.Duration) {
if interval == 0 {
interval = time.Millisecond * 100
}
oldResyncInterval := defaults.ResyncInterval
defaults.ResyncInterval = interval
// To detect tests that run in parallel incorrectly, call t.Setenv with a
// dummy env var - that function detects tests with parallel ancestors
// and panics, preventing improper use of this helper.
t.Setenv("WithResyncInterval", "1")
t.Cleanup(func() {
defaults.ResyncInterval = oldResyncInterval
})
}

// MakeTestServer creates a Teleport Server for testing.
func MakeTestServer(t *testing.T, opts ...TestServerOptFunc) (process *service.TeleportProcess) {
t.Helper()
Expand Down
Loading