Merge branch 'main' of github.com:mulgadc/hive

This commit is contained in:
benduncan
2025-10-16 15:00:39 +08:00
17 changed files with 563 additions and 521 deletions
+4 -41
View File
@@ -1,16 +1,14 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -120,8 +118,9 @@ func CopyImage(input *ec2.CopyImageInput) (output ec2.CopyImageOutput, err error
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_CopyImage(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewCopyImageHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -142,39 +141,3 @@ func CopyImage(input *ec2.CopyImageInput) (output ec2.CopyImageOutput, err error
return output, nil
}
func EC2_Process_CopyImage(jsonData []byte) (output []byte) {
var input ec2.CopyImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateCopyImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually copy the image.
// This is a placeholder response for testing the framework.
result := &ec2.CopyImageOutput{
ImageId: aws.String("ami-0987654321fedcba0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_CopyImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -41
View File
@@ -1,16 +1,14 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -119,8 +117,9 @@ func CreateImage(input *ec2.CreateImageInput) (output ec2.CreateImageOutput, err
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_CreateImage(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewCreateImageHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -141,39 +140,3 @@ func CreateImage(input *ec2.CreateImageInput) (output ec2.CreateImageOutput, err
return output, nil
}
func EC2_Process_CreateImage(jsonData []byte) (output []byte) {
var input ec2.CreateImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateCreateImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually create the image from the instance.
// This is a placeholder response for testing the framework.
result := &ec2.CreateImageOutput{
ImageId: aws.String("ami-0123456789abcdef0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_CreateImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -2
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/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
"github.com/stretchr/testify/assert"
)
@@ -90,7 +91,7 @@ func TestEC2ProcessCreateImage(t *testing.T) {
Name: aws.String("test-image"),
},
wantValidationErr: true,
wantErrCode: "ValidationError",
wantErrCode: "MissingParameter",
},
{
name: "ValidCreateImage",
@@ -117,7 +118,8 @@ func TestEC2ProcessCreateImage(t *testing.T) {
assert.NoError(t, err)
}
jsonResp := EC2_Process_CreateImage(jsonData)
handler := handlers_ec2_image.NewCreateImageHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
responseError, err := utils.ValidateErrorPayload(jsonResp)
if tt.wantValidationErr {
+4 -40
View File
@@ -1,7 +1,6 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -9,7 +8,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -74,8 +73,9 @@ func DeregisterImage(input *ec2.DeregisterImageInput) (output ec2.DeregisterImag
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DeregisterImage(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewDeregisterImageHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -96,39 +96,3 @@ func DeregisterImage(input *ec2.DeregisterImageInput) (output ec2.DeregisterImag
return output, nil
}
func EC2_Process_DeregisterImage(jsonData []byte) (output []byte) {
var input ec2.DeregisterImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDeregisterImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually deregister the image.
// This is a placeholder response for testing the framework.
// DeregisterImageOutput has no exported fields, so we return a simple success indicator
// to distinguish from error responses
result := map[string]bool{"Return": true}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DeregisterImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
@@ -1,16 +1,14 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -119,8 +117,9 @@ func DescribeImageAttribute(input *ec2.DescribeImageAttributeInput) (output ec2.
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DescribeImageAttribute(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewDescribeImageAttributeHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -141,42 +140,3 @@ func DescribeImageAttribute(input *ec2.DescribeImageAttributeInput) (output ec2.
return output, nil
}
func EC2_Process_DescribeImageAttribute(jsonData []byte) (output []byte) {
var input ec2.DescribeImageAttributeInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDescribeImageAttributeInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually describe image attributes.
// This is a placeholder response for testing the framework.
result := &ec2.DescribeImageAttributeOutput{
ImageId: input.ImageId,
Description: &ec2.AttributeValue{
Value: aws.String("Test image description"),
},
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DescribeImageAttribute could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -51
View File
@@ -1,16 +1,14 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -108,8 +106,9 @@ func DescribeImages(input *ec2.DescribeImagesInput) (output ec2.DescribeImagesOu
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DescribeImages(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewDescribeImagesHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -130,49 +129,3 @@ func DescribeImages(input *ec2.DescribeImagesInput) (output ec2.DescribeImagesOu
return output, nil
}
func EC2_Process_DescribeImages(jsonData []byte) (output []byte) {
var input ec2.DescribeImagesInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDescribeImagesInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually query images from the system.
// This is a placeholder response for testing the framework.
result := &ec2.DescribeImagesOutput{
Images: []*ec2.Image{
{
ImageId: aws.String("ami-0123456789abcdef0"),
Name: aws.String("test-image"),
State: aws.String("available"),
Architecture: aws.String("x86_64"),
ImageType: aws.String("machine"),
OwnerId: aws.String("123456789012"),
Public: aws.Bool(false),
},
},
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DescribeImages could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -40
View File
@@ -1,7 +1,6 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -9,7 +8,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -100,8 +99,9 @@ func ModifyImageAttribute(input *ec2.ModifyImageAttributeInput) (output ec2.Modi
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_ModifyImageAttribute(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewModifyImageAttributeHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -122,39 +122,3 @@ func ModifyImageAttribute(input *ec2.ModifyImageAttributeInput) (output ec2.Modi
return output, nil
}
func EC2_Process_ModifyImageAttribute(jsonData []byte) (output []byte) {
var input ec2.ModifyImageAttributeInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateModifyImageAttributeInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually modify image attributes.
// This is a placeholder response for testing the framework.
// ModifyImageAttributeOutput has no exported fields, so we return a simple success indicator
// to distinguish from error responses
result := map[string]bool{"Return": true}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_ModifyImageAttribute could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -41
View File
@@ -1,15 +1,13 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -139,8 +137,9 @@ func RegisterImage(input *ec2.RegisterImageInput) (output ec2.RegisterImageOutpu
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_RegisterImage(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewRegisterImageHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -161,39 +160,3 @@ func RegisterImage(input *ec2.RegisterImageInput) (output ec2.RegisterImageOutpu
return output, nil
}
func EC2_Process_RegisterImage(jsonData []byte) (output []byte) {
var input ec2.RegisterImageInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateRegisterImageInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually register the image.
// This is a placeholder response for testing the framework.
result := &ec2.RegisterImageOutput{
ImageId: aws.String("ami-0123456789abcdef0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_RegisterImage could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -40
View File
@@ -1,7 +1,6 @@
package gateway_ec2_image
import (
"bytes"
"encoding/json"
"errors"
"fmt"
@@ -9,7 +8,7 @@ import (
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/image"
"github.com/mulgadc/hive/hive/utils"
)
@@ -85,8 +84,9 @@ func ResetImageAttribute(input *ec2.ResetImageAttributeInput) (output ec2.ResetI
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_ResetImageAttribute(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_image.NewResetImageAttributeHandler(handlers_ec2_image.NewMockImageService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -107,39 +107,3 @@ func ResetImageAttribute(input *ec2.ResetImageAttributeInput) (output ec2.ResetI
return output, nil
}
func EC2_Process_ResetImageAttribute(jsonData []byte) (output []byte) {
var input ec2.ResetImageAttributeInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateResetImageAttributeInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually reset image attributes.
// This is a placeholder response for testing the framework.
// ResetImageAttributeOutput has no exported fields, so we return a simple success indicator
// to distinguish from error responses
result := map[string]bool{"Return": true}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_ResetImageAttribute could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+215
View File
@@ -0,0 +1,215 @@
package gateway_ec2_instance
import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/handlers/ec2/instance"
"github.com/mulgadc/hive/hive/utils"
)
type RunInstancesResponse struct {
Reservation *ec2.Reservation `locationName:"RunInstancesResponse"`
}
/*
Sample JSON:
"RunInstances":{
"name":"RunInstances", // Function name
"http":{
"method":"POST",
"requestUri":"/"
},
"input":{"shape":"RunInstancesRequest"}, // Input shape from AWS API
"output":{"shape":"Reservation"} // Output shape from AWS API
},
"RunInstancesRequest":{
"type":"structure",
"required":[
"MaxCount",
"MinCount"
],
"members":{
"BlockDeviceMappings":{
"shape":"BlockDeviceMappingRequestList",
"locationName":"BlockDeviceMapping"
},
"ImageId":{"shape":"ImageId"},
"InstanceType":{"shape":"InstanceType"},
"Ipv6AddressCount":{"shape":"Integer"},
"Ipv6Addresses":{
"shape":"InstanceIpv6AddressList",
"locationName":"Ipv6Address"
},
"KernelId":{"shape":"KernelId"},
"KeyName":{"shape":"KeyPairName"},
"MaxCount":{"shape":"Integer"},
"MinCount":{"shape":"Integer"},
"Monitoring":{"shape":"RunInstancesMonitoringEnabled"},
"Placement":{"shape":"Placement"},
"RamdiskId":{"shape":"RamdiskId"},
"SecurityGroupIds":{
"shape":"SecurityGroupIdStringList",
"locationName":"SecurityGroupId"
},
"SecurityGroups":{
"shape":"SecurityGroupStringList",
"locationName":"SecurityGroup"
},
"SubnetId":{"shape":"SubnetId"},
"UserData":{"shape":"RunInstancesUserData"},
"AdditionalInfo":{
"shape":"String",
"locationName":"additionalInfo"
},
"ClientToken":{
"shape":"String",
"idempotencyToken":true,
"locationName":"clientToken"
},
"DisableApiTermination":{
"shape":"Boolean",
"locationName":"disableApiTermination"
},
"DryRun":{
"shape":"Boolean",
"locationName":"dryRun"
},
"EbsOptimized":{
"shape":"Boolean",
"locationName":"ebsOptimized"
},
"IamInstanceProfile":{
"shape":"IamInstanceProfileSpecification",
"locationName":"iamInstanceProfile"
},
"InstanceInitiatedShutdownBehavior":{
"shape":"ShutdownBehavior",
"locationName":"instanceInitiatedShutdownBehavior"
},
"NetworkInterfaces":{
"shape":"InstanceNetworkInterfaceSpecificationList",
"locationName":"networkInterface"
},
"PrivateIpAddress":{
"shape":"String",
"locationName":"privateIpAddress"
},
"ElasticGpuSpecification":{"shape":"ElasticGpuSpecifications"},
"ElasticInferenceAccelerators":{
"shape":"ElasticInferenceAccelerators",
"locationName":"ElasticInferenceAccelerator"
},
"TagSpecifications":{
"shape":"TagSpecificationList",
"locationName":"TagSpecification"
},
"LaunchTemplate":{"shape":"LaunchTemplateSpecification"},
"InstanceMarketOptions":{"shape":"InstanceMarketOptionsRequest"},
"CreditSpecification":{"shape":"CreditSpecificationRequest"},
"CpuOptions":{"shape":"CpuOptionsRequest"},
"CapacityReservationSpecification":{"shape":"CapacityReservationSpecification"},
"HibernationOptions":{"shape":"HibernationOptionsRequest"},
"LicenseSpecifications":{
"shape":"LicenseSpecificationListRequest",
"locationName":"LicenseSpecification"
},
"MetadataOptions":{"shape":"InstanceMetadataOptionsRequest"},
"EnclaveOptions":{"shape":"EnclaveOptionsRequest"},
"PrivateDnsNameOptions":{"shape":"PrivateDnsNameOptionsRequest"},
"MaintenanceOptions":{"shape":"InstanceMaintenanceOptionsRequest"},
"DisableApiStop":{"shape":"Boolean"},
"EnablePrimaryIpv6":{"shape":"Boolean"}
}
},
*/
func ValidateRunInstancesInput(input *ec2.RunInstancesInput) (err error) {
// Check required args from JSON above
// required:[
// "MaxCount",
// "MinCount"
// ]
if input == nil {
return errors.New("MissingParameter")
}
if input.MinCount == nil {
return errors.New("MissingParameter")
}
if *input.MinCount == 0 {
return errors.New("InvalidParameterValue")
}
if *input.MaxCount == 0 {
return errors.New("InvalidParameterValue")
}
// Additional validation from EC2 spec
if *input.MinCount > *input.MaxCount {
return errors.New("InvalidParameterValue")
}
if input.ImageId == nil || *input.ImageId == "" {
return errors.New("MissingParameter")
}
if input.InstanceType == nil || *input.InstanceType == "" {
return errors.New("MissingParameter")
}
if !strings.HasPrefix(*input.ImageId, "ami-") {
return errors.New("InvalidAMIID.Malformed")
}
return
}
func RunInstances(input *ec2.RunInstancesInput) (reservation ec2.Reservation, err error) {
// Validate input
err = ValidateRunInstancesInput(input)
if err != nil {
return reservation, err
}
// Marshal to JSON, to send over the wire for processing (NATS)
jsonData, err := json.Marshal(input)
if err != nil {
return reservation, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_instance.NewRunInstancesHandler(handlers_ec2_instance.NewMockInstanceService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
if err != nil {
slog.Error("Runinstances failed", "err", responseError.Code)
return reservation, errors.New(*responseError.Code)
}
// Unmarshal the JSON response back into a Reservation struct
err = json.Unmarshal(jsonResp, &reservation)
if err != nil {
return reservation, fmt.Errorf("failed to unmarshal JSON response to Reservation: %w", err)
}
return reservation, nil
}
@@ -0,0 +1,288 @@
package gateway_ec2_instance
import (
"encoding/json"
"errors"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/handlers/ec2/instance"
"github.com/mulgadc/hive/hive/utils"
"github.com/stretchr/testify/assert"
)
var defaults = ec2.RunInstancesInput{
ImageId: aws.String("ami-0abcdef1234567890"),
InstanceType: aws.String("t2.micro"),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
KeyName: aws.String("my-key-pair"),
SecurityGroupIds: []*string{
aws.String("sg-0123456789abcdef0"),
},
SubnetId: aws.String("subnet-6e7f829e"),
}
func TestParseRunInstances(t *testing.T) {
// Group multiple tests
tests := []struct {
name string
input *ec2.RunInstancesInput
want error
}{
{
name: "InvalidMinCount",
input: &ec2.RunInstancesInput{
ImageId: defaults.ImageId,
InstanceType: defaults.InstanceType,
MinCount: aws.Int64(0),
MaxCount: aws.Int64(1),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidParameterValue"),
},
{
name: "InvalidMaxCount",
input: &ec2.RunInstancesInput{
ImageId: defaults.ImageId,
InstanceType: defaults.InstanceType,
MinCount: aws.Int64(1),
MaxCount: aws.Int64(0),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidParameterValue"),
},
{
name: "InvalidMinCount",
input: &ec2.RunInstancesInput{
ImageId: defaults.ImageId,
InstanceType: defaults.InstanceType,
MinCount: aws.Int64(0),
MaxCount: aws.Int64(1),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidParameterValue"),
},
{
name: "InvalidNoImageId",
input: &ec2.RunInstancesInput{
ImageId: aws.String(""),
InstanceType: defaults.InstanceType,
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidNoInstanceType",
input: &ec2.RunInstancesInput{
ImageId: defaults.ImageId,
InstanceType: aws.String(""),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("MissingParameter"),
},
{
name: "InvalidNoInstanceType",
input: &ec2.RunInstancesInput{
ImageId: aws.String("invalid-name-here"),
InstanceType: defaults.InstanceType,
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: errors.New("InvalidAMIID.Malformed"),
},
// Successful test
{
name: "ValidTest",
input: &ec2.RunInstancesInput{
ImageId: defaults.ImageId,
InstanceType: defaults.InstanceType,
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
KeyName: defaults.KeyName,
SecurityGroupIds: defaults.SecurityGroupIds,
SubnetId: defaults.SubnetId,
},
want: nil,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
response, err := RunInstances(test.input)
// Use assert to check if the error is as expected
assert.Equal(t, test.want, err)
if err != nil {
assert.Len(t, response.Instances, 0)
//assert.Nil(t, response)
} else {
// Check expected output
assert.Len(t, response.Instances, 1)
// ImageID returned
if len(response.Instances) > 0 {
// ImageId matches our AMI
assert.Equal(t, defaults.ImageId, response.Instances[0].ImageId)
// InstanceID starts with i-
assert.True(t, true, strings.HasPrefix(*response.Instances[0].ImageId, "i-"))
// InstanceType matches
assert.Equal(t, defaults.InstanceType, response.Instances[0].InstanceType)
// KeyName matches
assert.Equal(t, defaults.KeyName, response.Instances[0].KeyName)
// State should be 16, booting.
assert.Equal(t, aws.Int64(16), response.Instances[0].State.Code)
assert.Equal(t, aws.String("running"), response.Instances[0].State.Name)
// Subnet should match
assert.Equal(t, defaults.SubnetId, response.Instances[0].SubnetId)
}
}
})
}
// Additional test
}
func TestEC2ProcessRunInstances(t *testing.T) {
makeValidInput := func() *ec2.RunInstancesInput {
return &ec2.RunInstancesInput{
ImageId: aws.String(*defaults.ImageId),
InstanceType: aws.String(*defaults.InstanceType),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
KeyName: aws.String(*defaults.KeyName),
SecurityGroupIds: []*string{aws.String(*defaults.SecurityGroupIds[0])},
SubnetId: aws.String(*defaults.SubnetId),
}
}
tests := []struct {
name string
payload interface{}
rawJSON []byte
wantValidationErr bool
wantErrCode string
assertFn func(t *testing.T, reservation ec2.Reservation)
}{
{
name: "MismatchedShape",
payload: &ec2.DescribeInstancesInput{DryRun: aws.Bool(true)},
wantValidationErr: true,
wantErrCode: "ValidationError",
},
{
name: "MissingRequiredFields",
payload: &ec2.RunInstancesInput{
ImageId: aws.String("ami-0abcdef1234567890"),
MaxCount: aws.Int64(1),
},
wantValidationErr: true,
wantErrCode: "MissingParameter",
},
{
name: "UnknownFieldInPayload",
rawJSON: []byte(`{
"ImageId":"ami-0abcdef1234567890",
"InstanceType":"t2.micro",
"MinCount":1,
"MaxCount":1,
"Unexpected":true
}`),
wantValidationErr: true,
wantErrCode: "ValidationError",
},
{
name: "ValidRunInstances",
payload: makeValidInput(),
assertFn: func(t *testing.T, reservation ec2.Reservation) {
assert.Len(t, reservation.Instances, 1)
if len(reservation.Instances) == 0 {
return
}
instance := reservation.Instances[0]
assert.Equal(t, defaults.ImageId, instance.ImageId)
assert.Equal(t, defaults.InstanceType, instance.InstanceType)
assert.Equal(t, defaults.KeyName, instance.KeyName)
assert.Equal(t, aws.Int64(16), instance.State.Code)
assert.Equal(t, aws.String("running"), instance.State.Name)
assert.Equal(t, defaults.SubnetId, instance.SubnetId)
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var (
jsonData []byte
err error
)
if tt.rawJSON != nil {
jsonData = tt.rawJSON
} else {
jsonData, err = json.Marshal(tt.payload)
assert.NoError(t, err)
}
handler := handlers_ec2_instance.NewRunInstancesHandler(handlers_ec2_instance.NewMockInstanceService())
jsonResp := handler.Process(jsonData)
responseError, err := utils.ValidateErrorPayload(jsonResp)
if tt.wantValidationErr {
assert.Error(t, err)
assert.Equal(t, tt.wantErrCode, aws.StringValue(responseError.Code))
return
}
assert.NoError(t, err)
var reservation ec2.Reservation
assert.NoError(t, json.Unmarshal(jsonResp, &reservation))
if tt.assertFn != nil {
tt.assertFn(t, reservation)
}
})
}
}
+4 -44
View File
@@ -1,15 +1,13 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/mulgadc/hive/hive/utils"
)
@@ -76,8 +74,9 @@ func CreateKeyPair(input *ec2.CreateKeyPairInput) (output ec2.CreateKeyPairOutpu
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_CreateKeyPair(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_key.NewCreateKeyPairHandler(handlers_ec2_key.NewMockKeyService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -98,42 +97,3 @@ func CreateKeyPair(input *ec2.CreateKeyPairInput) (output ec2.CreateKeyPairOutpu
return output, nil
}
func EC2_Process_CreateKeyPair(jsonData []byte) (output []byte) {
var input ec2.CreateKeyPairInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateCreateKeyPairInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually create the key pair.
// This is a placeholder response for testing the framework.
result := &ec2.CreateKeyPairOutput{
KeyFingerprint: aws.String("1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f"),
KeyMaterial: aws.String("-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEA...\n-----END RSA PRIVATE KEY-----"),
KeyName: input.KeyName,
KeyPairId: aws.String("key-0123456789abcdef0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_CreateKeyPair could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/mulgadc/hive/hive/utils"
"github.com/stretchr/testify/assert"
)
@@ -83,7 +84,7 @@ func TestEC2ProcessCreateKeyPair(t *testing.T) {
KeyName: aws.String(""),
},
wantValidationErr: true,
wantErrCode: "ValidationError",
wantErrCode: "MissingParameter",
},
{
name: "ValidCreateKeyPair",
@@ -112,7 +113,8 @@ func TestEC2ProcessCreateKeyPair(t *testing.T) {
assert.NoError(t, err)
}
jsonResp := EC2_Process_CreateKeyPair(jsonData)
handler := handlers_ec2_key.NewCreateKeyPairHandler(handlers_ec2_key.NewMockKeyService())
jsonResp := handler.Process(jsonData)
responseError, err := utils.ValidateErrorPayload(jsonResp)
if tt.wantValidationErr {
+4 -42
View File
@@ -1,15 +1,13 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/mulgadc/hive/hive/utils"
)
@@ -85,8 +83,9 @@ func DeleteKeyPair(input *ec2.DeleteKeyPairInput) (output ec2.DeleteKeyPairOutpu
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DeleteKeyPair(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_key.NewDeleteKeyPairHandler(handlers_ec2_key.NewMockKeyService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -107,40 +106,3 @@ func DeleteKeyPair(input *ec2.DeleteKeyPairInput) (output ec2.DeleteKeyPairOutpu
return output, nil
}
func EC2_Process_DeleteKeyPair(jsonData []byte) (output []byte) {
var input ec2.DeleteKeyPairInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDeleteKeyPairInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually delete the key pair.
// This is a placeholder response for testing the framework.
result := &ec2.DeleteKeyPairOutput{
Return: aws.Bool(true),
KeyPairId: input.KeyPairId,
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DeleteKeyPair could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -48
View File
@@ -1,15 +1,13 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/mulgadc/hive/hive/utils"
)
@@ -77,8 +75,9 @@ func DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (output ec2.DescribeKeyP
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_DescribeKeyPairs(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_key.NewDescribeKeyPairsHandler(handlers_ec2_key.NewMockKeyService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -99,46 +98,3 @@ func DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (output ec2.DescribeKeyP
return output, nil
}
func EC2_Process_DescribeKeyPairs(jsonData []byte) (output []byte) {
var input ec2.DescribeKeyPairsInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateDescribeKeyPairsInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually query key pairs from the system.
// This is a placeholder response for testing the framework.
result := &ec2.DescribeKeyPairsOutput{
KeyPairs: []*ec2.KeyPairInfo{
{
KeyPairId: aws.String("key-0123456789abcdef0"),
KeyFingerprint: aws.String("1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f"),
KeyName: aws.String("test-key"),
KeyType: aws.String("rsa"),
},
},
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_DescribeKeyPairs could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -43
View File
@@ -1,15 +1,13 @@
package gateway_ec2_key
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log/slog"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/mulgadc/hive/hive/awserrors"
"github.com/mulgadc/hive/hive/handlers/ec2/key"
"github.com/mulgadc/hive/hive/utils"
)
@@ -91,8 +89,9 @@ func ImportKeyPair(input *ec2.ImportKeyPairInput) (output ec2.ImportKeyPairOutpu
return output, fmt.Errorf("failed to marshal input to JSON: %w", err)
}
// Run the simulated JSON request via NATS, which will return a JSON response
jsonResp := EC2_Process_ImportKeyPair(jsonData)
// Run the simulated JSON request via handler, which will return a JSON response
handler := handlers_ec2_key.NewImportKeyPairHandler(handlers_ec2_key.NewMockKeyService())
jsonResp := handler.Process(jsonData)
// Validate if the response is successful or an error
responseError, err := utils.ValidateErrorPayload(jsonResp)
@@ -113,41 +112,3 @@ func ImportKeyPair(input *ec2.ImportKeyPairInput) (output ec2.ImportKeyPairOutpu
return output, nil
}
func EC2_Process_ImportKeyPair(jsonData []byte) (output []byte) {
var input ec2.ImportKeyPairInput
decoder := json.NewDecoder(bytes.NewReader(jsonData))
decoder.DisallowUnknownFields()
err := decoder.Decode(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// Ensure the payload provided the fields that EC2 expects before proceeding.
err = ValidateImportKeyPairInput(&input)
if err != nil {
return utils.GenerateErrorPayload(awserrors.ErrorValidationError)
}
// TODO: Add the logic to actually import the key pair.
// This is a placeholder response for testing the framework.
result := &ec2.ImportKeyPairOutput{
KeyFingerprint: aws.String("1f:51:ae:28:bf:89:e9:d8:1f:25:5d:37:2d:7d:b8:ca:9f:f5:f1:6f"),
KeyName: input.KeyName,
KeyPairId: aws.String("key-0987654321fedcba0"),
}
// Return as JSON, to simulate the NATS response
jsonResponse, err := json.Marshal(result)
if err != nil {
slog.Error("EC2_Process_ImportKeyPair could not marshal output", "err", err)
return nil
}
return jsonResponse
}
+4 -2
View File
@@ -264,11 +264,13 @@ func ValidateErrorPayload(payload []byte) (responseError ec2.ResponseError, err
err = decoder.Decode(&responseError)
if err == nil {
// TODO: Move error codes with vars to errors.go
if err == nil && responseError.Code != nil {
// Successfully decoded as ResponseError AND has a non-nil Code field
// This is a real error response
return responseError, errors.New("ResponseError detected")
}
// Either failed to decode (not an error structure) or Code is nil (empty valid response)
return responseError, nil
}