provider/openstack: Fix Security Group EOF Error

When applying or removing 2+ security groups from an instance, an EOF
error will be triggered even though the action was successful. This
patch accounts for and ignores the EOF error. It also adds a test
case.

Security Group and Port documentation are also updated in this
commit.
This commit is contained in:
Joe Topjian 2016-07-02 14:18:04 +00:00
parent 1644af770e
commit bc6be73c1d
4 changed files with 167 additions and 21 deletions

View File

@ -622,28 +622,28 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e
for _, g := range secgroupsToRemove.List() { for _, g := range secgroupsToRemove.List() {
err := secgroups.RemoveServerFromGroup(computeClient, d.Id(), g.(string)).ExtractErr() err := secgroups.RemoveServerFromGroup(computeClient, d.Id(), g.(string)).ExtractErr()
if err != nil { if err != nil && err.Error() != "EOF" {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok { if !ok {
return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) return fmt.Errorf("Error removing security group (%s) from OpenStack server (%s): %s", g, d.Id(), err)
} }
if errCode.Actual == 404 { if errCode.Actual == 404 {
continue continue
} else { } else {
return fmt.Errorf("Error removing security group from OpenStack server (%s): %s", d.Id(), err) return fmt.Errorf("Error removing security group (%s) from OpenStack server (%s): %s", g, d.Id(), err)
} }
} else { } else {
log.Printf("[DEBUG] Removed security group (%s) from instance (%s)", g.(string), d.Id()) log.Printf("[DEBUG] Removed security group (%s) from instance (%s)", g, d.Id())
} }
} }
for _, g := range secgroupsToAdd.List() {
err := secgroups.AddServerToGroup(computeClient, d.Id(), g.(string)).ExtractErr()
if err != nil {
return fmt.Errorf("Error adding security group to OpenStack server (%s): %s", d.Id(), err)
}
log.Printf("[DEBUG] Added security group (%s) to instance (%s)", g.(string), d.Id())
}
for _, g := range secgroupsToAdd.List() {
err := secgroups.AddServerToGroup(computeClient, d.Id(), g.(string)).ExtractErr()
if err != nil && err.Error() != "EOF" {
return fmt.Errorf("Error adding security group (%s) to OpenStack server (%s): %s", g, d.Id(), err)
}
log.Printf("[DEBUG] Added security group (%s) to instance (%s)", g, d.Id())
}
} }
if d.HasChange("admin_pass") { if d.HasChange("admin_pass") {

View File

@ -316,11 +316,11 @@ func TestAccComputeV2Instance_floatingIPAttachAndChange(t *testing.T) {
} }
func TestAccComputeV2Instance_multi_secgroups(t *testing.T) { func TestAccComputeV2Instance_multi_secgroups(t *testing.T) {
var instance servers.Server var instance_1 servers.Server
var secgroup secgroups.SecurityGroup var secgroup_1 secgroups.SecurityGroup
var testAccComputeV2Instance_multi_secgroups = fmt.Sprintf(` var testAccComputeV2Instance_multi_secgroups = fmt.Sprintf(`
resource "openstack_compute_secgroup_v2" "foo" { resource "openstack_compute_secgroup_v2" "secgroup_1" {
name = "terraform-test" name = "secgroup_1"
description = "a security group" description = "a security group"
rule { rule {
from_port = 22 from_port = 22
@ -330,9 +330,9 @@ func TestAccComputeV2Instance_multi_secgroups(t *testing.T) {
} }
} }
resource "openstack_compute_instance_v2" "foo" { resource "openstack_compute_instance_v2" "instance_1" {
name = "terraform-test" name = "instance_1"
security_groups = ["default", "${openstack_compute_secgroup_v2.foo.name}"] security_groups = ["default", "${openstack_compute_secgroup_v2.secgroup_1.name}"]
network { network {
uuid = "%s" uuid = "%s"
} }
@ -347,8 +347,92 @@ func TestAccComputeV2Instance_multi_secgroups(t *testing.T) {
resource.TestStep{ resource.TestStep{
Config: testAccComputeV2Instance_multi_secgroups, Config: testAccComputeV2Instance_multi_secgroups,
Check: resource.ComposeTestCheckFunc( Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.foo", &secgroup), testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.secgroup_1", &secgroup_1),
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance), testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.instance_1", &instance_1),
),
},
},
})
}
func TestAccComputeV2Instance_multi_secgroups_update(t *testing.T) {
var instance_1 servers.Server
var secgroup_1, secgroup_2 secgroups.SecurityGroup
var testAccComputeV2Instance_multi_secgroups_update_1 = fmt.Sprintf(`
resource "openstack_compute_secgroup_v2" "secgroup_1" {
name = "secgroup_1"
description = "a security group"
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_compute_secgroup_v2" "secgroup_2" {
name = "secgroup_2"
description = "another security group"
rule {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_compute_instance_v2" "instance_1" {
name = "instance_1"
security_groups = ["default"]
}`)
var testAccComputeV2Instance_multi_secgroups_update_2 = fmt.Sprintf(`
resource "openstack_compute_secgroup_v2" "secgroup_1" {
name = "secgroup_1"
description = "a security group"
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_compute_secgroup_v2" "secgroup_2" {
name = "secgroup_2"
description = "another security group"
rule {
from_port = 80
to_port = 80
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_compute_instance_v2" "instance_1" {
name = "instance_1"
security_groups = ["default", "${openstack_compute_secgroup_v2.secgroup_1.name}", "${openstack_compute_secgroup_v2.secgroup_2.name}"]
}`)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeV2InstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeV2Instance_multi_secgroups_update_1,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.secgroup_1", &secgroup_1),
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.secgroup_2", &secgroup_2),
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.instance_1", &instance_1),
),
},
resource.TestStep{
Config: testAccComputeV2Instance_multi_secgroups_update_2,
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.secgroup_1", &secgroup_1),
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.secgroup_2", &secgroup_2),
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.instance_1", &instance_1),
), ),
}, },
}, },

