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 aws_appmesh_virtual_router resource #6720

Merged
merged 2 commits into from
Dec 5, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ func Provider() terraform.ResourceProvider {
"aws_appautoscaling_policy": resourceAwsAppautoscalingPolicy(),
"aws_appautoscaling_scheduled_action": resourceAwsAppautoscalingScheduledAction(),
"aws_appmesh_mesh": resourceAwsAppmeshMesh(),
"aws_appmesh_virtual_router": resourceAwsAppmeshVirtualRouter(),
"aws_appsync_api_key": resourceAwsAppsyncApiKey(),
"aws_appsync_datasource": resourceAwsAppsyncDatasource(),
"aws_appsync_graphql_api": resourceAwsAppsyncGraphqlApi(),
Expand Down
2 changes: 1 addition & 1 deletion aws/resource_aws_appmesh_mesh.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func resourceAwsAppmeshMeshRead(d *schema.ResourceData, meta interface{}) error
func resourceAwsAppmeshMeshDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).appmeshconn

log.Printf("[DEBUG] App Mesh service mesh: %s", d.Id())
log.Printf("[DEBUG] Deleting App Mesh service mesh: %s", d.Id())
_, err := conn.DeleteMesh(&appmesh.DeleteMeshInput{
MeshName: aws.String(d.Id()),
})
Expand Down
3 changes: 3 additions & 0 deletions aws/resource_aws_appmesh_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ func TestAccAWSAppmesh(t *testing.T) {
"Mesh": {
"basic": testAccAwsAppmeshMesh_basic,
},
"VirtualRouter": {
"basic": testAccAwsAppmeshVirtualRouter_basic,
},
}

for group, m := range testCases {
Expand Down
159 changes: 159 additions & 0 deletions aws/resource_aws_appmesh_virtual_router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package aws

import (
"fmt"
"log"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/appmesh"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAppmeshVirtualRouter() *schema.Resource {
return &schema.Resource{
Create: resourceAwsAppmeshVirtualRouterCreate,
Read: resourceAwsAppmeshVirtualRouterRead,
Update: resourceAwsAppmeshVirtualRouterUpdate,
Delete: resourceAwsAppmeshVirtualRouterDelete,

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"mesh_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"spec": {
Type: schema.TypeList,
Required: true,
MinItems: 1,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"service_names": {
Type: schema.TypeSet,
Required: true,
MinItems: 1,
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},
},
},
},

"arn": {
Type: schema.TypeString,
Computed: true,
},

"created_date": {
Type: schema.TypeString,
Computed: true,
},

"last_updated_date": {
Type: schema.TypeString,
Computed: true,
},
},
}
}

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

req := &appmesh.CreateVirtualRouterInput{
MeshName: aws.String(d.Get("mesh_name").(string)),
VirtualRouterName: aws.String(d.Get("name").(string)),
Spec: expandAppmeshVirtualRouterSpec(d.Get("spec").([]interface{})),
}

log.Printf("[DEBUG] Creating App Mesh virtual router: %#v", req)
resp, err := conn.CreateVirtualRouter(req)
if err != nil {
return fmt.Errorf("error creating App Mesh virtual router: %s", err)
}

d.SetId(aws.StringValue(resp.VirtualRouter.Metadata.Uid))

return resourceAwsAppmeshVirtualRouterUpdate(d, meta)
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be calling the Read function instead? Spec is already handled via Create

Suggested change
return resourceAwsAppmeshVirtualRouterUpdate(d, meta)
return resourceAwsAppmeshVirtualRouterRead(d, meta)

}

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

