From 4ac20bf761b3cda43e7f2620cfb7bff782ca5445 Mon Sep 17 00:00:00 2001 From: Nick Figgins Date: Sun, 26 Nov 2023 12:21:58 -0500 Subject: [PATCH] add v1 and v2 client for diff sdks, break out client --- client.go | 92 ++++++++++++++++++++++++++++++++ client_v2.go | 99 ++++++++++++++++++++++++++++++++++ cmd/{ => v1}/main.go | 2 +- cmd/v2/main.go | 33 ++++++++++++ go.mod | 4 +- go.sum | 31 ++++++++++- handler.go | 123 ++++++++++++++++--------------------------- handler_test.go | 8 ++- 8 files changed, 309 insertions(+), 83 deletions(-) create mode 100644 client.go create mode 100644 client_v2.go rename cmd/{ => v1}/main.go (95%) create mode 100644 cmd/v2/main.go diff --git a/client.go b/client.go new file mode 100644 index 0000000..41c7b53 --- /dev/null +++ b/client.go @@ -0,0 +1,92 @@ +package elasticspot + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" +) + +// Client will create a new client for version 1 of the AWS SDK. +type Client struct { + EC2API EC2API +} + +type EC2API interface { + DescribeAddresses(input *ec2.DescribeAddressesInput) (*ec2.DescribeAddressesOutput, error) + AssociateAddress(input *ec2.AssociateAddressInput) (*ec2.AssociateAddressOutput, error) + DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) +} + +func (c *Client) AssociateIP(ctx context.Context, ip, instanceID string) (*AssociateResponse, error) { + instance, err := c.getInstanceById(instanceID) + if err != nil { + return nil, fmt.Errorf("error fetching instance: %w", err) + } + + if *instance.PublicIpAddress == ip { + return &AssociateResponse{AlreadyAssociated: true}, nil + } + + address, err := c.getAddressForIp(ip) + if err != nil { + return nil, fmt.Errorf("error fetching elastic ip address: %w", err) + } + + assocRes, err := c.EC2API.AssociateAddress(&ec2.AssociateAddressInput{ + AllocationId: address.AllocationId, + InstanceId: aws.String(instanceID), + }) + if err != nil { + return nil, fmt.Errorf("error associating elastic ip address with %q %w", instanceID, err) + } + + return &AssociateResponse{ + AllocationID: *address.AllocationId, + AssociationID: *assocRes.AssociationId, + }, nil +} + +func (a *Client) getInstanceById(id string) (*ec2.Instance, error) { + instances, err := a.EC2API.DescribeInstances(&ec2.DescribeInstancesInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("instance-id"), + Values: aws.StringSlice([]string{id}), + }, + }, + }) + if err != nil { + return nil, fmt.Errorf("DescribeInstances failed: %w", err) + } + + if instances == nil || len(instances.Reservations) == 0 || len(instances.Reservations[0].Instances) == 0 { + return nil, errors.New("no instance found for the given id") + } + + return instances.Reservations[0].Instances[0], nil +} + +func (a *Client) getAddressForIp(ip string) (*ec2.Address, error) { + result, err := a.EC2API.DescribeAddresses(&ec2.DescribeAddressesInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("domain"), + Values: aws.StringSlice([]string{"vpc"}), + }, + }, + PublicIps: []*string{aws.String(ip)}, + }) + if err != nil { + return nil, err + } + + if len(result.Addresses) == 0 { + return nil, fmt.Errorf("elastic ip address not found in region %q", os.Getenv("AWS_REGION")) + } + + return result.Addresses[0], nil +} diff --git a/client_v2.go b/client_v2.go new file mode 100644 index 0000000..e09dbe1 --- /dev/null +++ b/client_v2.go @@ -0,0 +1,99 @@ +package elasticspot + +import ( + "context" + "errors" + "fmt" + "os" + + ec2v2 "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/aws/aws-sdk-go-v2/service/ec2/types" + "github.com/aws/aws-sdk-go/aws" +) + +type AssociateResponse struct { + AlreadyAssociated bool + AllocationID string + AssociationID string +} + +// ClientV2 will create a new client for version 2 of the AWS SDK. +type ClientV2 struct { + EC2API EC2APIV2 +} + +type EC2APIV2 interface { + DescribeAddresses(ctx context.Context, params *ec2v2.DescribeAddressesInput, optFns ...func(*ec2v2.Options)) (*ec2v2.DescribeAddressesOutput, error) + AssociateAddress(ctx context.Context, params *ec2v2.AssociateAddressInput, optFns ...func(*ec2v2.Options)) (*ec2v2.AssociateAddressOutput, error) + DescribeInstances(ctx context.Context, params *ec2v2.DescribeInstancesInput, optFns ...func(*ec2v2.Options)) (*ec2v2.DescribeInstancesOutput, error) +} + +func (a *ClientV2) AssociateIP(ctx context.Context, ip string, instanceID string) (*AssociateResponse, error) { + instance, err := a.getInstanceById(ctx, instanceID) + if err != nil { + return nil, fmt.Errorf("error fetching instance: %w", err) + } + + if *instance.PublicIpAddress == ip { + return &AssociateResponse{AlreadyAssociated: true}, nil + } + + address, err := a.getAddressForIp(ctx, ip) + if err != nil { + return nil, fmt.Errorf("error fetching elastic ip address: %w", err) + } + + assocRes, err := a.EC2API.AssociateAddress(ctx, &ec2v2.AssociateAddressInput{ + AllocationId: address.AllocationId, + InstanceId: aws.String(instanceID), + }) + if err != nil { + return nil, fmt.Errorf("error associating elastic ip address with %q %w", instanceID, err) + } + + return &AssociateResponse{ + AllocationID: *address.AllocationId, + AssociationID: *assocRes.AssociationId, + }, nil +} + +func (a *ClientV2) getInstanceById(ctx context.Context, id string) (*types.Instance, error) { + instances, err := a.EC2API.DescribeInstances(ctx, &ec2v2.DescribeInstancesInput{ + Filters: []types.Filter{ + { + Name: aws.String("instance-id"), + Values: []string{id}, + }, + }, + }) + if err != nil { + return nil, fmt.Errorf("DescribeInstances failed: %w", err) + } + + if instances == nil || len(instances.Reservations) == 0 || len(instances.Reservations[0].Instances) == 0 { + return nil, errors.New("no instance found for the given id") + } + + return &instances.Reservations[0].Instances[0], nil +} + +func (c *ClientV2) getAddressForIp(ctx context.Context, ip string) (*types.Address, error) { + result, err := c.EC2API.DescribeAddresses(ctx, &ec2v2.DescribeAddressesInput{ + Filters: []types.Filter{ + { + Name: aws.String("domain"), + Values: []string{"vpc"}, + }, + }, + PublicIps: []string{ip}, + }) + if err != nil { + return nil, err + } + + if len(result.Addresses) == 0 { + return nil, fmt.Errorf("elastic ip address not found in region %q", os.Getenv("AWS_REGION")) + } + + return &result.Addresses[0], nil +} diff --git a/cmd/main.go b/cmd/v1/main.go similarity index 95% rename from cmd/main.go rename to cmd/v1/main.go index 742274d..dd260ac 100644 --- a/cmd/main.go +++ b/cmd/v1/main.go @@ -11,7 +11,7 @@ import ( "github.com/nickfiggins/elasticspot" ) -var handler *elasticspot.V1Handler +var handler *elasticspot.Handler func init() { cfg := &aws.Config{Region: aws.String(os.Getenv("AWS_REGION"))} diff --git a/cmd/v2/main.go b/cmd/v2/main.go new file mode 100644 index 0000000..e73ad8f --- /dev/null +++ b/cmd/v2/main.go @@ -0,0 +1,33 @@ +package main + +import ( + "context" + "fmt" + "os" + + "github.com/aws/aws-lambda-go/lambda" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/ec2" + "github.com/nickfiggins/elasticspot" +) + +var handler elasticspot.HandleFunc + +func init() { + ctx := context.Background() + cfg, err := config.LoadDefaultConfig(ctx) + if err != nil { + panic(fmt.Sprintf("error loading default config: %v", err)) + } + + client := ec2.NewFromConfig(cfg) + elasticIP := os.Getenv("ELASTIC_IP") + if len(elasticIP) == 0 { + panic("Elastic IP has not been set.") + } + handler = elasticspot.HandleV2(client, elasticIP) +} + +func main() { + lambda.Start(handler) +} diff --git a/go.mod b/go.mod index d8ad45f..9c5e334 100644 --- a/go.mod +++ b/go.mod @@ -5,5 +5,7 @@ go 1.16 require ( github.com/aws/aws-lambda-go v1.28.0 github.com/aws/aws-sdk-go v1.43.12 - github.com/stretchr/testify v1.7.0 + github.com/aws/aws-sdk-go-v2/config v1.25.5 + github.com/aws/aws-sdk-go-v2/service/ec2 v1.137.1 + github.com/stretchr/testify v1.7.0 // indirect ) diff --git a/go.sum b/go.sum index 00b7ec1..9360149 100644 --- a/go.sum +++ b/go.sum @@ -3,11 +3,41 @@ github.com/aws/aws-lambda-go v1.28.0 h1:fZiik1PZqW2IyAN4rj+Y0UBaO1IDFlsNo9Zz/XnA github.com/aws/aws-lambda-go v1.28.0/go.mod h1:jJmlefzPfGnckuHdXX7/80O3BvUUi12XOkbv4w9SGLU= github.com/aws/aws-sdk-go v1.43.12 h1:wOdx6+reSDpUBFEuJDA6edCrojzy8rOtMzhS2rD9+7M= github.com/aws/aws-sdk-go v1.43.12/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= +github.com/aws/aws-sdk-go-v2 v1.23.1 h1:qXaFsOOMA+HsZtX8WoCa+gJnbyW7qyFFBlPqvTSzbaI= +github.com/aws/aws-sdk-go-v2 v1.23.1/go.mod h1:i1XDttT4rnf6vxc9AuskLc6s7XBee8rlLilKlc03uAA= +github.com/aws/aws-sdk-go-v2/config v1.25.5 h1:UGKm9hpQS2hoK8CEJ1BzAW8NbUpvwDJJ4lyqXSzu8bk= +github.com/aws/aws-sdk-go-v2/config v1.25.5/go.mod h1:Bf4gDvy4ZcFIK0rqDu1wp9wrubNba2DojiPB2rt6nvI= +github.com/aws/aws-sdk-go-v2/credentials v1.16.4 h1:i7UQYYDSJrtc30RSwJwfBKwLFNnBTiICqAJ0pPdum8E= +github.com/aws/aws-sdk-go-v2/credentials v1.16.4/go.mod h1:Kdh/okh+//vQ/AjEt81CjvkTo64+/zIE4OewP7RpfXk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5 h1:KehRNiVzIfAcj6gw98zotVbb/K67taJE0fkfgM6vzqU= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.5/go.mod h1:VhnExhw6uXy9QzetvpXDolo1/hjhx4u9qukBGkuUwjs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4 h1:LAm3Ycm9HJfbSCd5I+wqC2S9Ej7FPrgr5CQoOljJZcE= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.4/go.mod h1:xEhvbJcyUf/31yfGSQBe01fukXwXJ0gxDp7rLfymWE0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4 h1:4GV0kKZzUxiWxSVpn/9gwR0g21NF1Jsyduzo9rHgC/Q= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.4/go.mod h1:dYvTNAggxDZy6y1AF7YDwXsPuHFy/VNEpEI/2dWK9IU= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.137.1 h1:J/N4ydefXQZIwKBDPtvrhxrIuP/vaaYKnAsy3bKVIvU= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.137.1/go.mod h1:hrBzQzlQQRmiaeYRQPr0SdSx6fdqP+5YcGhb97LCt8M= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1 h1:rpkF4n0CyFcrJUG/rNNohoTmhtWlFTRI4BsZOh9PvLs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.1/go.mod h1:l9ymW25HOqymeU2m1gbUQ3rUIsTwKs8gYHXkqDQUhiI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4 h1:rdovz3rEu0vZKbzoMYPTehp0E8veoE9AyfzqCr5Eeao= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.4/go.mod h1:aYCGNjyUCUelhofxlZyj63srdxWUSsBSGg5l6MCuXuE= +github.com/aws/aws-sdk-go-v2/service/sso v1.17.3 h1:CdsSOGlFF3Pn+koXOIpTtvX7st0IuGsZ8kJqcWMlX54= +github.com/aws/aws-sdk-go-v2/service/sso v1.17.3/go.mod h1:oA6VjNsLll2eVuUoF2D+CMyORgNzPEW/3PyUdq6WQjI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1 h1:cbRqFTVnJV+KRpwFl76GJdIZJKKCdTPnjUZ7uWh3pIU= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.20.1/go.mod h1:hHL974p5auvXlZPIjJTblXJpbkfK4klBczlsEaMCGVY= +github.com/aws/aws-sdk-go-v2/service/sts v1.25.4 h1:yEvZ4neOQ/KpUqyR+X0ycUTW/kVRNR4nDZ38wStHGAA= +github.com/aws/aws-sdk-go-v2/service/sts v1.25.4/go.mod h1:feTnm2Tk/pJxdX+eooEsxvlvTWBvDm6CasRZ+JOs2IY= +github.com/aws/smithy-go v1.17.0 h1:wWJD7LX6PBV6etBUwO0zElG0nWN9rUhp0WdYeHSHAaI= +github.com/aws/smithy-go v1.17.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= @@ -28,7 +58,6 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/handler.go b/handler.go index 78189de..42f2c56 100644 --- a/handler.go +++ b/handler.go @@ -3,37 +3,56 @@ package elasticspot import ( "context" "encoding/json" - "errors" "fmt" - "os" "github.com/aws/aws-lambda-go/events" - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" ) -type V1Handler struct { - Ec2 EC2API - ElasticIP string +// Handler is a Lambda handler that associates an Elastic IP with an EC2 instance. +// For non-lambda environments, the Client may be used directly instead. +type Handler struct { + client associator + elasticIP string } -func NewV1Handler(ec2 EC2API, elasticIp string) *V1Handler { - return &V1Handler{Ec2: ec2, ElasticIP: elasticIp} +type associator interface { + AssociateIP(ctx context.Context, ip string, instanceID string) (*AssociateResponse, error) } -type EC2API interface { - DescribeAddresses(input *ec2.DescribeAddressesInput) (*ec2.DescribeAddressesOutput, error) - AssociateAddress(input *ec2.AssociateAddressInput) (*ec2.AssociateAddressOutput, error) - DescribeInstances(input *ec2.DescribeInstancesInput) (*ec2.DescribeInstancesOutput, error) +func NewV1Handler(ec2 EC2API, elasticIP string) *Handler { + return &Handler{client: &Client{EC2API: ec2}, elasticIP: elasticIP} } -type SuccessResponse struct { +type HandleFunc func(ctx context.Context, event *events.CloudWatchEvent) (*HandleResponse, error) + +// HandleV1 is a convenience function for creating an ElasticSpot lambda handler for v1 of the +// AWS SDK. +func HandleV1(ec2 EC2API, elasticIp string) HandleFunc { + h := NewV1Handler(ec2, elasticIp) + return h.Handle +} + +// HandleV2 is a convenience function for creating an ElasticSpot lambda handler for v2 of the +// AWS SDK. +func HandleV2(ec2 EC2APIV2, elasticIp string) HandleFunc { + h := NewV2Handler(ec2, elasticIp) + return h.Handle +} + +func NewV2Handler(ec2v2 EC2APIV2, elasticIP string) *Handler { + return &Handler{client: &ClientV2{EC2API: ec2v2}, elasticIP: elasticIP} +} + +type HandleResponse struct { InstanceId string `json:"instanceID,omitempty"` ElasticIP string `json:"elasticIP,omitempty"` Message string `json:"message,omitempty"` } -func (h *V1Handler) Handle(ctx context.Context, event *events.CloudWatchEvent) (*SuccessResponse, error) { +// Handle retrieves the EC2 instance ID from the CloudWatch event and associates the configured Elastic IP +// with the instance. If the Elastic IP is already associated with the instance, the handler will return +// a successful response with a message indicating that the Elastic IP is already associated with the instance. +func (h *Handler) Handle(ctx context.Context, event *events.CloudWatchEvent) (*HandleResponse, error) { var eventDetails EventDetails if err := json.Unmarshal(event.Detail, &eventDetails); err != nil { return nil, fmt.Errorf("error unmarshaling cloudwatch event: %w", err) @@ -41,79 +60,27 @@ func (h *V1Handler) Handle(ctx context.Context, event *events.CloudWatchEvent) ( instanceID := eventDetails.Ec2Instanceid - instance, err := h.getInstanceById(instanceID) + res, err := h.client.AssociateIP(ctx, h.elasticIP, instanceID) if err != nil { - return nil, fmt.Errorf("error fetching instance: %w", err) + return nil, err } - if *instance.PublicIpAddress == h.ElasticIP { - return &SuccessResponse{ - ElasticIP: h.ElasticIP, + if res.AlreadyAssociated { + return &HandleResponse{ + ElasticIP: h.elasticIP, InstanceId: instanceID, Message: "elastic ip already associated with instance id", }, nil } - address, err := h.getAddressForIp(h.ElasticIP) - if err != nil { - return nil, fmt.Errorf("error fetching elastic ip address: %w", err) - } + successMsg := fmt.Sprintf( + `Successfully allocated %s with instance %s. Allocation ID: %s Association ID: %s`, + h.elasticIP, instanceID, res.AllocationID, res.AssociationID, + ) - assocRes, err := h.Ec2.AssociateAddress(&ec2.AssociateAddressInput{ - AllocationId: address.AllocationId, - InstanceId: aws.String(instanceID), - }) - if err != nil { - return nil, fmt.Errorf("error associating elastic ip address with %q %w", instanceID, err) - } - - successMsg := fmt.Sprintf("Successfully allocated %s with instance %s.\n\tallocation id: %s, association id: %s\n", - *address.PublicIp, instanceID, *address.AllocationId, *assocRes.AssociationId) - - return &SuccessResponse{ - ElasticIP: h.ElasticIP, + return &HandleResponse{ + ElasticIP: h.elasticIP, InstanceId: eventDetails.Ec2Instanceid, Message: successMsg, }, nil } - -func (h *V1Handler) getInstanceById(id string) (*ec2.Instance, error) { - instances, err := h.Ec2.DescribeInstances(&ec2.DescribeInstancesInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("instance-id"), - Values: aws.StringSlice([]string{id}), - }, - }, - }) - if err != nil { - return nil, fmt.Errorf("DescribeInstances failed: %w", err) - } - - if instances == nil || len(instances.Reservations) == 0 || len(instances.Reservations[0].Instances) == 0 { - return nil, errors.New("no instance found for the given id") - } - - return instances.Reservations[0].Instances[0], nil -} - -func (h *V1Handler) getAddressForIp(ip string) (*ec2.Address, error) { - result, err := h.Ec2.DescribeAddresses(&ec2.DescribeAddressesInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("domain"), - Values: aws.StringSlice([]string{"vpc"}), - }, - }, - PublicIps: []*string{aws.String(ip)}, - }) - if err != nil { - return nil, err - } - - if len(result.Addresses) == 0 { - return nil, fmt.Errorf("elastic ip address not found in region %q", os.Getenv("AWS_REGION")) - } - - return result.Addresses[0], nil -} diff --git a/handler_test.go b/handler_test.go index 14ccc32..979319a 100644 --- a/handler_test.go +++ b/handler_test.go @@ -2,6 +2,7 @@ package elasticspot_test import ( "context" + "fmt" "testing" "github.com/nickfiggins/elasticspot" @@ -47,8 +48,11 @@ func TestHandler(t *testing.T) { }, }, }, - message: "Successfully allocated " + elasticIp + " with instance instance-id.\n\t" + - "allocation id: , association id: \n", + message: fmt.Sprintf( + `Successfully allocated %s with instance %s. Allocation ID: %s Association ID: %s`, + "192.0.0.1", "instance-id", "", ""), + //"Successfully allocated " + elasticIp + " with instance instance-id.\n\t" + + // "allocation id: , association id: \n", }, { scenario: "elastic ip already assigned to instance",