diff --git a/hive/daemon/daemon.go b/hive/daemon/daemon.go index acba2fc0e..1fb204ec9 100644 --- a/hive/daemon/daemon.go +++ b/hive/daemon/daemon.go @@ -26,6 +26,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/mulgadc/hive/hive/awserrors" "github.com/mulgadc/hive/hive/config" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" handlers_ec2_image "github.com/mulgadc/hive/hive/handlers/ec2/image" handlers_ec2_instance "github.com/mulgadc/hive/hive/handlers/ec2/instance" handlers_ec2_key "github.com/mulgadc/hive/hive/handlers/ec2/key" @@ -80,6 +81,7 @@ type Daemon struct { keyService *handlers_ec2_key.KeyServiceImpl imageService *handlers_ec2_image.ImageServiceImpl volumeService *handlers_ec2_volume.VolumeServiceImpl + accountService *handlers_ec2_account.AccountSettingsServiceImpl snapshotService *handlers_ec2_snapshot.SnapshotServiceImpl tagsService *handlers_ec2_tags.TagsServiceImpl ctx context.Context @@ -455,6 +457,12 @@ func (d *Daemon) subscribeAll() error { {"ec2.DescribeInstances", d.handleEC2DescribeInstances, ""}, {"ec2.DescribeInstanceTypes", d.handleEC2DescribeInstanceTypes, ""}, {"ec2.startinstances", d.handleEC2StartInstances, "hive-workers"}, + {"ec2.EnableEbsEncryptionByDefault", d.handleEC2EnableEbsEncryptionByDefault, "hive-workers"}, + {"ec2.DisableEbsEncryptionByDefault", d.handleEC2DisableEbsEncryptionByDefault, "hive-workers"}, + {"ec2.GetEbsEncryptionByDefault", d.handleEC2GetEbsEncryptionByDefault, "hive-workers"}, + {"ec2.GetSerialConsoleAccessStatus", d.handleEC2GetSerialConsoleAccessStatus, "hive-workers"}, + {"ec2.EnableSerialConsoleAccess", d.handleEC2EnableSerialConsoleAccess, "hive-workers"}, + {"ec2.DisableSerialConsoleAccess", d.handleEC2DisableSerialConsoleAccess, "hive-workers"}, {fmt.Sprintf("hive.admin.%s.health", d.node), d.handleHealthCheck, ""}, {"hive.nodes.discover", d.handleNodeDiscover, ""}, } @@ -501,6 +509,13 @@ func (d *Daemon) Start() error { d.snapshotService = handlers_ec2_snapshot.NewSnapshotServiceImpl(d.config, d.natsConn) d.tagsService = handlers_ec2_tags.NewTagsServiceImpl(d.config) + accountSvc, err := handlers_ec2_account.NewAccountSettingsServiceImplWithNATS(d.config, d.natsConn) + if err != nil { + slog.Warn("Failed to create account settings service with NATS, using in-memory fallback", "error", err) + accountSvc = handlers_ec2_account.NewAccountSettingsServiceImpl(d.config) + } + d.accountService = accountSvc + d.restoreInstances() if err := d.subscribeAll(); err != nil { diff --git a/hive/daemon/daemon_handlers.go b/hive/daemon/daemon_handlers.go index a8e217318..9cae19b29 100644 --- a/hive/daemon/daemon_handlers.go +++ b/hive/daemon/daemon_handlers.go @@ -1192,3 +1192,30 @@ func (d *Daemon) handleNodeDiscover(msg *nats.Msg) { } slog.Debug("Node discovery responded", "node", d.node) } + +// Account settings handlers + +func (d *Daemon) handleEC2EnableEbsEncryptionByDefault(msg *nats.Msg) { + handleNATSRequest(msg, d.accountService.EnableEbsEncryptionByDefault) +} + +func (d *Daemon) handleEC2DisableEbsEncryptionByDefault(msg *nats.Msg) { + handleNATSRequest(msg, d.accountService.DisableEbsEncryptionByDefault) +} + +func (d *Daemon) handleEC2GetEbsEncryptionByDefault(msg *nats.Msg) { + handleNATSRequest(msg, d.accountService.GetEbsEncryptionByDefault) +} + +func (d *Daemon) handleEC2GetSerialConsoleAccessStatus(msg *nats.Msg) { + handleNATSRequest(msg, d.accountService.GetSerialConsoleAccessStatus) +} + +func (d *Daemon) handleEC2EnableSerialConsoleAccess(msg *nats.Msg) { + handleNATSRequest(msg, d.accountService.EnableSerialConsoleAccess) +} + +func (d *Daemon) handleEC2DisableSerialConsoleAccess(msg *nats.Msg) { + handleNATSRequest(msg, d.accountService.DisableSerialConsoleAccess) +} + diff --git a/hive/gateway/ec2.go b/hive/gateway/ec2.go index eb2d1cad6..70cdf2dd1 100644 --- a/hive/gateway/ec2.go +++ b/hive/gateway/ec2.go @@ -115,6 +115,24 @@ var ec2Actions = map[string]EC2Handler{ "DescribeAccountAttributes": ec2Handler(func(input *ec2.DescribeAccountAttributesInput, gw *GatewayConfig) (any, error) { return gateway_ec2_account.DescribeAccountAttributes(input) }), + "EnableEbsEncryptionByDefault": ec2Handler(func(input *ec2.EnableEbsEncryptionByDefaultInput, gw *GatewayConfig) (any, error) { + return gateway_ec2_account.EnableEbsEncryptionByDefault(input, gw.NATSConn) + }), + "DisableEbsEncryptionByDefault": ec2Handler(func(input *ec2.DisableEbsEncryptionByDefaultInput, gw *GatewayConfig) (any, error) { + return gateway_ec2_account.DisableEbsEncryptionByDefault(input, gw.NATSConn) + }), + "GetEbsEncryptionByDefault": ec2Handler(func(input *ec2.GetEbsEncryptionByDefaultInput, gw *GatewayConfig) (any, error) { + return gateway_ec2_account.GetEbsEncryptionByDefault(input, gw.NATSConn) + }), + "GetSerialConsoleAccessStatus": ec2Handler(func(input *ec2.GetSerialConsoleAccessStatusInput, gw *GatewayConfig) (any, error) { + return gateway_ec2_account.GetSerialConsoleAccessStatus(input, gw.NATSConn) + }), + "EnableSerialConsoleAccess": ec2Handler(func(input *ec2.EnableSerialConsoleAccessInput, gw *GatewayConfig) (any, error) { + return gateway_ec2_account.EnableSerialConsoleAccess(input, gw.NATSConn) + }), + "DisableSerialConsoleAccess": ec2Handler(func(input *ec2.DisableSerialConsoleAccessInput, gw *GatewayConfig) (any, error) { + return gateway_ec2_account.DisableSerialConsoleAccess(input, gw.NATSConn) + }), "CreateTags": ec2Handler(func(input *ec2.CreateTagsInput, gw *GatewayConfig) (any, error) { return gateway_ec2_tags.CreateTags(input, gw.NATSConn) }), diff --git a/hive/gateway/ec2/account/DisableEbsEncryptionByDefault.go b/hive/gateway/ec2/account/DisableEbsEncryptionByDefault.go new file mode 100644 index 000000000..b56eba392 --- /dev/null +++ b/hive/gateway/ec2/account/DisableEbsEncryptionByDefault.go @@ -0,0 +1,33 @@ +package gateway_ec2_account + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/awserrors" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" + "github.com/nats-io/nats.go" +) + +func ValidateDisableEbsEncryptionByDefaultInput(input *ec2.DisableEbsEncryptionByDefaultInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +func DisableEbsEncryptionByDefault(input *ec2.DisableEbsEncryptionByDefaultInput, natsConn *nats.Conn) (ec2.DisableEbsEncryptionByDefaultOutput, error) { + var output ec2.DisableEbsEncryptionByDefaultOutput + + if err := ValidateDisableEbsEncryptionByDefaultInput(input); err != nil { + return output, err + } + + svc := handlers_ec2_account.NewNATSAccountSettingsService(natsConn) + result, err := svc.DisableEbsEncryptionByDefault(input) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/hive/gateway/ec2/account/DisableSerialConsoleAccess.go b/hive/gateway/ec2/account/DisableSerialConsoleAccess.go new file mode 100644 index 000000000..f02d16189 --- /dev/null +++ b/hive/gateway/ec2/account/DisableSerialConsoleAccess.go @@ -0,0 +1,33 @@ +package gateway_ec2_account + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/awserrors" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" + "github.com/nats-io/nats.go" +) + +func ValidateDisableSerialConsoleAccessInput(input *ec2.DisableSerialConsoleAccessInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +func DisableSerialConsoleAccess(input *ec2.DisableSerialConsoleAccessInput, natsConn *nats.Conn) (ec2.DisableSerialConsoleAccessOutput, error) { + var output ec2.DisableSerialConsoleAccessOutput + + if err := ValidateDisableSerialConsoleAccessInput(input); err != nil { + return output, err + } + + svc := handlers_ec2_account.NewNATSAccountSettingsService(natsConn) + result, err := svc.DisableSerialConsoleAccess(input) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/hive/gateway/ec2/account/EnableEbsEncryptionByDefault.go b/hive/gateway/ec2/account/EnableEbsEncryptionByDefault.go new file mode 100644 index 000000000..608bbb4fe --- /dev/null +++ b/hive/gateway/ec2/account/EnableEbsEncryptionByDefault.go @@ -0,0 +1,33 @@ +package gateway_ec2_account + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/awserrors" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" + "github.com/nats-io/nats.go" +) + +func ValidateEnableEbsEncryptionByDefaultInput(input *ec2.EnableEbsEncryptionByDefaultInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +func EnableEbsEncryptionByDefault(input *ec2.EnableEbsEncryptionByDefaultInput, natsConn *nats.Conn) (ec2.EnableEbsEncryptionByDefaultOutput, error) { + var output ec2.EnableEbsEncryptionByDefaultOutput + + if err := ValidateEnableEbsEncryptionByDefaultInput(input); err != nil { + return output, err + } + + svc := handlers_ec2_account.NewNATSAccountSettingsService(natsConn) + result, err := svc.EnableEbsEncryptionByDefault(input) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/hive/gateway/ec2/account/EnableSerialConsoleAccess.go b/hive/gateway/ec2/account/EnableSerialConsoleAccess.go new file mode 100644 index 000000000..57d353f1f --- /dev/null +++ b/hive/gateway/ec2/account/EnableSerialConsoleAccess.go @@ -0,0 +1,33 @@ +package gateway_ec2_account + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/awserrors" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" + "github.com/nats-io/nats.go" +) + +func ValidateEnableSerialConsoleAccessInput(input *ec2.EnableSerialConsoleAccessInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +func EnableSerialConsoleAccess(input *ec2.EnableSerialConsoleAccessInput, natsConn *nats.Conn) (ec2.EnableSerialConsoleAccessOutput, error) { + var output ec2.EnableSerialConsoleAccessOutput + + if err := ValidateEnableSerialConsoleAccessInput(input); err != nil { + return output, err + } + + svc := handlers_ec2_account.NewNATSAccountSettingsService(natsConn) + result, err := svc.EnableSerialConsoleAccess(input) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/hive/gateway/ec2/account/GetEbsEncryptionByDefault.go b/hive/gateway/ec2/account/GetEbsEncryptionByDefault.go new file mode 100644 index 000000000..94736babe --- /dev/null +++ b/hive/gateway/ec2/account/GetEbsEncryptionByDefault.go @@ -0,0 +1,33 @@ +package gateway_ec2_account + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/awserrors" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" + "github.com/nats-io/nats.go" +) + +func ValidateGetEbsEncryptionByDefaultInput(input *ec2.GetEbsEncryptionByDefaultInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +func GetEbsEncryptionByDefault(input *ec2.GetEbsEncryptionByDefaultInput, natsConn *nats.Conn) (ec2.GetEbsEncryptionByDefaultOutput, error) { + var output ec2.GetEbsEncryptionByDefaultOutput + + if err := ValidateGetEbsEncryptionByDefaultInput(input); err != nil { + return output, err + } + + svc := handlers_ec2_account.NewNATSAccountSettingsService(natsConn) + result, err := svc.GetEbsEncryptionByDefault(input) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/hive/gateway/ec2/account/GetSerialConsoleAccessStatus.go b/hive/gateway/ec2/account/GetSerialConsoleAccessStatus.go new file mode 100644 index 000000000..a33e6a47e --- /dev/null +++ b/hive/gateway/ec2/account/GetSerialConsoleAccessStatus.go @@ -0,0 +1,33 @@ +package gateway_ec2_account + +import ( + "errors" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/awserrors" + handlers_ec2_account "github.com/mulgadc/hive/hive/handlers/ec2/account" + "github.com/nats-io/nats.go" +) + +func ValidateGetSerialConsoleAccessStatusInput(input *ec2.GetSerialConsoleAccessStatusInput) error { + if input == nil { + return errors.New(awserrors.ErrorInvalidParameterValue) + } + return nil +} + +func GetSerialConsoleAccessStatus(input *ec2.GetSerialConsoleAccessStatusInput, natsConn *nats.Conn) (ec2.GetSerialConsoleAccessStatusOutput, error) { + var output ec2.GetSerialConsoleAccessStatusOutput + + if err := ValidateGetSerialConsoleAccessStatusInput(input); err != nil { + return output, err + } + + svc := handlers_ec2_account.NewNATSAccountSettingsService(natsConn) + result, err := svc.GetSerialConsoleAccessStatus(input) + if err != nil { + return output, err + } + + return *result, nil +} diff --git a/hive/handlers/ec2/account/service.go b/hive/handlers/ec2/account/service.go new file mode 100644 index 000000000..b90f8b890 --- /dev/null +++ b/hive/handlers/ec2/account/service.go @@ -0,0 +1,13 @@ +package handlers_ec2_account + +import "github.com/aws/aws-sdk-go/service/ec2" + +// AccountSettingsService defines the interface for EC2 account-level settings +type AccountSettingsService interface { + EnableEbsEncryptionByDefault(input *ec2.EnableEbsEncryptionByDefaultInput) (*ec2.EnableEbsEncryptionByDefaultOutput, error) + DisableEbsEncryptionByDefault(input *ec2.DisableEbsEncryptionByDefaultInput) (*ec2.DisableEbsEncryptionByDefaultOutput, error) + GetEbsEncryptionByDefault(input *ec2.GetEbsEncryptionByDefaultInput) (*ec2.GetEbsEncryptionByDefaultOutput, error) + GetSerialConsoleAccessStatus(input *ec2.GetSerialConsoleAccessStatusInput) (*ec2.GetSerialConsoleAccessStatusOutput, error) + EnableSerialConsoleAccess(input *ec2.EnableSerialConsoleAccessInput) (*ec2.EnableSerialConsoleAccessOutput, error) + DisableSerialConsoleAccess(input *ec2.DisableSerialConsoleAccessInput) (*ec2.DisableSerialConsoleAccessOutput, error) +} diff --git a/hive/handlers/ec2/account/service_impl.go b/hive/handlers/ec2/account/service_impl.go new file mode 100644 index 000000000..b31d0e65f --- /dev/null +++ b/hive/handlers/ec2/account/service_impl.go @@ -0,0 +1,226 @@ +package handlers_ec2_account + +import ( + "encoding/json" + "fmt" + "log/slog" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/config" + "github.com/nats-io/nats.go" +) + +const ( + KVBucketAccountSettings = "hive-ec2-account-settings" + KeyEbsEncryptionDefault = "ebs-encryption-default" + KeySerialConsoleAccess = "serial-console-access" +) + +// AccountSettingsRecord represents stored account settings +type AccountSettingsRecord struct { + EbsEncryptionByDefault bool `json:"ebs_encryption_by_default"` + SerialConsoleAccess bool `json:"serial_console_access"` +} + +// AccountSettingsServiceImpl implements account settings operations with NATS JetStream persistence +type AccountSettingsServiceImpl struct { + config *config.Config + js nats.JetStreamContext + settingsKV nats.KeyValue +} + +// NewAccountSettingsServiceImpl creates a new account settings service implementation +func NewAccountSettingsServiceImpl(cfg *config.Config) *AccountSettingsServiceImpl { + return &AccountSettingsServiceImpl{ + config: cfg, + } +} + +// NewAccountSettingsServiceImplWithNATS creates an account settings service with NATS JetStream for persistence +func NewAccountSettingsServiceImplWithNATS(cfg *config.Config, natsConn *nats.Conn) (*AccountSettingsServiceImpl, error) { + js, err := natsConn.JetStream() + if err != nil { + return nil, fmt.Errorf("failed to get JetStream context: %w", err) + } + + settingsKV, err := getOrCreateKVBucket(js, KVBucketAccountSettings, 10) + if err != nil { + slog.Warn("Failed to create account settings KV bucket", "error", err) + return NewAccountSettingsServiceImpl(cfg), nil + } + + slog.Info("Account settings service initialized with JetStream KV", "bucket", KVBucketAccountSettings) + + return &AccountSettingsServiceImpl{ + config: cfg, + js: js, + settingsKV: settingsKV, + }, nil +} + +func getOrCreateKVBucket(js nats.JetStreamContext, bucketName string, history int) (nats.KeyValue, error) { + kv, err := js.CreateKeyValue(&nats.KeyValueConfig{ + Bucket: bucketName, + History: uint8(history), + }) + if err != nil { + kv, err = js.KeyValue(bucketName) + if err != nil { + return nil, err + } + } + return kv, nil +} + +// getSettings retrieves current account settings +func (s *AccountSettingsServiceImpl) getSettings() (*AccountSettingsRecord, error) { + if s.settingsKV == nil { + // Return defaults if KV not available + return &AccountSettingsRecord{ + EbsEncryptionByDefault: false, + SerialConsoleAccess: false, + }, nil + } + + entry, err := s.settingsKV.Get("default") + if err != nil { + // Return defaults if not found + return &AccountSettingsRecord{ + EbsEncryptionByDefault: false, + SerialConsoleAccess: false, + }, nil + } + + var settings AccountSettingsRecord + if err := json.Unmarshal(entry.Value(), &settings); err != nil { + return nil, err + } + + return &settings, nil +} + +// saveSettings saves current account settings +func (s *AccountSettingsServiceImpl) saveSettings(settings *AccountSettingsRecord) error { + if s.settingsKV == nil { + slog.Warn("Cannot save settings - KV not initialized") + return nil // Return success anyway for in-memory fallback + } + + data, err := json.Marshal(settings) + if err != nil { + return err + } + + _, err = s.settingsKV.Put("default", data) + return err +} + +// EnableEbsEncryptionByDefault enables EBS encryption by default for the account +func (s *AccountSettingsServiceImpl) EnableEbsEncryptionByDefault(input *ec2.EnableEbsEncryptionByDefaultInput) (*ec2.EnableEbsEncryptionByDefaultOutput, error) { + slog.Info("EnableEbsEncryptionByDefault called") + + settings, err := s.getSettings() + if err != nil { + return nil, err + } + + settings.EbsEncryptionByDefault = true + + if err := s.saveSettings(settings); err != nil { + return nil, err + } + + return &ec2.EnableEbsEncryptionByDefaultOutput{ + EbsEncryptionByDefault: aws.Bool(true), + }, nil +} + +// DisableEbsEncryptionByDefault disables EBS encryption by default for the account +func (s *AccountSettingsServiceImpl) DisableEbsEncryptionByDefault(input *ec2.DisableEbsEncryptionByDefaultInput) (*ec2.DisableEbsEncryptionByDefaultOutput, error) { + slog.Info("DisableEbsEncryptionByDefault called") + + settings, err := s.getSettings() + if err != nil { + return nil, err + } + + settings.EbsEncryptionByDefault = false + + if err := s.saveSettings(settings); err != nil { + return nil, err + } + + return &ec2.DisableEbsEncryptionByDefaultOutput{ + EbsEncryptionByDefault: aws.Bool(false), + }, nil +} + +// GetEbsEncryptionByDefault gets the current EBS encryption by default setting +func (s *AccountSettingsServiceImpl) GetEbsEncryptionByDefault(input *ec2.GetEbsEncryptionByDefaultInput) (*ec2.GetEbsEncryptionByDefaultOutput, error) { + slog.Info("GetEbsEncryptionByDefault called") + + settings, err := s.getSettings() + if err != nil { + return nil, err + } + + return &ec2.GetEbsEncryptionByDefaultOutput{ + EbsEncryptionByDefault: aws.Bool(settings.EbsEncryptionByDefault), + }, nil +} + +// GetSerialConsoleAccessStatus gets the current serial console access status +func (s *AccountSettingsServiceImpl) GetSerialConsoleAccessStatus(input *ec2.GetSerialConsoleAccessStatusInput) (*ec2.GetSerialConsoleAccessStatusOutput, error) { + slog.Info("GetSerialConsoleAccessStatus called") + + settings, err := s.getSettings() + if err != nil { + return nil, err + } + + return &ec2.GetSerialConsoleAccessStatusOutput{ + SerialConsoleAccessEnabled: aws.Bool(settings.SerialConsoleAccess), + }, nil +} + +// EnableSerialConsoleAccess enables serial console access for the account +func (s *AccountSettingsServiceImpl) EnableSerialConsoleAccess(input *ec2.EnableSerialConsoleAccessInput) (*ec2.EnableSerialConsoleAccessOutput, error) { + slog.Info("EnableSerialConsoleAccess called") + + settings, err := s.getSettings() + if err != nil { + return nil, err + } + + settings.SerialConsoleAccess = true + + if err := s.saveSettings(settings); err != nil { + return nil, err + } + + return &ec2.EnableSerialConsoleAccessOutput{ + SerialConsoleAccessEnabled: aws.Bool(true), + }, nil +} + +// DisableSerialConsoleAccess disables serial console access for the account +func (s *AccountSettingsServiceImpl) DisableSerialConsoleAccess(input *ec2.DisableSerialConsoleAccessInput) (*ec2.DisableSerialConsoleAccessOutput, error) { + slog.Info("DisableSerialConsoleAccess called") + + settings, err := s.getSettings() + if err != nil { + return nil, err + } + + settings.SerialConsoleAccess = false + + if err := s.saveSettings(settings); err != nil { + return nil, err + } + + return &ec2.DisableSerialConsoleAccessOutput{ + SerialConsoleAccessEnabled: aws.Bool(false), + }, nil +} + diff --git a/hive/handlers/ec2/account/service_impl_test.go b/hive/handlers/ec2/account/service_impl_test.go new file mode 100644 index 000000000..e983ffe42 --- /dev/null +++ b/hive/handlers/ec2/account/service_impl_test.go @@ -0,0 +1,102 @@ +package handlers_ec2_account + +import ( + "testing" + "time" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/nats-io/nats-server/v2/server" + "github.com/nats-io/nats.go" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var _ AccountSettingsService = (*AccountSettingsServiceImpl)(nil) + +func setupTestAccountService(t *testing.T) *AccountSettingsServiceImpl { + t.Helper() + opts := &server.Options{ + Host: "127.0.0.1", + Port: -1, + JetStream: true, + StoreDir: t.TempDir(), + NoLog: true, + NoSigs: true, + } + ns, err := server.NewServer(opts) + require.NoError(t, err) + go ns.Start() + require.True(t, ns.ReadyForConnections(5*time.Second)) + t.Cleanup(func() { ns.Shutdown() }) + + nc, err := nats.Connect(ns.ClientURL()) + require.NoError(t, err) + t.Cleanup(func() { nc.Close() }) + + svc, err := NewAccountSettingsServiceImplWithNATS(nil, nc) + require.NoError(t, err) + return svc +} + +// EBS Encryption tests + +func TestEbsEncryption_DefaultOff(t *testing.T) { + svc := setupTestAccountService(t) + out, err := svc.GetEbsEncryptionByDefault(&ec2.GetEbsEncryptionByDefaultInput{}) + require.NoError(t, err) + assert.False(t, *out.EbsEncryptionByDefault) +} + +func TestEbsEncryption_EnableDisable(t *testing.T) { + svc := setupTestAccountService(t) + + // Enable + enableOut, err := svc.EnableEbsEncryptionByDefault(&ec2.EnableEbsEncryptionByDefaultInput{}) + require.NoError(t, err) + assert.True(t, *enableOut.EbsEncryptionByDefault) + + // Verify + getOut, err := svc.GetEbsEncryptionByDefault(&ec2.GetEbsEncryptionByDefaultInput{}) + require.NoError(t, err) + assert.True(t, *getOut.EbsEncryptionByDefault) + + // Disable + disableOut, err := svc.DisableEbsEncryptionByDefault(&ec2.DisableEbsEncryptionByDefaultInput{}) + require.NoError(t, err) + assert.False(t, *disableOut.EbsEncryptionByDefault) + + // Verify + getOut, err = svc.GetEbsEncryptionByDefault(&ec2.GetEbsEncryptionByDefaultInput{}) + require.NoError(t, err) + assert.False(t, *getOut.EbsEncryptionByDefault) +} + +// Serial Console tests + +func TestSerialConsole_DefaultOff(t *testing.T) { + svc := setupTestAccountService(t) + out, err := svc.GetSerialConsoleAccessStatus(&ec2.GetSerialConsoleAccessStatusInput{}) + require.NoError(t, err) + assert.False(t, *out.SerialConsoleAccessEnabled) +} + +func TestSerialConsole_EnableDisable(t *testing.T) { + svc := setupTestAccountService(t) + + enableOut, err := svc.EnableSerialConsoleAccess(&ec2.EnableSerialConsoleAccessInput{}) + require.NoError(t, err) + assert.True(t, *enableOut.SerialConsoleAccessEnabled) + + getOut, err := svc.GetSerialConsoleAccessStatus(&ec2.GetSerialConsoleAccessStatusInput{}) + require.NoError(t, err) + assert.True(t, *getOut.SerialConsoleAccessEnabled) + + disableOut, err := svc.DisableSerialConsoleAccess(&ec2.DisableSerialConsoleAccessInput{}) + require.NoError(t, err) + assert.False(t, *disableOut.SerialConsoleAccessEnabled) + + getOut, err = svc.GetSerialConsoleAccessStatus(&ec2.GetSerialConsoleAccessStatusInput{}) + require.NoError(t, err) + assert.False(t, *getOut.SerialConsoleAccessEnabled) +} + diff --git a/hive/handlers/ec2/account/service_nats.go b/hive/handlers/ec2/account/service_nats.go new file mode 100644 index 000000000..66bbd00b4 --- /dev/null +++ b/hive/handlers/ec2/account/service_nats.go @@ -0,0 +1,44 @@ +package handlers_ec2_account + +import ( + "time" + + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/mulgadc/hive/hive/utils" + "github.com/nats-io/nats.go" +) + +// NATSAccountSettingsService implements AccountSettingsService via NATS messaging +type NATSAccountSettingsService struct { + natsConn *nats.Conn +} + +// NewNATSAccountSettingsService creates a new NATS-based account settings service +func NewNATSAccountSettingsService(natsConn *nats.Conn) AccountSettingsService { + return &NATSAccountSettingsService{natsConn: natsConn} +} + +func (s *NATSAccountSettingsService) EnableEbsEncryptionByDefault(input *ec2.EnableEbsEncryptionByDefaultInput) (*ec2.EnableEbsEncryptionByDefaultOutput, error) { + return utils.NATSRequest[ec2.EnableEbsEncryptionByDefaultOutput](s.natsConn, "ec2.EnableEbsEncryptionByDefault", input, 30*time.Second) +} + +func (s *NATSAccountSettingsService) DisableEbsEncryptionByDefault(input *ec2.DisableEbsEncryptionByDefaultInput) (*ec2.DisableEbsEncryptionByDefaultOutput, error) { + return utils.NATSRequest[ec2.DisableEbsEncryptionByDefaultOutput](s.natsConn, "ec2.DisableEbsEncryptionByDefault", input, 30*time.Second) +} + +func (s *NATSAccountSettingsService) GetEbsEncryptionByDefault(input *ec2.GetEbsEncryptionByDefaultInput) (*ec2.GetEbsEncryptionByDefaultOutput, error) { + return utils.NATSRequest[ec2.GetEbsEncryptionByDefaultOutput](s.natsConn, "ec2.GetEbsEncryptionByDefault", input, 30*time.Second) +} + +func (s *NATSAccountSettingsService) GetSerialConsoleAccessStatus(input *ec2.GetSerialConsoleAccessStatusInput) (*ec2.GetSerialConsoleAccessStatusOutput, error) { + return utils.NATSRequest[ec2.GetSerialConsoleAccessStatusOutput](s.natsConn, "ec2.GetSerialConsoleAccessStatus", input, 30*time.Second) +} + +func (s *NATSAccountSettingsService) EnableSerialConsoleAccess(input *ec2.EnableSerialConsoleAccessInput) (*ec2.EnableSerialConsoleAccessOutput, error) { + return utils.NATSRequest[ec2.EnableSerialConsoleAccessOutput](s.natsConn, "ec2.EnableSerialConsoleAccess", input, 30*time.Second) +} + +func (s *NATSAccountSettingsService) DisableSerialConsoleAccess(input *ec2.DisableSerialConsoleAccessInput) (*ec2.DisableSerialConsoleAccessOutput, error) { + return utils.NATSRequest[ec2.DisableSerialConsoleAccessOutput](s.natsConn, "ec2.DisableSerialConsoleAccess", input, 30*time.Second) +} +