diff --git a/hive/handlers/ec2/image/service_nats.go b/hive/handlers/ec2/image/service_nats.go index 6a3558781..1d949b199 100644 --- a/hive/handlers/ec2/image/service_nats.go +++ b/hive/handlers/ec2/image/service_nats.go @@ -1,7 +1,6 @@ package handlers_ec2_image import ( - "encoding/json" "fmt" "time" @@ -20,37 +19,11 @@ func NewNATSImageService(conn *nats.Conn) ImageService { return &NATSImageService{natsConn: conn} } -// DescribeImages sends a request via NATS and waits for response func (s *NATSImageService) DescribeImages(input *ec2.DescribeImagesInput) (*ec2.DescribeImagesOutput, error) { - // Marshal input to JSON - jsonData, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - // Send NATS request with 30 second timeout - msg, err := s.natsConn.Request("ec2.DescribeImages", jsonData, 30*time.Second) - if err != nil { - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - // Validate error response - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - return nil, fmt.Errorf("daemon returned error: %s", *responseError.Code) - } - - // Unmarshal successful response - var output ec2.DescribeImagesOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.DescribeImagesOutput](s.natsConn, "ec2.DescribeImages", input, 30*time.Second) } -// Stub implementations for other ImageService methods +// Stub implementations for unimplemented ImageService methods func (s *NATSImageService) CreateImage(input *ec2.CreateImageInput) (*ec2.CreateImageOutput, error) { return nil, fmt.Errorf("CreateImage not yet implemented") } diff --git a/hive/handlers/ec2/instance/service_nats.go b/hive/handlers/ec2/instance/service_nats.go index 91335cbee..f629202c2 100644 --- a/hive/handlers/ec2/instance/service_nats.go +++ b/hive/handlers/ec2/instance/service_nats.go @@ -1,10 +1,6 @@ package handlers_ec2_instance import ( - "encoding/json" - "errors" - "fmt" - "log/slog" "time" "github.com/aws/aws-sdk-go/service/ec2" @@ -22,37 +18,6 @@ func NewNATSInstanceService(conn *nats.Conn) InstanceService { return &NATSInstanceService{natsConn: conn} } -// RunInstances sends a RunInstances request to the daemon via NATS func (s *NATSInstanceService) RunInstances(input *ec2.RunInstancesInput) (*ec2.Reservation, error) { - // Marshal input to JSON - jsonData, err := json.Marshal(input) - if err != nil { - slog.Error("NATSInstanceService: Failed to marshal RunInstancesInput", "err", err) - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - // Send request to daemon via NATS with 5 minute timeout - msg, err := s.natsConn.Request("ec2.RunInstances", jsonData, 5*time.Minute) - if err != nil { - slog.Error("NATSInstanceService: Failed to send NATS request", "err", err) - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - // Check if the response is an error - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - // Response is an error payload - return just the error code for gateway lookup - slog.Error("NATSInstanceService: Received error response from daemon", "code", responseError.Code) - return nil, errors.New(*responseError.Code) - } - - // Unmarshal successful response - var reservation ec2.Reservation - err = json.Unmarshal(msg.Data, &reservation) - if err != nil { - slog.Error("NATSInstanceService: Failed to unmarshal reservation", "err", err) - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &reservation, nil + return utils.NATSRequest[ec2.Reservation](s.natsConn, "ec2.RunInstances", input, 5*time.Minute) } diff --git a/hive/handlers/ec2/key/service_nats.go b/hive/handlers/ec2/key/service_nats.go index e0842b8fc..96a6cd779 100644 --- a/hive/handlers/ec2/key/service_nats.go +++ b/hive/handlers/ec2/key/service_nats.go @@ -1,9 +1,6 @@ package handlers_ec2_key import ( - "encoding/json" - "fmt" - "log/slog" "time" "github.com/aws/aws-sdk-go/service/ec2" @@ -21,139 +18,18 @@ func NewNATSKeyService(conn *nats.Conn) KeyService { return &NATSKeyService{natsConn: conn} } -// CreateKeyPair sends a CreateKeyPair request to the daemon via NATS func (s *NATSKeyService) CreateKeyPair(input *ec2.CreateKeyPairInput) (*ec2.CreateKeyPairOutput, error) { - // Marshal input to JSON - jsonData, err := json.Marshal(input) - if err != nil { - slog.Error("NATSKeyService: Failed to marshal CreateKeyPairInput", "err", err) - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - // Send request to daemon via NATS with 30 second timeout - msg, err := s.natsConn.Request("ec2.CreateKeyPair", jsonData, 30*time.Second) - if err != nil { - slog.Error("NATSKeyService: Failed to send NATS request", "err", err) - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - // Check if the response is an error - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - // Response is an error payload - slog.Error("NATSKeyService: Received error response from daemon", "code", responseError.Code) - return nil, fmt.Errorf("daemon returned error: %s", *responseError.Code) - } - - // Unmarshal successful response - var output ec2.CreateKeyPairOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - slog.Error("NATSKeyService: Failed to unmarshal output", "err", err) - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.CreateKeyPairOutput](s.natsConn, "ec2.CreateKeyPair", input, 30*time.Second) } func (s *NATSKeyService) DeleteKeyPair(input *ec2.DeleteKeyPairInput) (*ec2.DeleteKeyPairOutput, error) { - // Marshal input to JSON - jsonData, err := json.Marshal(input) - if err != nil { - slog.Error("NATSKeyService: Failed to marshal DeleteKeyPairInput", "err", err) - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - // Send request to daemon via NATS with 30 second timeout - msg, err := s.natsConn.Request("ec2.DeleteKeyPair", jsonData, 30*time.Second) - if err != nil { - slog.Error("NATSKeyService: Failed to send NATS request", "err", err) - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - // Check if the response is an error - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - // Response is an error payload - slog.Error("NATSKeyService: Received error response from daemon", "code", responseError.Code) - return nil, fmt.Errorf("daemon returned error: %s", *responseError.Code) - } - - // Unmarshal successful response - var output ec2.DeleteKeyPairOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - slog.Error("NATSKeyService: Failed to unmarshal output", "err", err) - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.DeleteKeyPairOutput](s.natsConn, "ec2.DeleteKeyPair", input, 30*time.Second) } func (s *NATSKeyService) DescribeKeyPairs(input *ec2.DescribeKeyPairsInput) (*ec2.DescribeKeyPairsOutput, error) { - // Marshal input to JSON - jsonData, err := json.Marshal(input) - if err != nil { - slog.Error("NATSKeyService: Failed to marshal DescribeKeyPairsInput", "err", err) - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - // Send request to daemon via NATS with 30 second timeout - msg, err := s.natsConn.Request("ec2.DescribeKeyPairs", jsonData, 30*time.Second) - if err != nil { - slog.Error("NATSKeyService: Failed to send NATS request", "err", err) - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - // Check if the response is an error - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - // Response is an error payload - slog.Error("NATSKeyService: Received error response from daemon", "code", responseError.Code) - return nil, fmt.Errorf("daemon returned error: %s", *responseError.Code) - } - - // Unmarshal successful response - var output ec2.DescribeKeyPairsOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - slog.Error("NATSKeyService: Failed to unmarshal output", "err", err) - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.DescribeKeyPairsOutput](s.natsConn, "ec2.DescribeKeyPairs", input, 30*time.Second) } func (s *NATSKeyService) ImportKeyPair(input *ec2.ImportKeyPairInput) (*ec2.ImportKeyPairOutput, error) { - // Marshal input to JSON - jsonData, err := json.Marshal(input) - if err != nil { - slog.Error("NATSKeyService: Failed to marshal ImportKeyPairInput", "err", err) - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - // Send request to daemon via NATS with 30 second timeout - msg, err := s.natsConn.Request("ec2.ImportKeyPair", jsonData, 30*time.Second) - if err != nil { - slog.Error("NATSKeyService: Failed to send NATS request", "err", err) - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - // Check if the response is an error - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - // Response is an error payload - slog.Error("NATSKeyService: Received error response from daemon", "code", responseError.Code) - return nil, fmt.Errorf("daemon returned error: %s", *responseError.Code) - } - - // Unmarshal successful response - var output ec2.ImportKeyPairOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - slog.Error("NATSKeyService: Failed to unmarshal output", "err", err) - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.ImportKeyPairOutput](s.natsConn, "ec2.ImportKeyPair", input, 30*time.Second) } diff --git a/hive/handlers/ec2/volume/service_nats.go b/hive/handlers/ec2/volume/service_nats.go index ca98d4c1e..f96e7c2aa 100644 --- a/hive/handlers/ec2/volume/service_nats.go +++ b/hive/handlers/ec2/volume/service_nats.go @@ -1,9 +1,6 @@ package handlers_ec2_volume import ( - "encoding/json" - "errors" - "fmt" "time" "github.com/aws/aws-sdk-go/service/ec2" @@ -21,132 +18,22 @@ func NewNATSVolumeService(conn *nats.Conn) VolumeService { return &NATSVolumeService{natsConn: conn} } -// CreateVolume sends a CreateVolume request via NATS and waits for response func (s *NATSVolumeService) CreateVolume(input *ec2.CreateVolumeInput) (*ec2.Volume, error) { - jsonData, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - msg, err := s.natsConn.Request("ec2.CreateVolume", jsonData, 30*time.Second) - if err != nil { - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - return nil, errors.New(*responseError.Code) - } - - var output ec2.Volume - err = json.Unmarshal(msg.Data, &output) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.Volume](s.natsConn, "ec2.CreateVolume", input, 30*time.Second) } -// DescribeVolumes sends a DescribeVolumes request via NATS and waits for response func (s *NATSVolumeService) DescribeVolumes(input *ec2.DescribeVolumesInput) (*ec2.DescribeVolumesOutput, error) { - jsonData, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - msg, err := s.natsConn.Request("ec2.DescribeVolumes", jsonData, 30*time.Second) - if err != nil { - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - return nil, errors.New(*responseError.Code) - } - - var output ec2.DescribeVolumesOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.DescribeVolumesOutput](s.natsConn, "ec2.DescribeVolumes", input, 30*time.Second) } -// ModifyVolume sends a ModifyVolume request via NATS and waits for response func (s *NATSVolumeService) ModifyVolume(input *ec2.ModifyVolumeInput) (*ec2.ModifyVolumeOutput, error) { - jsonData, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - msg, err := s.natsConn.Request("ec2.ModifyVolume", jsonData, 30*time.Second) - if err != nil { - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - return nil, errors.New(*responseError.Code) - } - - var output ec2.ModifyVolumeOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.ModifyVolumeOutput](s.natsConn, "ec2.ModifyVolume", input, 30*time.Second) } -// DescribeVolumeStatus sends a DescribeVolumeStatus request via NATS and waits for response func (s *NATSVolumeService) DescribeVolumeStatus(input *ec2.DescribeVolumeStatusInput) (*ec2.DescribeVolumeStatusOutput, error) { - jsonData, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - msg, err := s.natsConn.Request("ec2.DescribeVolumeStatus", jsonData, 30*time.Second) - if err != nil { - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - return nil, errors.New(*responseError.Code) - } - - var output ec2.DescribeVolumeStatusOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.DescribeVolumeStatusOutput](s.natsConn, "ec2.DescribeVolumeStatus", input, 30*time.Second) } -// DeleteVolume sends a DeleteVolume request via NATS and waits for response func (s *NATSVolumeService) DeleteVolume(input *ec2.DeleteVolumeInput) (*ec2.DeleteVolumeOutput, error) { - jsonData, err := json.Marshal(input) - if err != nil { - return nil, fmt.Errorf("failed to marshal input: %w", err) - } - - msg, err := s.natsConn.Request("ec2.DeleteVolume", jsonData, 30*time.Second) - if err != nil { - return nil, fmt.Errorf("NATS request failed: %w", err) - } - - responseError, err := utils.ValidateErrorPayload(msg.Data) - if err != nil { - return nil, errors.New(*responseError.Code) - } - - var output ec2.DeleteVolumeOutput - err = json.Unmarshal(msg.Data, &output) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal response: %w", err) - } - - return &output, nil + return utils.NATSRequest[ec2.DeleteVolumeOutput](s.natsConn, "ec2.DeleteVolume", input, 30*time.Second) } diff --git a/hive/utils/nats.go b/hive/utils/nats.go new file mode 100644 index 000000000..f188353d0 --- /dev/null +++ b/hive/utils/nats.go @@ -0,0 +1,37 @@ +package utils + +import ( + "encoding/json" + "errors" + "fmt" + "time" + + "github.com/nats-io/nats.go" +) + +// NATSRequest performs a NATS request-response with JSON marshaling. +// It marshals the input, sends to the given subject, validates the response +// for error payloads, and unmarshals the successful response into Out. +func NATSRequest[Out any](conn *nats.Conn, subject string, input any, timeout time.Duration) (*Out, error) { + jsonData, err := json.Marshal(input) + if err != nil { + return nil, fmt.Errorf("failed to marshal input: %w", err) + } + + msg, err := conn.Request(subject, jsonData, timeout) + if err != nil { + return nil, fmt.Errorf("NATS request failed: %w", err) + } + + responseError, err := ValidateErrorPayload(msg.Data) + if err != nil { + return nil, errors.New(*responseError.Code) + } + + var output Out + if err := json.Unmarshal(msg.Data, &output); err != nil { + return nil, fmt.Errorf("failed to unmarshal response: %w", err) + } + + return &output, nil +}