Skip to content

Commit cd0b46f

Browse files
committed
Merge pull request hashicorp#1 from maxenglander/2087-consul-service-resource
Feature request: consul_service resource
2 parents 0961f5c + fa94d7f commit cd0b46f

7 files changed

Lines changed: 513 additions & 10 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'",

builtin/providers/consul/get_dc.go

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

0 commit comments

Comments
 (0)