diff --git a/builtin/providers/rabbitmq/import_exchange_test.go b/builtin/providers/rabbitmq/import_exchange_test.go new file mode 100644 index 000000000..8fb8ac53b --- /dev/null +++ b/builtin/providers/rabbitmq/import_exchange_test.go @@ -0,0 +1,34 @@ +package rabbitmq + +import ( + "testing" + + "github.com/michaelklishin/rabbit-hole" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccExchange_importBasic(t *testing.T) { + resourceName := "rabbitmq_exchange.test" + var exchangeInfo rabbithole.ExchangeInfo + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccExchangeCheckDestroy(&exchangeInfo), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccExchangeConfig_basic, + Check: testAccExchangeCheck( + resourceName, &exchangeInfo, + ), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/builtin/providers/rabbitmq/provider.go b/builtin/providers/rabbitmq/provider.go index 98d45a6e0..99bf6ac40 100644 --- a/builtin/providers/rabbitmq/provider.go +++ b/builtin/providers/rabbitmq/provider.go @@ -72,6 +72,7 @@ func Provider() terraform.ResourceProvider { }, ResourcesMap: map[string]*schema.Resource{ + "rabbitmq_exchange": resourceExchange(), "rabbitmq_permissions": resourcePermissions(), "rabbitmq_user": resourceUser(), "rabbitmq_vhost": resourceVhost(), diff --git a/builtin/providers/rabbitmq/resource_exchange.go b/builtin/providers/rabbitmq/resource_exchange.go new file mode 100644 index 000000000..238a59e74 --- /dev/null +++ b/builtin/providers/rabbitmq/resource_exchange.go @@ -0,0 +1,189 @@ +package rabbitmq + +import ( + "fmt" + "log" + "strings" + + "github.com/michaelklishin/rabbit-hole" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceExchange() *schema.Resource { + return &schema.Resource{ + Create: CreateExchange, + Read: ReadExchange, + Delete: DeleteExchange, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + + "vhost": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + Default: "/", + ForceNew: true, + }, + + "settings": &schema.Schema{ + Type: schema.TypeList, + Required: true, + ForceNew: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "durable": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "auto_delete": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + + "arguments": &schema.Schema{ + Type: schema.TypeMap, + Optional: true, + }, + }, + }, + }, + }, + } +} + +func CreateExchange(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + name := d.Get("name").(string) + vhost := d.Get("vhost").(string) + settingsList := d.Get("settings").([]interface{}) + + settingsMap, ok := settingsList[0].(map[string]interface{}) + if !ok { + return fmt.Errorf("Unable to parse settings") + } + + if err := declareExchange(rmqc, vhost, name, settingsMap); err != nil { + return err + } + + id := fmt.Sprintf("%s@%s", name, vhost) + d.SetId(id) + + return ReadExchange(d, meta) +} + +func ReadExchange(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + exchangeId := strings.Split(d.Id(), "@") + if len(exchangeId) < 2 { + return fmt.Errorf("Unable to determine exchange ID") + } + + name := exchangeId[0] + vhost := exchangeId[1] + + exchangeSettings, err := rmqc.GetExchange(vhost, name) + if err != nil { + return checkDeleted(d, err) + } + + log.Printf("[DEBUG] RabbitMQ: Exchange retrieved %s: %#v", d.Id(), exchangeSettings) + + d.Set("name", exchangeSettings.Name) + d.Set("vhost", exchangeSettings.Vhost) + + exchange := make([]map[string]interface{}, 1) + e := make(map[string]interface{}) + e["type"] = exchangeSettings.Type + e["durable"] = exchangeSettings.Durable + e["auto_delete"] = exchangeSettings.AutoDelete + e["arguments"] = exchangeSettings.Arguments + exchange[0] = e + d.Set("settings", exchange) + + return nil +} + +func DeleteExchange(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + exchangeId := strings.Split(d.Id(), "@") + if len(exchangeId) < 2 { + return fmt.Errorf("Unable to determine exchange ID") + } + + name := exchangeId[0] + vhost := exchangeId[1] + + log.Printf("[DEBUG] RabbitMQ: Attempting to delete exchange %s", d.Id()) + + resp, err := rmqc.DeleteExchange(vhost, name) + log.Printf("[DEBUG] RabbitMQ: Exchange delete response: %#v", resp) + if err != nil { + return err + } + + if resp.StatusCode == 404 { + // The exchange was automatically deleted + return nil + } + + if resp.StatusCode >= 400 { + return fmt.Errorf("Error deleting RabbitMQ exchange: %s", resp.Status) + } + + return nil +} + +func declareExchange(rmqc *rabbithole.Client, vhost string, name string, settingsMap map[string]interface{}) error { + exchangeSettings := rabbithole.ExchangeSettings{} + + if v, ok := settingsMap["type"].(string); ok { + exchangeSettings.Type = v + } + + if v, ok := settingsMap["durable"].(bool); ok { + exchangeSettings.Durable = v + } + + if v, ok := settingsMap["auto_delete"].(bool); ok { + exchangeSettings.AutoDelete = v + } + + if v, ok := settingsMap["arguments"].(map[string]interface{}); ok { + exchangeSettings.Arguments = v + } + + log.Printf("[DEBUG] RabbitMQ: Attempting to declare exchange %s@%s: %#v", name, vhost, exchangeSettings) + + resp, err := rmqc.DeclareExchange(vhost, name, exchangeSettings) + log.Printf("[DEBUG] RabbitMQ: Exchange declare response: %#v", resp) + if err != nil { + return err + } + + if resp.StatusCode >= 400 { + return fmt.Errorf("Error declaring RabbitMQ exchange: %s", resp.Status) + } + + return nil +} diff --git a/builtin/providers/rabbitmq/resource_exchange_test.go b/builtin/providers/rabbitmq/resource_exchange_test.go new file mode 100644 index 000000000..7747e489d --- /dev/null +++ b/builtin/providers/rabbitmq/resource_exchange_test.go @@ -0,0 +1,103 @@ +package rabbitmq + +import ( + "fmt" + "strings" + "testing" + + "github.com/michaelklishin/rabbit-hole" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccExchange(t *testing.T) { + var exchangeInfo rabbithole.ExchangeInfo + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccExchangeCheckDestroy(&exchangeInfo), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccExchangeConfig_basic, + Check: testAccExchangeCheck( + "rabbitmq_exchange.test", &exchangeInfo, + ), + }, + }, + }) +} + +func testAccExchangeCheck(rn string, exchangeInfo *rabbithole.ExchangeInfo) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[rn] + if !ok { + return fmt.Errorf("resource not found: %s", rn) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("exchange id not set") + } + + rmqc := testAccProvider.Meta().(*rabbithole.Client) + exchParts := strings.Split(rs.Primary.ID, "@") + + exchanges, err := rmqc.ListExchangesIn(exchParts[1]) + if err != nil { + return fmt.Errorf("Error retrieving exchange: %s", err) + } + + for _, exchange := range exchanges { + if exchange.Name == exchParts[0] && exchange.Vhost == exchParts[1] { + exchangeInfo = &exchange + return nil + } + } + + return fmt.Errorf("Unable to find exchange %s", rn) + } +} + +func testAccExchangeCheckDestroy(exchangeInfo *rabbithole.ExchangeInfo) resource.TestCheckFunc { + return func(s *terraform.State) error { + rmqc := testAccProvider.Meta().(*rabbithole.Client) + + exchanges, err := rmqc.ListExchangesIn(exchangeInfo.Vhost) + if err != nil { + return fmt.Errorf("Error retrieving exchange: %s", err) + } + + for _, exchange := range exchanges { + if exchange.Name == exchangeInfo.Name && exchange.Vhost == exchangeInfo.Vhost { + return fmt.Errorf("Exchange %s@%s still exist", exchangeInfo.Name, exchangeInfo.Vhost) + } + } + + return nil + } +} + +const testAccExchangeConfig_basic = ` +resource "rabbitmq_vhost" "test" { + name = "test" +} + +resource "rabbitmq_permissions" "guest" { + user = "guest" + vhost = "${rabbitmq_vhost.test.name}" + permissions { + configure = ".*" + write = ".*" + read = ".*" + } +} + +resource "rabbitmq_exchange" "test" { + name = "test" + vhost = "${rabbitmq_permissions.guest.vhost}" + settings { + type = "fanout" + durable = false + auto_delete = true + } +}`