View File

@ -227,7 +227,9 @@ The following arguments are supported:
* `security_groups` - (Optional) An array of one or more security group names * `security_groups` - (Optional) An array of one or more security group names
to associate with the server. Changing this results in adding/removing to associate with the server. Changing this results in adding/removing
security groups from the existing server. security groups from the existing server. *Note*: When attaching the
instance to networks using Ports, place the security groups on the Port
and not the instance.
* `availability_zone` - (Optional) The availability zone in which to create * `availability_zone` - (Optional) The availability zone in which to create
the server. Changing this creates a new server. the server. Changing this creates a new server.
@ -430,3 +432,55 @@ resource "openstack_compute_instance_v2" "foo" {
} }
} }
``` ```
### Instances and Ports
Neutron Ports are a great feature and provide a lot of functionality. However,
there are some notes to be aware of when mixing Instances and Ports:
* When attaching an Instance to one or more networks using Ports, place the
security groups on the Port and not the Instance. If you place the security
groups on the Instance, the security groups will not be applied upon creation,
but they will be applied upon a refresh. This is a known OpenStack bug.
* Network IP information is not available within an instance for networks that
are attached with Ports. This is mostly due to the flexibility Neutron Ports
provide when it comes to IP addresses. For example, a Neutron Port can have
multiple Fixed IP addresses associated with it. It's not possible to know which
single IP address the user would want returned to the Instance's state
information. Therefore, in order for a Provisioner to connect to an Instance
via it's network Port, customize the `connection` information:
```
resource "openstack_networking_port_v2" "port_1" {
name = "port_1"
admin_state_up = "true"
network_id = "0a1d0a27-cffa-4de3-92c5-9d3fd3f2e74d"
security_group_ids = [
"2f02d20a-8dca-49b7-b26f-b6ce9fddaf4f",
"ca1e5ed7-dae8-4605-987b-fadaeeb30461",
]
}
resource "openstack_compute_instance_v2" "instance_1" {
name = "instance_1"
network {
port = "${openstack_networking_port_v2.port_1.id}"
}
connection {
user = "root"
host = "${openstack_networking_port_v2.port_1.fixed_ip.0.ip_address}"
private_key = "~/path/to/key"
}
provisioner "remote-exec" {
inline = [
"echo terraform executed > /tmp/foo"
]
}
}
```

View File

@ -85,3 +85,11 @@ The following attributes are exported:
* `security_groups` - See Argument Reference above. * `security_groups` - See Argument Reference above.
* `device_id` - See Argument Reference above. * `device_id` - See Argument Reference above.
* `fixed_ip/ip_address` - See Argument Reference above. * `fixed_ip/ip_address` - See Argument Reference above.
## Notes
### Ports and Instances
There are some notes to consider when connecting Instances to networks using
Ports. Please see the `openstack_compute_instance_v2` documentation for further
documentation.