From bd0b46f511d0dceaa60d4bdba2e01671925321cd Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Sun, 12 Nov 2017 07:59:26 +0000 Subject: [PATCH 1/3] New Resource: aws_mq_configuration --- aws/config.go | 3 + aws/provider.go | 1 + aws/resource_aws_mq_configuration.go | 178 ++++++++++++++++++++++ aws/resource_aws_mq_configuration_test.go | 174 +++++++++++++++++++++ 4 files changed, 356 insertions(+) create mode 100644 aws/resource_aws_mq_configuration.go create mode 100644 aws/resource_aws_mq_configuration_test.go diff --git a/aws/config.go b/aws/config.go index f0bcb8a5992..de0b223f347 100644 --- a/aws/config.go +++ b/aws/config.go @@ -58,6 +58,7 @@ import ( "github.com/aws/aws-sdk-go/service/kms" "github.com/aws/aws-sdk-go/service/lambda" "github.com/aws/aws-sdk-go/service/lightsail" + "github.com/aws/aws-sdk-go/service/mq" "github.com/aws/aws-sdk-go/service/opsworks" "github.com/aws/aws-sdk-go/service/rds" "github.com/aws/aws-sdk-go/service/redshift" @@ -172,6 +173,7 @@ type AWSClient struct { elastictranscoderconn *elastictranscoder.ElasticTranscoder lambdaconn *lambda.Lambda lightsailconn *lightsail.Lightsail + mqconn *mq.MQ opsworksconn *opsworks.OpsWorks glacierconn *glacier.Glacier codebuildconn *codebuild.CodeBuild @@ -406,6 +408,7 @@ func (c *Config) Client() (interface{}, error) { client.kmsconn = kms.New(awsKmsSess) client.lambdaconn = lambda.New(sess) client.lightsailconn = lightsail.New(sess) + client.mqconn = mq.New(sess) client.opsworksconn = opsworks.New(sess) client.r53conn = route53.New(r53Sess) client.rdsconn = rds.New(awsRdsSess) diff --git a/aws/provider.go b/aws/provider.go index f5bb4bf6743..b264c7a4b43 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -398,6 +398,7 @@ func Provider() terraform.ResourceProvider { "aws_load_balancer_listener_policy": resourceAwsLoadBalancerListenerPolicies(), "aws_lb_ssl_negotiation_policy": resourceAwsLBSSLNegotiationPolicy(), "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), + "aws_mq_configuration": resourceAwsMqConfiguration(), "aws_nat_gateway": resourceAwsNatGateway(), "aws_network_acl": resourceAwsNetworkAcl(), "aws_default_network_acl": resourceAwsDefaultNetworkAcl(), diff --git a/aws/resource_aws_mq_configuration.go b/aws/resource_aws_mq_configuration.go new file mode 100644 index 00000000000..265a02d6c3f --- /dev/null +++ b/aws/resource_aws_mq_configuration.go @@ -0,0 +1,178 @@ +package aws + +import ( + "encoding/base64" + "fmt" + "log" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsMqConfiguration() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsMqConfigurationCreate, + Read: resourceAwsMqConfigurationRead, + Update: resourceAwsMqConfigurationUpdate, + Delete: resourceAwsMqConfigurationDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { + if diff.HasChange("description") { + return diff.SetNewComputed("latest_revision") + } + if diff.HasChange("data") { + o, n := diff.GetChange("data") + os := o.(string) + ns := n.(string) + if !suppressXMLEquivalentConfig("data", os, ns, nil) { + return diff.SetNewComputed("latest_revision") + } + } + return nil + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "data": { + Type: schema.TypeString, + Required: true, + DiffSuppressFunc: suppressXMLEquivalentConfig, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "engine_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "engine_version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "latest_revision": { + Type: schema.TypeInt, + Computed: true, + }, + }, + } +} + +func resourceAwsMqConfigurationCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + input := mq.CreateConfigurationRequest{ + EngineType: aws.String(d.Get("engine_type").(string)), + EngineVersion: aws.String(d.Get("engine_version").(string)), + Name: aws.String(d.Get("name").(string)), + } + + log.Printf("[INFO] Creating MQ Configuration: %s", input) + out, err := conn.CreateConfiguration(&input) + if err != nil { + return err + } + + d.SetId(*out.Id) + d.Set("arn", out.Arn) + + return resourceAwsMqConfigurationUpdate(d, meta) +} + +func resourceAwsMqConfigurationRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + log.Printf("[INFO] Reading MQ Configuration %s", d.Id()) + out, err := conn.DescribeConfiguration(&mq.DescribeConfigurationInput{ + ConfigurationId: aws.String(d.Id()), + }) + if err != nil { + if isAWSErr(err, "NotFoundException", "") { + log.Printf("[WARN] MQ Configuration %q not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + d.Set("arn", out.Arn) + d.Set("description", out.LatestRevision.Description) + d.Set("engine_type", out.EngineType) + d.Set("engine_version", out.EngineVersion) + d.Set("name", out.Name) + d.Set("latest_revision", out.LatestRevision.Revision) + + rOut, err := conn.DescribeConfigurationRevision(&mq.DescribeConfigurationRevisionInput{ + ConfigurationId: aws.String(d.Id()), + ConfigurationRevision: aws.String(fmt.Sprintf("%d", *out.LatestRevision.Revision)), + }) + if err != nil { + return err + } + + b, err := base64.StdEncoding.DecodeString(*rOut.Data) + if err != nil { + return err + } + + d.Set("data", string(b)) + + return nil +} + +func resourceAwsMqConfigurationUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + rawData := d.Get("data").(string) + data := base64.StdEncoding.EncodeToString([]byte(rawData)) + + input := mq.UpdateConfigurationRequest{ + ConfigurationId: aws.String(d.Id()), + Data: aws.String(data), + } + if v, ok := d.GetOk("description"); ok { + input.Description = aws.String(v.(string)) + } + + log.Printf("[INFO] Updating MQ Configuration %s: %s", d.Id(), input) + _, err := conn.UpdateConfiguration(&input) + if err != nil { + return err + } + + return resourceAwsMqConfigurationRead(d, meta) +} + +func resourceAwsMqConfigurationDelete(d *schema.ResourceData, meta interface{}) error { + // TODO: Delete is not available in the API + + return nil +} + +func suppressXMLEquivalentConfig(k, old, new string, d *schema.ResourceData) bool { + os, err := canonicalXML(old) + if err != nil { + log.Printf("[ERR] Error getting cannonicalXML from state (%s): %s", k, err) + return false + } + ns, err := canonicalXML(new) + if err != nil { + log.Printf("[ERR] Error getting cannonicalXML from config (%s): %s", k, err) + return false + } + + return os == ns +} diff --git a/aws/resource_aws_mq_configuration_test.go b/aws/resource_aws_mq_configuration_test.go new file mode 100644 index 00000000000..ba2182b1f43 --- /dev/null +++ b/aws/resource_aws_mq_configuration_test.go @@ -0,0 +1,174 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAwsMqConfiguration_basic(t *testing.T) { + configurationName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsMqConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMqConfigurationConfig(configurationName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqConfigurationExists("aws_mq_configuration.test"), + resource.TestCheckResourceAttrSet("aws_mq_configuration.test", "arn"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "description", "TfAccTest MQ Configuration"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "engine_type", "ActiveMQ"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "engine_version", "5.15.0"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "latest_revision", "2"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "name", configurationName), + ), + }, + { + Config: testAccMqConfigurationConfig_descriptionUpdated(configurationName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqConfigurationExists("aws_mq_configuration.test"), + resource.TestCheckResourceAttrSet("aws_mq_configuration.test", "arn"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "description", "TfAccTest MQ Configuration Updated"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "engine_type", "ActiveMQ"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "engine_version", "5.15.0"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "latest_revision", "3"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "name", configurationName), + ), + }, + }, + }) +} + +func TestAccAwsMqConfiguration_withData(t *testing.T) { + configurationName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsMqConfigurationDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMqConfigurationWithDataConfig(configurationName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqConfigurationExists("aws_mq_configuration.test"), + resource.TestCheckResourceAttrSet("aws_mq_configuration.test", "arn"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "description", "TfAccTest MQ Configuration"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "engine_type", "ActiveMQ"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "engine_version", "5.15.0"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "latest_revision", "2"), + resource.TestCheckResourceAttr("aws_mq_configuration.test", "name", configurationName), + ), + }, + }, + }) +} + +func testAccCheckAwsMqConfigurationDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).mqconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_mq_configuration" { + continue + } + + input := &mq.DescribeConfigurationInput{ + ConfigurationId: aws.String(rs.Primary.ID), + } + + _, err := conn.DescribeConfiguration(input) + if err != nil { + if isAWSErr(err, "NotFoundException", "") { + return nil + } + return err + } + + // TODO: Delete is not available in the API + return nil + //return fmt.Errorf("Expected MQ configuration to be destroyed, %s found", rs.Primary.ID) + } + + return nil +} + +func testAccCheckAwsMqConfigurationExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +func testAccMqConfigurationConfig(configurationName string) string { + return fmt.Sprintf(` +resource "aws_mq_configuration" "test" { + description = "TfAccTest MQ Configuration" + name = "%s" + engine_type = "ActiveMQ" + engine_version = "5.15.0" + data = < + + +DATA +}`, configurationName) +} + +func testAccMqConfigurationConfig_descriptionUpdated(configurationName string) string { + return fmt.Sprintf(` +resource "aws_mq_configuration" "test" { + description = "TfAccTest MQ Configuration Updated" + name = "%s" + engine_type = "ActiveMQ" + engine_version = "5.15.0" + data = < + + +DATA +}`, configurationName) +} + +func testAccMqConfigurationWithDataConfig(configurationName string) string { + return fmt.Sprintf(` +resource "aws_mq_configuration" "test" { + description = "TfAccTest MQ Configuration" + name = "%s" + engine_type = "ActiveMQ" + engine_version = "5.15.0" + data = < + + + + + + + + + + + + + + + + + + + + +DATA +}`, configurationName) +} From 6106552b62362801145791033dda8da2eab9edf4 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 28 Nov 2017 17:36:00 -0800 Subject: [PATCH 2/3] New Resource: aws_mq_broker --- aws/provider.go | 1 + aws/resource_aws_mq_broker.go | 533 ++++++++++++++++++++++++++ aws/resource_aws_mq_broker_test.go | 587 +++++++++++++++++++++++++++++ aws/structure.go | 129 +++++++ 4 files changed, 1250 insertions(+) create mode 100644 aws/resource_aws_mq_broker.go create mode 100644 aws/resource_aws_mq_broker_test.go diff --git a/aws/provider.go b/aws/provider.go index b264c7a4b43..56400620eed 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -398,6 +398,7 @@ func Provider() terraform.ResourceProvider { "aws_load_balancer_listener_policy": resourceAwsLoadBalancerListenerPolicies(), "aws_lb_ssl_negotiation_policy": resourceAwsLBSSLNegotiationPolicy(), "aws_main_route_table_association": resourceAwsMainRouteTableAssociation(), + "aws_mq_broker": resourceAwsMqBroker(), "aws_mq_configuration": resourceAwsMqConfiguration(), "aws_nat_gateway": resourceAwsNatGateway(), "aws_network_acl": resourceAwsNetworkAcl(), diff --git a/aws/resource_aws_mq_broker.go b/aws/resource_aws_mq_broker.go new file mode 100644 index 00000000000..0bbcd2d48b3 --- /dev/null +++ b/aws/resource_aws_mq_broker.go @@ -0,0 +1,533 @@ +package aws + +import ( + "bytes" + "fmt" + "log" + "reflect" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/mitchellh/copystructure" +) + +func resourceAwsMqBroker() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsMqBrokerCreate, + Read: resourceAwsMqBrokerRead, + Update: resourceAwsMqBrokerUpdate, + Delete: resourceAwsMqBrokerDelete, + + Schema: map[string]*schema.Schema{ + "apply_immediately": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "auto_minor_version_upgrade": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "broker_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "configuration": { + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "revision": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + }, + }, + }, + }, + "deployment_mode": { + Type: schema.TypeString, + Optional: true, + Default: "SINGLE_INSTANCE", + ForceNew: true, + }, + "engine_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "engine_version": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "host_instance_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "maintenance_window_start_time": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + ForceNew: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "day_of_week": { + Type: schema.TypeString, + Required: true, + }, + "time_of_day": { + Type: schema.TypeString, + Required: true, + }, + "time_zone": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + "publicly_accessible": { + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + "security_groups": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Required: true, + ForceNew: true, + }, + "subnet_ids": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Computed: true, + ForceNew: true, + }, + "user": { + Type: schema.TypeSet, + Required: true, + Set: resourceAwsMqUserHash, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "console_access": { + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "groups": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + Optional: true, + }, + "password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "username": { + Type: schema.TypeString, + Required: true, + }, + }, + }, + }, + + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "instances": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "console_url": { + Type: schema.TypeString, + Computed: true, + }, + "endpoints": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + }, + }, + }, + } +} + +func resourceAwsMqBrokerCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + name := d.Get("broker_name").(string) + requestId := resource.PrefixedUniqueId(fmt.Sprintf("tf-%s", name)) + input := mq.CreateBrokerRequest{ + AutoMinorVersionUpgrade: aws.Bool(d.Get("auto_minor_version_upgrade").(bool)), + BrokerName: aws.String(name), + CreatorRequestId: aws.String(requestId), + EngineType: aws.String(d.Get("engine_type").(string)), + EngineVersion: aws.String(d.Get("engine_version").(string)), + HostInstanceType: aws.String(d.Get("host_instance_type").(string)), + PubliclyAccessible: aws.Bool(d.Get("publicly_accessible").(bool)), + SecurityGroups: expandStringSet(d.Get("security_groups").(*schema.Set)), + Users: expandMqUsers(d.Get("user").(*schema.Set).List()), + } + + if v, ok := d.GetOk("configuration"); ok { + input.Configuration = expandMqConfigurationId(v.([]interface{})) + } + if v, ok := d.GetOk("deployment_mode"); ok { + input.DeploymentMode = aws.String(v.(string)) + } + if v, ok := d.GetOk("maintenance_window_start_time"); ok { + input.MaintenanceWindowStartTime = expandMqWeeklyStartTime(v.([]interface{})) + } + if v, ok := d.GetOk("subnet_ids"); ok { + input.SubnetIds = expandStringList(v.(*schema.Set).List()) + } + + log.Printf("[INFO] Creating MQ Broker: %s", input) + out, err := conn.CreateBroker(&input) + if err != nil { + return err + } + + d.SetId(*out.BrokerId) + d.Set("arn", out.BrokerArn) + + stateConf := resource.StateChangeConf{ + Pending: []string{ + mq.BrokerStateCreationInProgress, + mq.BrokerStateRebootInProgress, + }, + Target: []string{mq.BrokerStateRunning}, + Timeout: 30 * time.Minute, + Refresh: func() (interface{}, string, error) { + out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ + BrokerId: aws.String(d.Id()), + }) + if err != nil { + return 42, "", err + } + + return out, *out.BrokerState, nil + }, + } + _, err = stateConf.WaitForState() + if err != nil { + return err + } + + return resourceAwsMqBrokerRead(d, meta) +} + +func resourceAwsMqBrokerRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + log.Printf("[INFO] Reading MQ Broker: %s", d.Id()) + out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ + BrokerId: aws.String(d.Id()), + }) + if err != nil { + if isAWSErr(err, "NotFoundException", "") { + log.Printf("[WARN] MQ Broker %q not found, removing from state", d.Id()) + d.SetId("") + return nil + } + // API docs say a 404 can also return a 403 + if isAWSErr(err, "ForbiddenException", "Forbidden") { + log.Printf("[WARN] MQ Broker %q not found, removing from state", d.Id()) + d.SetId("") + return nil + } + return err + } + + d.Set("auto_minor_version_upgrade", out.AutoMinorVersionUpgrade) + d.Set("arn", out.BrokerArn) + d.Set("instances", flattenMqBrokerInstances(out.BrokerInstances)) + d.Set("broker_name", out.BrokerName) + d.Set("deployment_mode", out.DeploymentMode) + d.Set("engine_type", out.EngineType) + d.Set("engine_version", out.EngineVersion) + d.Set("host_instance_type", out.HostInstanceType) + d.Set("publicly_accessible", out.PubliclyAccessible) + err = d.Set("maintenance_window_start_time", flattenMqWeeklyStartTime(out.MaintenanceWindowStartTime)) + if err != nil { + return err + } + d.Set("security_groups", aws.StringValueSlice(out.SecurityGroups)) + d.Set("subnet_ids", aws.StringValueSlice(out.SubnetIds)) + + err = d.Set("configuration", flattenMqConfigurationId(out.Configurations.Current)) + if err != nil { + return err + } + + rawUsers := make([]*mq.User, len(out.Users), len(out.Users)) + for i, u := range out.Users { + uOut, err := conn.DescribeUser(&mq.DescribeUserInput{ + BrokerId: aws.String(d.Id()), + Username: u.Username, + }) + if err != nil { + return err + } + + rawUsers[i] = &mq.User{ + ConsoleAccess: uOut.ConsoleAccess, + Groups: uOut.Groups, + Username: uOut.Username, + } + } + + users := flattenMqUsers(rawUsers, d.Get("user").(*schema.Set).List()) + err = d.Set("user", users) + if err != nil { + return err + } + + return nil +} + +func resourceAwsMqBrokerUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + if d.HasChange("configuration") { + _, err := conn.UpdateBroker(&mq.UpdateBrokerRequest{ + BrokerId: aws.String(d.Id()), + Configuration: expandMqConfigurationId(d.Get("configuration").([]interface{})), + }) + if err != nil { + return err + } + } + + if d.HasChange("user") { + o, n := d.GetChange("user") + err := updateAwsMqBrokerUsers(conn, d.Id(), + o.(*schema.Set).List(), n.(*schema.Set).List()) + if err != nil { + return err + } + } + + if d.Get("apply_immediately").(bool) { + _, err := conn.RebootBroker(&mq.RebootBrokerInput{ + BrokerId: aws.String(d.Id()), + }) + if err != nil { + return err + } + + stateConf := resource.StateChangeConf{ + Pending: []string{ + mq.BrokerStateRunning, + mq.BrokerStateRebootInProgress, + }, + Target: []string{mq.BrokerStateRunning}, + Timeout: 30 * time.Minute, + Refresh: func() (interface{}, string, error) { + out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ + BrokerId: aws.String(d.Id()), + }) + if err != nil { + return 42, "", err + } + + return out, *out.BrokerState, nil + }, + } + _, err = stateConf.WaitForState() + if err != nil { + return err + } + } + + return nil +} + +func resourceAwsMqBrokerDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).mqconn + + log.Printf("[INFO] Deleting MQ Broker: %s", d.Id()) + _, err := conn.DeleteBroker(&mq.DeleteBrokerInput{ + BrokerId: aws.String(d.Id()), + }) + if err != nil { + return err + } + + return waitForMqBrokerDeletion(conn, d.Id()) +} + +func resourceAwsMqUserHash(v interface{}) int { + var buf bytes.Buffer + + m := v.(map[string]interface{}) + buf.WriteString(fmt.Sprintf("%t-", m["console_access"].(bool))) + if g, ok := m["groups"]; ok { + buf.WriteString(fmt.Sprintf("%v-", g.(*schema.Set).List())) + } + if p, ok := m["password"]; ok { + buf.WriteString(fmt.Sprintf("%s-", p.(string))) + } + buf.WriteString(fmt.Sprintf("%s-", m["username"].(string))) + + return hashcode.String(buf.String()) +} + +func waitForMqBrokerDeletion(conn *mq.MQ, id string) error { + stateConf := resource.StateChangeConf{ + Pending: []string{ + mq.BrokerStateRunning, + mq.BrokerStateRebootInProgress, + mq.BrokerStateDeletionInProgress, + }, + Target: []string{""}, + Timeout: 30 * time.Minute, + Refresh: func() (interface{}, string, error) { + out, err := conn.DescribeBroker(&mq.DescribeBrokerInput{ + BrokerId: aws.String(id), + }) + if err != nil { + if isAWSErr(err, "NotFoundException", "") { + return 42, "", nil + } + return 42, "", err + } + + return out, *out.BrokerState, nil + }, + } + _, err := stateConf.WaitForState() + return err +} + +func updateAwsMqBrokerUsers(conn *mq.MQ, bId string, oldUsers, newUsers []interface{}) error { + createL, deleteL, updateL, err := diffAwsMqBrokerUsers(bId, oldUsers, newUsers) + if err != nil { + return err + } + + for _, c := range createL { + _, err := conn.CreateUser(c) + if err != nil { + return err + } + } + for _, d := range deleteL { + _, err := conn.DeleteUser(d) + if err != nil { + return err + } + } + for _, u := range updateL { + _, err := conn.UpdateUser(u) + if err != nil { + return err + } + } + + return nil +} + +func diffAwsMqBrokerUsers(bId string, oldUsers, newUsers []interface{}) ( + cr []*mq.CreateUserRequest, di []*mq.DeleteUserInput, ur []*mq.UpdateUserRequest, e error) { + + existingUsers := make(map[string]interface{}, 0) + for _, ou := range oldUsers { + u := ou.(map[string]interface{}) + username := u["username"].(string) + // Convert Set to slice to allow easier comparison + if g, ok := u["groups"]; ok { + groups := g.(*schema.Set).List() + u["groups"] = groups + } + + existingUsers[username] = u + } + + for _, nu := range newUsers { + // Still need access to the original map + // because Set contents doesn't get copied + // Likely related to https://github.com/mitchellh/copystructure/issues/17 + nuOriginal := nu.(map[string]interface{}) + + // Create a mutable copy + newUser, err := copystructure.Copy(nu) + if err != nil { + e = err + return + } + + newUserMap := newUser.(map[string]interface{}) + username := newUserMap["username"].(string) + + // Convert Set to slice to allow easier comparison + var ng []interface{} + if g, ok := nuOriginal["groups"]; ok { + ng = g.(*schema.Set).List() + newUserMap["groups"] = ng + } + + if eu, ok := existingUsers[username]; ok { + + existingUserMap := eu.(map[string]interface{}) + + if !reflect.DeepEqual(existingUserMap, newUserMap) { + ur = append(ur, &mq.UpdateUserRequest{ + BrokerId: aws.String(bId), + ConsoleAccess: aws.Bool(newUserMap["console_access"].(bool)), + Groups: expandStringList(ng), + Password: aws.String(newUserMap["password"].(string)), + Username: aws.String(username), + }) + } + + // Delete after processing, so we know what's left for deletion + delete(existingUsers, username) + } else { + cur := &mq.CreateUserRequest{ + BrokerId: aws.String(bId), + ConsoleAccess: aws.Bool(newUserMap["console_access"].(bool)), + Password: aws.String(newUserMap["password"].(string)), + Username: aws.String(username), + } + if len(ng) > 0 { + cur.Groups = expandStringList(ng) + } + cr = append(cr, cur) + } + } + + for username, _ := range existingUsers { + di = append(di, &mq.DeleteUserInput{ + BrokerId: aws.String(bId), + Username: aws.String(username), + }) + } + + return +} diff --git a/aws/resource_aws_mq_broker_test.go b/aws/resource_aws_mq_broker_test.go new file mode 100644 index 00000000000..0ef11a88f43 --- /dev/null +++ b/aws/resource_aws_mq_broker_test.go @@ -0,0 +1,587 @@ +package aws + +import ( + "fmt" + "log" + "regexp" + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/mq" + "github.com/hashicorp/terraform/helper/acctest" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/terraform" +) + +func init() { + resource.AddTestSweepers("aws_mq_broker", &resource.Sweeper{ + Name: "aws_mq_broker", + F: testSweepMqBrokers, + }) +} + +func testSweepMqBrokers(region string) error { + client, err := sharedClientForRegion(region) + if err != nil { + return fmt.Errorf("error getting client: %s", err) + } + conn := client.(*AWSClient).mqconn + + resp, err := conn.ListBrokers(&mq.ListBrokersInput{ + MaxResults: aws.Int64(100), + }) + if err != nil { + return fmt.Errorf("Error listing MQ brokers: %s", err) + } + + if len(resp.BrokerSummaries) == 0 { + log.Print("[DEBUG] No MQ brokers found to sweep") + return nil + } + log.Printf("[DEBUG] %d MQ brokers found", len(resp.BrokerSummaries)) + + for _, bs := range resp.BrokerSummaries { + if !strings.HasPrefix(*bs.BrokerName, "tf-acc-test-") { + continue + } + + log.Printf("[INFO] Deleting MQ broker %s", *bs.BrokerId) + _, err := conn.DeleteBroker(&mq.DeleteBrokerInput{ + BrokerId: bs.BrokerId, + }) + if err != nil { + return err + } + err = waitForMqBrokerDeletion(conn, *bs.BrokerId) + if err != nil { + return err + } + } + + return nil +} + +func TestDiffAwsMqBrokerUsers(t *testing.T) { + testCases := []struct { + OldUsers []interface{} + NewUsers []interface{} + + Creations []*mq.CreateUserRequest + Deletions []*mq.DeleteUserInput + Updates []*mq.UpdateUserRequest + }{ + { + OldUsers: []interface{}{}, + NewUsers: []interface{}{ + map[string]interface{}{ + "console_access": false, + "username": "second", + "password": "TestTest2222", + "groups": schema.NewSet(schema.HashString, []interface{}{"admin"}), + }, + }, + Creations: []*mq.CreateUserRequest{ + { + BrokerId: aws.String("test"), + ConsoleAccess: aws.Bool(false), + Username: aws.String("second"), + Password: aws.String("TestTest2222"), + Groups: aws.StringSlice([]string{"admin"}), + }, + }, + Deletions: []*mq.DeleteUserInput{}, + Updates: []*mq.UpdateUserRequest{}, + }, + { + OldUsers: []interface{}{ + map[string]interface{}{ + "console_access": true, + "username": "first", + "password": "TestTest1111", + }, + }, + NewUsers: []interface{}{ + map[string]interface{}{ + "console_access": false, + "username": "second", + "password": "TestTest2222", + }, + }, + Creations: []*mq.CreateUserRequest{ + { + BrokerId: aws.String("test"), + ConsoleAccess: aws.Bool(false), + Username: aws.String("second"), + Password: aws.String("TestTest2222"), + }, + }, + Deletions: []*mq.DeleteUserInput{ + {BrokerId: aws.String("test"), Username: aws.String("first")}, + }, + Updates: []*mq.UpdateUserRequest{}, + }, + { + OldUsers: []interface{}{ + map[string]interface{}{ + "console_access": true, + "username": "first", + "password": "TestTest1111updated", + }, + map[string]interface{}{ + "console_access": false, + "username": "second", + "password": "TestTest2222", + }, + }, + NewUsers: []interface{}{ + map[string]interface{}{ + "console_access": false, + "username": "second", + "password": "TestTest2222", + "groups": schema.NewSet(schema.HashString, []interface{}{"admin"}), + }, + }, + Creations: []*mq.CreateUserRequest{}, + Deletions: []*mq.DeleteUserInput{ + {BrokerId: aws.String("test"), Username: aws.String("first")}, + }, + Updates: []*mq.UpdateUserRequest{ + { + BrokerId: aws.String("test"), + ConsoleAccess: aws.Bool(false), + Username: aws.String("second"), + Password: aws.String("TestTest2222"), + Groups: aws.StringSlice([]string{"admin"}), + }, + }, + }, + } + + for _, tc := range testCases { + creations, deletions, updates, err := diffAwsMqBrokerUsers("test", tc.OldUsers, tc.NewUsers) + if err != nil { + t.Fatal(err) + } + + expectedCreations := fmt.Sprintf("%s", tc.Creations) + creationsString := fmt.Sprintf("%s", creations) + if creationsString != expectedCreations { + t.Fatalf("Expected creations: %s\nGiven: %s", expectedCreations, creationsString) + } + + expectedDeletions := fmt.Sprintf("%s", tc.Deletions) + deletionsString := fmt.Sprintf("%s", deletions) + if deletionsString != expectedDeletions { + t.Fatalf("Expected deletions: %s\nGiven: %s", expectedDeletions, deletionsString) + } + + expectedUpdates := fmt.Sprintf("%s", tc.Updates) + updatesString := fmt.Sprintf("%s", updates) + if updatesString != expectedUpdates { + t.Fatalf("Expected updates: %s\nGiven: %s", expectedUpdates, updatesString) + } + } +} + +func TestAccAwsMqBroker_basic(t *testing.T) { + sgName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + brokerName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsMqBrokerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMqBrokerConfig(sgName, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "auto_minor_version_upgrade", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "broker_name", brokerName), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.#", "1"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "configuration.0.id", regexp.MustCompile(`^c-[a-z0-9-]+$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "configuration.0.revision", regexp.MustCompile(`^[0-9]+$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "deployment_mode", "SINGLE_INSTANCE"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "engine_type", "ActiveMQ"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "engine_version", "5.15.0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "host_instance_type", "mq.t2.micro"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.0.day_of_week", "MONDAY"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.0.time_of_day", "01:00"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.0.time_zone", "UTC"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "publicly_accessible", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "security_groups.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "subnet_ids.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.console_access", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.groups.#", "0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.username", "Test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.password", "TestTest1234"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "arn", + regexp.MustCompile("^arn:aws:mq:[a-z0-9-]+:[0-9]{12}:broker:[a-z0-9-]+:[a-f0-9-]+$")), + resource.TestCheckResourceAttr("aws_mq_broker.test", "instances.#", "1"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.console_url", + regexp.MustCompile(`^https://[a-f0-9-]+\.mq.[a-z0-9-]+.amazonaws.com:8162$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "instances.0.endpoints.#", "5"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.0", regexp.MustCompile(`^ssl://[a-z0-9-\.]+:61617$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.1", regexp.MustCompile(`^amqp\+ssl://[a-z0-9-\.]+:5671$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.2", regexp.MustCompile(`^stomp\+ssl://[a-z0-9-\.]+:61614$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.3", regexp.MustCompile(`^mqtt\+ssl://[a-z0-9-\.]+:8883$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.4", regexp.MustCompile(`^wss://[a-z0-9-\.]+:61619$`)), + ), + }, + }, + }) +} + +func TestAccAwsMqBroker_allFields(t *testing.T) { + sgName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + cfgNameBefore := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + cfgNameAfter := fmt.Sprintf("tf-acc-test-updated-%s", acctest.RandString(5)) + brokerName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + + cfgBodyBefore := ` + +` + cfgBodyAfter := ` + + + + + + +` + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsMqBrokerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMqBrokerConfig_allFields(sgName, cfgNameBefore, cfgBodyBefore, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "auto_minor_version_upgrade", "true"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "broker_name", brokerName), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.#", "1"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "configuration.0.id", regexp.MustCompile(`^c-[a-z0-9-]+$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.0.revision", "2"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "deployment_mode", "ACTIVE_STANDBY_MULTI_AZ"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "engine_type", "ActiveMQ"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "engine_version", "5.15.0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "host_instance_type", "mq.t2.micro"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.0.day_of_week", "TUESDAY"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.0.time_of_day", "02:00"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "maintenance_window_start_time.0.time_zone", "CET"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "publicly_accessible", "true"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "security_groups.#", "2"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "subnet_ids.#", "2"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.#", "2"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.console_access", "true"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.groups.#", "3"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.groups.2456940119", "first"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.groups.3055489385", "second"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.groups.607264868", "third"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.password", "SecondTestTest1234"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1344916805.username", "SecondTest"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.console_access", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.groups.#", "0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.password", "TestTest1234"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3793764891.username", "Test"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "arn", + regexp.MustCompile("^arn:aws:mq:[a-z0-9-]+:[0-9]{12}:broker:[a-z0-9-]+:[a-f0-9-]+$")), + resource.TestCheckResourceAttr("aws_mq_broker.test", "instances.#", "2"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.console_url", + regexp.MustCompile(`^https://[a-f0-9-]+\.mq.[a-z0-9-]+.amazonaws.com:8162$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "instances.0.endpoints.#", "5"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.0", regexp.MustCompile(`^ssl://[a-z0-9-\.]+:61617$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.1", regexp.MustCompile(`^amqp\+ssl://[a-z0-9-\.]+:5671$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.2", regexp.MustCompile(`^stomp\+ssl://[a-z0-9-\.]+:61614$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.3", regexp.MustCompile(`^mqtt\+ssl://[a-z0-9-\.]+:8883$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.0.endpoints.4", regexp.MustCompile(`^wss://[a-z0-9-\.]+:61619$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.1.console_url", + regexp.MustCompile(`^https://[a-f0-9-]+\.mq.[a-z0-9-]+.amazonaws.com:8162$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "instances.1.endpoints.#", "5"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.1.endpoints.0", regexp.MustCompile(`^ssl://[a-z0-9-\.]+:61617$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.1.endpoints.1", regexp.MustCompile(`^amqp\+ssl://[a-z0-9-\.]+:5671$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.1.endpoints.2", regexp.MustCompile(`^stomp\+ssl://[a-z0-9-\.]+:61614$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.1.endpoints.3", regexp.MustCompile(`^mqtt\+ssl://[a-z0-9-\.]+:8883$`)), + resource.TestMatchResourceAttr("aws_mq_broker.test", "instances.1.endpoints.4", regexp.MustCompile(`^wss://[a-z0-9-\.]+:61619$`)), + ), + }, + { + // Update configuration in-place + Config: testAccMqBrokerConfig_allFields(sgName, cfgNameBefore, cfgBodyAfter, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "broker_name", brokerName), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.#", "1"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "configuration.0.id", regexp.MustCompile(`^c-[a-z0-9-]+$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.0.revision", "3"), + ), + }, + { + // Replace configuration + Config: testAccMqBrokerConfig_allFields(sgName, cfgNameAfter, cfgBodyAfter, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "broker_name", brokerName), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.#", "1"), + resource.TestMatchResourceAttr("aws_mq_broker.test", "configuration.0.id", regexp.MustCompile(`^c-[a-z0-9-]+$`)), + resource.TestCheckResourceAttr("aws_mq_broker.test", "configuration.0.revision", "2"), + ), + }, + }, + }) +} + +func TestAccAwsMqBroker_updateUsers(t *testing.T) { + sgName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + brokerName := fmt.Sprintf("tf-acc-test-%s", acctest.RandString(5)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsMqBrokerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccMqBrokerConfig_updateUsers1(sgName, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3400735725.console_access", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3400735725.groups.#", "0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3400735725.password", "TestTest1111"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.3400735725.username", "first"), + ), + }, + // Adding new user + modify existing + { + Config: testAccMqBrokerConfig_updateUsers2(sgName, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.#", "2"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1074486012.console_access", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1074486012.groups.#", "0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1074486012.password", "TestTest2222"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1074486012.username", "second"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1166726986.console_access", "true"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1166726986.groups.#", "0"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1166726986.password", "TestTest1111updated"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.1166726986.username", "first"), + ), + }, + // Deleting user + modify existing + { + Config: testAccMqBrokerConfig_updateUsers3(sgName, brokerName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsMqBrokerExists("aws_mq_broker.test"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.2244717082.console_access", "false"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.2244717082.groups.#", "1"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.2244717082.groups.2282622326", "admin"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.2244717082.password", "TestTest2222"), + resource.TestCheckResourceAttr("aws_mq_broker.test", "user.2244717082.username", "second"), + ), + }, + }, + }) +} + +func testAccCheckAwsMqBrokerDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).mqconn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_mq_broker" { + continue + } + + input := &mq.DescribeBrokerInput{ + BrokerId: aws.String(rs.Primary.ID), + } + + _, err := conn.DescribeBroker(input) + if err != nil { + if isAWSErr(err, "NotFoundException", "") { + return nil + } + return err + } + + return fmt.Errorf("Expected MQ Broker to be destroyed, %s found", rs.Primary.ID) + } + + return nil +} + +func testAccCheckAwsMqBrokerExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + return nil + } +} + +func testAccMqBrokerConfig(sgName, brokerName string) string { + return fmt.Sprintf(` +resource "aws_security_group" "test" { + name = "%s" +} + +resource "aws_mq_broker" "test" { + broker_name = "%s" + engine_type = "ActiveMQ" + engine_version = "5.15.0" + host_instance_type = "mq.t2.micro" + security_groups = ["${aws_security_group.test.id}"] + user { + username = "Test" + password = "TestTest1234" + } +}`, sgName, brokerName) +} + +func testAccMqBrokerConfig_allFields(sgName, cfgName, cfgBody, brokerName string) string { + return fmt.Sprintf(` +data "aws_availability_zones" "available" {} + +resource "aws_vpc" "main" { + cidr_block = "10.11.0.0/16" + tags { + Name = "TfAccTest-MqBroker" + } +} + +resource "aws_subnet" "private" { + count = 2 + cidr_block = "10.11.${count.index}.0/24" + availability_zone = "${data.aws_availability_zones.available.names[count.index]}" + vpc_id = "${aws_vpc.main.id}" + tags { + Name = "TfAccTest-MqBroker" + } +} + +resource "aws_security_group" "mq1" { + name = "%s-1" + vpc_id = "${aws_vpc.main.id}" +} + +resource "aws_security_group" "mq2" { + name = "%s-2" + vpc_id = "${aws_vpc.main.id}" +} + +resource "aws_mq_configuration" "test" { + name = "%s" + engine_type = "ActiveMQ" + engine_version = "5.15.0" + data = < 0 { + m["groups"] = schema.NewSet(schema.HashString, flattenStringList(u.Groups)) + } + out = append(out, m) + } + return schema.NewSet(resourceAwsMqUserHash, out) +} + +func expandMqWeeklyStartTime(cfg []interface{}) *mq.WeeklyStartTime { + if len(cfg) < 1 { + return nil + } + + m := cfg[0].(map[string]interface{}) + return &mq.WeeklyStartTime{ + DayOfWeek: aws.String(m["day_of_week"].(string)), + TimeOfDay: aws.String(m["time_of_day"].(string)), + TimeZone: aws.String(m["time_zone"].(string)), + } +} + +func flattenMqWeeklyStartTime(wst *mq.WeeklyStartTime) []interface{} { + if wst == nil { + return []interface{}{} + } + m := make(map[string]interface{}, 0) + if wst.DayOfWeek != nil { + m["day_of_week"] = *wst.DayOfWeek + } + if wst.TimeOfDay != nil { + m["time_of_day"] = *wst.TimeOfDay + } + if wst.TimeZone != nil { + m["time_zone"] = *wst.TimeZone + } + return []interface{}{m} +} + +func expandMqConfigurationId(cfg []interface{}) *mq.ConfigurationId { + if len(cfg) < 1 { + return nil + } + + m := cfg[0].(map[string]interface{}) + out := mq.ConfigurationId{ + Id: aws.String(m["id"].(string)), + } + if v, ok := m["revision"].(int); ok && v > 0 { + out.Revision = aws.Int64(int64(v)) + } + + return &out +} + +func flattenMqConfigurationId(cid *mq.ConfigurationId) []interface{} { + if cid == nil { + return []interface{}{} + } + m := make(map[string]interface{}, 0) + if cid.Id != nil { + m["id"] = *cid.Id + } + if cid.Revision != nil { + m["revision"] = *cid.Revision + } + return []interface{}{m} +} + +func flattenMqBrokerInstances(instances []*mq.BrokerInstance) []interface{} { + if len(instances) == 0 { + return []interface{}{} + } + l := make([]interface{}, len(instances), len(instances)) + for i, instance := range instances { + m := make(map[string]interface{}, 0) + if instance.ConsoleURL != nil { + m["console_url"] = *instance.ConsoleURL + } + if len(instance.Endpoints) > 0 { + m["endpoints"] = aws.StringValueSlice(instance.Endpoints) + } + l[i] = m + } + + return l +} From 04a46fb968d9f5b67049bbfb9372fe1363a8df82 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Tue, 28 Nov 2017 16:16:23 -0800 Subject: [PATCH 3/3] docs: Add MQ docs --- website/aws.erb | 12 ++ website/docs/r/mq_broker.html.markdown | 104 ++++++++++++++++++ website/docs/r/mq_configuration.html.markdown | 49 +++++++++ 3 files changed, 165 insertions(+) create mode 100644 website/docs/r/mq_broker.html.markdown create mode 100644 website/docs/r/mq_configuration.html.markdown diff --git a/website/aws.erb b/website/aws.erb index d82eb2734f6..4c8b9ebb329 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1109,6 +1109,18 @@ + > + MQ Resources + + + > OpsWorks Resources