Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Lightsail load balancer resource #11405

Merged
merged 20 commits into from
Oct 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions aws/internal/service/lightsail/waiter/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package waiter

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

// LightsailOperationStatus is a method to check the status of a Lightsail Operation
func LightsailOperationStatus(conn *lightsail.Lightsail, oid *string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
input := &lightsail.GetOperationInput{
OperationId: oid,
}

oidValue := aws.StringValue(oid)
log.Printf("[DEBUG] Checking if Lightsail Operation (%s) is Completed", oidValue)

output, err := conn.GetOperation(input)

if err != nil {
return output, "FAILED", err
}

if output.Operation == nil {
return nil, "Failed", fmt.Errorf("Error retrieving Operation info for operation (%s)", oidValue)
}

log.Printf("[DEBUG] Lightsail Operation (%s) is currently %q", oidValue, *output.Operation.Status)
return output, *output.Operation.Status, nil
}
}
48 changes: 48 additions & 0 deletions aws/internal/service/lightsail/waiter/waiter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package waiter

import (
"time"

"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

const (
// OperationStatusNotStarted is a OperationStatus enum value
OperationStatusNotStarted = "NotStarted"
// OperationStatusStarted is a OperationStatus enum value
OperationStatusStarted = "Started"
// OperationStatusFailed is a OperationStatus enum value
OperationStatusFailed = "Failed"
// OperationStatusCompleted is a OperationStatus enum value
OperationStatusCompleted = "Completed"
// OperationStatusSucceeded is a OperationStatus enum value
OperationStatusSucceeded = "Succeeded"

// OperationTimeout is the Timout Value for Operations
OperationTimeout = 10 * time.Minute
// OperationDelay is the Delay Value for Operations
OperationDelay = 5 * time.Second
// OperationMinTimeout is the MinTimout Value for Operations
OperationMinTimeout = 3 * time.Second
)

// OperationCreated waits for an Operation to return Succeeded or Compleated
func OperationCreated(conn *lightsail.Lightsail, oid *string) (*lightsail.GetOperationOutput, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{OperationStatusStarted},
Target: []string{OperationStatusCompleted, OperationStatusSucceeded},
Refresh: LightsailOperationStatus(conn, oid),
Timeout: OperationTimeout,
Delay: OperationDelay,
MinTimeout: OperationMinTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.(*lightsail.GetOperationOutput); ok {
return output, err
}

return nil, err
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@ func Provider() *schema.Provider {
"aws_lightsail_domain": resourceAwsLightsailDomain(),
"aws_lightsail_instance": resourceAwsLightsailInstance(),
"aws_lightsail_key_pair": resourceAwsLightsailKeyPair(),
"aws_lightsail_load_balancer": resourceAwsLightsailLoadBalancer(),
"aws_lightsail_static_ip": resourceAwsLightsailStaticIp(),
"aws_lightsail_static_ip_attachment": resourceAwsLightsailStaticIpAttachment(),
"aws_lb_cookie_stickiness_policy": resourceAwsLBCookieStickinessPolicy(),
Expand Down
2 changes: 1 addition & 1 deletion aws/resource_aws_lightsail_instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ func TestAccAWSLightsailInstance_Tags(t *testing.T) {
})
}

func TestAccAWSLightsailInstance_disapear(t *testing.T) {
func TestAccAWSLightsailInstance_disappear(t *testing.T) {
var conf lightsail.Instance
lightsailName := fmt.Sprintf("tf-test-lightsail-%d", acctest.RandInt())

Expand Down
191 changes: 191 additions & 0 deletions aws/resource_aws_lightsail_load_balancer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package aws

import (
"fmt"
"log"
"regexp"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/lightsail"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/lightsail/waiter"
)

func resourceAwsLightsailLoadBalancer() *schema.Resource {
return &schema.Resource{
Create: resourceAwsLightsailLoadBalancerCreate,
Read: resourceAwsLightsailLoadBalancerRead,
Update: resourceAwsLightsailLoadBalancerUpdate,
Delete: resourceAwsLightsailLoadBalancerDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.All(
validation.StringLenBetween(2, 255),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z]`), "must begin with an alphabetic character"),
validation.StringMatch(regexp.MustCompile(`^[a-zA-Z0-9_\-.]+[^._\-]$`), "must contain only alphanumeric characters, underscores, hyphens, and dots"),
),
},
"health_check_path": {
Type: schema.TypeString,
Optional: true,
Default: "/",
},
"instance_port": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
ValidateFunc: validation.IntBetween(0, 65535),
},
"tags": tagsSchema(),
"arn": {
Type: schema.TypeString,
Computed: true,
},
"created_at": {
Type: schema.TypeString,
Computed: true,
},
"dns_name": {
Type: schema.TypeString,
Computed: true,
},
"protocol": {
Type: schema.TypeString,
Computed: true,
},
"public_ports": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeInt},
},
},
}
}

func resourceAwsLightsailLoadBalancerCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn

req := lightsail.CreateLoadBalancerInput{
HealthCheckPath: aws.String(d.Get("health_check_path").(string)),
InstancePort: aws.Int64(int64(d.Get("instance_port").(int))),
LoadBalancerName: aws.String(d.Get("name").(string)),
}

if v := d.Get("tags").(map[string]interface{}); len(v) > 0 {
req.Tags = keyvaluetags.New(v).IgnoreAws().LightsailTags()
}

resp, err := conn.CreateLoadBalancer(&req)
if err != nil {
return err
}

if len(resp.Operations) == 0 {
return fmt.Errorf("No operations found for CreateInstance request")
}

op := resp.Operations[0]
d.SetId(d.Get("name").(string))

_, err = waiter.OperationCreated(conn, op.Id)
if err != nil {
return fmt.Errorf("Error waiting for load balancer (%s) to become ready: %s", d.Id(), err)
}

return resourceAwsLightsailLoadBalancerRead(d, meta)
}

func resourceAwsLightsailLoadBalancerRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig

resp, err := conn.GetLoadBalancer(&lightsail.GetLoadBalancerInput{
LoadBalancerName: aws.String(d.Id()),
})

lb := resp.LoadBalancer

if err != nil {
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "NotFoundException" {
log.Printf("[WARN] Lightsail load balancer (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
return err
}
return err
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lb := resp.LoadBalancer

and reuse it for all others instead repeating it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the feedback, this change will be implemented once the following pull request is merged by @akonrath akonrath#96

d.Set("arn", lb.Arn)
d.Set("created_at", lb.CreatedAt.Format(time.RFC3339))
d.Set("health_check_path", lb.HealthCheckPath)
d.Set("instance_port", lb.InstancePort)
d.Set("name", lb.Name)
d.Set("protocol", lb.Protocol)
d.Set("public_ports", lb.PublicPorts)
d.Set("dns_name", lb.DnsName)

if err := d.Set("tags", keyvaluetags.LightsailKeyValueTags(lb.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil {
return fmt.Errorf("error setting tags: %s", err)
}

return nil
}

func resourceAwsLightsailLoadBalancerDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn
resp, err := conn.DeleteLoadBalancer(&lightsail.DeleteLoadBalancerInput{
LoadBalancerName: aws.String(d.Id()),
})

op := resp.Operations[0]

if err != nil {
return err
}

_, err = waiter.OperationCreated(conn, op.Id)
if err != nil {
return fmt.Errorf("Error waiting for load balancer (%s) to become destroyed: %s", d.Id(), err)
}

return err
}

func resourceAwsLightsailLoadBalancerUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).lightsailconn

if d.HasChange("health_check_path") {
_, err := conn.UpdateLoadBalancerAttribute(&lightsail.UpdateLoadBalancerAttributeInput{
AttributeName: aws.String("HealthCheckPath"),
AttributeValue: aws.String(d.Get("health_check_path").(string)),
LoadBalancerName: aws.String(d.Get("name").(string)),
})
d.Set("health_check_path", d.Get("health_check_path").(string))
if err != nil {
return err
}
}

if d.HasChange("tags") {
o, n := d.GetChange("tags")

if err := keyvaluetags.LightsailUpdateTags(conn, d.Id(), o, n); err != nil {
return fmt.Errorf("error updating Lightsail Instance (%s) tags: %s", d.Id(), err)
}
}

return resourceAwsLightsailLoadBalancerRead(d, meta)
}
Loading