diff --git a/builtin/providers/consul/resource_consul_agent_service.go b/builtin/providers/consul/resource_consul_agent_service.go index 1a15bf6d8..9ede63bf3 100644 --- a/builtin/providers/consul/resource_consul_agent_service.go +++ b/builtin/providers/consul/resource_consul_agent_service.go @@ -24,9 +24,7 @@ func resourceConsulAgentService() *schema.Resource { "id": &schema.Schema{ Type: schema.TypeString, - Optional: true, Computed: true, - ForceNew: true, }, "name": &schema.Schema{ @@ -61,10 +59,6 @@ func resourceConsulAgentServiceCreate(d *schema.ResourceData, meta interface{}) registration.Address = address.(string) } - if id, ok := d.GetOk("id"); ok { - registration.ID = id.(string) - } - if port, ok := d.GetOk("port"); ok { registration.Port = port.(int) } @@ -89,10 +83,15 @@ func resourceConsulAgentServiceCreate(d *schema.ResourceData, meta interface{}) return fmt.Errorf("Failed to read service '%s' from Consul agent: %v", name, err) } else { d.Set("address", service.Address) + d.Set("id", service.ID) + d.SetId(service.ID) d.Set("name", service.Service) d.Set("port", service.Port) - d.Set("tags", service.Tags) - d.SetId(service.ID) + tags := make([]string, 0, len(service.Tags)) + for _, tag := range service.Tags { + tags = append(tags, tag) + } + d.Set("tags", tags) } return nil @@ -110,10 +109,15 @@ func resourceConsulAgentServiceRead(d *schema.ResourceData, meta interface{}) er return fmt.Errorf("Failed to get service '%s' from Consul agent", name) } else { d.Set("address", service.Address) + d.Set("id", service.ID) + d.SetId(service.ID) d.Set("name", service.Service) d.Set("port", service.Port) - d.Set("tags", service.Tags) - d.SetId(service.ID) + tags := make([]string, 0, len(service.Tags)) + for _, tag := range service.Tags { + tags = append(tags, tag) + } + d.Set("tags", tags) } return nil @@ -123,10 +127,10 @@ func resourceConsulAgentServiceDelete(d *schema.ResourceData, meta interface{}) client := meta.(*consulapi.Client) catalog := client.Agent() - name := d.Get("name").(string) + id := d.Get("id").(string) - if err := catalog.ServiceDeregister(name); err != nil { - return fmt.Errorf("Failed to deregister service '%s' from Consul agent: %v", name, err) + if err := catalog.ServiceDeregister(id); err != nil { + return fmt.Errorf("Failed to deregister service '%s' from Consul agent: %v", id, err) } // Clear the ID diff --git a/builtin/providers/consul/resource_consul_agent_service_test.go b/builtin/providers/consul/resource_consul_agent_service_test.go index 5d5b022d8..5150c4e85 100644 --- a/builtin/providers/consul/resource_consul_agent_service_test.go +++ b/builtin/providers/consul/resource_consul_agent_service_test.go @@ -22,6 +22,10 @@ func TestAccConsulAgentService_basic(t *testing.T) { testAccCheckConsulAgentServiceValue("consul_agent_service.app", "address", "www.google.com"), testAccCheckConsulAgentServiceValue("consul_agent_service.app", "id", "google"), testAccCheckConsulAgentServiceValue("consul_agent_service.app", "name", "google"), + testAccCheckConsulAgentServiceValue("consul_agent_service.app", "port", "80"), + testAccCheckConsulAgentServiceValue("consul_agent_service.app", "tags.#", "2"), + testAccCheckConsulAgentServiceValue("consul_agent_service.app", "tags.0", "tag0"), + testAccCheckConsulAgentServiceValue("consul_agent_service.app", "tags.1", "tag1"), ), }, }, @@ -78,8 +82,9 @@ func testAccCheckConsulAgentServiceValue(n, attr, val string) resource.TestCheck const testAccConsulAgentServiceConfig = ` resource "consul_agent_service" "app" { - name = "google" address = "www.google.com" + name = "google" port = 80 + tags = ["tag0", "tag1"] } ` diff --git a/builtin/providers/consul/resource_consul_catalog_entry.go b/builtin/providers/consul/resource_consul_catalog_entry.go index 73ed90307..263c21f71 100644 --- a/builtin/providers/consul/resource_consul_catalog_entry.go +++ b/builtin/providers/consul/resource_consul_catalog_entry.go @@ -70,10 +70,11 @@ func resourceConsulCatalogEntry() *schema.Resource { }, "tags": &schema.Schema{ - Type: schema.TypeList, + Type: schema.TypeSet, Optional: true, ForceNew: true, Elem: &schema.Schema{Type: schema.TypeString}, + Set: resourceConsulCatalogEntryServiceTagsHash, }, }, }, @@ -88,6 +89,10 @@ func resourceConsulCatalogEntry() *schema.Resource { } } +func resourceConsulCatalogEntryServiceTagsHash(v interface{}) int { + return hashcode.String(v.(string)) +} + func resourceConsulCatalogEntryServicesHash(v interface{}) int { var buf bytes.Buffer m := v.(map[string]interface{}) @@ -96,7 +101,7 @@ func resourceConsulCatalogEntryServicesHash(v interface{}) int { buf.WriteString(fmt.Sprintf("%s-", m["address"].(string))) buf.WriteString(fmt.Sprintf("%d-", m["port"].(int))) if v, ok := m["tags"]; ok { - vs := v.([]interface{}) + vs := v.(*schema.Set).List() s := make([]string, len(vs)) for i, raw := range vs { s[i] = raw.(string) @@ -146,7 +151,7 @@ func resourceConsulCatalogEntryCreate(d *schema.ResourceData, meta interface{}) serviceIDs[i] = serviceID var tags []string - if v := serviceData["tags"].([]interface{}); len(v) > 0 { + if v := serviceData["tags"].(*schema.Set).List(); len(v) > 0 { tags = make([]string, len(v)) for i, raw := range v { tags[i] = raw.(string) diff --git a/builtin/providers/consul/resource_consul_catalog_entry_test.go b/builtin/providers/consul/resource_consul_catalog_entry_test.go new file mode 100644 index 000000000..0a28b675c --- /dev/null +++ b/builtin/providers/consul/resource_consul_catalog_entry_test.go @@ -0,0 +1,100 @@ +package consul + +import ( + "fmt" + "testing" + + consulapi "github.com/hashicorp/consul/api" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccConsulCatalogEntry_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() {}, + Providers: testAccProviders, + CheckDestroy: testAccCheckConsulCatalogEntryDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccConsulCatalogEntryConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckConsulCatalogEntryExists(), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "address", "127.0.0.1"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "node", "bastion"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.#", "1"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.address", "www.google.com"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.id", "google1"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.name", "google"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.port", "80"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.tags.#", "2"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.tags.2154398732", "tag0"), + testAccCheckConsulCatalogEntryValue("consul_catalog_entry.app", "service.3112399829.tags.4151227546", "tag1"), + ), + }, + }, + }) +} + +func testAccCheckConsulCatalogEntryDestroy(s *terraform.State) error { + catalog := testAccProvider.Meta().(*consulapi.Client).Catalog() + qOpts := consulapi.QueryOptions{} + services, _, err := catalog.Services(&qOpts) + if err != nil { + return fmt.Errorf("Could not retrieve services: %#v", err) + } + _, ok := services["google"] + if ok { + return fmt.Errorf("Service still exists: %#v", "google") + } + return nil +} + +func testAccCheckConsulCatalogEntryExists() resource.TestCheckFunc { + return func(s *terraform.State) error { + catalog := testAccProvider.Meta().(*consulapi.Client).Catalog() + qOpts := consulapi.QueryOptions{} + services, _, err := catalog.Services(&qOpts) + if err != nil { + return err + } + _, ok := services["google"] + if !ok { + return fmt.Errorf("Service does not exist: %#v", "google") + } + return nil + } +} + +func testAccCheckConsulCatalogEntryValue(n, attr, val string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rn, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Resource not found") + } + out, ok := rn.Primary.Attributes[attr] + if !ok { + return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes) + } + if val != "" && out != val { + return fmt.Errorf("Attribute '%s' value '%s' != '%s'", attr, out, val) + } + if val == "" && out == "" { + return fmt.Errorf("Attribute '%s' value '%s'", attr, out) + } + return nil + } +} + +const testAccConsulCatalogEntryConfig = ` +resource "consul_catalog_entry" "app" { + address = "127.0.0.1" + node = "bastion" + service = { + address = "www.google.com" + id = "google1" + name = "google" + port = 80 + tags = ["tag0", "tag1"] + } +} +` diff --git a/builtin/providers/consul/resource_provider.go b/builtin/providers/consul/resource_provider.go index b109b9fe8..08a153046 100644 --- a/builtin/providers/consul/resource_provider.go +++ b/builtin/providers/consul/resource_provider.go @@ -36,7 +36,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "consul_agent_service": resourceConsulAgentService(), "consul_catalog_entry": resourceConsulCatalogEntry(), - "consul_keys": resourceConsulKeys(), + "consul_keys": resourceConsulKeys(), }, ConfigureFunc: providerConfigure, diff --git a/website/source/docs/providers/consul/r/agent_service.html.markdown b/website/source/docs/providers/consul/r/agent_service.html.markdown new file mode 100644 index 000000000..edf7524dd --- /dev/null +++ b/website/source/docs/providers/consul/r/agent_service.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "consul" +page_title: "Consul: consul_agent_service" +sidebar_current: "docs-consul-resource-agent-service" +description: |- + Provides access to Agent Service data in Consul. This can be used to define a service associated with a particular agent. Currently, defining health checks for an agent service is not supported. +--- + +# consul\_agent\_service + +Provides access to Agent Service data in Consul. This can be used to define a service associated with a particular agent. Currently, defining health checks for an agent service is not supported. + +## Example Usage + +``` +resource "consul_agent_service" "app" { + address = "www.google.com" + name = "google" + port = 80 + tags = ["tag0", "tag1"] +} +``` + +## Argument Reference + +The following arguments are supported: + +* `address` - (Optional) The address of the service. Defaults to the + address of the agent. + +* `name` - (Required) The name of the service. + +* `port` - (Optional) The port of the service. + +* `tags` - (Optional) A list of values that are opaque to Consul, + but can be used to distinguish between services or nodes. + + +## Attributes Reference + +The following attributes are exported: + +* `address` - The address of the service. +* `id` - The id of the service, defaults to the value of `name`. +* `name` - The name of the service. +* `port` - The port of the service. +* `tags` - The tags of the service. diff --git a/website/source/docs/providers/consul/r/catalog_entry.html.markdown b/website/source/docs/providers/consul/r/catalog_entry.html.markdown new file mode 100644 index 000000000..d750642c5 --- /dev/null +++ b/website/source/docs/providers/consul/r/catalog_entry.html.markdown @@ -0,0 +1,58 @@ +--- +layout: "consul" +page_title: "Consul: consul_catalog_entry" +sidebar_current: "docs-consul-resource-catalog-entry" +description: |- + Provides access to Catalog data in Consul. This can be used to define a node or a service. Currently, defining health checks is not supported. +--- + +# consul\_catalog\_entry_ + +Provides access to Catalog data in Consul. This can be used to define a node or a service. Currently, defining health checks is not supported. + +## Example Usage + +``` +resource "consul_catalog_entry" "app" { + address = "192.168.10.10" + name = "foobar" + service = { + address = "127.0.0.1" + id = "redis1" + name = "redis" + port = 8000 + tags = ["master", "v1"] + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `address` - (Required) The address of the node being added to + or referenced in the catalog. + +* `node` - (Required) The name of the node being added to or + referenced in the catalog. + +* `service` - (Optional) A service to optionally associated with + the node. Supported values documented below. + +The `service` block supports the following: + +* `address` - (Optional) The address of the service. Defaults to the + node address. +* `id` - (Optional) The ID of the service. Defaults to the `name`. +* `name` - (Required) The name of the service +* `port` - (Optional) The port of the service. +* `tags` - (Optional) A list of values that are opaque to Consul, + but can be used to distinguish between services or nodes. + + +## Attributes Reference + +The following attributes are exported: + +* `address` - The address of the service. +* `node` - The id of the service, defaults to the value of `name`.