Lokasi ngalangkungan proxy:   [ UP ]  
[Ngawartoskeun bug]   [Panyetelan cookie]                
Skip to content

Commit e4d8c69

Browse files
committed
Add top-level ELB Attachment resource
Add an aws_elb_attachment resource so that the attment of instances to an ELB can be managed separately from an aws_elb and prevent dependency cycles.
1 parent f891ab8 commit e4d8c69

5 files changed

Lines changed: 394 additions & 0 deletions

File tree

builtin/providers/aws/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ func Provider() terraform.ResourceProvider {
176176
"aws_elastic_beanstalk_environment": resourceAwsElasticBeanstalkEnvironment(),
177177
"aws_elasticsearch_domain": resourceAwsElasticSearchDomain(),
178178
"aws_elb": resourceAwsElb(),
179+
"aws_elb_attachment": resourceAwsElbAttachment(),
179180
"aws_flow_log": resourceAwsFlowLog(),
180181
"aws_glacier_vault": resourceAwsGlacierVault(),
181182
"aws_iam_access_key": resourceAwsIamAccessKey(),
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package aws
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/aws/aws-sdk-go/aws"
8+
"github.com/aws/aws-sdk-go/service/elb"
9+
"github.com/hashicorp/terraform/helper/resource"
10+
"github.com/hashicorp/terraform/helper/schema"
11+
)
12+
13+
func resourceAwsElbAttachment() *schema.Resource {
14+
return &schema.Resource{
15+
Create: resourceAwsElbAttachmentCreate,
16+
Read: resourceAwsElbAttachmentRead,
17+
Delete: resourceAwsElbAttachmentDelete,
18+
19+
Schema: map[string]*schema.Schema{
20+
"elb": &schema.Schema{
21+
Type: schema.TypeString,
22+
ForceNew: true,
23+
Required: true,
24+
},
25+
26+
"instance": &schema.Schema{
27+
Type: schema.TypeString,
28+
ForceNew: true,
29+
Required: true,
30+
},
31+
},
32+
}
33+
}
34+
35+
func resourceAwsElbAttachmentCreate(d *schema.ResourceData, meta interface{}) error {
36+
elbconn := meta.(*AWSClient).elbconn
37+
elbName := d.Get("elb").(string)
38+
39+
instance := d.Get("instance").(string)
40+
41+
registerInstancesOpts := elb.RegisterInstancesWithLoadBalancerInput{
42+
LoadBalancerName: aws.String(elbName),
43+
Instances: []*elb.Instance{{InstanceId: aws.String(instance)}},
44+
}
45+
46+
log.Printf("[INFO] registering instance %s with ELB %s", instance, elbName)
47+
48+
_, err := elbconn.RegisterInstancesWithLoadBalancer(&registerInstancesOpts)
49+
if err != nil {
50+
return fmt.Errorf("Failure registering instances with ELB: %s", err)
51+
}
52+
53+
d.SetId(resource.PrefixedUniqueId(fmt.Sprintf("%s-", elbName)))
54+
55+
return nil
56+
}
57+
58+
func resourceAwsElbAttachmentRead(d *schema.ResourceData, meta interface{}) error {
59+
elbconn := meta.(*AWSClient).elbconn
60+
elbName := d.Get("elb").(string)
61+
62+
// only add the instance that was previously defined for this resource
63+
expected := d.Get("instance").(string)
64+
65+
// Retrieve the ELB properties to get a list of attachments
66+
describeElbOpts := &elb.DescribeLoadBalancersInput{
67+
LoadBalancerNames: []*string{aws.String(elbName)},
68+
}
69+
70+
resp, err := elbconn.DescribeLoadBalancers(describeElbOpts)
71+
if err != nil {
72+
if isLoadBalancerNotFound(err) {
73+
log.Printf("[ERROR] ELB %s not found", elbName)
74+
d.SetId("")
75+
return nil
76+
}
77+
return fmt.Errorf("Error retrieving ELB: %s", err)
78+
}
79+
if len(resp.LoadBalancerDescriptions) != 1 {
80+
log.Printf("[ERROR] Unable to find ELB: %s", resp.LoadBalancerDescriptions)
81+
d.SetId("")
82+
return nil
83+
}
84+
85+
// only set the instance Id that this resource manages
86+
found := false
87+
for _, i := range resp.LoadBalancerDescriptions[0].Instances {
88+
if expected == *i.InstanceId {
89+
d.Set("instance", expected)
90+
found = true
91+
}
92+
}
93+
94+
if !found {
95+
log.Printf("[WARN] instance %s not found in elb attachments", expected)
96+
d.SetId("")
97+
}
98+
99+
return nil
100+
}
101+
102+
func resourceAwsElbAttachmentDelete(d *schema.ResourceData, meta interface{}) error {
103+
elbconn := meta.(*AWSClient).elbconn
104+
elbName := d.Get("elb").(string)
105+
106+
instance := d.Get("instance").(string)
107+
108+
log.Printf("[INFO] Deleting Attachment %s from: %s", instance, elbName)
109+
110+
deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
111+
LoadBalancerName: aws.String(elbName),
112+
Instances: []*elb.Instance{{InstanceId: aws.String(instance)}},
113+
}
114+
115+
_, err := elbconn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
116+
if err != nil {
117+
return fmt.Errorf("Failure deregistering instances from ELB: %s", err)
118+
}
119+
120+
return nil
121+
}
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package aws
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"testing"
7+
8+
"github.com/aws/aws-sdk-go/service/elb"
9+
"github.com/hashicorp/terraform/helper/resource"
10+
"github.com/hashicorp/terraform/terraform"
11+
)
12+
13+
func TestAccAWSELBAttachment_basic(t *testing.T) {
14+
var conf elb.LoadBalancerDescription
15+
16+
testCheckInstanceAttached := func(count int) resource.TestCheckFunc {
17+
return func(*terraform.State) error {
18+
if len(conf.Instances) != count {
19+
return fmt.Errorf("instance count does not match")
20+
}
21+
return nil
22+
}
23+
}
24+
25+
resource.Test(t, resource.TestCase{
26+
PreCheck: func() { testAccPreCheck(t) },
27+
IDRefreshName: "aws_elb.bar",
28+
Providers: testAccProviders,
29+
CheckDestroy: testAccCheckAWSELBDestroy,
30+
Steps: []resource.TestStep{
31+
resource.TestStep{
32+
Config: testAccAWSELBAttachmentConfig1,
33+
Check: resource.ComposeTestCheckFunc(
34+
testAccCheckAWSELBExists("aws_elb.bar", &conf),
35+
testCheckInstanceAttached(1),
36+
),
37+
},
38+
39+
resource.TestStep{
40+
Config: testAccAWSELBAttachmentConfig2,
41+
Check: resource.ComposeTestCheckFunc(
42+
testAccCheckAWSELBExists("aws_elb.bar", &conf),
43+
testCheckInstanceAttached(2),
44+
),
45+
},
46+
47+
resource.TestStep{
48+
Config: testAccAWSELBAttachmentConfig3,
49+
Check: resource.ComposeTestCheckFunc(
50+
testAccCheckAWSELBExists("aws_elb.bar", &conf),
51+
testCheckInstanceAttached(2),
52+
),
53+
},
54+
55+
resource.TestStep{
56+
Config: testAccAWSELBAttachmentConfig4,
57+
Check: resource.ComposeTestCheckFunc(
58+
testAccCheckAWSELBExists("aws_elb.bar", &conf),
59+
testCheckInstanceAttached(0),
60+
),
61+
},
62+
},
63+
})
64+
}
65+
66+
// remove and instance and check that it's correctly re-attached.
67+
func TestAccAWSELBAttachment_drift(t *testing.T) {
68+
var conf elb.LoadBalancerDescription
69+
70+
deregInstance := func() {
71+
conn := testAccProvider.Meta().(*AWSClient).elbconn
72+
73+
deRegisterInstancesOpts := elb.DeregisterInstancesFromLoadBalancerInput{
74+
LoadBalancerName: conf.LoadBalancerName,
75+
Instances: conf.Instances,
76+
}
77+
78+
log.Printf("[DEBUG] deregistering instance %s from ELB", conf.Instances[0].InstanceId)
79+
80+
_, err := conn.DeregisterInstancesFromLoadBalancer(&deRegisterInstancesOpts)
81+
if err != nil {
82+
t.Fatalf("Failure deregistering instances from ELB: %s", err)
83+
}
84+
85+
}
86+
87+
testCheckInstanceAttached := func(count int) resource.TestCheckFunc {
88+
return func(*terraform.State) error {
89+
if len(conf.Instances) != count {
90+
return fmt.Errorf("instance count does not match")
91+
}
92+
return nil
93+
}
94+
}
95+
96+
resource.Test(t, resource.TestCase{
97+
PreCheck: func() { testAccPreCheck(t) },
98+
IDRefreshName: "aws_elb.bar",
99+
Providers: testAccProviders,
100+
CheckDestroy: testAccCheckAWSELBDestroy,
101+
Steps: []resource.TestStep{
102+
resource.TestStep{
103+
Config: testAccAWSELBAttachmentConfig1,
104+
Check: resource.ComposeTestCheckFunc(
105+
testAccCheckAWSELBExists("aws_elb.bar", &conf),
106+
testCheckInstanceAttached(1),
107+
),
108+
},
109+
110+
// remove an instance from the ELB, and make sure it gets re-added
111+
resource.TestStep{
112+
Config: testAccAWSELBAttachmentConfig1,
113+
PreConfig: deregInstance,
114+
Check: resource.ComposeTestCheckFunc(
115+
testAccCheckAWSELBExists("aws_elb.bar", &conf),
116+
testCheckInstanceAttached(1),
117+
),
118+
},
119+
},
120+
})
121+
}
122+
123+
// add one attachment
124+
const testAccAWSELBAttachmentConfig1 = `
125+
resource "aws_elb" "bar" {
126+
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
127+
128+
listener {
129+
instance_port = 8000
130+
instance_protocol = "http"
131+
lb_port = 80
132+
lb_protocol = "http"
133+
}
134+
}
135+
136+
resource "aws_instance" "foo1" {
137+
# us-west-2
138+
ami = "ami-043a5034"
139+
instance_type = "t1.micro"
140+
}
141+
142+
resource "aws_elb_attachment" "foo1" {
143+
elb = "${aws_elb.bar.id}"
144+
instance = "${aws_instance.foo1.id}"
145+
}
146+
`
147+
148+
// add a second attachment
149+
const testAccAWSELBAttachmentConfig2 = `
150+
resource "aws_elb" "bar" {
151+
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
152+
153+
listener {
154+
instance_port = 8000
155+
instance_protocol = "http"
156+
lb_port = 80
157+
lb_protocol = "http"
158+
}
159+
}
160+
161+
resource "aws_instance" "foo1" {
162+
# us-west-2
163+
ami = "ami-043a5034"
164+
instance_type = "t1.micro"
165+
}
166+
167+
resource "aws_instance" "foo2" {
168+
# us-west-2
169+
ami = "ami-043a5034"
170+
instance_type = "t1.micro"
171+
}
172+
173+
resource "aws_elb_attachment" "foo1" {
174+
elb = "${aws_elb.bar.id}"
175+
instance = "${aws_instance.foo1.id}"
176+
}
177+
178+
resource "aws_elb_attachment" "foo2" {
179+
elb = "${aws_elb.bar.id}"
180+
instance = "${aws_instance.foo2.id}"
181+
}
182+
`
183+
184+
// swap attachments between resources
185+
const testAccAWSELBAttachmentConfig3 = `
186+
resource "aws_elb" "bar" {
187+
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
188+
189+
listener {
190+
instance_port = 8000
191+
instance_protocol = "http"
192+
lb_port = 80
193+
lb_protocol = "http"
194+
}
195+
}
196+
197+
resource "aws_instance" "foo1" {
198+
# us-west-2
199+
ami = "ami-043a5034"
200+
instance_type = "t1.micro"
201+
}
202+
203+
resource "aws_instance" "foo2" {
204+
# us-west-2
205+
ami = "ami-043a5034"
206+
instance_type = "t1.micro"
207+
}
208+
209+
resource "aws_elb_attachment" "foo1" {
210+
elb = "${aws_elb.bar.id}"
211+
instance = "${aws_instance.foo2.id}"
212+
}
213+
214+
resource "aws_elb_attachment" "foo2" {
215+
elb = "${aws_elb.bar.id}"
216+
instance = "${aws_instance.foo1.id}"
217+
}
218+
`
219+
220+
// destroy attachments
221+
const testAccAWSELBAttachmentConfig4 = `
222+
resource "aws_elb" "bar" {
223+
availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]
224+
225+
listener {
226+
instance_port = 8000
227+
instance_protocol = "http"
228+
lb_port = 80
229+
lb_protocol = "http"
230+
}
231+
}
232+
`

website/source/docs/providers/aws/r/elb.html.markdown

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ description: |-
1010

1111
Provides an Elastic Load Balancer resource.
1212

13+
~> **NOTE on ELB Instances and ELB Attachments:** Terraform currently
14+
provides both a standalone [ELB Attachment resource](elb_attachment.html)
15+
(describing an instance attached to an ELB), and an ELB resource with
16+
`instances` defined in-line. At this time you cannot use an ELB with in-line
17+
instaces in conjunction with a ELB Attachment resources. Doing so will cause a
18+
conflict and will overwrite attachments.
1319
## Example Usage
1420

1521
```

0 commit comments

Comments
 (0)