fix: use typed aws errors

This commit is contained in:
Julian Sommer
2026-02-11 10:11:37 +10:00
parent e95c2536da
commit 221a1ef258
20 changed files with 100 additions and 85 deletions
+1 -1
View File
@@ -40,7 +40,7 @@
- use more secure crypto, can we stop using sha1 or is required for aws?
- centralise random gen ids, all scattered
- make runinstances nats naming be consistent with others - ec2.RunInstances
- split up utils.go, eg conversions can have their own file
- split up utils.go
### Issues to investigate
+1 -1
View File
@@ -180,7 +180,7 @@ func (gw *GatewayConfig) EC2_Request(ctx *fiber.Ctx) error {
action := queryArgs["Action"]
handler, ok := ec2Actions[action]
if !ok {
return errors.New("InvalidAction")
return errors.New(awserrors.ErrorInvalidAction)
}
if gw.NATSConn == nil && !ec2LocalActions[action] {
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_image "github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/nats-io/nats.go"
)
@@ -18,7 +19,7 @@ func ValidateDescribeImagesInput(input *ec2.DescribeImagesInput) (err error) {
if input.ImageIds != nil {
for _, imageId := range input.ImageIds {
if imageId != nil && !strings.HasPrefix(*imageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
return errors.New(awserrors.ErrorInvalidAMIIDMalformed)
}
}
}
@@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/stretchr/testify/assert"
)
@@ -30,7 +31,7 @@ func TestValidateDescribeImagesInput(t *testing.T) {
input: &ec2.DescribeImagesInput{
ImageIds: []*string{aws.String("invalid-id")},
},
want: errors.New("InvalidAMIID.Malformed"),
want: errors.New(awserrors.ErrorInvalidAMIIDMalformed),
},
{
name: "ValidInput",
+9 -8
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_instance "github.com/mulgadc/hive/hive/handlers/ec2/instance"
"github.com/nats-io/nats.go"
)
@@ -15,36 +16,36 @@ type RunInstancesResponse struct {
func ValidateRunInstancesInput(input *ec2.RunInstancesInput) (err error) {
if input == nil {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if input.MinCount == nil {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if *input.MinCount == 0 {
return errors.New("InvalidParameterValue")
return errors.New(awserrors.ErrorInvalidParameterValue)
}
if *input.MaxCount == 0 {
return errors.New("InvalidParameterValue")
return errors.New(awserrors.ErrorInvalidParameterValue)
}
// Additional validation from EC2 spec
if *input.MinCount > *input.MaxCount {
return errors.New("InvalidParameterValue")
return errors.New(awserrors.ErrorInvalidParameterValue)
}
if input.ImageId == nil || *input.ImageId == "" {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if input.InstanceType == nil || *input.InstanceType == "" {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if !strings.HasPrefix(*input.ImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
return errors.New(awserrors.ErrorInvalidAMIIDMalformed)
}
@@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/stretchr/testify/assert"
)
@@ -41,7 +42,7 @@ func TestParseRunInstances(t *testing.T) {
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidParameterValue"),
want: errors.New(awserrors.ErrorInvalidParameterValue),
},
{
@@ -55,7 +56,7 @@ func TestParseRunInstances(t *testing.T) {
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidParameterValue"),
want: errors.New(awserrors.ErrorInvalidParameterValue),
},
{
@@ -69,7 +70,7 @@ func TestParseRunInstances(t *testing.T) {
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidParameterValue"),
want: errors.New(awserrors.ErrorInvalidParameterValue),
},
{
@@ -83,7 +84,7 @@ func TestParseRunInstances(t *testing.T) {
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
@@ -97,7 +98,7 @@ func TestParseRunInstances(t *testing.T) {
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
@@ -111,7 +112,7 @@ func TestParseRunInstances(t *testing.T) {
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidAMIID.Malformed"),
want: errors.New(awserrors.ErrorInvalidAMIIDMalformed),
},
// Successful test
+3 -2
View File
@@ -5,17 +5,18 @@ import (
"log/slog"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_key "github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/nats-io/nats.go"
)
func ValidateCreateKeyPairInput(input *ec2.CreateKeyPairInput) (err error) {
if input == nil {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if input.KeyName == nil || *input.KeyName == "" {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
return
+3 -2
View File
@@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
)
func TestValidateCreateKeyPairInput(t *testing.T) {
@@ -17,14 +18,14 @@ func TestValidateCreateKeyPairInput(t *testing.T) {
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "MissingKeyName",
input: &ec2.CreateKeyPairInput{
KeyName: aws.String(""),
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "ValidInput",
+3 -2
View File
@@ -5,18 +5,19 @@ import (
"log/slog"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_key "github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/nats-io/nats.go"
)
func ValidateDeleteKeyPairInput(input *ec2.DeleteKeyPairInput) (err error) {
if input == nil {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
// At least one of KeyName or KeyPairId must be provided
if (input.KeyName == nil || *input.KeyName == "") && (input.KeyPairId == nil || *input.KeyPairId == "") {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
return
+3 -2
View File
@@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
)
func TestValidateDeleteKeyPairInput(t *testing.T) {
@@ -17,7 +18,7 @@ func TestValidateDeleteKeyPairInput(t *testing.T) {
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "MissingBothKeyNameAndKeyPairId",
@@ -25,7 +26,7 @@ func TestValidateDeleteKeyPairInput(t *testing.T) {
KeyName: aws.String(""),
KeyPairId: aws.String(""),
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "ValidInputWithKeyName",
+4 -3
View File
@@ -4,21 +4,22 @@ import (
"errors"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_key "github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/nats-io/nats.go"
)
func ValidateImportKeyPairInput(input *ec2.ImportKeyPairInput) (err error) {
if input == nil {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if input.KeyName == nil || *input.KeyName == "" {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
if len(input.PublicKeyMaterial) == 0 {
return errors.New("MissingParameter")
return errors.New(awserrors.ErrorMissingParameter)
}
return
+5 -4
View File
@@ -6,6 +6,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/stretchr/testify/assert"
)
@@ -18,21 +19,21 @@ func TestValidateImportKeyPairInput(t *testing.T) {
{
name: "NilInput",
input: nil,
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "MissingKeyName",
input: &ec2.ImportKeyPairInput{
PublicKeyMaterial: []byte("ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..."),
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "MissingPublicKeyMaterial",
input: &ec2.ImportKeyPairInput{
KeyName: aws.String("test-key"),
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "EmptyPublicKeyMaterial",
@@ -40,7 +41,7 @@ func TestValidateImportKeyPairInput(t *testing.T) {
KeyName: aws.String("test-key"),
PublicKeyMaterial: []byte{},
},
want: errors.New("MissingParameter"),
want: errors.New(awserrors.ErrorMissingParameter),
},
{
name: "ValidInput",
+2 -1
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_volume "github.com/mulgadc/hive/hive/handlers/ec2/volume"
"github.com/nats-io/nats.go"
)
@@ -19,7 +20,7 @@ func ValidateDescribeVolumesInput(input *ec2.DescribeVolumesInput) error {
if input.VolumeIds != nil {
for _, volumeId := range input.VolumeIds {
if volumeId != nil && !strings.HasPrefix(*volumeId, "vol-") {
return errors.New("InvalidVolume.Malformed")
return errors.New(awserrors.ErrorInvalidVolumeIDMalformed)
}
}
}
@@ -49,7 +49,7 @@ func TestValidateDescribeVolumesInput(t *testing.T) {
VolumeIds: []*string{aws.String("invalid-id")},
},
wantErr: true,
errMsg: "InvalidVolume.Malformed",
errMsg: "InvalidVolumeID.Malformed",
},
{
name: "InvalidVolumeId_WrongPrefix",
@@ -57,7 +57,7 @@ func TestValidateDescribeVolumesInput(t *testing.T) {
VolumeIds: []*string{aws.String("ami-0123456789abcdef0")},
},
wantErr: true,
errMsg: "InvalidVolume.Malformed",
errMsg: "InvalidVolumeID.Malformed",
},
{
name: "InvalidVolumeId_EmptyString",
@@ -65,7 +65,7 @@ func TestValidateDescribeVolumesInput(t *testing.T) {
VolumeIds: []*string{aws.String("")},
},
wantErr: true,
errMsg: "InvalidVolume.Malformed",
errMsg: "InvalidVolumeID.Malformed",
},
{
name: "MixedValidAndInvalid",
@@ -76,7 +76,7 @@ func TestValidateDescribeVolumesInput(t *testing.T) {
},
},
wantErr: true,
errMsg: "InvalidVolume.Malformed",
errMsg: "InvalidVolumeID.Malformed",
},
{
name: "NilVolumeIdInList",
+4 -3
View File
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
handlers_ec2_volume "github.com/mulgadc/hive/hive/handlers/ec2/volume"
"github.com/nats-io/nats.go"
)
@@ -12,15 +13,15 @@ import (
// ValidateModifyVolumeInput validates the input parameters
func ValidateModifyVolumeInput(input *ec2.ModifyVolumeInput) error {
if input == nil {
return errors.New("InvalidParameterValue")
return errors.New(awserrors.ErrorInvalidParameterValue)
}
if input.VolumeId == nil || !strings.HasPrefix(*input.VolumeId, "vol-") {
return errors.New("InvalidVolumeID.Malformed")
return errors.New(awserrors.ErrorInvalidVolumeIDMalformed)
}
if input.Size != nil && *input.Size <= 0 {
return errors.New("InvalidParameterValue")
return errors.New(awserrors.ErrorInvalidParameterValue)
}
return nil
+4 -4
View File
@@ -143,7 +143,7 @@ func (gw *GatewayConfig) Request(ctx *fiber.Ctx) error {
case "iam":
err = gw.IAM_Request(ctx)
default:
err = errors.New("UnsupportedOperation")
err = errors.New(awserrors.ErrorUnsupportedOperation)
}
if err != nil {
@@ -164,7 +164,7 @@ func (gw *GatewayConfig) GetService(ctx *fiber.Ctx) (string, error) {
}
if !supportedServices[svc] {
slog.Debug("Unsupported service", "service", svc)
return "", errors.New("UnsupportedOperation")
return "", errors.New(awserrors.ErrorUnsupportedOperation)
}
return svc, nil
}
@@ -186,7 +186,7 @@ func (gw *GatewayConfig) ErrorHandler(ctx *fiber.Ctx, err error) error {
// Check if the error lookup exists
if _, exists := awserrors.ErrorLookup[err.Error()]; !exists {
slog.Warn("Unknown error code", "error", err.Error())
err = errors.New("InternalError")
err = errors.New(awserrors.ErrorInternalError)
}
errorMsg = awserrors.ErrorLookup[err.Error()]
@@ -254,7 +254,7 @@ func ParseArgsToStruct(input *any, args map[string]string) (err error) {
err = awsec2query.QueryParamsToStruct(args, input)
if err != nil {
return errors.New("InvalidParameter")
return errors.New(awserrors.ErrorInvalidParameter)
}
return nil
+2 -1
View File
@@ -8,6 +8,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/config"
"github.com/mulgadc/hive/hive/utils"
"github.com/nats-io/nats.go"
)
@@ -62,7 +63,7 @@ func NewAccountSettingsServiceImplWithNATS(cfg *config.Config, natsConn *nats.Co
func getOrCreateKVBucket(js nats.JetStreamContext, bucketName string, history int) (nats.KeyValue, error) {
kv, err := js.CreateKeyValue(&nats.KeyValueConfig{
Bucket: bucketName,
History: uint8(history),
History: utils.SafeIntToUint8(history),
})
if err != nil {
kv, err = js.KeyValue(bucketName)
+1 -1
View File
@@ -101,7 +101,7 @@ func (s *KeyServiceImpl) CreateKeyPair(input *ec2.CreateKeyPairInput) (*ec2.Crea
case "ed25519":
keyType = "ed25519"
default:
return nil, errors.New("InvalidParameterValue")
return nil, errors.New(awserrors.ErrorInvalidParameterValue)
}
}
+38
View File
@@ -0,0 +1,38 @@
package utils
import "math"
// SafeInt64ToUint64 converts int64 to uint64, returning 0 if negative
func SafeInt64ToUint64(v int64) uint64 {
if v < 0 {
return 0
}
return uint64(v)
}
// SafeIntToUint8 converts int to uint8, clamping to [0, 255]
func SafeIntToUint8(v int) uint8 {
if v < 0 {
return 0
}
if v > math.MaxUint8 {
return math.MaxUint8
}
return uint8(v)
}
// SafeIntToUint64 converts int to uint64, returning 0 if negative
func SafeIntToUint64(v int) uint64 {
if v < 0 {
return 0
}
return uint64(v)
}
// SafeUint64ToInt64 converts uint64 to int64, capping at math.MaxInt64
func SafeUint64ToInt64(v uint64) int64 {
if v > math.MaxInt64 {
return math.MaxInt64
}
return int64(v)
}
+2 -38
View File
@@ -11,7 +11,6 @@ import (
"fmt"
"io"
"log/slog"
"math"
"net/http"
"os"
"os/exec"
@@ -30,6 +29,7 @@ import (
"github.com/aws/aws-sdk-go/private/protocol/xml/xmlutil"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/config"
"github.com/pterm/pterm"
"golang.org/x/net/http2"
@@ -39,41 +39,6 @@ import (
var ErrQCOWDetected = errors.New("qcow format detected")
// SafeInt64ToUint64 converts int64 to uint64, returning 0 if negative
func SafeInt64ToUint64(v int64) uint64 {
if v < 0 {
return 0
}
return uint64(v)
}
// SafeIntToUint8 converts int to uint8, clamping to [0, 255]
func SafeIntToUint8(v int) uint8 {
if v < 0 {
return 0
}
if v > math.MaxUint8 {
return math.MaxUint8
}
return uint8(v)
}
// SafeIntToUint64 converts int to uint64, returning 0 if negative
func SafeIntToUint64(v int) uint64 {
if v < 0 {
return 0
}
return uint64(v)
}
// SafeUint64ToInt64 converts uint64 to int64, capping at math.MaxInt64
func SafeUint64ToInt64(v uint64) int64 {
if v > math.MaxInt64 {
return math.MaxInt64
}
return int64(v)
}
type Images struct {
Name string `json:"name"`
Description string `json:"description"`
@@ -618,8 +583,7 @@ func ValidateKeyPairName(name string) error {
char == '.'
if !valid {
// Import needed: github.com/mulgadc/hive/hive/awserrors
return fmt.Errorf("InvalidKeyPair.Format")
return errors.New(awserrors.ErrorInvalidKeyPairFormat)
}
}