-
Notifications
You must be signed in to change notification settings - Fork 9.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added support for ElastiCache reserved nodes
- Loading branch information
1 parent
c69d133
commit 47a1d8d
Showing
8 changed files
with
568 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package elasticache | ||
|
||
const ( | ||
ResNameTags = "Tags" | ||
) | ||
|
||
const ( | ||
ReservedCacheNodeStateActive = "active" | ||
ReservedCacheNodeStateRetired = "retired" | ||
ReservedCacheNodeStatePaymentPending = "payment-pending" | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
package elasticache | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws" | ||
"github.com/aws/aws-sdk-go/service/elasticache" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-provider-aws/internal/conns" | ||
"github.com/hashicorp/terraform-provider-aws/internal/create" | ||
tftags "github.com/hashicorp/terraform-provider-aws/internal/tags" | ||
"github.com/hashicorp/terraform-provider-aws/internal/tfresource" | ||
"github.com/hashicorp/terraform-provider-aws/internal/verify" | ||
"github.com/hashicorp/terraform-provider-aws/names" | ||
) | ||
|
||
const ( | ||
ResNameReservedCacheNode = "Reserved Cache Node" | ||
) | ||
|
||
// @SDKResource("aws_elasticache_reserved_cache_node") | ||
func ResourceReservedCacheNode() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateWithoutTimeout: resourceReservedCacheNodeCreate, | ||
ReadWithoutTimeout: resourceReservedCacheNodeRead, | ||
UpdateWithoutTimeout: resourceReservedCacheNodeUpdate, | ||
DeleteWithoutTimeout: resourceReservedCacheNodeDelete, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
Timeouts: &schema.ResourceTimeout{ | ||
Create: schema.DefaultTimeout(30 * time.Minute), | ||
Update: schema.DefaultTimeout(10 * time.Minute), | ||
Delete: schema.DefaultTimeout(1 * time.Minute), | ||
}, | ||
Schema: map[string]*schema.Schema{ | ||
"arn": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"cache_node_type": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"duration": { | ||
Type: schema.TypeInt, | ||
Computed: true, | ||
}, | ||
"fixed_price": { | ||
Type: schema.TypeFloat, | ||
Computed: true, | ||
}, | ||
"cache_node_count": { | ||
Type: schema.TypeInt, | ||
Optional: true, | ||
ForceNew: true, | ||
Default: 1, | ||
}, | ||
"offering_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
"offering_type": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"product_description": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"recurring_charges": { | ||
Type: schema.TypeList, | ||
Computed: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"recurring_charge_amount": { | ||
Type: schema.TypeInt, | ||
Computed: true, | ||
}, | ||
"recurring_charge_frequency": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
"reservation_id": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
ForceNew: true, | ||
}, | ||
"start_time": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"state": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"usage_price": { | ||
Type: schema.TypeFloat, | ||
Computed: true, | ||
}, | ||
"tags": tftags.TagsSchema(), | ||
"tags_all": tftags.TagsSchemaComputed(), | ||
}, | ||
|
||
CustomizeDiff: verify.SetTagsDiff, | ||
} | ||
} | ||
|
||
func resourceReservedCacheNodeCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn := meta.(*conns.AWSClient).ElastiCacheConn() | ||
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig | ||
tags := defaultTagsConfig.MergeTags(tftags.New(ctx, d.Get("tags").(map[string]interface{}))) | ||
|
||
input := &elasticache.PurchaseReservedCacheNodesOfferingInput{ | ||
ReservedCacheNodesOfferingId: aws.String(d.Get("offering_id").(string)), | ||
} | ||
|
||
if v, ok := d.Get("cache_node_count").(int); ok && v > 0 { | ||
input.CacheNodeCount = aws.Int64(int64(d.Get("cache_node_count").(int))) | ||
} | ||
|
||
if v, ok := d.Get("reservation_id").(string); ok && v != "" { | ||
input.ReservedCacheNodeId = aws.String(v) | ||
} | ||
|
||
if len(tags) > 0 { | ||
input.Tags = Tags(tags.IgnoreAWS()) | ||
} | ||
|
||
resp, err := conn.PurchaseReservedCacheNodesOfferingWithContext(ctx, input) | ||
if err != nil { | ||
return create.DiagError(names.ElastiCache, create.ErrActionCreating, ResNameReservedCacheNode, fmt.Sprintf("offering_id: %s, reservation_id: %s", d.Get("offering_id").(string), d.Get("reservation_id").(string)), err) | ||
} | ||
|
||
d.SetId(aws.ToString(resp.ReservedCacheNode.ReservedCacheNodeId)) | ||
|
||
if err := waitReservedCacheNodeCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { | ||
return create.DiagError(names.ElastiCache, create.ErrActionWaitingForCreation, ResNameReservedCacheNode, d.Id(), err) | ||
} | ||
|
||
return resourceReservedCacheNodeRead(ctx, d, meta) | ||
} | ||
|
||
func resourceReservedCacheNodeRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn := meta.(*conns.AWSClient).ElastiCacheConn() | ||
defaultTagsConfig := meta.(*conns.AWSClient).DefaultTagsConfig | ||
ignoreTagsConfig := meta.(*conns.AWSClient).IgnoreTagsConfig | ||
|
||
reservation, err := FindReservedCacheNodeByID(ctx, conn, d.Id()) | ||
|
||
if !d.IsNewResource() && tfresource.NotFound(err) { | ||
create.LogNotFoundRemoveState(names.ElastiCache, create.ErrActionReading, ResNameReservedCacheNode, d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
if err != nil { | ||
return create.DiagError(names.ElastiCache, create.ErrActionReading, ResNameReservedCacheNode, d.Id(), err) | ||
} | ||
|
||
d.Set("arn", reservation.ReservationARN) | ||
d.Set("cache_node_type", reservation.CacheNodeType) | ||
d.Set("duration", reservation.Duration) | ||
d.Set("fixed_price", reservation.FixedPrice) | ||
d.Set("cache_node_count", reservation.CacheNodeCount) | ||
d.Set("offering_id", reservation.ReservedCacheNodesOfferingId) | ||
d.Set("offering_type", reservation.OfferingType) | ||
d.Set("product_description", reservation.ProductDescription) | ||
d.Set("recurring_charges", flattenRecurringCharges(reservation.RecurringCharges)) | ||
d.Set("reservation_id", reservation.ReservedCacheNodeId) | ||
d.Set("start_time", (reservation.StartTime).Format(time.RFC3339)) | ||
d.Set("state", reservation.State) | ||
d.Set("usage_price", reservation.UsagePrice) | ||
|
||
tags, err := ListTags(ctx, conn, aws.ToString(reservation.ReservationARN)) | ||
tags = tags.IgnoreAWS().IgnoreConfig(ignoreTagsConfig) | ||
|
||
if err != nil { | ||
return create.DiagError(names.CE, create.ErrActionReading, ResNameTags, d.Id(), err) | ||
} | ||
|
||
//lintignore:AWSR002 | ||
if err := d.Set("tags", tags.RemoveDefaultConfig(defaultTagsConfig).Map()); err != nil { | ||
return create.DiagError(names.CE, create.ErrActionUpdating, ResNameTags, d.Id(), err) | ||
} | ||
|
||
if err := d.Set("tags_all", tags.Map()); err != nil { | ||
return create.DiagError(names.CE, create.ErrActionUpdating, ResNameTags, d.Id(), err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceReservedCacheNodeUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn := meta.(*conns.AWSClient).ElastiCacheConn() | ||
|
||
if d.HasChange("tags") { | ||
o, n := d.GetChange("tags") | ||
|
||
if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { | ||
return create.DiagError(names.ElastiCache, create.ErrActionUpdating, ResNameTags, d.Id(), err) | ||
} | ||
} | ||
|
||
if d.HasChange("tags_all") { | ||
o, n := d.GetChange("tags_all") | ||
|
||
if err := UpdateTags(ctx, conn, d.Get("arn").(string), o, n); err != nil { | ||
return create.DiagError(names.ElastiCache, create.ErrActionUpdating, ResNameTags, d.Id(), err) | ||
} | ||
} | ||
|
||
return resourceReservedCacheNodeRead(ctx, d, meta) | ||
} | ||
|
||
func resourceReservedCacheNodeDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
// Reservations cannot be deleted. Removing from state. | ||
log.Printf("[DEBUG] %s %s cannot be deleted. Removing from state.: %s", names.ElastiCache, ResNameReservedCacheNode, d.Id()) | ||
|
||
return nil | ||
} | ||
|
||
func flattenRecurringCharges(recurringCharges []*elasticache.RecurringCharge) []interface{} { | ||
if len(recurringCharges) == 0 { | ||
return []interface{}{} | ||
} | ||
|
||
var rawRecurringCharges []interface{} | ||
for _, recurringCharge := range recurringCharges { | ||
rawRecurringCharge := map[string]interface{}{ | ||
"recurring_charge_amount": recurringCharge.RecurringChargeAmount, | ||
"recurring_charge_frequency": aws.ToString(recurringCharge.RecurringChargeFrequency), | ||
} | ||
|
||
rawRecurringCharges = append(rawRecurringCharges, rawRecurringCharge) | ||
} | ||
|
||
return rawRecurringCharges | ||
} |
97 changes: 97 additions & 0 deletions
97
internal/service/elasticache/reserved_cache_node_offering_data_source.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package elasticache | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
|
||
"github.com/aws/aws-sdk-go-v2/aws" | ||
"github.com/aws/aws-sdk-go/service/elasticache" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
"github.com/hashicorp/terraform-provider-aws/internal/conns" | ||
"github.com/hashicorp/terraform-provider-aws/internal/create" | ||
"github.com/hashicorp/terraform-provider-aws/names" | ||
) | ||
|
||
const ( | ||
ResNameReservedCacheNodeOffering = "Reserved Cache Node Offering" | ||
) | ||
|
||
// @SDKDataSource("aws_elasticache_reserved_cache_node_offering") | ||
func DataSourceReservedCacheNodeOffering() *schema.Resource { | ||
return &schema.Resource{ | ||
ReadWithoutTimeout: dataSourceReservedCacheNodeOfferingRead, | ||
Schema: map[string]*schema.Schema{ | ||
"cache_node_type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"duration": { | ||
Type: schema.TypeInt, | ||
Required: true, | ||
}, | ||
"fixed_price": { | ||
Type: schema.TypeFloat, | ||
Computed: true, | ||
}, | ||
"offering_id": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"offering_type": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
"Light Utilization", | ||
"Medium Utilization", | ||
"Heavy Utilization", | ||
"Partial Upfront", | ||
"All Upfront", | ||
"No Upfront", | ||
}, false), | ||
}, | ||
"product_description": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceReservedCacheNodeOfferingRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
conn := meta.(*conns.AWSClient).ElastiCacheConn() | ||
|
||
input := &elasticache.DescribeReservedCacheNodesOfferingsInput{ | ||
CacheNodeType: aws.String(d.Get("cache_node_type").(string)), | ||
Duration: aws.String(fmt.Sprint(d.Get("duration").(int))), | ||
OfferingType: aws.String(d.Get("offering_type").(string)), | ||
ProductDescription: aws.String(d.Get("product_description").(string)), | ||
} | ||
|
||
resp, err := conn.DescribeReservedCacheNodesOfferingsWithContext(ctx, input) | ||
|
||
if err != nil { | ||
return create.DiagError(names.ElastiCache, create.ErrActionReading, ResNameReservedCacheNodeOffering, "unknown", err) | ||
} | ||
|
||
if len(resp.ReservedCacheNodesOfferings) == 0 { | ||
return diag.Errorf("no %s %s found matching criteria; try different search", names.ElastiCache, ResNameReservedCacheNodeOffering) | ||
} | ||
|
||
if len(resp.ReservedCacheNodesOfferings) > 1 { | ||
return diag.Errorf("More than one %s %s found matching criteria; try different search", names.ElastiCache, ResNameReservedCacheNodeOffering) | ||
} | ||
|
||
offering := resp.ReservedCacheNodesOfferings[0] | ||
|
||
d.SetId(aws.ToString(offering.ReservedCacheNodesOfferingId)) | ||
d.Set("cache_node_type", offering.CacheNodeType) | ||
d.Set("duration", offering.Duration) | ||
d.Set("fixed_price", offering.FixedPrice) | ||
d.Set("offering_type", offering.OfferingType) | ||
d.Set("product_description", offering.ProductDescription) | ||
d.Set("offering_id", offering.ReservedCacheNodesOfferingId) | ||
|
||
return nil | ||
} |
Oops, something went wrong.