[#152] authmate: Add basic error types and exit codes

Signed-off-by: Artem Tataurov <a.tataurov@yadro.com>
This commit is contained in:
Artem Tataurov 2023-09-04 21:01:56 +03:00
parent 69227b4845
commit 54e1c333a1
8 changed files with 94 additions and 24 deletions

View file

@ -17,6 +17,7 @@ This document outlines major changes between releases.
- Use correct keys in `list-multipart-uploads` response (#185) - Use correct keys in `list-multipart-uploads` response (#185)
### Added ### Added
- Add basic error types and exit codes to `frostfs-s3-authmate` (#152)
- Add a metric with addresses of nodes of the same and highest priority that are currently healthy (#51) - Add a metric with addresses of nodes of the same and highest priority that are currently healthy (#51)
- Support dump metrics descriptions (#80) - Support dump metrics descriptions (#80)
- Add `copies_numbers` section to `placement_policy` in config file and support vectors of copies numbers (#70, #101) - Add `copies_numbers` section to `placement_policy` in config file and support vectors of copies numbers (#70, #101)

View file

@ -15,6 +15,6 @@ func main() {
if cmd, err := modules.Execute(ctx); err != nil { if cmd, err := modules.Execute(ctx); err != nil {
cmd.PrintErrln("Error:", err.Error()) cmd.PrintErrln("Error:", err.Error())
cmd.PrintErrf("Run '%v --help' for usage.\n", cmd.CommandPath()) cmd.PrintErrf("Run '%v --help' for usage.\n", cmd.CommandPath())
os.Exit(1) os.Exit(modules.ExitCode(err))
} }
} }

View file

@ -0,0 +1,53 @@
package modules
type (
preparationError struct {
err error
}
frostFSInitError struct {
err error
}
businessLogicError struct {
err error
}
)
func wrapPreparationError(e error) error {
return preparationError{e}
}
func (e preparationError) Error() string {
return e.err.Error()
}
func wrapFrostFSInitError(e error) error {
return frostFSInitError{e}
}
func (e frostFSInitError) Error() string {
return e.err.Error()
}
func wrapBusinessLogicError(e error) error {
return businessLogicError{e}
}
func (e businessLogicError) Error() string {
return e.err.Error()
}
// ExitCode picks corresponding error code depending on the type of error provided.
// Returns 1 if error type is unknown.
func ExitCode(e error) int {
switch e.(type) {
case preparationError:
return 2
case frostFSInitError:
return 3
case businessLogicError:
return 4
}
return 1
}

View file

@ -76,7 +76,7 @@ func runGeneratePresignedURLCmd(*cobra.Command, []string) error {
SharedConfigState: session.SharedConfigEnable, SharedConfigState: session.SharedConfigEnable,
}) })
if err != nil { if err != nil {
return fmt.Errorf("couldn't get aws credentials: %w", err) return wrapPreparationError(fmt.Errorf("couldn't get aws credentials: %w", err))
} }
reqData := auth.RequestData{ reqData := auth.RequestData{
@ -94,7 +94,7 @@ func runGeneratePresignedURLCmd(*cobra.Command, []string) error {
req, err := auth.PresignRequest(sess.Config.Credentials, reqData, presignData) req, err := auth.PresignRequest(sess.Config.Credentials, reqData, presignData)
if err != nil { if err != nil {
return err return wrapBusinessLogicError(err)
} }
res := &struct{ URL string }{ res := &struct{ URL string }{
@ -104,5 +104,9 @@ func runGeneratePresignedURLCmd(*cobra.Command, []string) error {
enc := json.NewEncoder(os.Stdout) enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ") enc.SetIndent("", " ")
enc.SetEscapeHTML(false) enc.SetEscapeHTML(false)
return enc.Encode(res) err = enc.Encode(res)
if err != nil {
return wrapBusinessLogicError(err)
}
return nil
} }

View file

@ -92,14 +92,14 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg) password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg)
key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password) key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password)
if err != nil { if err != nil {
return fmt.Errorf("failed to load frostfs private key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load frostfs private key: %s", err))
} }
var cnrID cid.ID var cnrID cid.ID
containerID := viper.GetString(containerIDFlag) containerID := viper.GetString(containerIDFlag)
if len(containerID) > 0 { if len(containerID) > 0 {
if err = cnrID.DecodeString(containerID); err != nil { if err = cnrID.DecodeString(containerID); err != nil {
return fmt.Errorf("failed to parse auth container id: %s", err) return wrapPreparationError(fmt.Errorf("failed to parse auth container id: %s", err))
} }
} }
@ -107,35 +107,35 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
for _, keyStr := range viper.GetStringSlice(gatePublicKeyFlag) { for _, keyStr := range viper.GetStringSlice(gatePublicKeyFlag) {
gpk, err := keys.NewPublicKeyFromString(keyStr) gpk, err := keys.NewPublicKeyFromString(keyStr)
if err != nil { if err != nil {
return fmt.Errorf("failed to load gate's public key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load gate's public key: %s", err))
} }
gatesPublicKeys = append(gatesPublicKeys, gpk) gatesPublicKeys = append(gatesPublicKeys, gpk)
} }
lifetime := viper.GetDuration(lifetimeFlag) lifetime := viper.GetDuration(lifetimeFlag)
if lifetime <= 0 { if lifetime <= 0 {
return fmt.Errorf("lifetime must be greater 0, current value: %d", lifetime) return wrapPreparationError(fmt.Errorf("lifetime must be greater 0, current value: %d", lifetime))
} }
policies, err := parsePolicies(viper.GetString(containerPolicyFlag)) policies, err := parsePolicies(viper.GetString(containerPolicyFlag))
if err != nil { if err != nil {
return fmt.Errorf("couldn't parse container policy: %s", err.Error()) return wrapPreparationError(fmt.Errorf("couldn't parse container policy: %s", err.Error()))
} }
disableImpersonate := viper.GetBool(disableImpersonateFlag) disableImpersonate := viper.GetBool(disableImpersonateFlag)
eaclRules := viper.GetString(bearerRulesFlag) eaclRules := viper.GetString(bearerRulesFlag)
if !disableImpersonate && eaclRules != "" { if !disableImpersonate && eaclRules != "" {
return errors.New("--bearer-rules flag can be used only with --disable-impersonate") return wrapPreparationError(errors.New("--bearer-rules flag can be used only with --disable-impersonate"))
} }
bearerRules, err := getJSONRules(eaclRules) bearerRules, err := getJSONRules(eaclRules)
if err != nil { if err != nil {
return fmt.Errorf("couldn't parse 'bearer-rules' flag: %s", err.Error()) return wrapPreparationError(fmt.Errorf("couldn't parse 'bearer-rules' flag: %s", err.Error()))
} }
sessionRules, skipSessionRules, err := getSessionRules(viper.GetString(sessionTokensFlag)) sessionRules, skipSessionRules, err := getSessionRules(viper.GetString(sessionTokensFlag))
if err != nil { if err != nil {
return fmt.Errorf("couldn't parse 'session-tokens' flag: %s", err.Error()) return wrapPreparationError(fmt.Errorf("couldn't parse 'session-tokens' flag: %s", err.Error()))
} }
poolCfg := PoolConfig{ poolCfg := PoolConfig{
@ -149,7 +149,7 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
frostFS, err := createFrostFS(ctx, log, poolCfg) frostFS, err := createFrostFS(ctx, log, poolCfg)
if err != nil { if err != nil {
return fmt.Errorf("failed to create FrostFS component: %s", err) return wrapFrostFSInitError(fmt.Errorf("failed to create FrostFS component: %s", err))
} }
issueSecretOptions := &authmate.IssueSecretOptions{ issueSecretOptions := &authmate.IssueSecretOptions{
@ -170,7 +170,7 @@ func runIssueSecretCmd(cmd *cobra.Command, _ []string) error {
} }
if err = authmate.New(log, frostFS).IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil { if err = authmate.New(log, frostFS).IssueSecret(ctx, os.Stdout, issueSecretOptions); err != nil {
return fmt.Errorf("failed to issue secret: %s", err) return wrapBusinessLogicError(fmt.Errorf("failed to issue secret: %s", err))
} }
return nil return nil
} }

View file

@ -58,13 +58,13 @@ func runObtainSecretCmd(cmd *cobra.Command, _ []string) error {
password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg) password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg)
key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password) key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password)
if err != nil { if err != nil {
return fmt.Errorf("failed to load frostfs private key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load frostfs private key: %s", err))
} }
gatePassword := wallet.GetPassword(viper.GetViper(), walletGatePassphraseCfg) gatePassword := wallet.GetPassword(viper.GetViper(), walletGatePassphraseCfg)
gateKey, err := wallet.GetKeyFromPath(viper.GetString(gateWalletFlag), viper.GetString(gateAddressFlag), gatePassword) gateKey, err := wallet.GetKeyFromPath(viper.GetString(gateWalletFlag), viper.GetString(gateAddressFlag), gatePassword)
if err != nil { if err != nil {
return fmt.Errorf("failed to load s3 gate private key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load s3 gate private key: %s", err))
} }
poolCfg := PoolConfig{ poolCfg := PoolConfig{
@ -78,7 +78,7 @@ func runObtainSecretCmd(cmd *cobra.Command, _ []string) error {
frostFS, err := createFrostFS(ctx, log, poolCfg) frostFS, err := createFrostFS(ctx, log, poolCfg)
if err != nil { if err != nil {
return cli.Exit(fmt.Sprintf("failed to create FrostFS component: %s", err), 2) return wrapFrostFSInitError(cli.Exit(fmt.Sprintf("failed to create FrostFS component: %s", err), 2))
} }
obtainSecretOptions := &authmate.ObtainSecretOptions{ obtainSecretOptions := &authmate.ObtainSecretOptions{
@ -87,7 +87,7 @@ func runObtainSecretCmd(cmd *cobra.Command, _ []string) error {
} }
if err = authmate.New(log, frostFS).ObtainSecret(ctx, os.Stdout, obtainSecretOptions); err != nil { if err = authmate.New(log, frostFS).ObtainSecret(ctx, os.Stdout, obtainSecretOptions); err != nil {
return fmt.Errorf("failed to obtain secret: %s", err) return wrapBusinessLogicError(fmt.Errorf("failed to obtain secret: %s", err))
} }
return nil return nil

View file

@ -56,26 +56,26 @@ func runUpdateSecretCmd(cmd *cobra.Command, _ []string) error {
password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg) password := wallet.GetPassword(viper.GetViper(), walletPassphraseCfg)
key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password) key, err := wallet.GetKeyFromPath(viper.GetString(walletFlag), viper.GetString(addressFlag), password)
if err != nil { if err != nil {
return fmt.Errorf("failed to load frostfs private key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load frostfs private key: %s", err))
} }
gatePassword := wallet.GetPassword(viper.GetViper(), walletGatePassphraseCfg) gatePassword := wallet.GetPassword(viper.GetViper(), walletGatePassphraseCfg)
gateKey, err := wallet.GetKeyFromPath(viper.GetString(gateWalletFlag), viper.GetString(gateAddressFlag), gatePassword) gateKey, err := wallet.GetKeyFromPath(viper.GetString(gateWalletFlag), viper.GetString(gateAddressFlag), gatePassword)
if err != nil { if err != nil {
return fmt.Errorf("failed to load s3 gate private key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load s3 gate private key: %s", err))
} }
var accessBoxAddress oid.Address var accessBoxAddress oid.Address
credAddr := strings.Replace(viper.GetString(accessKeyIDFlag), "0", "/", 1) credAddr := strings.Replace(viper.GetString(accessKeyIDFlag), "0", "/", 1)
if err = accessBoxAddress.DecodeString(credAddr); err != nil { if err = accessBoxAddress.DecodeString(credAddr); err != nil {
return fmt.Errorf("failed to parse creds address: %w", err) return wrapPreparationError(fmt.Errorf("failed to parse creds address: %w", err))
} }
var gatesPublicKeys []*keys.PublicKey var gatesPublicKeys []*keys.PublicKey
for _, keyStr := range viper.GetStringSlice(gatePublicKeyFlag) { for _, keyStr := range viper.GetStringSlice(gatePublicKeyFlag) {
gpk, err := keys.NewPublicKeyFromString(keyStr) gpk, err := keys.NewPublicKeyFromString(keyStr)
if err != nil { if err != nil {
return fmt.Errorf("failed to load gate's public key: %s", err) return wrapPreparationError(fmt.Errorf("failed to load gate's public key: %s", err))
} }
gatesPublicKeys = append(gatesPublicKeys, gpk) gatesPublicKeys = append(gatesPublicKeys, gpk)
} }
@ -91,7 +91,7 @@ func runUpdateSecretCmd(cmd *cobra.Command, _ []string) error {
frostFS, err := createFrostFS(ctx, log, poolCfg) frostFS, err := createFrostFS(ctx, log, poolCfg)
if err != nil { if err != nil {
return fmt.Errorf("failed to create FrostFS component: %s", err) return wrapFrostFSInitError(fmt.Errorf("failed to create FrostFS component: %s", err))
} }
updateSecretOptions := &authmate.UpdateSecretOptions{ updateSecretOptions := &authmate.UpdateSecretOptions{
@ -102,7 +102,7 @@ func runUpdateSecretCmd(cmd *cobra.Command, _ []string) error {
} }
if err = authmate.New(log, frostFS).UpdateSecret(ctx, os.Stdout, updateSecretOptions); err != nil { if err = authmate.New(log, frostFS).UpdateSecret(ctx, os.Stdout, updateSecretOptions); err != nil {
return fmt.Errorf("failed to update secret: %s", err) return wrapBusinessLogicError(fmt.Errorf("failed to update secret: %s", err))
} }
return nil return nil
} }

View file

@ -27,6 +27,7 @@ potentially).
3. [Obtainment of a secret](#obtaining-credential-secrets) 3. [Obtainment of a secret](#obtaining-credential-secrets)
4. [Generate presigned url](#generate-presigned-url) 4. [Generate presigned url](#generate-presigned-url)
5. [Update secrets](#update-secret) 5. [Update secrets](#update-secret)
6. [Exit codes](#exit-codes)
## Generation of wallet ## Generation of wallet
@ -371,3 +372,14 @@ Enter password for s3-wallet.json >
"container_id": "HwrdXgetdGcEWAQwi68r1PMvw4iSm1Y5Z1fsFNSD6sQP" "container_id": "HwrdXgetdGcEWAQwi68r1PMvw4iSm1Y5Z1fsFNSD6sQP"
} }
``` ```
## Exit codes
There are several non-zero exit codes added at the moment.
| Code | Description |
|-------|--------------------------------------------------------------------------------------------|
| 1 | Any unknown errors, or errors generated by the parser of command line parameters. |
| 2 | Preparation errors: malformed configuration, issues with input data parsing. |
| 3 | FrostFS errors: connectivity problems, misconfiguration. |
| 4 | Business logic errors: `authmate` could not execute its task because of some restrictions. |