From b9d44966acf7156c2666cbaaf03a2e05ac9f13fd Mon Sep 17 00:00:00 2001 From: Zac Bergquist Date: Tue, 9 May 2023 13:29:34 -0600 Subject: [PATCH 1/2] User os.UserHomeDir where possible In #24156 we removed a lot of unnecessary calls to user.Current, which can take a long time in Active Directory environments. This commit removes a few other uses of user.Current where we were doing a full user lookup only to find the user's home directory, which is usually more readily available via the environment. --- api/profile/profile.go | 10 +++++++++- lib/client/db/mysql/optionfile.go | 14 ++++++++++---- lib/client/db/postgres/servicefile.go | 14 ++++++++++---- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/api/profile/profile.go b/api/profile/profile.go index a45a3cb3d3f0e..9b0dc273fe04a 100644 --- a/api/profile/profile.go +++ b/api/profile/profile.go @@ -313,7 +313,15 @@ func FullProfilePath(dir string) string { // defaultProfilePath retrieves the default path of the TSH profile. func defaultProfilePath() string { - home := os.TempDir() + // start with UserHomeDir, which is the fastest option as it + // relies only on environment variables and does not perform + // a user lookup (which can be very slow on large AD environments) + home, err := os.UserHomeDir() + if err == nil && home != "" { + return home + } + + home = os.TempDir() if u, err := utils.CurrentUser(); err == nil && u.HomeDir != "" { home = u.HomeDir } diff --git a/lib/client/db/mysql/optionfile.go b/lib/client/db/mysql/optionfile.go index 27557a72bc991..7bcee08192080 100644 --- a/lib/client/db/mysql/optionfile.go +++ b/lib/client/db/mysql/optionfile.go @@ -17,6 +17,7 @@ limitations under the License. package mysql import ( + "os" "path/filepath" "strconv" "strings" @@ -44,11 +45,16 @@ type OptionFile struct { func DefaultConfigPath() (string, error) { // Default location is .my.cnf file in the user's home directory. - usr, err := utils.CurrentUser() - if err != nil { - return "", trace.ConvertSystemError(err) + home, err := os.UserHomeDir() + if err != nil || home == "" { + usr, err := utils.CurrentUser() + if err != nil { + return "", trace.ConvertSystemError(err) + } + home = usr.HomeDir } - return filepath.Join(usr.HomeDir, mysqlOptionFile), nil + + return filepath.Join(home, mysqlOptionFile), nil } // Load loads MySQL option file from the default location. diff --git a/lib/client/db/postgres/servicefile.go b/lib/client/db/postgres/servicefile.go index e23fb07b27877..6361c7d91f25c 100644 --- a/lib/client/db/postgres/servicefile.go +++ b/lib/client/db/postgres/servicefile.go @@ -17,6 +17,7 @@ limitations under the License. package postgres import ( + "os" "path/filepath" "strconv" "strings" @@ -46,11 +47,16 @@ type ServiceFile struct { func Load() (*ServiceFile, error) { // Default location is .pg_service.conf file in the user's home directory. // TODO(r0mant): Check PGSERVICEFILE and PGSYSCONFDIR env vars as well. - user, err := utils.CurrentUser() - if err != nil { - return nil, trace.ConvertSystemError(err) + home, err := os.UserHomeDir() + if err != nil || home == "" { + user, err := utils.CurrentUser() + if err != nil { + return nil, trace.ConvertSystemError(err) + } + home = user.HomeDir } - return LoadFromPath(filepath.Join(user.HomeDir, pgServiceFile)) + + return LoadFromPath(filepath.Join(home, pgServiceFile)) } // LoadFromPath loads Posrtgres connection service file from the specified path. From eb83c4f079463a4eca8d112d4ca6c0be86e7e1a9 Mon Sep 17 00:00:00 2001 From: Zac Bergquist Date: Wed, 10 May 2023 11:15:09 -0600 Subject: [PATCH 2/2] Fix tsh profile dir This fixes a bug introduced in #25950 --- api/profile/profile.go | 2 +- api/profile/profile_test.go | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/api/profile/profile.go b/api/profile/profile.go index 9b0dc273fe04a..d9c5987480cff 100644 --- a/api/profile/profile.go +++ b/api/profile/profile.go @@ -318,7 +318,7 @@ func defaultProfilePath() string { // a user lookup (which can be very slow on large AD environments) home, err := os.UserHomeDir() if err == nil && home != "" { - return home + return filepath.Join(home, profileDir) } home = os.TempDir() diff --git a/api/profile/profile_test.go b/api/profile/profile_test.go index 6d22e436ae655..eab120e15093c 100644 --- a/api/profile/profile_test.go +++ b/api/profile/profile_test.go @@ -20,6 +20,7 @@ package profile_test import ( "os" "path/filepath" + "runtime" "testing" "github.com/gravitational/trace" @@ -102,3 +103,16 @@ func TestAppPath(t *testing.T) { expected := filepath.Join(dir, "keys", "proxy", "testuser-app", "example.com", "banana-x509.pem") require.Equal(t, expected, p.AppCertPath("banana")) } + +func TestProfilePath(t *testing.T) { + switch runtime.GOOS { + case "darwin", "linux": + default: + t.Skip("this test only runs on Unix") + } + dir := t.TempDir() + t.Setenv("HOME", dir) + + require.Equal(t, "/foo/bar", profile.FullProfilePath("/foo/bar")) + require.Equal(t, filepath.Join(dir, ".tsh"), profile.FullProfilePath("")) +}