From b40f7e66de5735c6bee6ff90a700b083e45f7b03 Mon Sep 17 00:00:00 2001 From: Brian Bockelman Date: Thu, 24 Jul 2025 17:05:08 -0500 Subject: [PATCH 1/3] Improve the error messages for when the user provides a bad password Captures the underlying package's text-based error and converts it to a first-class named error, allowing us to test with `errors.Is(...)`. --- cmd/config_mgr.go | 20 +++++++++++++++++++- cmd/object_copy.go | 13 ++++++++++--- cmd/object_delete.go | 9 ++++++++- cmd/object_get.go | 7 +++++++ cmd/object_ls.go | 6 ++++++ cmd/object_prestage.go | 7 +++++++ cmd/object_put.go | 7 +++++++ cmd/object_stat.go | 6 ++++++ cmd/object_sync.go | 7 +++++++ config/encrypted.go | 18 ++++++++++++++++++ 10 files changed, 95 insertions(+), 5 deletions(-) diff --git a/cmd/config_mgr.go b/cmd/config_mgr.go index 4730396d9..6655a8cc0 100644 --- a/cmd/config_mgr.go +++ b/cmd/config_mgr.go @@ -23,6 +23,7 @@ import ( "net/http" "os" + "github.com/pkg/errors" "github.com/spf13/cobra" "gopkg.in/yaml.v3" @@ -101,12 +102,29 @@ func addConfigSubcommands(configCmd *cobra.Command) { Run: func(cmd *cobra.Command, args []string) { err := config.ResetPassword() if err != nil { - fmt.Fprintln(os.Stderr, "Failed to get reset password:", err) + if errors.Is(err, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to reset password - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + } else { + fmt.Fprintln(os.Stderr, "Failed to reset password:", err) + } os.Exit(1) } }, }) + configCmd.AddCommand(&cobra.Command{ + Use: "reset-local", + Short: "Delete all local credentials for the current user", + Long: "Delete all local credentials for the current user", + Run: func(cmd *cobra.Command, args []string) { + if err := config.DeleteCredentials(); err != nil { + fmt.Fprintln(os.Stderr, "Failed to delete local credentials:", err) + os.Exit(1) + } + }, + }) } func printOauthConfig() { diff --git a/cmd/object_copy.go b/cmd/object_copy.go index 0090b3571..638e92cc3 100644 --- a/cmd/object_copy.go +++ b/cmd/object_copy.go @@ -19,6 +19,7 @@ package main import ( + "fmt" "os" "path/filepath" "strings" @@ -177,10 +178,16 @@ func copyMain(cmd *cobra.Command, args []string) { // Print the list of errors errMsg := result.Error() var te *client.TransferErrors - if errors.As(result, &te) { - errMsg = te.UserError() + if errors.Is(result, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + } else { + if errors.As(result, &te) { + errMsg = te.UserError() + } + log.Errorln("Failure transferring " + lastSrc + ": " + errMsg) } - log.Errorln("Failure transferring " + lastSrc + ": " + errMsg) if client.ShouldRetry(err) { log.Errorln("Errors are retryable") os.Exit(11) diff --git a/cmd/object_delete.go b/cmd/object_delete.go index 9ce99356a..9f7fbbe3d 100644 --- a/cmd/object_delete.go +++ b/cmd/object_delete.go @@ -22,6 +22,7 @@ import ( "fmt" "os" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" @@ -80,7 +81,13 @@ func deleteMain(cmd *cobra.Command, args []string) error { err = client.DoDelete(ctx, remoteDestination, isRecursive, client.WithTokenLocation(tokenLocation)) if err != nil { - log.Errorf("Failure deleting %s: %v", remoteDestination, err.Error()) + if errors.Is(err, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + } else { + log.Errorf("Failure deleting %s: %v", remoteDestination, err.Error()) + } os.Exit(1) } diff --git a/cmd/object_get.go b/cmd/object_get.go index 485b735ee..e90672287 100644 --- a/cmd/object_get.go +++ b/cmd/object_get.go @@ -20,6 +20,7 @@ package main import ( "encoding/json" + "fmt" "os" "github.com/pkg/errors" @@ -136,6 +137,12 @@ func getMain(cmd *cobra.Command, args []string) { if errors.As(attemptErr, &te) { errMsg = te.UserError() } + if errors.Is(attemptErr, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + os.Exit(1) + } if errors.Is(attemptErr, &pe) { errMsg = pe.Error() log.Errorln("Failure getting " + lastSrc + ": " + errMsg) diff --git a/cmd/object_ls.go b/cmd/object_ls.go index e718d9e68..0cc9c9734 100644 --- a/cmd/object_ls.go +++ b/cmd/object_ls.go @@ -100,6 +100,12 @@ func listMain(cmd *cobra.Command, args []string) error { // Print the list of errors errMsg := err.Error() var te *client.TransferErrors + if errors.Is(err, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + os.Exit(1) + } if errors.As(err, &te) { errMsg = te.UserError() } diff --git a/cmd/object_prestage.go b/cmd/object_prestage.go index 7757a52f1..3a3b59e32 100644 --- a/cmd/object_prestage.go +++ b/cmd/object_prestage.go @@ -19,6 +19,7 @@ package main import ( + "fmt" "os" "github.com/pkg/errors" @@ -115,6 +116,12 @@ func prestageMain(cmd *cobra.Command, args []string) { errMsg := err.Error() var pe error_codes.PelicanError var te *client.TransferErrors + if errors.Is(err, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + os.Exit(1) + } if errors.As(err, &te) { errMsg = te.UserError() } diff --git a/cmd/object_put.go b/cmd/object_put.go index 2c4aec4bc..82438f42a 100644 --- a/cmd/object_put.go +++ b/cmd/object_put.go @@ -24,6 +24,7 @@ import ( "crypto/sha1" "encoding/hex" "encoding/json" + "fmt" "hash" "hash/crc32" "io" @@ -246,6 +247,12 @@ func putMain(cmd *cobra.Command, args []string) { // Exit with failure if result != nil { // Print the list of errors + if errors.Is(result, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + os.Exit(1) + } errMsg := result.Error() var te *client.TransferErrors if errors.As(result, &te) { diff --git a/cmd/object_stat.go b/cmd/object_stat.go index 24de8e7e9..c477736bb 100644 --- a/cmd/object_stat.go +++ b/cmd/object_stat.go @@ -107,6 +107,12 @@ func statMain(cmd *cobra.Command, args []string) { // Exit with failure if err != nil { // Print the list of errors + if errors.Is(err, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + os.Exit(1) + } errMsg := err.Error() var te *client.TransferErrors if errors.As(err, &te) { diff --git a/cmd/object_sync.go b/cmd/object_sync.go index a0db6bdfa..859e4a0a8 100644 --- a/cmd/object_sync.go +++ b/cmd/object_sync.go @@ -19,6 +19,7 @@ package main import ( + "fmt" "net/url" "os" "strings" @@ -197,6 +198,12 @@ func syncMain(cmd *cobra.Command, args []string) { // Exit with failure if err != nil { // Print the list of errors + if errors.Is(err, config.ErrIncorrectPassword) { + fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + os.Exit(1) + } errMsg := err.Error() var pe error_codes.PelicanError var te *client.TransferErrors diff --git a/config/encrypted.go b/config/encrypted.go index 6bea56804..afb961a84 100644 --- a/config/encrypted.go +++ b/config/encrypted.go @@ -33,6 +33,7 @@ import ( "io" "os" "path/filepath" + "strings" log "github.com/sirupsen/logrus" "github.com/spf13/viper" @@ -48,6 +49,8 @@ import ( // the password again later. var setEmptyPassword = false +var ErrIncorrectPassword = errors.New("incorrect password") + func GetEncryptedConfigName() (string, error) { configDir := viper.GetString("ConfigDir") if GetPreferredPrefix() == PelicanPrefix || IsRootExecution() { @@ -79,6 +82,18 @@ func EncryptedConfigExists() (bool, error) { return true, nil } +// Delete the user's local credential file +func DeleteCredentials() error { + filename, err := GetEncryptedConfigName() + if err != nil { + return err + } + if err = os.Remove(filename); errors.Is(err, os.ErrNotExist) { + return nil + } + return err +} + // Return the PEM-formatted contents of the encrypted configuration file func GetEncryptedContents() (string, error) { filename, err := GetEncryptedConfigName() @@ -242,6 +257,9 @@ func GetCredentialConfigContents() (OSDFConfig, error) { return config, errors.New("Encrypted key present; must have non-empty password") } if key, err = pkcs8.ParsePKCS8PrivateKey(block.Bytes, password); err != nil { + if strings.Contains(err.Error(), "pkcs8: incorrect password") { + err = ErrIncorrectPassword + } return config, err } if typedPassword { From a7d5097380b60afebba6ae75f425c54a16a1c1e6 Mon Sep 17 00:00:00 2001 From: Howard Zhong Date: Mon, 5 Jan 2026 23:29:58 +0000 Subject: [PATCH 2/3] =?UTF-8?q?Centralize=20the=20incorrect=E2=80=91passwo?= =?UTF-8?q?rd=20handler=20-=20Swap=20repeated=20password=E2=80=91error=20b?= =?UTF-8?q?locks=20to=20helper=20calls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/cmd_utils.go | 22 ++++++++++++++++++++++ cmd/config_mgr.go | 10 +++------- cmd/object_copy.go | 16 ++++++---------- cmd/object_delete.go | 13 ++++--------- cmd/object_get.go | 10 +++------- cmd/object_ls.go | 9 +++------ cmd/object_prestage.go | 10 +++------- cmd/object_put.go | 8 ++------ cmd/object_stat.go | 7 ++----- cmd/object_sync.go | 8 ++------ 10 files changed, 50 insertions(+), 63 deletions(-) diff --git a/cmd/cmd_utils.go b/cmd/cmd_utils.go index 90c5ac711..6936f0435 100644 --- a/cmd/cmd_utils.go +++ b/cmd/cmd_utils.go @@ -19,13 +19,16 @@ package main import ( + "fmt" "net/url" + "os" "strings" "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/spf13/viper" + "github.com/pelicanplatform/pelican/config" "github.com/pelicanplatform/pelican/param" ) @@ -58,3 +61,22 @@ func getPreferredCaches() ([]*url.URL, error) { return caches, nil } + +const ( + incorrectPasswordAccessMessage = "Failed to access local credential file - entered incorrect local decryption password" + incorrectPasswordResetMessage = "Failed to reset password - entered incorrect local decryption password" +) + +func handleIncorrectPassword(err error, actionMessage string) bool { + if err == nil || !errors.Is(err, config.ErrIncorrectPassword) { + return false + } + fmt.Fprintln(os.Stderr, actionMessage) + fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") + fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + return true +} + +func handleCredentialPasswordError(err error) bool { + return handleIncorrectPassword(err, incorrectPasswordAccessMessage) +} diff --git a/cmd/config_mgr.go b/cmd/config_mgr.go index 6655a8cc0..86153d788 100644 --- a/cmd/config_mgr.go +++ b/cmd/config_mgr.go @@ -23,7 +23,6 @@ import ( "net/http" "os" - "github.com/pkg/errors" "github.com/spf13/cobra" "gopkg.in/yaml.v3" @@ -102,13 +101,10 @@ func addConfigSubcommands(configCmd *cobra.Command) { Run: func(cmd *cobra.Command, args []string) { err := config.ResetPassword() if err != nil { - if errors.Is(err, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to reset password - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) - } else { - fmt.Fprintln(os.Stderr, "Failed to reset password:", err) + if handleIncorrectPassword(err, incorrectPasswordResetMessage) { + os.Exit(1) } + fmt.Fprintln(os.Stderr, "Failed to reset password:", err) os.Exit(1) } }, diff --git a/cmd/object_copy.go b/cmd/object_copy.go index 638e92cc3..00b216bb2 100644 --- a/cmd/object_copy.go +++ b/cmd/object_copy.go @@ -19,7 +19,6 @@ package main import ( - "fmt" "os" "path/filepath" "strings" @@ -175,19 +174,16 @@ func copyMain(cmd *cobra.Command, args []string) { // Exit with failure if result != nil { + if handleCredentialPasswordError(result) { + os.Exit(1) + } // Print the list of errors errMsg := result.Error() var te *client.TransferErrors - if errors.Is(result, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) - } else { - if errors.As(result, &te) { - errMsg = te.UserError() - } - log.Errorln("Failure transferring " + lastSrc + ": " + errMsg) + if errors.As(result, &te) { + errMsg = te.UserError() } + log.Errorln("Failure transferring " + lastSrc + ": " + errMsg) if client.ShouldRetry(err) { log.Errorln("Errors are retryable") os.Exit(11) diff --git a/cmd/object_delete.go b/cmd/object_delete.go index 9f7fbbe3d..dd865dadc 100644 --- a/cmd/object_delete.go +++ b/cmd/object_delete.go @@ -20,11 +20,9 @@ package main import ( "fmt" - "os" - - "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "os" "github.com/pelicanplatform/pelican/client" "github.com/pelicanplatform/pelican/config" @@ -81,13 +79,10 @@ func deleteMain(cmd *cobra.Command, args []string) error { err = client.DoDelete(ctx, remoteDestination, isRecursive, client.WithTokenLocation(tokenLocation)) if err != nil { - if errors.Is(err, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) - } else { - log.Errorf("Failure deleting %s: %v", remoteDestination, err.Error()) + if handleCredentialPasswordError(err) { + os.Exit(1) } + log.Errorf("Failure deleting %s: %v", remoteDestination, err.Error()) os.Exit(1) } diff --git a/cmd/object_get.go b/cmd/object_get.go index e90672287..7d5784acd 100644 --- a/cmd/object_get.go +++ b/cmd/object_get.go @@ -20,7 +20,6 @@ package main import ( "encoding/json" - "fmt" "os" "github.com/pkg/errors" @@ -131,18 +130,15 @@ func getMain(cmd *cobra.Command, args []string) { // Exit with failure if attemptErr != nil { // Print the list of errors + if handleCredentialPasswordError(attemptErr) { + os.Exit(1) + } errMsg := attemptErr.Error() var pe error_codes.PelicanError var te *client.TransferErrors if errors.As(attemptErr, &te) { errMsg = te.UserError() } - if errors.Is(attemptErr, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) - os.Exit(1) - } if errors.Is(attemptErr, &pe) { errMsg = pe.Error() log.Errorln("Failure getting " + lastSrc + ": " + errMsg) diff --git a/cmd/object_ls.go b/cmd/object_ls.go index 0cc9c9734..a2bfa7255 100644 --- a/cmd/object_ls.go +++ b/cmd/object_ls.go @@ -97,15 +97,12 @@ func listMain(cmd *cobra.Command, args []string) error { // Exit with failure if err != nil { + if handleCredentialPasswordError(err) { + os.Exit(1) + } // Print the list of errors errMsg := err.Error() var te *client.TransferErrors - if errors.Is(err, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) - os.Exit(1) - } if errors.As(err, &te) { errMsg = te.UserError() } diff --git a/cmd/object_prestage.go b/cmd/object_prestage.go index 3a3b59e32..e3f321286 100644 --- a/cmd/object_prestage.go +++ b/cmd/object_prestage.go @@ -19,7 +19,6 @@ package main import ( - "fmt" "os" "github.com/pkg/errors" @@ -113,15 +112,12 @@ func prestageMain(cmd *cobra.Command, args []string) { // Exit with failure if err != nil { // Print the list of errors + if handleCredentialPasswordError(err) { + os.Exit(1) + } errMsg := err.Error() var pe error_codes.PelicanError var te *client.TransferErrors - if errors.Is(err, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) - os.Exit(1) - } if errors.As(err, &te) { errMsg = te.UserError() } diff --git a/cmd/object_put.go b/cmd/object_put.go index 82438f42a..8d67bd34b 100644 --- a/cmd/object_put.go +++ b/cmd/object_put.go @@ -24,7 +24,6 @@ import ( "crypto/sha1" "encoding/hex" "encoding/json" - "fmt" "hash" "hash/crc32" "io" @@ -246,13 +245,10 @@ func putMain(cmd *cobra.Command, args []string) { // Exit with failure if result != nil { - // Print the list of errors - if errors.Is(result, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + if handleCredentialPasswordError(result) { os.Exit(1) } + // Print the list of errors errMsg := result.Error() var te *client.TransferErrors if errors.As(result, &te) { diff --git a/cmd/object_stat.go b/cmd/object_stat.go index c477736bb..fdb430653 100644 --- a/cmd/object_stat.go +++ b/cmd/object_stat.go @@ -106,13 +106,10 @@ func statMain(cmd *cobra.Command, args []string) { // Exit with failure if err != nil { - // Print the list of errors - if errors.Is(err, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + if handleCredentialPasswordError(err) { os.Exit(1) } + // Print the list of errors errMsg := err.Error() var te *client.TransferErrors if errors.As(err, &te) { diff --git a/cmd/object_sync.go b/cmd/object_sync.go index 859e4a0a8..ced91123d 100644 --- a/cmd/object_sync.go +++ b/cmd/object_sync.go @@ -19,7 +19,6 @@ package main import ( - "fmt" "net/url" "os" "strings" @@ -197,13 +196,10 @@ func syncMain(cmd *cobra.Command, args []string) { // Exit with failure if err != nil { - // Print the list of errors - if errors.Is(err, config.ErrIncorrectPassword) { - fmt.Fprintln(os.Stderr, "Failed to access local credential file - entered incorrect local decryption password") - fmt.Fprintln(os.Stderr, "If you have forgotten your password, you can reset the local state (deleting all on-disk credentials)") - fmt.Fprintf(os.Stderr, "by running '%s credentials reset-local'\n", os.Args[0]) + if handleCredentialPasswordError(err) { os.Exit(1) } + // Print the list of errors errMsg := err.Error() var pe error_codes.PelicanError var te *client.TransferErrors From 86d9a19e24929e853a2e7906128218478efacb52 Mon Sep 17 00:00:00 2001 From: Howard Zhong Date: Mon, 5 Jan 2026 23:47:33 +0000 Subject: [PATCH 3/3] Fix linter error --- cmd/object_delete.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/object_delete.go b/cmd/object_delete.go index dd865dadc..474b8269c 100644 --- a/cmd/object_delete.go +++ b/cmd/object_delete.go @@ -20,9 +20,10 @@ package main import ( "fmt" + "os" + log "github.com/sirupsen/logrus" "github.com/spf13/cobra" - "os" "github.com/pelicanplatform/pelican/client" "github.com/pelicanplatform/pelican/config"