-
Notifications
You must be signed in to change notification settings - Fork 9.3k
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
New Resource: aws_appsync_api_key #3827
Changes from 3 commits
c9965af
f05211a
09bea43
dc0a6ab
980784a
4ace375
76d141d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"strings" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/appsync" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
) | ||
|
||
func resourceAwsAppsyncApiKey() *schema.Resource { | ||
|
||
return &schema.Resource{ | ||
Create: resourceAwsAppsyncApiKeyCreate, | ||
Read: resourceAwsAppsyncApiKeyRead, | ||
Update: resourceAwsAppsyncApiKeyUpdate, | ||
Delete: resourceAwsAppsyncApiKeyDelete, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"description": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "Managed by Terraform", | ||
}, | ||
"appsync_api_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"valid_till_date": { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few notes here:
|
||
Type: schema.TypeString, | ||
ConflictsWith: []string{"validity_period_days"}, | ||
Optional: true, | ||
ValidateFunc: func(v interface{}, k string) (ws []string, es []error) { | ||
value := v.(string) | ||
// reference - http://www.regexlib.com/REDetails.aspx?regexp_id=409 | ||
if !regexp.MustCompile(`^(((0[1-9]|[12]\d|3[01])\/(0[13578]|1[02])\/((1[6-9]|[2-9]\d)\d{2}))|((0[1-9]|[12]\d|30)\/(0[13456789]|1[012])\/((1[6-9]|[2-9]\d)\d{2}))|((0[1-9]|1\d|2[0-8])\/02\/((1[6-9]|[2-9]\d)\d{2}))|(29\/02\/((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))))$`).MatchString(value) { | ||
es = append(es, fmt.Errorf( | ||
"only dd/mm/yyyy in %q", k)) | ||
} | ||
return | ||
}, | ||
}, | ||
"validity_period_days": { | ||
Type: schema.TypeInt, | ||
ConflictsWith: []string{"valid_till_date"}, | ||
Optional: true, | ||
}, | ||
"expiry_date": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"key": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
Sensitive: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceAwsAppsyncApiKeyCreate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).appsyncconn | ||
params := &appsync.CreateApiKeyInput{ | ||
ApiId: aws.String(d.Get("appsync_api_id").(string)), | ||
Description: aws.String(d.Get("description").(string)), | ||
} | ||
layout := "02/01/2006 15:04:05 -0700 MST" | ||
if v, ok := d.GetOk("validity_period_days"); ok { | ||
params.Expires = aws.Int64(time.Now().Add(time.Hour * 24 * time.Duration(v.(int))).Unix()) | ||
} | ||
if v, ok := d.GetOk("valid_till_date"); ok { | ||
tx := strings.Split(time.Now().Format(layout), " ") | ||
tx[0] = v.(string) | ||
t, _ := time.Parse(layout, strings.Join(tx, " ")) | ||
params.Expires = aws.Int64(t.Unix()) | ||
} | ||
|
||
resp, err := conn.CreateApiKey(params) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(*resp.ApiKey.Id) | ||
return resourceAwsAppsyncApiKeyRead(d, meta) | ||
} | ||
|
||
func resourceAwsAppsyncApiKeyRead(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).appsyncconn | ||
|
||
input := &appsync.ListApiKeysInput{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to account for |
||
ApiId: aws.String(d.Get("appsync_api_id").(string)), | ||
} | ||
|
||
resp, err := conn.ListApiKeys(input) | ||
if err != nil { | ||
return err | ||
} | ||
var key appsync.ApiKey | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is not accounting for if the key is not found in the // After exhausting all ListApiKeys searching
if key == nil {
log.Printf("[WARN] AppSync API Key %q not found, removing from state", d.Id())
d.SetId("")
return nil
}
d.Set("key", key.Id)
// ... |
||
for _, v := range resp.ApiKeys { | ||
if *v.Id == d.Id() { | ||
key = *v | ||
} | ||
} | ||
|
||
d.Set("key", key.Id) | ||
d.Set("description", key.Description) | ||
d.Set("expiry_date", time.Unix(*key.Expires, 0).String()) | ||
return nil | ||
} | ||
|
||
func resourceAwsAppsyncApiKeyUpdate(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).appsyncconn | ||
|
||
params := &appsync.UpdateApiKeyInput{ | ||
ApiId: aws.String(d.Get("appsync_api_id").(string)), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the API key seems a subset of the API ID, we should probably add this ID to the Terraform resource ID. In the Create function (if d.SetId(fmt.Sprintf("%s:%s"), d.Get("appsync_api_id").(string), *resp.ApiKey.Id) We can then parse it with a helper function, e.g. func decodeAppSyncApiKeyId(id string) (string, string, error) {
parts := strings.Split(id, ":")
if len(parts) != 2 {
return "", "", fmt.Errorf("Unexpected format of ID (%q), expected API-ID:API-KEY-ID", id)
}
return parts[0], parts[1], nil
} And anywhere necessary to call that: apiID, apiKeyID, err := decodeAppSyncApiKeyId(d.Id())
if err != nil {
return err
} This will allow us to support importing easily with: Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
}, |
||
Id: aws.String(d.Id()), | ||
} | ||
if d.HasChange("description") { | ||
params.Description = aws.String(d.Get("description").(string)) | ||
} | ||
if v, ok := d.GetOk("validity_period_days"); ok { | ||
|
||
if d.HasChange("validity_period_days") { | ||
params.Expires = aws.Int64(time.Now().Add(time.Hour * 24 * time.Duration(v.(int))).Unix()) | ||
} | ||
} | ||
if v, ok := d.GetOk("valid_till_date"); ok { | ||
layout := "02/01/2006 15:04:05 -0700 MST" | ||
if d.HasChange("valid_till_date") { | ||
tx := strings.Split(time.Now().Format(layout), " ") | ||
tx[0] = v.(string) | ||
t, _ := time.Parse(layout, strings.Join(tx, " ")) | ||
params.Expires = aws.Int64(t.Unix()) | ||
|
||
} | ||
} | ||
|
||
_, err := conn.UpdateApiKey(params) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return resourceAwsAppsyncApiKeyRead(d, meta) | ||
|
||
} | ||
|
||
func resourceAwsAppsyncApiKeyDelete(d *schema.ResourceData, meta interface{}) error { | ||
conn := meta.(*AWSClient).appsyncconn | ||
|
||
input := &appsync.DeleteApiKeyInput{ | ||
ApiId: aws.String(d.Get("appsync_api_id").(string)), | ||
Id: aws.String(d.Id()), | ||
} | ||
_, err := conn.DeleteApiKey(input) | ||
if err != nil { | ||
if isAWSErr(err, appsync.ErrCodeNotFoundException, "") { | ||
return nil | ||
} | ||
return err | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package aws | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"github.com/aws/aws-sdk-go/aws" | ||
"github.com/aws/aws-sdk-go/service/appsync" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccAwsAppsyncApiKey_basic(t *testing.T) { | ||
// sample date to test | ||
dateAfterOneYear := time.Now().Add(time.Hour * 24 * time.Duration(360)).Format("02/01/2006") | ||
// test sample date against time of expiry | ||
layout := "02/01/2006 15:04:05 -0700 MST" | ||
tx := strings.Split(time.Now().Format(layout), " ") | ||
tx[0] = dateAfterOneYear | ||
timeAfterOneYear, _ := time.Parse(layout, strings.Join(tx, " ")) | ||
|
||
thirtyDays := "30" | ||
timeAfterThirdyDays := time.Now().Add(time.Hour * 24 * 30) | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
CheckDestroy: testAccCheckAwsAppsyncApiKeyDestroy, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testAccAppsyncApiKeyConfigValidTillDate(dateAfterOneYear), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAwsAppsyncApiKeyExistsTillDate( | ||
"aws_appsync_graphql_api.test1", | ||
"aws_appsync_api_key.test_valid_till_date", | ||
timeAfterOneYear.Unix(), | ||
), | ||
), | ||
}, | ||
{ | ||
Config: testAccAppsyncApiKeyConfigValidityPeriodDays(thirtyDays), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckAwsAppsyncApiKeyExistsTillDate( | ||
"aws_appsync_graphql_api.test2", | ||
"aws_appsync_api_key.test_validity_period_days", | ||
timeAfterThirdyDays.Unix(), | ||
), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckAwsAppsyncApiKeyDestroy(s *terraform.State) error { | ||
conn := testAccProvider.Meta().(*AWSClient).appsyncconn | ||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "aws_appsync_api_key" { | ||
continue | ||
} | ||
|
||
describe, err := conn.ListApiKeys(&appsync.ListApiKeysInput{}) | ||
|
||
if err == nil { | ||
if len(describe.ApiKeys) != 0 && | ||
*describe.ApiKeys[0].Id == rs.Primary.ID { | ||
return fmt.Errorf("Appsync ApiKey still exists") | ||
} | ||
return err | ||
} | ||
|
||
} | ||
return nil | ||
} | ||
|
||
func testAccCheckAwsAppsyncApiKeyExistsTillDate(GqlApiName string, ApiKeyName string, date int64) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rsGql, ok := s.RootModule().Resources[GqlApiName] | ||
if !ok { | ||
return fmt.Errorf("Gql Not found in state: %s", GqlApiName) | ||
} | ||
|
||
rsApiKey, ok := s.RootModule().Resources[ApiKeyName] | ||
if !ok { | ||
return fmt.Errorf("Key Not found in state: %s", ApiKeyName) | ||
} | ||
|
||
conn := testAccProvider.Meta().(*AWSClient).appsyncconn | ||
|
||
input := &appsync.ListApiKeysInput{ | ||
ApiId: aws.String(rsGql.Primary.ID), | ||
} | ||
|
||
resp, err := conn.ListApiKeys(input) | ||
if err != nil { | ||
return err | ||
} | ||
var key appsync.ApiKey | ||
for _, v := range resp.ApiKeys { | ||
if *v.Id == *aws.String(rsApiKey.Primary.ID) { | ||
key = *v | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would recommend splitting this logic into two functions: func testAccCheckAwsAppsyncApiKeyExists(resourceName string, apiKey *appsync.ApiKey) which calls Then separately: func testAccCheckAwsAppsyncApiKeyTillDate(apiKey *appsync.ApiKey, date time.Time) which handles the date/time logic below. |
||
} | ||
} | ||
if key.Id == nil { | ||
return fmt.Errorf("Key Not found: %s %s", ApiKeyName, *aws.String(rsApiKey.Primary.ID)) | ||
} | ||
// aws when they create, slight difference will be in the minutes, so better check date | ||
if time.Unix(*key.Expires, 0).Format("02/01/2006") != time.Unix(date, 0).Format("02/01/2006") { | ||
|
||
return fmt.Errorf("Expiry date got is: %s and expected is %s", time.Unix(*key.Expires, 0).Format("02/01/2006"), | ||
time.Unix(date, 0).Format("02/01/2006")) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func testAccAppsyncApiKeyConfigValidTillDate(rDate string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_appsync_graphql_api" "test1" { | ||
authentication_type = "API_KEY" | ||
name = "tf_appsync_test1" | ||
} | ||
resource "aws_appsync_api_key" "test_valid_till_date" { | ||
appsync_api_id = "${aws_appsync_graphql_api.test1.id}" | ||
valid_till_date = "%s" | ||
} | ||
|
||
`, rDate) | ||
} | ||
|
||
func testAccAppsyncApiKeyConfigValidityPeriodDays(rDays string) string { | ||
return fmt.Sprintf(` | ||
resource "aws_appsync_graphql_api" "test2" { | ||
authentication_type = "API_KEY" | ||
name = "tf_appsync_test2" | ||
} | ||
|
||
resource "aws_appsync_api_key" "test_validity_period_days" { | ||
appsync_api_id = "${aws_appsync_graphql_api.test2.id}" | ||
validity_period_days = %s | ||
} | ||
|
||
`, rDays) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--- | ||
layout: "aws" | ||
page_title: "AWS: aws_appsync_api_key" | ||
sidebar_current: "docs-aws-resource-appsync-api-key" | ||
description: |- | ||
Provides an AppSync API Key. | ||
--- | ||
|
||
# aws_appsync_api_key | ||
|
||
Provides an AppSync API Key. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "aws_appsync_graphql_api" "example" { | ||
authentication_type = "API_KEY" | ||
name = "example" | ||
} | ||
resource "aws_appsync_api_key" "self" { | ||
appsync_api_id = "${aws_appsync_graphql_api.example.id}" | ||
validity_period_days = 364 | ||
} | ||
resource "aws_appsync_api_key" "partner" { | ||
appsync_api_id = "${aws_appsync_graphql_api.example.id}" | ||
valid_till_date = "30/11/2018" | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `appsync_api_id` - (Required) The ID of the associated AppSync API | ||
* `description` - (Optional) The API key description. Defaults to "Managed by Terraform". | ||
* `validity_period_days` - (Optional) The number of days of validity from date of creation, (aws supports less than 365 days). By default, it is 7 days. | ||
* `valid_till_date` - (Optional) The date till the key should be valid, use the format dd/mm/yyyy. | ||
|
||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
* `id` - API ID | ||
* `key` - The API key |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to prepend
appsync_
to this attribute name. 👍