Files
vault-tui/internal/vault/errors.go
T
f.weber ae30ba1240 feat(vault-tui): implement KV client and service for managing secrets
- Added internal/vault/client.go for creating a Vault client with configuration settings.
- Introduced internal/vault/errors.go to classify Vault API errors for better UI handling.
- Created internal/vault/kv.go to manage KV secrets, including listing, reading, writing, and deleting operations.
- Implemented internal/vault/mounts.go to list and describe secret engine mounts.
- Developed internal/vault/service.go to provide a unified entry point for Vault operations.
- Added internal/vault/kv_test.go for comprehensive testing of KV operations.
- Introduced internal/ui/toast.go for transient notifications in the UI.
- Added renovate.json for dependency management and updates.
2026-08-14 11:09:03 +02:00

78 lines
1.8 KiB
Go

package vault
import (
"context"
"errors"
"net"
"strings"
"github.com/hashicorp/vault/api"
)
// ErrKind classifies a Vault API error into a small, UI-actionable set.
// See internal/ui's error surfacing tiers: Cancelled is dropped silently,
// NotFound/Forbidden/CAS become a toast plus an inline hint, Unauthorized
// sends the user back to the auth screen, Sealed/Network get a full-body
// retry panel.
type ErrKind uint8
const (
ErrUnknown ErrKind = iota
ErrForbidden
ErrNotFound
ErrUnauthorized
ErrSealed
ErrCAS
ErrNetwork
ErrCancelled
)
// Classify inspects err (typically returned from a Logical() call) and
// returns its kind plus a short human-readable message.
func Classify(err error) (ErrKind, string) {
if err == nil {
return ErrUnknown, ""
}
if errors.Is(err, context.Canceled) {
return ErrCancelled, "cancelled"
}
var respErr *api.ResponseError
if errors.As(err, &respErr) {
msg := strings.Join(respErr.Errors, "; ")
if msg == "" {
msg = err.Error()
}
switch respErr.StatusCode {
case 403:
if isTokenInvalid(msg) {
return ErrUnauthorized, "token is invalid or expired"
}
return ErrForbidden, "permission denied"
case 404:
return ErrNotFound, "not found"
case 400:
if strings.Contains(strings.ToLower(msg), "check-and-set") {
return ErrCAS, "changed underneath you (check-and-set mismatch)"
}
return ErrUnknown, msg
case 503:
return ErrSealed, "vault is sealed or in standby"
default:
return ErrUnknown, msg
}
}
var netErr net.Error
if errors.As(err, &netErr) {
return ErrNetwork, netErr.Error()
}
return ErrUnknown, err.Error()
}
func isTokenInvalid(msg string) bool {
m := strings.ToLower(msg)
return strings.Contains(m, "permission denied") && strings.Contains(m, "token")
}