Skip to content

Commit dd2db44

Browse files
committed
Merge branch 'maxenglander-hashicorpgh-2087-consul-service-resource'
2 parents 2c16677 + 47c274f commit dd2db44

14 files changed

Lines changed: 1275 additions & 2 deletions

builtin/providers/consul/config.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
type Config struct {
1010
Datacenter string `mapstructure:"datacenter"`
1111
Address string `mapstructure:"address"`
12+
Token string `mapstructure:"token"`
1213
Scheme string `mapstructure:"scheme"`
1314
}
1415

@@ -25,6 +26,9 @@ func (c *Config) Client() (*consulapi.Client, error) {
2526
if c.Scheme != "" {
2627
config.Scheme = c.Scheme
2728
}
29+
if c.Token != "" {
30+
config.Token = c.Token
31+
}
2832
client, err := consulapi.NewClient(config)
2933

3034
log.Printf("[INFO] Consul Client configured with address: '%s', scheme: '%s', datacenter: '%s'",
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package consul
2+
3+
import (
4+
"fmt"
5+
6+
consulapi "github.com/hashicorp/consul/api"
7+
"github.com/hashicorp/terraform/helper/schema"
8+
)
9+
10+
func resourceConsulAgentService() *schema.Resource {
11+
return &schema.Resource{
12+
Create: resourceConsulAgentServiceCreate,
13+
Update: resourceConsulAgentServiceCreate,
14+
Read: resourceConsulAgentServiceRead,
15+
Delete: resourceConsulAgentServiceDelete,
16+
17+
Schema: map[string]*schema.Schema{
18+
"address": &schema.Schema{
19+
Type: schema.TypeString,
20+
Optional: true,
21+
Computed: true,
22+
ForceNew: true,
23+
},
24+
25+
"id": &schema.Schema{
26+
Type: schema.TypeString,
27+
Computed: true,
28+
},
29+
30+
"name": &schema.Schema{
31+
Type: schema.TypeString,
32+
Required: true,
33+
},
34+
35+
"port": &schema.Schema{
36+
Type: schema.TypeInt,
37+
Optional: true,
38+
ForceNew: true,
39+
},
40+
41+
"tags": &schema.Schema{
42+
Type: schema.TypeList,
43+
Optional: true,
44+
Elem: &schema.Schema{Type: schema.TypeString},
45+
ForceNew: true,
46+
},
47+
},
48+
}
49+
}
50+
51+
func resourceConsulAgentServiceCreate(d *schema.ResourceData, meta interface{}) error {
52+
client := meta.(*consulapi.Client)
53+
agent := client.Agent()
54+
55+
name := d.Get("name").(string)
56+
registration := consulapi.AgentServiceRegistration{Name: name}
57+
58+
if address, ok := d.GetOk("address"); ok {
59+
registration.Address = address.(string)
60+
}
61+
62+
if port, ok := d.GetOk("port"); ok {
63+
registration.Port = port.(int)
64+
}
65+
66+
if v, ok := d.GetOk("tags"); ok {
67+
vs := v.([]interface{})
68+
s := make([]string, len(vs))
69+
for i, raw := range vs {
70+
s[i] = raw.(string)
71+
}
72+
registration.Tags = s
73+
}
74+
75+
if err := agent.ServiceRegister(&registration); err != nil {
76+
return fmt.Errorf("Failed to register service '%s' with Consul agent: %v", name, err)
77+
}
78+
79+
// Update the resource
80+
if serviceMap, err := agent.Services(); err != nil {
81+
return fmt.Errorf("Failed to read services from Consul agent: %v", err)
82+
} else if service, ok := serviceMap[name]; !ok {
83+
return fmt.Errorf("Failed to read service '%s' from Consul agent: %v", name, err)
84+
} else {
85+
d.Set("address", service.Address)
86+
d.Set("id", service.ID)
87+
d.SetId(service.ID)
88+
d.Set("name", service.Service)
89+
d.Set("port", service.Port)
90+
tags := make([]string, 0, len(service.Tags))
91+
for _, tag := range service.Tags {
92+
tags = append(tags, tag)
93+
}
94+
d.Set("tags", tags)
95+
}
96+
97+
return nil
98+
}
99+
100+
func resourceConsulAgentServiceRead(d *schema.ResourceData, meta interface{}) error {
101+
client := meta.(*consulapi.Client)
102+
agent := client.Agent()
103+
104+
name := d.Get("name").(string)
105+
106+
if services, err := agent.Services(); err != nil {
107+
return fmt.Errorf("Failed to get services from Consul agent: %v", err)
108+
} else if service, ok := services[name]; !ok {
109+
d.Set("id", "")
110+
} else {
111+
d.Set("address", service.Address)
112+
d.Set("id", service.ID)
113+
d.SetId(service.ID)
114+
d.Set("name", service.Service)
115+
d.Set("port", service.Port)
116+
tags := make([]string, 0, len(service.Tags))
117+
for _, tag := range service.Tags {
118+
tags = append(tags, tag)
119+
}
120+
d.Set("tags", tags)
121+
}
122+
123+
return nil
124+
}
125+
126+
func resourceConsulAgentServiceDelete(d *schema.ResourceData, meta interface{}) error {
127+
client := meta.(*consulapi.Client)
128+
catalog := client.Agent()
129+
130+
id := d.Get("id").(string)
131+
132+
if err := catalog.ServiceDeregister(id); err != nil {
133+
return fmt.Errorf("Failed to deregister service '%s' from Consul agent: %v", id, err)
134+
}
135+
136+
// Clear the ID
137+
d.SetId("")
138+
return nil
139+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package consul
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
consulapi "github.com/hashicorp/consul/api"
8+
"github.com/hashicorp/terraform/helper/resource"
9+
"github.com/hashicorp/terraform/terraform"
10+
)
11+
12+
func TestAccConsulAgentService_basic(t *testing.T) {
13+
resource.Test(t, resource.TestCase{
14+
PreCheck: func() {},
15+
Providers: testAccProviders,
16+
CheckDestroy: testAccCheckConsulAgentServiceDestroy,
17+
Steps: []resource.TestStep{
18+
resource.TestStep{
19+
Config: testAccConsulAgentServiceConfig,
20+
Check: resource.ComposeTestCheckFunc(
21+
testAccCheckConsulAgentServiceExists(),
22+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "address", "www.google.com"),
23+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "id", "google"),
24+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "name", "google"),
25+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "port", "80"),
26+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "tags.#", "2"),
27+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "tags.0", "tag0"),
28+
testAccCheckConsulAgentServiceValue("consul_agent_service.app", "tags.1", "tag1"),
29+
),
30+
},
31+
},
32+
})
33+
}
34+
35+
func testAccCheckConsulAgentServiceDestroy(s *terraform.State) error {
36+
agent := testAccProvider.Meta().(*consulapi.Client).Agent()
37+
services, err := agent.Services()
38+
if err != nil {
39+
return fmt.Errorf("Could not retrieve services: %#v", err)
40+
}
41+
_, ok := services["google"]
42+
if ok {
43+
return fmt.Errorf("Service still exists: %#v", "google")
44+
}
45+
return nil
46+
}
47+
48+
func testAccCheckConsulAgentServiceExists() resource.TestCheckFunc {
49+
return func(s *terraform.State) error {
50+
agent := testAccProvider.Meta().(*consulapi.Client).Agent()
51+
services, err := agent.Services()
52+
if err != nil {
53+
return err
54+
}
55+
_, ok := services["google"]
56+
if !ok {
57+
return fmt.Errorf("Service does not exist: %#v", "google")
58+
}
59+
return nil
60+
}
61+
}
62+
63+
func testAccCheckConsulAgentServiceValue(n, attr, val string) resource.TestCheckFunc {
64+
return func(s *terraform.State) error {
65+
rn, ok := s.RootModule().Resources[n]
66+
if !ok {
67+
return fmt.Errorf("Resource not found")
68+
}
69+
out, ok := rn.Primary.Attributes[attr]
70+
if !ok {
71+
return fmt.Errorf("Attribute '%s' not found: %#v", attr, rn.Primary.Attributes)
72+
}
73+
if val != "<any>" && out != val {
74+
return fmt.Errorf("Attribute '%s' value '%s' != '%s'", attr, out, val)
75+
}
76+
if val == "<any>" && out == "" {
77+
return fmt.Errorf("Attribute '%s' value '%s'", attr, out)
78+
}
79+
return nil
80+
}
81+
}
82+
83+
const testAccConsulAgentServiceConfig = `
84+
resource "consul_agent_service" "app" {
85+
address = "www.google.com"
86+
name = "google"
87+
port = 80
88+
tags = ["tag0", "tag1"]
89+
}
90+
`

0 commit comments

Comments
 (0)