From 7f2bd0b6d4b4fa54cc69f0ee32ff4b248c36021d Mon Sep 17 00:00:00 2001 From: Joe Topjian Date: Wed, 20 Jul 2016 05:36:24 +0000 Subject: [PATCH] provider/rabbitmq: rabbitmq_policy resource --- .../providers/rabbitmq/import_policy_test.go | 34 +++ builtin/providers/rabbitmq/provider.go | 1 + builtin/providers/rabbitmq/resource_policy.go | 239 ++++++++++++++++++ .../rabbitmq/resource_policy_test.go | 141 +++++++++++ 4 files changed, 415 insertions(+) create mode 100644 builtin/providers/rabbitmq/import_policy_test.go create mode 100644 builtin/providers/rabbitmq/resource_policy.go create mode 100644 builtin/providers/rabbitmq/resource_policy_test.go diff --git a/builtin/providers/rabbitmq/import_policy_test.go b/builtin/providers/rabbitmq/import_policy_test.go new file mode 100644 index 0000000000..7fa59ba886 --- /dev/null +++ b/builtin/providers/rabbitmq/import_policy_test.go @@ -0,0 +1,34 @@ +package rabbitmq + +import ( + "testing" + + "github.com/michaelklishin/rabbit-hole" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccPolicy_importBasic(t *testing.T) { + resourceName := "rabbitmq_policy.test" + var policy rabbithole.Policy + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccPolicyCheckDestroy(&policy), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccPolicyConfig_basic, + Check: testAccPolicyCheck( + resourceName, &policy, + ), + }, + + resource.TestStep{ + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/builtin/providers/rabbitmq/provider.go b/builtin/providers/rabbitmq/provider.go index 601aa3b152..f7db53a4b4 100644 --- a/builtin/providers/rabbitmq/provider.go +++ b/builtin/providers/rabbitmq/provider.go @@ -75,6 +75,7 @@ func Provider() terraform.ResourceProvider { "rabbitmq_binding": resourceBinding(), "rabbitmq_exchange": resourceExchange(), "rabbitmq_permissions": resourcePermissions(), + "rabbitmq_policy": resourcePolicy(), "rabbitmq_queue": resourceQueue(), "rabbitmq_user": resourceUser(), "rabbitmq_vhost": resourceVhost(), diff --git a/builtin/providers/rabbitmq/resource_policy.go b/builtin/providers/rabbitmq/resource_policy.go new file mode 100644 index 0000000000..31154373f5 --- /dev/null +++ b/builtin/providers/rabbitmq/resource_policy.go @@ -0,0 +1,239 @@ +package rabbitmq + +import ( + "fmt" + "log" + "strings" + + "github.com/michaelklishin/rabbit-hole" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourcePolicy() *schema.Resource { + return &schema.Resource{ + Create: CreatePolicy, + Update: UpdatePolicy, + Read: ReadPolicy, + Delete: DeletePolicy, + 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, + Required: true, + ForceNew: true, + }, + + "policy": &schema.Schema{ + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "pattern": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "priority": &schema.Schema{ + Type: schema.TypeInt, + Required: true, + }, + + "apply_to": &schema.Schema{ + Type: schema.TypeString, + Required: true, + }, + + "definition": &schema.Schema{ + Type: schema.TypeMap, + Required: true, + }, + }, + }, + }, + }, + } +} + +func CreatePolicy(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + name := d.Get("name").(string) + vhost := d.Get("vhost").(string) + policyList := d.Get("policy").([]interface{}) + + policyMap, ok := policyList[0].(map[string]interface{}) + if !ok { + return fmt.Errorf("Unable to parse policy") + } + + if err := putPolicy(rmqc, vhost, name, policyMap); err != nil { + return err + } + + id := fmt.Sprintf("%s@%s", name, vhost) + d.SetId(id) + + return ReadPolicy(d, meta) +} + +func ReadPolicy(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + policyId := strings.Split(d.Id(), "@") + if len(policyId) < 2 { + return fmt.Errorf("Unable to determine policy ID") + } + + user := policyId[0] + vhost := policyId[1] + + policy, err := rmqc.GetPolicy(vhost, user) + if err != nil { + return checkDeleted(d, err) + } + + log.Printf("[DEBUG] RabbitMQ: Policy retrieved for %s: %#v", d.Id(), policy) + + d.Set("name", policy.Name) + d.Set("vhost", policy.Vhost) + + setPolicy := make([]map[string]interface{}, 1) + p := make(map[string]interface{}) + p["pattern"] = policy.Pattern + p["priority"] = policy.Priority + p["apply_to"] = policy.ApplyTo + + policyDefinition := make(map[string]interface{}) + for key, value := range policy.Definition { + if v, ok := value.([]interface{}); ok { + var nodes []string + for _, node := range v { + if n, ok := node.(string); ok { + nodes = append(nodes, n) + } + } + value = strings.Join(nodes, ",") + } + policyDefinition[key] = value + } + p["definition"] = policyDefinition + setPolicy[0] = p + + d.Set("policy", setPolicy) + + return nil +} + +func UpdatePolicy(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + policyId := strings.Split(d.Id(), "@") + if len(policyId) < 2 { + return fmt.Errorf("Unable to determine policy ID") + } + + user := policyId[0] + vhost := policyId[1] + + if d.HasChange("policy") { + _, newPolicy := d.GetChange("policy") + + policyList := newPolicy.([]interface{}) + policyMap, ok := policyList[0].(map[string]interface{}) + if !ok { + return fmt.Errorf("Unable to parse policy") + } + + if err := putPolicy(rmqc, user, vhost, policyMap); err != nil { + return err + } + } + + return ReadPolicy(d, meta) +} + +func DeletePolicy(d *schema.ResourceData, meta interface{}) error { + rmqc := meta.(*rabbithole.Client) + + policyId := strings.Split(d.Id(), "@") + if len(policyId) < 2 { + return fmt.Errorf("Unable to determine policy ID") + } + + user := policyId[0] + vhost := policyId[1] + + log.Printf("[DEBUG] RabbitMQ: Attempting to delete policy for %s", d.Id()) + + resp, err := rmqc.DeletePolicy(vhost, user) + log.Printf("[DEBUG] RabbitMQ: Policy delete response: %#v", resp) + if err != nil { + return err + } + + if resp.StatusCode == 404 { + // the policy was automatically deleted + return nil + } + + if resp.StatusCode >= 400 { + return fmt.Errorf("Error deleting RabbitMQ policy: %s", resp.Status) + } + + return nil +} + +func putPolicy(rmqc *rabbithole.Client, vhost string, name string, policyMap map[string]interface{}) error { + policy := rabbithole.Policy{} + policy.Vhost = vhost + policy.Name = name + + if v, ok := policyMap["pattern"].(string); ok { + policy.Pattern = v + } + + if v, ok := policyMap["priority"].(int); ok { + policy.Priority = v + } + + if v, ok := policyMap["apply_to"].(string); ok { + policy.ApplyTo = v + } + + if v, ok := policyMap["definition"].(map[string]interface{}); ok { + // special case for ha-mode = nodes + if x, ok := v["ha-mode"]; ok && x == "nodes" { + var nodes rabbithole.NodeNames + nodes = strings.Split(v["ha-params"].(string), ",") + v["ha-params"] = nodes + } + policyDefinition := rabbithole.PolicyDefinition{} + policyDefinition = v + policy.Definition = policyDefinition + } + + log.Printf("[DEBUG] RabbitMQ: Attempting to declare policy for %s@%s: %#v", name, vhost, policy) + + resp, err := rmqc.PutPolicy(vhost, name, policy) + log.Printf("[DEBUG] RabbitMQ: Policy declare response: %#v", resp) + if err != nil { + return err + } + + if resp.StatusCode >= 400 { + return fmt.Errorf("Error declaring RabbitMQ policy: %s", resp.Status) + } + + return nil +} diff --git a/builtin/providers/rabbitmq/resource_policy_test.go b/builtin/providers/rabbitmq/resource_policy_test.go new file mode 100644 index 0000000000..614620b690 --- /dev/null +++ b/builtin/providers/rabbitmq/resource_policy_test.go @@ -0,0 +1,141 @@ +package rabbitmq + +import ( + "fmt" + "strings" + "testing" + + "github.com/michaelklishin/rabbit-hole" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccPolicy(t *testing.T) { + var policy rabbithole.Policy + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccPolicyCheckDestroy(&policy), + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccPolicyConfig_basic, + Check: testAccPolicyCheck( + "rabbitmq_policy.test", &policy, + ), + }, + resource.TestStep{ + Config: testAccPolicyConfig_update, + Check: testAccPolicyCheck( + "rabbitmq_policy.test", &policy, + ), + }, + }, + }) +} + +func testAccPolicyCheck(rn string, policy *rabbithole.Policy) 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("policy id not set") + } + + rmqc := testAccProvider.Meta().(*rabbithole.Client) + policyParts := strings.Split(rs.Primary.ID, "@") + + policies, err := rmqc.ListPolicies() + if err != nil { + return fmt.Errorf("Error retrieving policies: %s", err) + } + + for _, p := range policies { + if p.Name == policyParts[0] && p.Vhost == policyParts[1] { + policy = &p + return nil + } + } + + return fmt.Errorf("Unable to find policy %s", rn) + } +} + +func testAccPolicyCheckDestroy(policy *rabbithole.Policy) resource.TestCheckFunc { + return func(s *terraform.State) error { + rmqc := testAccProvider.Meta().(*rabbithole.Client) + + policies, err := rmqc.ListPolicies() + if err != nil { + return fmt.Errorf("Error retrieving policies: %s", err) + } + + for _, p := range policies { + if p.Name == policy.Name && p.Vhost == policy.Vhost { + return fmt.Errorf("Policy %s@%s still exist", policy.Name, policy.Vhost) + } + } + + return nil + } +} + +const testAccPolicyConfig_basic = ` +resource "rabbitmq_vhost" "test" { + name = "test" +} + +resource "rabbitmq_permissions" "guest" { + user = "guest" + vhost = "${rabbitmq_vhost.test.name}" + permissions { + configure = ".*" + write = ".*" + read = ".*" + } +} + +resource "rabbitmq_policy" "test" { + name = "test" + vhost = "${rabbitmq_permissions.guest.vhost}" + policy { + pattern = ".*" + priority = 0 + apply_to = "all" + definition { + ha-mode = "nodes" + ha-params = "a,b,c" + } + } +}` + +const testAccPolicyConfig_update = ` +resource "rabbitmq_vhost" "test" { + name = "test" +} + +resource "rabbitmq_permissions" "guest" { + user = "guest" + vhost = "${rabbitmq_vhost.test.name}" + permissions { + configure = ".*" + write = ".*" + read = ".*" + } +} + +resource "rabbitmq_policy" "test" { + name = "test" + vhost = "${rabbitmq_permissions.guest.vhost}" + policy { + pattern = ".*" + priority = 0 + apply_to = "all" + definition { + ha-mode = "all" + } + } +}`