if !d.IsNewResource() && d.HasChange("spec") {
Copy link
Contributor

Choose a reason for hiding this comment

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

The d.IsNewResource() check here is extraneous if Read is called after Create

Suggested change
if !d.IsNewResource() && d.HasChange("spec") {
if d.HasChange("spec") {

_, v := d.GetChange("spec")
req := &appmesh.UpdateVirtualRouterInput{
MeshName: aws.String(d.Get("mesh_name").(string)),
VirtualRouterName: aws.String(d.Get("name").(string)),
Spec: expandAppmeshVirtualRouterSpec(v.([]interface{})),
}

log.Printf("[DEBUG] Updating App Mesh virtual router: %#v", req)
_, err := conn.UpdateVirtualRouter(req)
if err != nil {
return fmt.Errorf("error updating App Mesh virtual router: %s", err)
}
}

return resourceAwsAppmeshVirtualRouterRead(d, meta)
}

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

resp, err := conn.DescribeVirtualRouter(&appmesh.DescribeVirtualRouterInput{
MeshName: aws.String(d.Get("mesh_name").(string)),
VirtualRouterName: aws.String(d.Get("name").(string)),
})
if err != nil {
if isAWSErr(err, "NotFoundException", "") {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: SDK provided constant available:

Suggested change
if isAWSErr(err, "NotFoundException", "") {
if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") {

log.Printf("[WARN] App Mesh virtual router (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}
return fmt.Errorf("error reading App Mesh virtual router: %s", err)
}
if aws.StringValue(resp.VirtualRouter.Status.Status) == appmesh.VirtualRouterStatusCodeDeleted {
log.Printf("[WARN] App Mesh virtual router (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

d.Set("name", resp.VirtualRouter.VirtualRouterName)
d.Set("mesh_name", resp.VirtualRouter.MeshName)
d.Set("arn", resp.VirtualRouter.Metadata.Arn)
d.Set("created_date", resp.VirtualRouter.Metadata.CreatedAt.Format(time.RFC3339))
d.Set("last_updated_date", resp.VirtualRouter.Metadata.LastUpdatedAt.Format(time.RFC3339))
if err := d.Set("spec", flattenAppmeshVirtualRouterSpec(resp.VirtualRouter.Spec)); err != nil {
return err
}

return nil
}

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

log.Printf("[DEBUG] Deleting App Mesh virtual router: %s", d.Id())
_, err := conn.DeleteVirtualRouter(&appmesh.DeleteVirtualRouterInput{
MeshName: aws.String(d.Get("mesh_name").(string)),
VirtualRouterName: aws.String(d.Get("name").(string)),
})
if err != nil {
if isAWSErr(err, "NotFoundException", "") {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: SDK provided constant available:

Suggested change
if isAWSErr(err, "NotFoundException", "") {
if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") {

return nil
}
return fmt.Errorf("error deleting App Mesh virtual router: %s", err)
}

return nil
}
154 changes: 154 additions & 0 deletions aws/resource_aws_appmesh_virtual_router_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package aws

import (
"fmt"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/appmesh"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func testAccAwsAppmeshVirtualRouter_basic(t *testing.T) {
var vr appmesh.VirtualRouterData
resourceName := "aws_appmesh_virtual_router.foo"
meshName := fmt.Sprintf("tf-test-mesh-%d", acctest.RandInt())
vrName := fmt.Sprintf("tf-test-router-%d", acctest.RandInt())

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAppmeshVirtualRouterDestroy,
Steps: []resource.TestStep{
{
Config: testAccAppmeshVirtualRouterConfig(meshName, vrName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAppmeshVirtualRouterExists(
resourceName, &vr),
resource.TestCheckResourceAttr(
resourceName, "name", vrName),
resource.TestCheckResourceAttr(
resourceName, "mesh_name", meshName),
resource.TestCheckResourceAttr(
resourceName, "spec.#", "1"),
resource.TestCheckResourceAttr(
resourceName, "spec.0.service_names.#", "1"),
resource.TestCheckResourceAttr(
resourceName, "spec.0.service_names.423761483", "serviceb.simpleapp.local"),
resource.TestCheckResourceAttrSet(
resourceName, "created_date"),
resource.TestCheckResourceAttrSet(
resourceName, "last_updated_date"),
resource.TestMatchResourceAttr(
resourceName, "arn", regexp.MustCompile(fmt.Sprintf("^arn:[^:]+:appmesh:[^:]+:\\d{12}:mesh/%s/virtualRouter/%s", meshName, vrName))),
),
},
{
Config: testAccAppmeshVirtualRouterConfig_serviceNamesUpdated(meshName, vrName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAppmeshVirtualRouterExists(
resourceName, &vr),
resource.TestCheckResourceAttr(
resourceName, "name", vrName),
resource.TestCheckResourceAttr(
resourceName, "mesh_name", meshName),
resource.TestCheckResourceAttr(
resourceName, "spec.#", "1"),
resource.TestCheckResourceAttr(
resourceName, "spec.0.service_names.#", "2"),
resource.TestCheckResourceAttr(
resourceName, "spec.0.service_names.3826429429", "serviceb1.simpleapp.local"),
resource.TestCheckResourceAttr(
resourceName, "spec.0.service_names.3079206513", "serviceb2.simpleapp.local"),
),
},
},
})
}

func testAccCheckAppmeshVirtualRouterDestroy(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).appmeshconn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_appmesh_virtual_router" {
continue
}

_, err := conn.DescribeVirtualRouter(&appmesh.DescribeVirtualRouterInput{
MeshName: aws.String(rs.Primary.Attributes["mesh_name"]),
VirtualRouterName: aws.String(rs.Primary.Attributes["name"]),
})
if err != nil {
if isAWSErr(err, "NotFoundException", "") {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: SDK provided constant available:

Suggested change
if isAWSErr(err, "NotFoundException", "") {
if isAWSErr(err, appmesh.ErrCodeNotFoundException, "") {

return nil
}
return err
}
return fmt.Errorf("still exist.")
}

return nil
}

func testAccCheckAppmeshVirtualRouterExists(name string, v *appmesh.VirtualRouterData) resource.TestCheckFunc {
return func(s *terraform.State) error {
conn := testAccProvider.Meta().(*AWSClient).appmeshconn

rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("Not found: %s", name)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

resp, err := conn.DescribeVirtualRouter(&appmesh.DescribeVirtualRouterInput{
MeshName: aws.String(rs.Primary.Attributes["mesh_name"]),
VirtualRouterName: aws.String(rs.Primary.Attributes["name"]),
})
if err != nil {
return err
}

*v = *resp.VirtualRouter

return nil
}
}

func testAccAppmeshVirtualRouterConfig(meshName, vrName string) string {
return fmt.Sprintf(`
resource "aws_appmesh_mesh" "foo" {
name = "%s"
}

resource "aws_appmesh_virtual_router" "foo" {
name = "%s"
mesh_name = "${aws_appmesh_mesh.foo.id}"

spec {
service_names = ["serviceb.simpleapp.local"]
}
}
`, meshName, vrName)
}

func testAccAppmeshVirtualRouterConfig_serviceNamesUpdated(meshName, vrName string) string {
return fmt.Sprintf(`
resource "aws_appmesh_mesh" "foo" {
name = "%s"
}

resource "aws_appmesh_virtual_router" "foo" {
name = "%s"
mesh_name = "${aws_appmesh_mesh.foo.id}"

spec {
service_names = ["serviceb1.simpleapp.local", "serviceb2.simpleapp.local"]
}
}
`, meshName, vrName)
}
32 changes: 32 additions & 0 deletions aws/structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/private/protocol/json/jsonutil"
"github.com/aws/aws-sdk-go/service/apigateway"
"github.com/aws/aws-sdk-go/service/appmesh"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudformation"
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
Expand Down Expand Up @@ -4682,3 +4683,34 @@ func flattenRdsScalingConfigurationInfo(scalingConfigurationInfo *rds.ScalingCon

return []interface{}{m}
}

func expandAppmeshVirtualRouterSpec(v []interface{}) *appmesh.VirtualRouterSpec {
if len(v) == 0 || v[0] == nil {
return nil
}

m := v[0].(map[string]interface{})
spec := &appmesh.VirtualRouterSpec{}

if s, ok := m["service_names"].(*schema.Set); ok && s.Len() > 0 {
serviceNames := []string{}
for _, n := range s.List() {
serviceNames = append(serviceNames, n.(string))
}
spec.ServiceNames = aws.StringSlice(serviceNames)
}

return spec
}

func flattenAppmeshVirtualRouterSpec(spec *appmesh.VirtualRouterSpec) []interface{} {
if spec == nil || len(spec.ServiceNames) == 0 {
return []interface{}{}
}

m := map[string]interface{}{
"service_names": schema.NewSet(schema.HashString, flattenStringList(spec.ServiceNames)),
}

return []interface{}{m}
}
Loading