Merge pull request #9407 from jtopjian/gophercloud-migration
provider/openstack: Gophercloud Migration
This commit is contained in:
commit
a148180505
|
@ -7,8 +7,8 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack"
|
"github.com/gophercloud/gophercloud/openstack"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -16,7 +16,6 @@ type Config struct {
|
||||||
UserID string
|
UserID string
|
||||||
Password string
|
Password string
|
||||||
Token string
|
Token string
|
||||||
APIKey string
|
|
||||||
IdentityEndpoint string
|
IdentityEndpoint string
|
||||||
TenantID string
|
TenantID string
|
||||||
TenantName string
|
TenantName string
|
||||||
|
@ -45,7 +44,6 @@ func (c *Config) loadAndValidate() error {
|
||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Password: c.Password,
|
Password: c.Password,
|
||||||
TokenID: c.Token,
|
TokenID: c.Token,
|
||||||
APIKey: c.APIKey,
|
|
||||||
IdentityEndpoint: c.IdentityEndpoint,
|
IdentityEndpoint: c.IdentityEndpoint,
|
||||||
TenantID: c.TenantID,
|
TenantID: c.TenantID,
|
||||||
TenantName: c.TenantName,
|
TenantName: c.TenantName,
|
||||||
|
|
|
@ -48,11 +48,6 @@ func Provider() terraform.ResourceProvider {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
DefaultFunc: schema.EnvDefaultFunc("OS_AUTH_TOKEN", ""),
|
DefaultFunc: schema.EnvDefaultFunc("OS_AUTH_TOKEN", ""),
|
||||||
},
|
},
|
||||||
"api_key": &schema.Schema{
|
|
||||||
Type: schema.TypeString,
|
|
||||||
Optional: true,
|
|
||||||
DefaultFunc: schema.EnvDefaultFunc("OS_API_KEY", ""),
|
|
||||||
},
|
|
||||||
"domain_id": &schema.Schema{
|
"domain_id": &schema.Schema{
|
||||||
Type: schema.TypeString,
|
Type: schema.TypeString,
|
||||||
Optional: true,
|
Optional: true,
|
||||||
|
@ -133,7 +128,6 @@ func configureProvider(d *schema.ResourceData) (interface{}, error) {
|
||||||
UserID: d.Get("user_id").(string),
|
UserID: d.Get("user_id").(string),
|
||||||
Password: d.Get("password").(string),
|
Password: d.Get("password").(string),
|
||||||
Token: d.Get("token").(string),
|
Token: d.Get("token").(string),
|
||||||
APIKey: d.Get("api_key").(string),
|
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
TenantName: d.Get("tenant_name").(string),
|
TenantName: d.Get("tenant_name").(string),
|
||||||
DomainID: d.Get("domain_id").(string),
|
DomainID: d.Get("domain_id").(string),
|
||||||
|
|
|
@ -6,12 +6,12 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceBlockStorageVolumeV1() *schema.Resource {
|
func resourceBlockStorageVolumeV1() *schema.Resource {
|
||||||
|
@ -169,7 +169,7 @@ func resourceBlockStorageVolumeV1Read(d *schema.ResourceData, meta interface{})
|
||||||
return CheckDeleted(d, err, "volume")
|
return CheckDeleted(d, err, "volume")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v)
|
log.Printf("[DEBUG] Retrieved volume %s: %+v", d.Id(), v)
|
||||||
|
|
||||||
d.Set("size", v.Size)
|
d.Set("size", v.Size)
|
||||||
d.Set("description", v.Description)
|
d.Set("description", v.Description)
|
||||||
|
@ -306,11 +306,7 @@ func VolumeV1StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
v, err := volumes.Get(client, volumeID).Extract()
|
v, err := volumes.Get(client, volumeID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
return v, "deleted", nil
|
return v, "deleted", nil
|
||||||
}
|
}
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccBlockStorageV1Volume_basic(t *testing.T) {
|
func TestAccBlockStorageV1Volume_basic(t *testing.T) {
|
||||||
|
@ -117,13 +117,10 @@ func testAccCheckBlockStorageV1VolumeDoesNotExist(t *testing.T, n string, volume
|
||||||
|
|
||||||
_, err = volumes.Get(blockStorageClient, volume.ID).Extract()
|
_, err = volumes.Get(blockStorageClient, volume.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceBlockStorageVolumeV2() *schema.Resource {
|
func resourceBlockStorageVolumeV2() *schema.Resource {
|
||||||
|
@ -181,7 +181,7 @@ func resourceBlockStorageVolumeV2Read(d *schema.ResourceData, meta interface{})
|
||||||
return CheckDeleted(d, err, "volume")
|
return CheckDeleted(d, err, "volume")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived volume %s: %+v", d.Id(), v)
|
log.Printf("[DEBUG] Retrieved volume %s: %+v", d.Id(), v)
|
||||||
|
|
||||||
d.Set("size", v.Size)
|
d.Set("size", v.Size)
|
||||||
d.Set("description", v.Description)
|
d.Set("description", v.Description)
|
||||||
|
@ -195,9 +195,9 @@ func resourceBlockStorageVolumeV2Read(d *schema.ResourceData, meta interface{})
|
||||||
attachments := make([]map[string]interface{}, len(v.Attachments))
|
attachments := make([]map[string]interface{}, len(v.Attachments))
|
||||||
for i, attachment := range v.Attachments {
|
for i, attachment := range v.Attachments {
|
||||||
attachments[i] = make(map[string]interface{})
|
attachments[i] = make(map[string]interface{})
|
||||||
attachments[i]["id"] = attachment["id"]
|
attachments[i]["id"] = attachment.ID
|
||||||
attachments[i]["instance_id"] = attachment["server_id"]
|
attachments[i]["instance_id"] = attachment.ServerID
|
||||||
attachments[i]["device"] = attachment["device"]
|
attachments[i]["device"] = attachment.Device
|
||||||
log.Printf("[DEBUG] attachment: %v", attachment)
|
log.Printf("[DEBUG] attachment: %v", attachment)
|
||||||
}
|
}
|
||||||
d.Set("attachment", attachments)
|
d.Set("attachment", attachments)
|
||||||
|
@ -249,7 +249,7 @@ func resourceBlockStorageVolumeV2Delete(d *schema.ResourceData, meta interface{}
|
||||||
} else {
|
} else {
|
||||||
for _, volumeAttachment := range v.Attachments {
|
for _, volumeAttachment := range v.Attachments {
|
||||||
log.Printf("[DEBUG] Attachment: %v", volumeAttachment)
|
log.Printf("[DEBUG] Attachment: %v", volumeAttachment)
|
||||||
if err := volumeattach.Delete(computeClient, volumeAttachment["server_id"].(string), volumeAttachment["id"].(string)).ExtractErr(); err != nil {
|
if err := volumeattach.Delete(computeClient, volumeAttachment.ServerID, volumeAttachment.ID).ExtractErr(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,11 +318,7 @@ func VolumeV2StateRefreshFunc(client *gophercloud.ServiceClient, volumeID string
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
v, err := volumes.Get(client, volumeID).Extract()
|
v, err := volumes.Get(client, volumeID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
return v, "deleted", nil
|
return v, "deleted", nil
|
||||||
}
|
}
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v2/volumes"
|
"github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccBlockStorageV2Volume_basic(t *testing.T) {
|
func TestAccBlockStorageV2Volume_basic(t *testing.T) {
|
||||||
|
@ -130,11 +130,7 @@ func testAccCheckBlockStorageV2VolumeDoesNotExist(t *testing.T, n string, volume
|
||||||
|
|
||||||
_, err = volumes.Get(blockStorageClient, volume.ID).Extract()
|
_, err = volumes.Get(blockStorageClient, volume.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceComputeFloatingIPV2() *schema.Resource {
|
func resourceComputeFloatingIPV2() *schema.Resource {
|
||||||
|
@ -58,11 +58,11 @@ func resourceComputeFloatingIPV2Create(d *schema.ResourceData, meta interface{})
|
||||||
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
createOpts := &floatingip.CreateOpts{
|
createOpts := &floatingips.CreateOpts{
|
||||||
Pool: d.Get("pool").(string),
|
Pool: d.Get("pool").(string),
|
||||||
}
|
}
|
||||||
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
||||||
newFip, err := floatingip.Create(computeClient, createOpts).Extract()
|
newFip, err := floatingips.Create(computeClient, createOpts).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error creating Floating IP: %s", err)
|
return fmt.Errorf("Error creating Floating IP: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func resourceComputeFloatingIPV2Read(d *schema.ResourceData, meta interface{}) e
|
||||||
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
return fmt.Errorf("Error creating OpenStack compute client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fip, err := floatingip.Get(computeClient, d.Id()).Extract()
|
fip, err := floatingips.Get(computeClient, d.Id()).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CheckDeleted(d, err, "floating ip")
|
return CheckDeleted(d, err, "floating ip")
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ func resourceComputeFloatingIPV2Delete(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Deleting Floating IP %s", d.Id())
|
log.Printf("[DEBUG] Deleting Floating IP %s", d.Id())
|
||||||
if err := floatingip.Delete(computeClient, d.Id()).ExtractErr(); err != nil {
|
if err := floatingips.Delete(computeClient, d.Id()).ExtractErr(); err != nil {
|
||||||
return fmt.Errorf("Error deleting Floating IP: %s", err)
|
return fmt.Errorf("Error deleting Floating IP: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccComputeV2FloatingIP_basic(t *testing.T) {
|
func TestAccComputeV2FloatingIP_basic(t *testing.T) {
|
||||||
var floatingIP floatingip.FloatingIP
|
var floatingIP floatingips.FloatingIP
|
||||||
|
|
||||||
resource.Test(t, resource.TestCase{
|
resource.Test(t, resource.TestCase{
|
||||||
PreCheck: func() { testAccPreCheck(t) },
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
@ -32,7 +32,7 @@ func TestAccComputeV2FloatingIP_basic(t *testing.T) {
|
||||||
|
|
||||||
func TestAccComputeV2FloatingIP_attach(t *testing.T) {
|
func TestAccComputeV2FloatingIP_attach(t *testing.T) {
|
||||||
var instance servers.Server
|
var instance servers.Server
|
||||||
var fip floatingip.FloatingIP
|
var fip floatingips.FloatingIP
|
||||||
var testAccComputeV2FloatingIP_attach = fmt.Sprintf(`
|
var testAccComputeV2FloatingIP_attach = fmt.Sprintf(`
|
||||||
resource "openstack_compute_floatingip_v2" "myip" {
|
resource "openstack_compute_floatingip_v2" "myip" {
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func testAccCheckComputeV2FloatingIPDestroy(s *terraform.State) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := floatingip.Get(computeClient, rs.Primary.ID).Extract()
|
_, err := floatingips.Get(computeClient, rs.Primary.ID).Extract()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("FloatingIP still exists")
|
return fmt.Errorf("FloatingIP still exists")
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ func testAccCheckComputeV2FloatingIPDestroy(s *terraform.State) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccCheckComputeV2FloatingIPExists(t *testing.T, n string, kp *floatingip.FloatingIP) resource.TestCheckFunc {
|
func testAccCheckComputeV2FloatingIPExists(t *testing.T, n string, kp *floatingips.FloatingIP) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
rs, ok := s.RootModule().Resources[n]
|
rs, ok := s.RootModule().Resources[n]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -103,7 +103,7 @@ func testAccCheckComputeV2FloatingIPExists(t *testing.T, n string, kp *floatingi
|
||||||
return fmt.Errorf("(testAccCheckComputeV2FloatingIPExists) Error creating OpenStack compute client: %s", err)
|
return fmt.Errorf("(testAccCheckComputeV2FloatingIPExists) Error creating OpenStack compute client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
found, err := floatingip.Get(computeClient, rs.Primary.ID).Extract()
|
found, err := floatingips.Get(computeClient, rs.Primary.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,21 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/images"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/schedulerhints"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/startstop"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/tenantnetworks"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/flavors"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/images"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceComputeInstanceV2() *schema.Resource {
|
func resourceComputeInstanceV2() *schema.Resource {
|
||||||
|
@ -376,6 +376,8 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configDrive := d.Get("config_drive").(bool)
|
||||||
|
|
||||||
createOpts = &servers.CreateOpts{
|
createOpts = &servers.CreateOpts{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
ImageRef: imageId,
|
ImageRef: imageId,
|
||||||
|
@ -384,7 +386,7 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
AvailabilityZone: d.Get("availability_zone").(string),
|
AvailabilityZone: d.Get("availability_zone").(string),
|
||||||
Networks: networks,
|
Networks: networks,
|
||||||
Metadata: resourceInstanceMetadataV2(d),
|
Metadata: resourceInstanceMetadataV2(d),
|
||||||
ConfigDrive: d.Get("config_drive").(bool),
|
ConfigDrive: &configDrive,
|
||||||
AdminPass: d.Get("admin_pass").(string),
|
AdminPass: d.Get("admin_pass").(string),
|
||||||
UserData: []byte(d.Get("user_data").(string)),
|
UserData: []byte(d.Get("user_data").(string)),
|
||||||
Personality: resourceInstancePersonalityV2(d),
|
Personality: resourceInstancePersonalityV2(d),
|
||||||
|
@ -398,7 +400,11 @@ func resourceComputeInstanceV2Create(d *schema.ResourceData, meta interface{}) e
|
||||||
}
|
}
|
||||||
|
|
||||||
if vL, ok := d.GetOk("block_device"); ok {
|
if vL, ok := d.GetOk("block_device"); ok {
|
||||||
blockDevices := resourceInstanceBlockDevicesV2(d, vL.([]interface{}))
|
blockDevices, err := resourceInstanceBlockDevicesV2(d, vL.([]interface{}))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
createOpts = &bootfromvolume.CreateOptsExt{
|
createOpts = &bootfromvolume.CreateOptsExt{
|
||||||
CreateOptsBuilder: createOpts,
|
CreateOptsBuilder: createOpts,
|
||||||
BlockDevice: blockDevices,
|
BlockDevice: blockDevices,
|
||||||
|
@ -493,7 +499,7 @@ func resourceComputeInstanceV2Read(d *schema.ResourceData, meta interface{}) err
|
||||||
return CheckDeleted(d, err, "server")
|
return CheckDeleted(d, err, "server")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived Server %s: %+v", d.Id(), server)
|
log.Printf("[DEBUG] Retrieved Server %s: %+v", d.Id(), server)
|
||||||
|
|
||||||
d.Set("name", server.Name)
|
d.Set("name", server.Name)
|
||||||
|
|
||||||
|
@ -613,24 +619,20 @@ func resourceComputeInstanceV2Update(d *schema.ResourceData, meta interface{}) e
|
||||||
log.Printf("[DEBUG] Security groups to remove: %v", secgroupsToRemove)
|
log.Printf("[DEBUG] Security groups to remove: %v", secgroupsToRemove)
|
||||||
|
|
||||||
for _, g := range secgroupsToRemove.List() {
|
for _, g := range secgroupsToRemove.List() {
|
||||||
err := secgroups.RemoveServerFromGroup(computeClient, d.Id(), g.(string)).ExtractErr()
|
err := secgroups.RemoveServer(computeClient, d.Id(), g.(string)).ExtractErr()
|
||||||
if err != nil && err.Error() != "EOF" {
|
if err != nil && err.Error() != "EOF" {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error removing security group (%s) from OpenStack server (%s): %s", g, d.Id(), err)
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
return fmt.Errorf("Error removing security group (%s) from OpenStack server (%s): %s", g, 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, d.Id())
|
log.Printf("[DEBUG] Removed security group (%s) from instance (%s)", g, d.Id())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, g := range secgroupsToAdd.List() {
|
for _, g := range secgroupsToAdd.List() {
|
||||||
err := secgroups.AddServerToGroup(computeClient, d.Id(), g.(string)).ExtractErr()
|
err := secgroups.AddServer(computeClient, d.Id(), g.(string)).ExtractErr()
|
||||||
if err != nil && err.Error() != "EOF" {
|
if err != nil && err.Error() != "EOF" {
|
||||||
return fmt.Errorf("Error adding security group (%s) to OpenStack server (%s): %s", g, d.Id(), err)
|
return fmt.Errorf("Error adding security group (%s) to OpenStack server (%s): %s", g, d.Id(), err)
|
||||||
}
|
}
|
||||||
|
@ -873,11 +875,7 @@ func ServerV2StateRefreshFunc(client *gophercloud.ServiceClient, instanceID stri
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
s, err := servers.Get(client, instanceID).Extract()
|
s, err := servers.Get(client, instanceID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
return s, "DELETED", nil
|
return s, "DELETED", nil
|
||||||
}
|
}
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
|
@ -970,17 +968,21 @@ func getInstanceNetworks(computeClient *gophercloud.ServiceClient, d *schema.Res
|
||||||
|
|
||||||
allPages, err := tenantnetworks.List(computeClient).AllPages()
|
allPages, err := tenantnetworks.List(computeClient).AllPages()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
log.Printf("[DEBUG] os-tenant-networks disabled")
|
||||||
return nil, err
|
tenantNetworkExt = false
|
||||||
}
|
}
|
||||||
|
|
||||||
if errCode.Actual == 404 || errCode.Actual == 403 {
|
log.Printf("[DEBUG] Err looks like: %+v", err)
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 403 {
|
||||||
|
log.Printf("[DEBUG] os-tenant-networks disabled.")
|
||||||
tenantNetworkExt = false
|
tenantNetworkExt = false
|
||||||
} else {
|
} else {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
networkID := ""
|
networkID := ""
|
||||||
networkName := ""
|
networkName := ""
|
||||||
|
@ -1137,13 +1139,12 @@ func associateFloatingIPsToInstance(computeClient *gophercloud.ServiceClient, d
|
||||||
}
|
}
|
||||||
|
|
||||||
func associateFloatingIPToInstance(computeClient *gophercloud.ServiceClient, floatingIP string, instanceID string, fixedIP string) error {
|
func associateFloatingIPToInstance(computeClient *gophercloud.ServiceClient, floatingIP string, instanceID string, fixedIP string) error {
|
||||||
associateOpts := floatingip.AssociateOpts{
|
associateOpts := floatingips.AssociateOpts{
|
||||||
ServerID: instanceID,
|
|
||||||
FloatingIP: floatingIP,
|
FloatingIP: floatingIP,
|
||||||
FixedIP: fixedIP,
|
FixedIP: fixedIP,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := floatingip.AssociateInstance(computeClient, associateOpts).ExtractErr(); err != nil {
|
if err := floatingips.AssociateInstance(computeClient, instanceID, associateOpts).ExtractErr(); err != nil {
|
||||||
return fmt.Errorf("Error associating floating IP: %s", err)
|
return fmt.Errorf("Error associating floating IP: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1151,13 +1152,11 @@ func associateFloatingIPToInstance(computeClient *gophercloud.ServiceClient, flo
|
||||||
}
|
}
|
||||||
|
|
||||||
func disassociateFloatingIPFromInstance(computeClient *gophercloud.ServiceClient, floatingIP string, instanceID string, fixedIP string) error {
|
func disassociateFloatingIPFromInstance(computeClient *gophercloud.ServiceClient, floatingIP string, instanceID string, fixedIP string) error {
|
||||||
associateOpts := floatingip.AssociateOpts{
|
disassociateOpts := floatingips.DisassociateOpts{
|
||||||
ServerID: instanceID,
|
|
||||||
FloatingIP: floatingIP,
|
FloatingIP: floatingIP,
|
||||||
FixedIP: fixedIP,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := floatingip.DisassociateInstance(computeClient, associateOpts).ExtractErr(); err != nil {
|
if err := floatingips.DisassociateInstance(computeClient, instanceID, disassociateOpts).ExtractErr(); err != nil {
|
||||||
return fmt.Errorf("Error disassociating floating IP: %s", err)
|
return fmt.Errorf("Error disassociating floating IP: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1172,24 +1171,45 @@ func resourceInstanceMetadataV2(d *schema.ResourceData) map[string]string {
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceInstanceBlockDevicesV2(d *schema.ResourceData, bds []interface{}) []bootfromvolume.BlockDevice {
|
func resourceInstanceBlockDevicesV2(d *schema.ResourceData, bds []interface{}) ([]bootfromvolume.BlockDevice, error) {
|
||||||
blockDeviceOpts := make([]bootfromvolume.BlockDevice, len(bds))
|
blockDeviceOpts := make([]bootfromvolume.BlockDevice, len(bds))
|
||||||
for i, bd := range bds {
|
for i, bd := range bds {
|
||||||
bdM := bd.(map[string]interface{})
|
bdM := bd.(map[string]interface{})
|
||||||
sourceType := bootfromvolume.SourceType(bdM["source_type"].(string))
|
|
||||||
blockDeviceOpts[i] = bootfromvolume.BlockDevice{
|
blockDeviceOpts[i] = bootfromvolume.BlockDevice{
|
||||||
UUID: bdM["uuid"].(string),
|
UUID: bdM["uuid"].(string),
|
||||||
SourceType: sourceType,
|
|
||||||
VolumeSize: bdM["volume_size"].(int),
|
VolumeSize: bdM["volume_size"].(int),
|
||||||
DestinationType: bdM["destination_type"].(string),
|
|
||||||
BootIndex: bdM["boot_index"].(int),
|
BootIndex: bdM["boot_index"].(int),
|
||||||
DeleteOnTermination: bdM["delete_on_termination"].(bool),
|
DeleteOnTermination: bdM["delete_on_termination"].(bool),
|
||||||
GuestFormat: bdM["guest_format"].(string),
|
GuestFormat: bdM["guest_format"].(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceType := bdM["source_type"].(string)
|
||||||
|
switch sourceType {
|
||||||
|
case "blank":
|
||||||
|
blockDeviceOpts[i].SourceType = bootfromvolume.SourceBlank
|
||||||
|
case "image":
|
||||||
|
blockDeviceOpts[i].SourceType = bootfromvolume.SourceImage
|
||||||
|
case "snapshot":
|
||||||
|
blockDeviceOpts[i].SourceType = bootfromvolume.SourceSnapshot
|
||||||
|
case "volume":
|
||||||
|
blockDeviceOpts[i].SourceType = bootfromvolume.SourceVolume
|
||||||
|
default:
|
||||||
|
return blockDeviceOpts, fmt.Errorf("unknown block device source type %s", sourceType)
|
||||||
|
}
|
||||||
|
|
||||||
|
destinationType := bdM["destination_type"].(string)
|
||||||
|
switch destinationType {
|
||||||
|
case "local":
|
||||||
|
blockDeviceOpts[i].DestinationType = bootfromvolume.DestinationLocal
|
||||||
|
case "volume":
|
||||||
|
blockDeviceOpts[i].DestinationType = bootfromvolume.DestinationVolume
|
||||||
|
default:
|
||||||
|
return blockDeviceOpts, fmt.Errorf("unknown block device destination type %s", destinationType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Block Device Options: %+v", blockDeviceOpts)
|
log.Printf("[DEBUG] Block Device Options: %+v", blockDeviceOpts)
|
||||||
return blockDeviceOpts
|
return blockDeviceOpts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceInstanceSchedulerHintsV2(d *schema.ResourceData, schedulerHintsRaw map[string]interface{}) schedulerhints.SchedulerHints {
|
func resourceInstanceSchedulerHintsV2(d *schema.ResourceData, schedulerHintsRaw map[string]interface{}) schedulerhints.SchedulerHints {
|
||||||
|
@ -1291,19 +1311,14 @@ func setImageInformation(computeClient *gophercloud.ServiceClient, server *serve
|
||||||
if imageId != "" {
|
if imageId != "" {
|
||||||
d.Set("image_id", imageId)
|
d.Set("image_id", imageId)
|
||||||
if image, err := images.Get(computeClient, imageId).Extract(); err != nil {
|
if image, err := images.Get(computeClient, imageId).Extract(); err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
// If the image name can't be found, set the value to "Image not found".
|
// If the image name can't be found, set the value to "Image not found".
|
||||||
// The most likely scenario is that the image no longer exists in the Image Service
|
// The most likely scenario is that the image no longer exists in the Image Service
|
||||||
// but the instance still has a record from when it existed.
|
// but the instance still has a record from when it existed.
|
||||||
d.Set("image_name", "Image not found")
|
d.Set("image_name", "Image not found")
|
||||||
return nil
|
return nil
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
} else {
|
} else {
|
||||||
d.Set("image_name", image.Name)
|
d.Set("image_name", image.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,13 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/blockstorage/v1/volumes"
|
"github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/floatingip"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccComputeV2Instance_basic(t *testing.T) {
|
func TestAccComputeV2Instance_basic(t *testing.T) {
|
||||||
|
@ -431,7 +431,7 @@ func TestAccComputeV2Instance_volumeAttachToNewInstance(t *testing.T) {
|
||||||
|
|
||||||
func TestAccComputeV2Instance_floatingIPAttachGlobally(t *testing.T) {
|
func TestAccComputeV2Instance_floatingIPAttachGlobally(t *testing.T) {
|
||||||
var instance servers.Server
|
var instance servers.Server
|
||||||
var fip floatingip.FloatingIP
|
var fip floatingips.FloatingIP
|
||||||
var testAccComputeV2Instance_floatingIPAttachGlobally = fmt.Sprintf(`
|
var testAccComputeV2Instance_floatingIPAttachGlobally = fmt.Sprintf(`
|
||||||
resource "openstack_compute_floatingip_v2" "myip" {
|
resource "openstack_compute_floatingip_v2" "myip" {
|
||||||
}
|
}
|
||||||
|
@ -466,7 +466,7 @@ func TestAccComputeV2Instance_floatingIPAttachGlobally(t *testing.T) {
|
||||||
|
|
||||||
func TestAccComputeV2Instance_floatingIPAttachToNetwork(t *testing.T) {
|
func TestAccComputeV2Instance_floatingIPAttachToNetwork(t *testing.T) {
|
||||||
var instance servers.Server
|
var instance servers.Server
|
||||||
var fip floatingip.FloatingIP
|
var fip floatingips.FloatingIP
|
||||||
var testAccComputeV2Instance_floatingIPAttachToNetwork = fmt.Sprintf(`
|
var testAccComputeV2Instance_floatingIPAttachToNetwork = fmt.Sprintf(`
|
||||||
resource "openstack_compute_floatingip_v2" "myip" {
|
resource "openstack_compute_floatingip_v2" "myip" {
|
||||||
}
|
}
|
||||||
|
@ -502,7 +502,7 @@ func TestAccComputeV2Instance_floatingIPAttachToNetwork(t *testing.T) {
|
||||||
|
|
||||||
func TestAccComputeV2Instance_floatingIPAttachAndChange(t *testing.T) {
|
func TestAccComputeV2Instance_floatingIPAttachAndChange(t *testing.T) {
|
||||||
var instance servers.Server
|
var instance servers.Server
|
||||||
var fip floatingip.FloatingIP
|
var fip floatingips.FloatingIP
|
||||||
var testAccComputeV2Instance_floatingIPAttachToNetwork_1 = fmt.Sprintf(`
|
var testAccComputeV2Instance_floatingIPAttachToNetwork_1 = fmt.Sprintf(`
|
||||||
resource "openstack_compute_floatingip_v2" "myip_1" {
|
resource "openstack_compute_floatingip_v2" "myip_1" {
|
||||||
}
|
}
|
||||||
|
@ -1098,11 +1098,7 @@ func testAccCheckComputeV2InstanceDoesNotExist(t *testing.T, n string, instance
|
||||||
|
|
||||||
_, err = servers.Get(computeClient, instance.ID).Extract()
|
_, err = servers.Get(computeClient, instance.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -1124,7 +1120,7 @@ func testAccCheckComputeV2InstanceMetadata(
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if v == value.(string) {
|
if v == value {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1221,7 +1217,7 @@ func testAccCheckComputeV2InstanceBootVolumeAttachment(
|
||||||
}
|
}
|
||||||
|
|
||||||
func testAccCheckComputeV2InstanceFloatingIPAttach(
|
func testAccCheckComputeV2InstanceFloatingIPAttach(
|
||||||
instance *servers.Server, fip *floatingip.FloatingIP) resource.TestCheckFunc {
|
instance *servers.Server, fip *floatingips.FloatingIP) resource.TestCheckFunc {
|
||||||
return func(s *terraform.State) error {
|
return func(s *terraform.State) error {
|
||||||
if fip.InstanceID == instance.ID {
|
if fip.InstanceID == instance.ID {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceComputeKeypairV2() *schema.Resource {
|
func resourceComputeKeypairV2() *schema.Resource {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/keypairs"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccComputeV2Keypair_basic(t *testing.T) {
|
func TestAccComputeV2Keypair_basic(t *testing.T) {
|
||||||
|
|
|
@ -7,11 +7,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||||
"github.com/hashicorp/terraform/helper/hashcode"
|
"github.com/hashicorp/terraform/helper/hashcode"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceComputeSecGroupV2() *schema.Resource {
|
func resourceComputeSecGroupV2() *schema.Resource {
|
||||||
|
@ -197,15 +197,11 @@ func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) e
|
||||||
rule := resourceSecGroupRuleV2(d, r)
|
rule := resourceSecGroupRuleV2(d, r)
|
||||||
err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr()
|
err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
continue
|
continue
|
||||||
} else {
|
|
||||||
return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id())
|
||||||
} else {
|
} else {
|
||||||
log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
|
log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccComputeV2SecGroup_basic(t *testing.T) {
|
func TestAccComputeV2SecGroup_basic(t *testing.T) {
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceComputeServerGroupV2() *schema.Resource {
|
func resourceComputeServerGroupV2() *schema.Resource {
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/servergroups"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/servergroups"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccComputeV2ServerGroup_basic(t *testing.T) {
|
func TestAccComputeV2ServerGroup_basic(t *testing.T) {
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceFWFirewallV1() *schema.Resource {
|
func resourceFWFirewallV1() *schema.Resource {
|
||||||
|
@ -231,14 +231,11 @@ func waitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id str
|
||||||
log.Printf("[DEBUG] Get firewall %s => %#v", id, fw)
|
log.Printf("[DEBUG] Get firewall %s => %#v", id, fw)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpStatus := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
log.Printf("[DEBUG] Get firewall %s status is %d", id, httpStatus.Actual)
|
|
||||||
|
|
||||||
if httpStatus.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Firewall %s is actually deleted", id)
|
log.Printf("[DEBUG] Firewall %s is actually deleted", id)
|
||||||
return "", "DELETED", nil
|
return "", "DELETED", nil
|
||||||
}
|
}
|
||||||
return nil, "", fmt.Errorf("Unexpected status code %d", httpStatus.Actual)
|
return nil, "", fmt.Errorf("Unexpected error: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Firewall %s deletion is pending", id)
|
log.Printf("[DEBUG] Firewall %s deletion is pending", id)
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccFWFirewallV1_basic(t *testing.T) {
|
func TestAccFWFirewallV1_basic(t *testing.T) {
|
||||||
|
@ -47,13 +47,13 @@ func testAccCheckFWFirewallV1Destroy(s *terraform.State) error {
|
||||||
if rs.Type != "openstack_firewall" {
|
if rs.Type != "openstack_firewall" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract()
|
_, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Firewall (%s) still exists.", rs.Primary.ID)
|
return fmt.Errorf("Firewall (%s) still exists.", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); !ok {
|
||||||
if !ok || httpError.Actual != 404 {
|
return err
|
||||||
return httpError
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -84,19 +84,15 @@ func testAccCheckFWFirewallV1Exists(n, expectedName, expectedDescription string,
|
||||||
// if we get a 404 error. Fail on any other error.
|
// if we get a 404 error. Fail on any other error.
|
||||||
found, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract()
|
found, err = firewalls.Get(networkingClient, rs.Primary.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok || httpError.Actual != 404 {
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if found.Name != expectedName {
|
if found.Name != expectedName {
|
||||||
return fmt.Errorf("Expected Name to be <%s> but found <%s>", expectedName, found.Name)
|
return fmt.Errorf("Expected Name to be <%s> but found <%s>", expectedName, found.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,10 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceFWPolicyV1() *schema.Resource {
|
func resourceFWPolicyV1() *schema.Resource {
|
||||||
|
@ -61,7 +62,6 @@ func resourceFWPolicyV1() *schema.Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error {
|
func resourceFWPolicyV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -130,7 +130,6 @@ func resourceFWPolicyV1Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error {
|
func resourceFWPolicyV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -179,24 +178,38 @@ func resourceFWPolicyV1Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 15; i++ {
|
stateConf := &resource.StateChangeConf{
|
||||||
|
Pending: []string{"ACTIVE"},
|
||||||
err = policies.Delete(networkingClient, d.Id()).Err
|
Target: []string{"DELETED"},
|
||||||
if err == nil {
|
Refresh: waitForFirewallPolicyDeletion(networkingClient, d.Id()),
|
||||||
break
|
Timeout: 120 * time.Second,
|
||||||
|
Delay: 0,
|
||||||
|
MinTimeout: 2 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, err = stateConf.WaitForState(); err != nil {
|
||||||
if !ok || httpError.Actual != 409 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForFirewallPolicyDeletion(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc {
|
||||||
|
return func() (interface{}, string, error) {
|
||||||
|
err := policies.Delete(networkingClient, id).Err
|
||||||
|
if err == nil {
|
||||||
|
return "", "DELETED", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
// This error usually means that the policy is attached
|
// This error usually means that the policy is attached
|
||||||
// to a firewall. At this point, the firewall is probably
|
// to a firewall. At this point, the firewall is probably
|
||||||
// being delete. So, we retry a few times.
|
// being delete. So, we retry a few times.
|
||||||
|
return nil, "ACTIVE", nil
|
||||||
time.Sleep(time.Second * 2)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return nil, "ACTIVE", err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccFWPolicyV1_basic(t *testing.T) {
|
func TestAccFWPolicyV1_basic(t *testing.T) {
|
||||||
|
@ -80,9 +80,8 @@ func testAccCheckFWPolicyV1Destroy(s *terraform.State) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Firewall policy (%s) still exists.", rs.Primary.ID)
|
return fmt.Errorf("Firewall policy (%s) still exists.", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); !ok {
|
||||||
if !ok || httpError.Actual != 404 {
|
return err
|
||||||
return httpError
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -113,19 +112,15 @@ func testAccCheckFWPolicyV1Exists(n, name, description string, ruleCount int) re
|
||||||
// if we get a 404 error. Fail on any other error.
|
// if we get a 404 error. Fail on any other error.
|
||||||
found, err = policies.Get(networkingClient, rs.Primary.ID).Extract()
|
found, err = policies.Get(networkingClient, rs.Primary.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok || httpError.Actual != 404 {
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if name != found.Name {
|
if name != found.Name {
|
||||||
return fmt.Errorf("Expected name <%s>, but found <%s>", name, found.Name)
|
return fmt.Errorf("Expected name <%s>, but found <%s>", name, found.Name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,10 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/policies"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceFWRuleV1() *schema.Resource {
|
func resourceFWRuleV1() *schema.Resource {
|
||||||
|
@ -86,13 +87,14 @@ func resourceFWRuleV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
enabled := d.Get("enabled").(bool)
|
enabled := d.Get("enabled").(bool)
|
||||||
|
ipVersion := resourceFWRuleV1DetermineIPVersion(d.Get("ip_version").(int))
|
||||||
|
|
||||||
ruleConfiguration := rules.CreateOpts{
|
ruleConfiguration := rules.CreateOpts{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
Description: d.Get("description").(string),
|
Description: d.Get("description").(string),
|
||||||
Protocol: d.Get("protocol").(string),
|
Protocol: d.Get("protocol").(string),
|
||||||
Action: d.Get("action").(string),
|
Action: d.Get("action").(string),
|
||||||
IPVersion: d.Get("ip_version").(int),
|
IPVersion: ipVersion,
|
||||||
SourceIPAddress: d.Get("source_ip_address").(string),
|
SourceIPAddress: d.Get("source_ip_address").(string),
|
||||||
DestinationIPAddress: d.Get("destination_ip_address").(string),
|
DestinationIPAddress: d.Get("destination_ip_address").(string),
|
||||||
SourcePort: d.Get("source_port").(string),
|
SourcePort: d.Get("source_port").(string),
|
||||||
|
@ -101,6 +103,11 @@ func resourceFWRuleV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ip_version"); ok {
|
||||||
|
ipVersion := resourceFWRuleV1DetermineIPVersion(v.(int))
|
||||||
|
ruleConfiguration.IPVersion = ipVersion
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Create firewall rule: %#v", ruleConfiguration)
|
log.Printf("[DEBUG] Create firewall rule: %#v", ruleConfiguration)
|
||||||
|
|
||||||
rule, err := rules.Create(networkingClient, ruleConfiguration).Extract()
|
rule, err := rules.Create(networkingClient, ruleConfiguration).Extract()
|
||||||
|
@ -156,39 +163,54 @@ func resourceFWRuleV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
opts := rules.UpdateOpts{}
|
opts := rules.UpdateOpts{}
|
||||||
|
|
||||||
if d.HasChange("name") {
|
if d.HasChange("name") {
|
||||||
opts.Name = d.Get("name").(string)
|
v := d.Get("name").(string)
|
||||||
|
opts.Name = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("description") {
|
if d.HasChange("description") {
|
||||||
opts.Description = d.Get("description").(string)
|
v := d.Get("description").(string)
|
||||||
|
opts.Description = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("protocol") {
|
if d.HasChange("protocol") {
|
||||||
opts.Protocol = d.Get("protocol").(string)
|
v := d.Get("protocol").(string)
|
||||||
|
opts.Protocol = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("action") {
|
if d.HasChange("action") {
|
||||||
opts.Action = d.Get("action").(string)
|
v := d.Get("action").(string)
|
||||||
|
opts.Action = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("ip_version") {
|
if d.HasChange("ip_version") {
|
||||||
opts.IPVersion = d.Get("ip_version").(int)
|
v := d.Get("ip_version").(int)
|
||||||
|
ipVersion := resourceFWRuleV1DetermineIPVersion(v)
|
||||||
|
opts.IPVersion = &ipVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("source_ip_address") {
|
if d.HasChange("source_ip_address") {
|
||||||
sourceIPAddress := d.Get("source_ip_address").(string)
|
v := d.Get("source_ip_address").(string)
|
||||||
opts.SourceIPAddress = &sourceIPAddress
|
opts.SourceIPAddress = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("destination_ip_address") {
|
if d.HasChange("destination_ip_address") {
|
||||||
destinationIPAddress := d.Get("destination_ip_address").(string)
|
v := d.Get("destination_ip_address").(string)
|
||||||
opts.DestinationIPAddress = &destinationIPAddress
|
opts.DestinationIPAddress = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("source_port") {
|
if d.HasChange("source_port") {
|
||||||
sourcePort := d.Get("source_port").(string)
|
v := d.Get("source_port").(string)
|
||||||
opts.SourcePort = &sourcePort
|
opts.SourcePort = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("destination_port") {
|
if d.HasChange("destination_port") {
|
||||||
destinationPort := d.Get("destination_port").(string)
|
v := d.Get("destination_port").(string)
|
||||||
opts.DestinationPort = &destinationPort
|
opts.DestinationPort = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("enabled") {
|
if d.HasChange("enabled") {
|
||||||
enabled := d.Get("enabled").(bool)
|
v := d.Get("enabled").(bool)
|
||||||
opts.Enabled = &enabled
|
opts.Enabled = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Updating firewall rules: %#v", opts)
|
log.Printf("[DEBUG] Updating firewall rules: %#v", opts)
|
||||||
|
@ -216,7 +238,7 @@ func resourceFWRuleV1Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if rule.PolicyID != "" {
|
if rule.PolicyID != "" {
|
||||||
err := policies.RemoveRule(networkingClient, rule.PolicyID, rule.ID)
|
_, err := policies.RemoveRule(networkingClient, rule.PolicyID, rule.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -224,3 +246,16 @@ func resourceFWRuleV1Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
return rules.Delete(networkingClient, d.Id()).Err
|
return rules.Delete(networkingClient, d.Id()).Err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceFWRuleV1DetermineIPVersion(ipv int) gophercloud.IPVersion {
|
||||||
|
// Determine the IP Version
|
||||||
|
var ipVersion gophercloud.IPVersion
|
||||||
|
switch ipv {
|
||||||
|
case 4:
|
||||||
|
ipVersion = gophercloud.IPv4
|
||||||
|
case 6:
|
||||||
|
ipVersion = gophercloud.IPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipVersion
|
||||||
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/fwaas/rules"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccFWRuleV1_basic(t *testing.T) {
|
func TestAccFWRuleV1_basic(t *testing.T) {
|
||||||
|
@ -88,9 +88,8 @@ func testAccCheckFWRuleV1Destroy(s *terraform.State) error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Firewall rule (%s) still exists.", rs.Primary.ID)
|
return fmt.Errorf("Firewall rule (%s) still exists.", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); !ok {
|
||||||
if !ok || httpError.Actual != 404 {
|
return err
|
||||||
return httpError
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -121,19 +120,15 @@ func testAccCheckFWRuleV1Exists(n string, expected *rules.Rule) resource.TestChe
|
||||||
// if we get a 404 error. Fail on any other error.
|
// if we get a 404 error. Fail on any other error.
|
||||||
found, err = rules.Get(networkingClient, rs.Primary.ID).Extract()
|
found, err = rules.Get(networkingClient, rs.Primary.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok || httpError.Actual != 404 {
|
|
||||||
time.Sleep(time.Second)
|
time.Sleep(time.Second)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
expected.ID = found.ID
|
expected.ID = found.ID
|
||||||
// Erase the tenant id because we don't want to compare
|
// Erase the tenant id because we don't want to compare
|
||||||
// it as long it is not present in the expected
|
// it as long it is not present in the expected
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceListenerV2() *schema.Resource {
|
func resourceListenerV2() *schema.Resource {
|
||||||
|
@ -178,7 +178,7 @@ func resourceListenerV2Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LBV2 listener")
|
return CheckDeleted(d, err, "LBV2 listener")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LBaaSV2 listener %s: %+v", d.Id(), listener)
|
log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 listener %s: %+v", d.Id(), listener)
|
||||||
|
|
||||||
d.Set("id", listener.ID)
|
d.Set("id", listener.ID)
|
||||||
d.Set("name", listener.Name)
|
d.Set("name", listener.Name)
|
||||||
|
@ -285,27 +285,29 @@ func waitForListenerDelete(networkingClient *gophercloud.ServiceClient, listener
|
||||||
|
|
||||||
listener, err := listeners.Get(networkingClient, listenerID).Extract()
|
listener, err := listeners.Get(networkingClient, listenerID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return listener, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
|
||||||
return listener, "DELETED", nil
|
return listener, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return listener, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Openstack LBaaSV2 listener: %+v", listener)
|
log.Printf("[DEBUG] Openstack LBaaSV2 listener: %+v", listener)
|
||||||
err = listeners.Delete(networkingClient, listenerID).ExtractErr()
|
err = listeners.Delete(networkingClient, listenerID).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return listener, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
|
||||||
return listener, "DELETED", nil
|
return listener, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
log.Printf("[DEBUG] OpenStack LBaaSV2 listener (%s) is still in use.", listenerID)
|
||||||
|
return listener, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LBaaSV2 listener %s still active.", listenerID)
|
log.Printf("[DEBUG] OpenStack LBaaSV2 listener %s still active.", listenerID)
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV2Listener_basic(t *testing.T) {
|
func TestAccLBV2Listener_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceLoadBalancerV2() *schema.Resource {
|
func resourceLoadBalancerV2() *schema.Resource {
|
||||||
|
@ -138,7 +138,7 @@ func resourceLoadBalancerV2Read(d *schema.ResourceData, meta interface{}) error
|
||||||
return CheckDeleted(d, err, "LoadBalancerV2")
|
return CheckDeleted(d, err, "LoadBalancerV2")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LoadBalancerV2 %s: %+v", d.Id(), lb)
|
log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 LoadBalancer %s: %+v", d.Id(), lb)
|
||||||
|
|
||||||
d.Set("name", lb.Name)
|
d.Set("name", lb.Name)
|
||||||
d.Set("description", lb.Description)
|
d.Set("description", lb.Description)
|
||||||
|
@ -199,7 +199,7 @@ func resourceLoadBalancerV2Delete(d *schema.ResourceData, meta interface{}) erro
|
||||||
|
|
||||||
_, err = stateConf.WaitForState()
|
_, err = stateConf.WaitForState()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error deleting OpenStack LB Pool: %s", err)
|
return fmt.Errorf("Error deleting OpenStack LBaaSV2 LoadBalancer: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
|
@ -213,7 +213,7 @@ func waitForLoadBalancerActive(networkingClient *gophercloud.ServiceClient, lbID
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LoadBalancer: %+v", lb)
|
log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer: %+v", lb)
|
||||||
if lb.ProvisioningStatus == "ACTIVE" {
|
if lb.ProvisioningStatus == "ACTIVE" {
|
||||||
return lb, "ACTIVE", nil
|
return lb, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
|
@ -224,34 +224,36 @@ func waitForLoadBalancerActive(networkingClient *gophercloud.ServiceClient, lbID
|
||||||
|
|
||||||
func waitForLoadBalancerDelete(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc {
|
func waitForLoadBalancerDelete(networkingClient *gophercloud.ServiceClient, lbID string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
log.Printf("[DEBUG] Attempting to delete OpenStack LoadBalancerV2 %s", lbID)
|
log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 LoadBalancer %s", lbID)
|
||||||
|
|
||||||
lb, err := loadbalancers.Get(networkingClient, lbID).Extract()
|
lb, err := loadbalancers.Get(networkingClient, lbID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 LoadBalancer %s", lbID)
|
||||||
return lb, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LoadBalancerV2 %s", lbID)
|
|
||||||
return lb, "DELETED", nil
|
return lb, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return lb, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Openstack LoadBalancerV2: %+v", lb)
|
log.Printf("[DEBUG] Openstack LoadBalancerV2: %+v", lb)
|
||||||
err = loadbalancers.Delete(networkingClient, lbID).ExtractErr()
|
err = loadbalancers.Delete(networkingClient, lbID).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 LoadBalancer %s", lbID)
|
||||||
return lb, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LoadBalancerV2 %s", lbID)
|
|
||||||
return lb, "DELETED", nil
|
return lb, "DELETED", nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LoadBalancerV2 %s still active.", lbID)
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer (%s) is still in use.", lbID)
|
||||||
|
return lb, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lb, "ACTIVE", err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] OpenStack LBaaSV2 LoadBalancer (%s) still active.", lbID)
|
||||||
return lb, "ACTIVE", nil
|
return lb, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,11 @@ package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/loadbalancers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV2LoadBalancer_basic(t *testing.T) {
|
func TestAccLBV2LoadBalancer_basic(t *testing.T) {
|
||||||
|
@ -42,8 +41,6 @@ func testAccCheckLBV2LoadBalancerDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rs := range s.RootModule().Resources {
|
for _, rs := range s.RootModule().Resources {
|
||||||
log.Printf("[FINDME] rs TYPE is: %#v", rs.Type)
|
|
||||||
|
|
||||||
if rs.Type != "openstack_lb_loadbalancer_v2" {
|
if rs.Type != "openstack_lb_loadbalancer_v2" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceLBMemberV1() *schema.Resource {
|
func resourceLBMemberV1() *schema.Resource {
|
||||||
|
@ -104,8 +104,9 @@ func resourceLBMemberV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
d.SetId(m.ID)
|
d.SetId(m.ID)
|
||||||
|
|
||||||
// Due to the way Gophercloud is currently set up, AdminStateUp must be set post-create
|
// Due to the way Gophercloud is currently set up, AdminStateUp must be set post-create
|
||||||
|
asu := d.Get("admin_state_up").(bool)
|
||||||
updateOpts := members.UpdateOpts{
|
updateOpts := members.UpdateOpts{
|
||||||
AdminStateUp: d.Get("admin_state_up").(bool),
|
AdminStateUp: &asu,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB Member Update Options: %#v", createOpts)
|
log.Printf("[DEBUG] OpenStack LB Member Update Options: %#v", createOpts)
|
||||||
|
@ -129,7 +130,7 @@ func resourceLBMemberV1Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LB member")
|
return CheckDeleted(d, err, "LB member")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LB member %s: %+v", d.Id(), m)
|
log.Printf("[DEBUG] Retrieved OpenStack LB member %s: %+v", d.Id(), m)
|
||||||
|
|
||||||
d.Set("address", m.Address)
|
d.Set("address", m.Address)
|
||||||
d.Set("pool_id", m.PoolID)
|
d.Set("pool_id", m.PoolID)
|
||||||
|
@ -150,7 +151,7 @@ func resourceLBMemberV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
var updateOpts members.UpdateOpts
|
var updateOpts members.UpdateOpts
|
||||||
if d.HasChange("admin_state_up") {
|
if d.HasChange("admin_state_up") {
|
||||||
asu := d.Get("admin_state_up").(bool)
|
asu := d.Get("admin_state_up").(bool)
|
||||||
updateOpts.AdminStateUp = asu
|
updateOpts.AdminStateUp = &asu
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Updating LB member %s with options: %+v", d.Id(), updateOpts)
|
log.Printf("[DEBUG] Updating LB member %s with options: %+v", d.Id(), updateOpts)
|
||||||
|
@ -215,14 +216,11 @@ func waitForLBMemberDelete(networkingClient *gophercloud.ServiceClient, memberId
|
||||||
|
|
||||||
m, err := members.Get(networkingClient, memberId).Extract()
|
m, err := members.Get(networkingClient, memberId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return m, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB member %s", memberId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB member %s", memberId)
|
||||||
return m, "DELETED", nil
|
return m, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return m, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB member %s still active.", memberId)
|
log.Printf("[DEBUG] OpenStack LB member %s still active.", memberId)
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV1Member_basic(t *testing.T) {
|
func TestAccLBV1Member_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceMemberV2() *schema.Resource {
|
func resourceMemberV2() *schema.Resource {
|
||||||
|
@ -99,9 +99,8 @@ func resourceMemberV2Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
subnetID := d.Get("subnet_id").(string)
|
|
||||||
adminStateUp := d.Get("admin_state_up").(bool)
|
adminStateUp := d.Get("admin_state_up").(bool)
|
||||||
createOpts := pools.MemberCreateOpts{
|
createOpts := pools.CreateMemberOpts{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
Address: d.Get("address").(string),
|
Address: d.Get("address").(string),
|
||||||
|
@ -109,15 +108,16 @@ func resourceMemberV2Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
Weight: d.Get("weight").(int),
|
Weight: d.Get("weight").(int),
|
||||||
AdminStateUp: &adminStateUp,
|
AdminStateUp: &adminStateUp,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must omit if not set
|
// Must omit if not set
|
||||||
if subnetID != "" {
|
if v, ok := d.GetOk("subnet_id"); ok {
|
||||||
createOpts.SubnetID = subnetID
|
createOpts.SubnetID = v.(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
poolID := d.Get("pool_id").(string)
|
poolID := d.Get("pool_id").(string)
|
||||||
|
|
||||||
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
||||||
member, err := pools.CreateAssociateMember(networkingClient, poolID, createOpts).ExtractMember()
|
member, err := pools.CreateMember(networkingClient, poolID, createOpts).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error creating OpenStack LBaaSV2 member: %s", err)
|
return fmt.Errorf("Error creating OpenStack LBaaSV2 member: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -151,12 +151,12 @@ func resourceMemberV2Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
member, err := pools.GetAssociateMember(networkingClient, d.Get("pool_id").(string), d.Id()).ExtractMember()
|
member, err := pools.GetMember(networkingClient, d.Get("pool_id").(string), d.Id()).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CheckDeleted(d, err, "LBV2 Member")
|
return CheckDeleted(d, err, "LBV2 Member")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LBaaSV2 Member %s: %+v", d.Id(), member)
|
log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Member %s: %+v", d.Id(), member)
|
||||||
|
|
||||||
d.Set("name", member.Name)
|
d.Set("name", member.Name)
|
||||||
d.Set("weight", member.Weight)
|
d.Set("weight", member.Weight)
|
||||||
|
@ -177,7 +177,7 @@ func resourceMemberV2Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateOpts pools.MemberUpdateOpts
|
var updateOpts pools.UpdateMemberOpts
|
||||||
if d.HasChange("name") {
|
if d.HasChange("name") {
|
||||||
updateOpts.Name = d.Get("name").(string)
|
updateOpts.Name = d.Get("name").(string)
|
||||||
}
|
}
|
||||||
|
@ -191,7 +191,7 @@ func resourceMemberV2Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Member %s with options: %+v", d.Id(), updateOpts)
|
log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Member %s with options: %+v", d.Id(), updateOpts)
|
||||||
|
|
||||||
_, err = pools.UpdateAssociateMember(networkingClient, d.Get("pool_id").(string), d.Id(), updateOpts).ExtractMember()
|
_, err = pools.UpdateMember(networkingClient, d.Get("pool_id").(string), d.Id(), updateOpts).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error updating OpenStack LBaaSV2 Member: %s", err)
|
return fmt.Errorf("Error updating OpenStack LBaaSV2 Member: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -226,7 +226,7 @@ func resourceMemberV2Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
func waitForMemberActive(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc {
|
func waitForMemberActive(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
member, err := pools.GetAssociateMember(networkingClient, poolID, memberID).ExtractMember()
|
member, err := pools.GetMember(networkingClient, poolID, memberID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
@ -241,29 +241,31 @@ func waitForMemberDelete(networkingClient *gophercloud.ServiceClient, poolID str
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Member %s", memberID)
|
log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Member %s", memberID)
|
||||||
|
|
||||||
member, err := pools.GetAssociateMember(networkingClient, poolID, memberID).ExtractMember()
|
member, err := pools.GetMember(networkingClient, poolID, memberID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return member, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID)
|
||||||
return member, "DELETED", nil
|
return member, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return member, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Openstack LBaaSV2 Member: %+v", member)
|
log.Printf("[DEBUG] Openstack LBaaSV2 Member: %+v", member)
|
||||||
err = pools.DeleteMember(networkingClient, poolID, memberID).ExtractErr()
|
err = pools.DeleteMember(networkingClient, poolID, memberID).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return member, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID)
|
||||||
return member, "DELETED", nil
|
return member, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID)
|
||||||
|
return member, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return member, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LBaaSV2 Member %s still active.", memberID)
|
log.Printf("[DEBUG] OpenStack LBaaSV2 Member %s still active.", memberID)
|
||||||
|
|
|
@ -2,12 +2,11 @@ package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV2Member_basic(t *testing.T) {
|
func TestAccLBV2Member_basic(t *testing.T) {
|
||||||
|
@ -42,14 +41,11 @@ func testAccCheckLBV2MemberDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rs := range s.RootModule().Resources {
|
for _, rs := range s.RootModule().Resources {
|
||||||
log.Printf("[FINDME] rs TYPE is: %T", rs)
|
|
||||||
|
|
||||||
if rs.Type != "openstack_lb_member_v2" {
|
if rs.Type != "openstack_lb_member_v2" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[FINDME] rs.Primary.Attributes: %#v", rs.Primary.Attributes)
|
_, err := pools.GetMember(networkingClient, rs.Primary.Attributes["pool_id"], rs.Primary.ID).Extract()
|
||||||
_, err := pools.GetAssociateMember(networkingClient, rs.Primary.Attributes["pool_id"], rs.Primary.ID).ExtractMember()
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Member still exists: %s", rs.Primary.ID)
|
return fmt.Errorf("Member still exists: %s", rs.Primary.ID)
|
||||||
}
|
}
|
||||||
|
@ -75,7 +71,7 @@ func testAccCheckLBV2MemberExists(t *testing.T, n string, member *pools.Member)
|
||||||
return fmt.Errorf("(testAccCheckLBV2MemberExists) Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("(testAccCheckLBV2MemberExists) Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
found, err := pools.GetAssociateMember(networkingClient, rs.Primary.Attributes["pool_id"], rs.Primary.ID).ExtractMember()
|
found, err := pools.GetMember(networkingClient, rs.Primary.Attributes["pool_id"], rs.Primary.ID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceLBMonitorV1() *schema.Resource {
|
func resourceLBMonitorV1() *schema.Resource {
|
||||||
|
@ -90,7 +90,6 @@ func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
createOpts := monitors.CreateOpts{
|
createOpts := monitors.CreateOpts{
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
Type: d.Get("type").(string),
|
|
||||||
Delay: d.Get("delay").(int),
|
Delay: d.Get("delay").(int),
|
||||||
Timeout: d.Get("timeout").(int),
|
Timeout: d.Get("timeout").(int),
|
||||||
MaxRetries: d.Get("max_retries").(int),
|
MaxRetries: d.Get("max_retries").(int),
|
||||||
|
@ -99,6 +98,11 @@ func resourceLBMonitorV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
HTTPMethod: d.Get("http_method").(string),
|
HTTPMethod: d.Get("http_method").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("type"); ok {
|
||||||
|
monitorType := resourceLBMonitorV1DetermineType(v.(string))
|
||||||
|
createOpts.Type = monitorType
|
||||||
|
}
|
||||||
|
|
||||||
asuRaw := d.Get("admin_state_up").(string)
|
asuRaw := d.Get("admin_state_up").(string)
|
||||||
if asuRaw != "" {
|
if asuRaw != "" {
|
||||||
asu, err := strconv.ParseBool(asuRaw)
|
asu, err := strconv.ParseBool(asuRaw)
|
||||||
|
@ -148,7 +152,7 @@ func resourceLBMonitorV1Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LB monitor")
|
return CheckDeleted(d, err, "LB monitor")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LB Monitor %s: %+v", d.Id(), m)
|
log.Printf("[DEBUG] Retrieved OpenStack LB Monitor %s: %+v", d.Id(), m)
|
||||||
|
|
||||||
d.Set("type", m.Type)
|
d.Set("type", m.Type)
|
||||||
d.Set("delay", m.Delay)
|
d.Set("delay", m.Delay)
|
||||||
|
@ -225,6 +229,22 @@ func resourceLBMonitorV1Delete(d *schema.ResourceData, meta interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceLBMonitorV1DetermineType(t string) monitors.MonitorType {
|
||||||
|
var monitorType monitors.MonitorType
|
||||||
|
switch t {
|
||||||
|
case "PING":
|
||||||
|
monitorType = monitors.TypePING
|
||||||
|
case "TCP":
|
||||||
|
monitorType = monitors.TypeTCP
|
||||||
|
case "HTTP":
|
||||||
|
monitorType = monitors.TypeHTTP
|
||||||
|
case "HTTPS":
|
||||||
|
monitorType = monitors.TypeHTTPS
|
||||||
|
}
|
||||||
|
|
||||||
|
return monitorType
|
||||||
|
}
|
||||||
|
|
||||||
func waitForLBMonitorActive(networkingClient *gophercloud.ServiceClient, monitorId string) resource.StateRefreshFunc {
|
func waitForLBMonitorActive(networkingClient *gophercloud.ServiceClient, monitorId string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
m, err := monitors.Get(networkingClient, monitorId).Extract()
|
m, err := monitors.Get(networkingClient, monitorId).Extract()
|
||||||
|
@ -244,37 +264,39 @@ func waitForLBMonitorDelete(networkingClient *gophercloud.ServiceClient, monitor
|
||||||
|
|
||||||
m, err := monitors.Get(networkingClient, monitorId).Extract()
|
m, err := monitors.Get(networkingClient, monitorId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return m, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB Monitor %s", monitorId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB Monitor %s", monitorId)
|
||||||
return m, "DELETED", nil
|
return m, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
if errCode.Actual == 409 {
|
if errCode.Actual == 409 {
|
||||||
log.Printf("[DEBUG] OpenStack LB Monitor (%s) is waiting for Pool to delete.", monitorId)
|
log.Printf("[DEBUG] OpenStack LB Monitor (%s) is waiting for Pool to delete.", monitorId)
|
||||||
return m, "PENDING", nil
|
return m, "PENDING", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return m, "ACTIVE", err
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB Monitor: %+v", m)
|
log.Printf("[DEBUG] OpenStack LB Monitor: %+v", m)
|
||||||
err = monitors.Delete(networkingClient, monitorId).ExtractErr()
|
err = monitors.Delete(networkingClient, monitorId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return m, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB Monitor %s", monitorId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB Monitor %s", monitorId)
|
||||||
return m, "DELETED", nil
|
return m, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
if errCode.Actual == 409 {
|
if errCode.Actual == 409 {
|
||||||
log.Printf("[DEBUG] OpenStack LB Monitor (%s) is waiting for Pool to delete.", monitorId)
|
log.Printf("[DEBUG] OpenStack LB Monitor (%s) is waiting for Pool to delete.", monitorId)
|
||||||
return m, "PENDING", nil
|
return m, "PENDING", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return m, "ACTIVE", err
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB Monitor %s still active.", monitorId)
|
log.Printf("[DEBUG] OpenStack LB Monitor %s still active.", monitorId)
|
||||||
return m, "ACTIVE", nil
|
return m, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV1Monitor_basic(t *testing.T) {
|
func TestAccLBV1Monitor_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceMonitorV2() *schema.Resource {
|
func resourceMonitorV2() *schema.Resource {
|
||||||
|
@ -154,7 +154,7 @@ func resourceMonitorV2Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LBV2 Monitor")
|
return CheckDeleted(d, err, "LBV2 Monitor")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LBaaSV2 Monitor %s: %+v", d.Id(), monitor)
|
log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Monitor %s: %+v", d.Id(), monitor)
|
||||||
|
|
||||||
d.Set("id", monitor.ID)
|
d.Set("id", monitor.ID)
|
||||||
d.Set("tenant_id", monitor.TenantID)
|
d.Set("tenant_id", monitor.TenantID)
|
||||||
|
@ -258,27 +258,29 @@ func waitForMonitorDelete(networkingClient *gophercloud.ServiceClient, monitorID
|
||||||
|
|
||||||
monitor, err := monitors.Get(networkingClient, monitorID).Extract()
|
monitor, err := monitors.Get(networkingClient, monitorID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return monitor, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Monitor %s", monitorID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Monitor %s", monitorID)
|
||||||
return monitor, "DELETED", nil
|
return monitor, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return monitor, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Openstack LBaaSV2 Monitor: %+v", monitor)
|
log.Printf("[DEBUG] Openstack LBaaSV2 Monitor: %+v", monitor)
|
||||||
err = monitors.Delete(networkingClient, monitorID).ExtractErr()
|
err = monitors.Delete(networkingClient, monitorID).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return monitor, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Monitor %s", monitorID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Monitor %s", monitorID)
|
||||||
return monitor, "DELETED", nil
|
return monitor, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
log.Printf("[DEBUG] OpenStack LBaaSV2 Monitor (%s) is still in use.", monitorID)
|
||||||
|
return monitor, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return monitor, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LBaaSV2 Monitor %s still active.", monitorID)
|
log.Printf("[DEBUG] OpenStack LBaaSV2 Monitor %s still active.", monitorID)
|
||||||
|
|
|
@ -2,12 +2,11 @@ package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/monitors"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV2Monitor_basic(t *testing.T) {
|
func TestAccLBV2Monitor_basic(t *testing.T) {
|
||||||
|
@ -44,13 +43,10 @@ func testAccCheckLBV2MonitorDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rs := range s.RootModule().Resources {
|
for _, rs := range s.RootModule().Resources {
|
||||||
log.Printf("[FINDME] rs TYPE is: %T", rs)
|
|
||||||
|
|
||||||
if rs.Type != "openstack_lb_monitor_v2" {
|
if rs.Type != "openstack_lb_monitor_v2" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[FINDME] rs.Primary.Attributes: %#v", rs.Primary.Attributes)
|
|
||||||
_, err := monitors.Get(networkingClient, rs.Primary.ID).Extract()
|
_, err := monitors.Get(networkingClient, rs.Primary.ID).Extract()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return fmt.Errorf("Monitor still exists: %s", rs.Primary.ID)
|
return fmt.Errorf("Monitor still exists: %s", rs.Primary.ID)
|
||||||
|
|
|
@ -10,10 +10,10 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/members"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceLBPoolV1() *schema.Resource {
|
func resourceLBPoolV1() *schema.Resource {
|
||||||
|
@ -122,13 +122,21 @@ func resourceLBPoolV1Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
createOpts := pools.CreateOpts{
|
createOpts := pools.CreateOpts{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
Protocol: d.Get("protocol").(string),
|
|
||||||
SubnetID: d.Get("subnet_id").(string),
|
SubnetID: d.Get("subnet_id").(string),
|
||||||
LBMethod: d.Get("lb_method").(string),
|
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
Provider: d.Get("lb_provider").(string),
|
Provider: d.Get("lb_provider").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("protocol"); ok {
|
||||||
|
protocol := resourceLBPoolV1DetermineProtocol(v.(string))
|
||||||
|
createOpts.Protocol = protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("lb_method"); ok {
|
||||||
|
lbMethod := resourceLBPoolV1DetermineLBMethod(v.(string))
|
||||||
|
createOpts.LBMethod = lbMethod
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
||||||
p, err := pools.Create(networkingClient, createOpts).Extract()
|
p, err := pools.Create(networkingClient, createOpts).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -187,7 +195,7 @@ func resourceLBPoolV1Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LB pool")
|
return CheckDeleted(d, err, "LB pool")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LB Pool %s: %+v", d.Id(), p)
|
log.Printf("[DEBUG] Retrieved OpenStack LB Pool %s: %+v", d.Id(), p)
|
||||||
|
|
||||||
d.Set("name", p.Name)
|
d.Set("name", p.Name)
|
||||||
d.Set("protocol", p.Protocol)
|
d.Set("protocol", p.Protocol)
|
||||||
|
@ -213,7 +221,9 @@ func resourceLBPoolV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
// Gophercloud complains if one is empty.
|
// Gophercloud complains if one is empty.
|
||||||
if d.HasChange("name") || d.HasChange("lb_method") {
|
if d.HasChange("name") || d.HasChange("lb_method") {
|
||||||
updateOpts.Name = d.Get("name").(string)
|
updateOpts.Name = d.Get("name").(string)
|
||||||
updateOpts.LBMethod = d.Get("lb_method").(string)
|
|
||||||
|
lbMethod := resourceLBPoolV1DetermineLBMethod(d.Get("lb_method").(string))
|
||||||
|
updateOpts.LBMethod = lbMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts)
|
log.Printf("[DEBUG] Updating OpenStack LB Pool %s with options: %+v", d.Id(), updateOpts)
|
||||||
|
@ -379,6 +389,32 @@ func resourceLBMemberV1Hash(v interface{}) int {
|
||||||
return hashcode.String(buf.String())
|
return hashcode.String(buf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceLBPoolV1DetermineProtocol(v string) pools.LBProtocol {
|
||||||
|
var protocol pools.LBProtocol
|
||||||
|
switch v {
|
||||||
|
case "TCP":
|
||||||
|
protocol = pools.ProtocolTCP
|
||||||
|
case "HTTP":
|
||||||
|
protocol = pools.ProtocolHTTP
|
||||||
|
case "HTTPS":
|
||||||
|
protocol = pools.ProtocolHTTPS
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceLBPoolV1DetermineLBMethod(v string) pools.LBMethod {
|
||||||
|
var lbMethod pools.LBMethod
|
||||||
|
switch v {
|
||||||
|
case "ROUND_ROBIN":
|
||||||
|
lbMethod = pools.LBMethodRoundRobin
|
||||||
|
case "LEAST_CONNECTIONS":
|
||||||
|
lbMethod = pools.LBMethodLeastConnections
|
||||||
|
}
|
||||||
|
|
||||||
|
return lbMethod
|
||||||
|
}
|
||||||
|
|
||||||
func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc {
|
func waitForLBPoolActive(networkingClient *gophercloud.ServiceClient, poolId string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
p, err := pools.Get(networkingClient, poolId).Extract()
|
p, err := pools.Get(networkingClient, poolId).Extract()
|
||||||
|
@ -401,27 +437,21 @@ func waitForLBPoolDelete(networkingClient *gophercloud.ServiceClient, poolId str
|
||||||
|
|
||||||
p, err := pools.Get(networkingClient, poolId).Extract()
|
p, err := pools.Get(networkingClient, poolId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return p, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
|
||||||
return p, "DELETED", nil
|
return p, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return p, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB Pool: %+v", p)
|
log.Printf("[DEBUG] OpenStack LB Pool: %+v", p)
|
||||||
err = pools.Delete(networkingClient, poolId).ExtractErr()
|
err = pools.Delete(networkingClient, poolId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return p, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB Pool %s", poolId)
|
||||||
return p, "DELETED", nil
|
return p, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return p, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId)
|
log.Printf("[DEBUG] OpenStack LB Pool %s still active.", poolId)
|
||||||
|
|
|
@ -7,13 +7,13 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/monitors"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/pools"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV1Pool_basic(t *testing.T) {
|
func TestAccLBV1Pool_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourcePoolV2() *schema.Resource {
|
func resourcePoolV2() *schema.Resource {
|
||||||
|
@ -201,7 +201,7 @@ func resourcePoolV2Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LBV2 Pool")
|
return CheckDeleted(d, err, "LBV2 Pool")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LBaaSV2 Pool %s: %+v", d.Id(), pool)
|
log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Pool %s: %+v", d.Id(), pool)
|
||||||
|
|
||||||
d.Set("lb_method", pool.LBMethod)
|
d.Set("lb_method", pool.LBMethod)
|
||||||
d.Set("protocol", pool.Protocol)
|
d.Set("protocol", pool.Protocol)
|
||||||
|
@ -291,27 +291,29 @@ func waitForPoolDelete(networkingClient *gophercloud.ServiceClient, poolID strin
|
||||||
|
|
||||||
pool, err := pools.Get(networkingClient, poolID).Extract()
|
pool, err := pools.Get(networkingClient, poolID).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return pool, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID)
|
||||||
return pool, "DELETED", nil
|
return pool, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return pool, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Openstack LBaaSV2 Pool: %+v", pool)
|
log.Printf("[DEBUG] Openstack LBaaSV2 Pool: %+v", pool)
|
||||||
err = pools.Delete(networkingClient, poolID).ExtractErr()
|
err = pools.Delete(networkingClient, poolID).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return pool, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID)
|
||||||
return pool, "DELETED", nil
|
return pool, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
log.Printf("[DEBUG] OpenStack LBaaSV2 Pool (%s) is still in use.", poolID)
|
||||||
|
return pool, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LBaaSV2 Pool %s still active.", poolID)
|
log.Printf("[DEBUG] OpenStack LBaaSV2 Pool %s still active.", poolID)
|
||||||
|
|
|
@ -2,12 +2,11 @@ package openstack
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV2Pool_basic(t *testing.T) {
|
func TestAccLBV2Pool_basic(t *testing.T) {
|
||||||
|
@ -42,8 +41,6 @@ func testAccCheckLBV2PoolDestroy(s *terraform.State) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, rs := range s.RootModule().Resources {
|
for _, rs := range s.RootModule().Resources {
|
||||||
log.Printf("[FINDME] rs TYPE is: %T", rs)
|
|
||||||
|
|
||||||
if rs.Type != "openstack_lb_pool_v2" {
|
if rs.Type != "openstack_lb_pool_v2" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceLBVipV1() *schema.Resource {
|
func resourceLBVipV1() *schema.Resource {
|
||||||
|
@ -171,7 +171,7 @@ func resourceLBVipV1Read(d *schema.ResourceData, meta interface{}) error {
|
||||||
return CheckDeleted(d, err, "LB VIP")
|
return CheckDeleted(d, err, "LB VIP")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived OpenStack LB VIP %s: %+v", d.Id(), p)
|
log.Printf("[DEBUG] Retrieved OpenStack LB VIP %s: %+v", d.Id(), p)
|
||||||
|
|
||||||
d.Set("name", p.Name)
|
d.Set("name", p.Name)
|
||||||
d.Set("subnet_id", p.SubnetID)
|
d.Set("subnet_id", p.SubnetID)
|
||||||
|
@ -207,20 +207,24 @@ func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
var updateOpts vips.UpdateOpts
|
var updateOpts vips.UpdateOpts
|
||||||
if d.HasChange("name") {
|
if d.HasChange("name") {
|
||||||
updateOpts.Name = d.Get("name").(string)
|
v := d.Get("name").(string)
|
||||||
|
updateOpts.Name = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("pool_id") {
|
if d.HasChange("pool_id") {
|
||||||
updateOpts.PoolID = d.Get("pool_id").(string)
|
v := d.Get("pool_id").(string)
|
||||||
|
updateOpts.PoolID = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("description") {
|
if d.HasChange("description") {
|
||||||
updateOpts.Description = d.Get("description").(string)
|
v := d.Get("description").(string)
|
||||||
}
|
updateOpts.Description = &v
|
||||||
if d.HasChange("persistence") {
|
|
||||||
updateOpts.Persistence = resourceVipPersistenceV1(d)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("conn_limit") {
|
if d.HasChange("conn_limit") {
|
||||||
updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int))
|
updateOpts.ConnLimit = gophercloud.MaybeInt(d.Get("conn_limit").(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("floating_ip") {
|
if d.HasChange("floating_ip") {
|
||||||
portID := d.Get("port_id").(string)
|
portID := d.Get("port_id").(string)
|
||||||
|
|
||||||
|
@ -240,8 +244,9 @@ func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
|
||||||
// If a floating IP is found we unassign it
|
// If a floating IP is found we unassign it
|
||||||
if len(fips) == 1 {
|
if len(fips) == 1 {
|
||||||
|
portID := ""
|
||||||
updateOpts := floatingips.UpdateOpts{
|
updateOpts := floatingips.UpdateOpts{
|
||||||
PortID: "",
|
PortID: &portID,
|
||||||
}
|
}
|
||||||
if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
|
if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -254,11 +259,15 @@ func resourceLBVipV1Update(d *schema.ResourceData, meta interface{}) error {
|
||||||
lbVipV1AssignFloatingIP(floatingIP, portID, networkingClient)
|
lbVipV1AssignFloatingIP(floatingIP, portID, networkingClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("admin_state_up") {
|
if d.HasChange("admin_state_up") {
|
||||||
asu := d.Get("admin_state_up").(bool)
|
asu := d.Get("admin_state_up").(bool)
|
||||||
updateOpts.AdminStateUp = &asu
|
updateOpts.AdminStateUp = &asu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Persistence has to be included, even if it hasn't changed.
|
||||||
|
updateOpts.Persistence = resourceVipPersistenceV1(d)
|
||||||
|
|
||||||
log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts)
|
log.Printf("[DEBUG] Updating OpenStack LB VIP %s with options: %+v", d.Id(), updateOpts)
|
||||||
|
|
||||||
_, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract()
|
_, err = vips.Update(networkingClient, d.Id(), updateOpts).Extract()
|
||||||
|
@ -330,7 +339,7 @@ func lbVipV1AssignFloatingIP(floatingIP, portID string, networkingClient *gopher
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOpts := floatingips.UpdateOpts{
|
updateOpts := floatingips.UpdateOpts{
|
||||||
PortID: portID,
|
PortID: &portID,
|
||||||
}
|
}
|
||||||
if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
|
if err = floatingips.Update(networkingClient, fips[0].ID, updateOpts).Err; err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -361,27 +370,21 @@ func waitForLBVIPDelete(networkingClient *gophercloud.ServiceClient, vipId strin
|
||||||
|
|
||||||
p, err := vips.Get(networkingClient, vipId).Extract()
|
p, err := vips.Get(networkingClient, vipId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return p, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
|
||||||
return p, "DELETED", nil
|
return p, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return p, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB VIP: %+v", p)
|
log.Printf("[DEBUG] OpenStack LB VIP: %+v", p)
|
||||||
err = vips.Delete(networkingClient, vipId).ExtractErr()
|
err = vips.Delete(networkingClient, vipId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return p, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack LB VIP %s", vipId)
|
||||||
return p, "DELETED", nil
|
return p, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return p, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack LB VIP %s still active.", vipId)
|
log.Printf("[DEBUG] OpenStack LB VIP %s still active.", vipId)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas/vips"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccLBV1VIP_basic(t *testing.T) {
|
func TestAccLBV1VIP_basic(t *testing.T) {
|
||||||
|
@ -152,9 +152,9 @@ var testAccLBV1VIP_update = fmt.Sprintf(`
|
||||||
protocol = "HTTP"
|
protocol = "HTTP"
|
||||||
port = 80
|
port = 80
|
||||||
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
|
pool_id = "${openstack_lb_pool_v1.pool_1.id}"
|
||||||
|
admin_state_up = true
|
||||||
persistence {
|
persistence {
|
||||||
type = "SOURCE_IP"
|
type = "SOURCE_IP"
|
||||||
}
|
}
|
||||||
admin_state_up = true
|
|
||||||
}`,
|
}`,
|
||||||
OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME)
|
OS_REGION_NAME, OS_REGION_NAME, OS_REGION_NAME)
|
||||||
|
|
|
@ -8,10 +8,10 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
"github.com/rackspace/gophercloud/pagination"
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingFloatingIPV2() *schema.Resource {
|
func resourceNetworkingFloatingIPV2() *schema.Resource {
|
||||||
|
@ -139,7 +139,8 @@ func resourceNetworkFloatingIPV2Update(d *schema.ResourceData, meta interface{})
|
||||||
var updateOpts floatingips.UpdateOpts
|
var updateOpts floatingips.UpdateOpts
|
||||||
|
|
||||||
if d.HasChange("port_id") {
|
if d.HasChange("port_id") {
|
||||||
updateOpts.PortID = d.Get("port_id").(string)
|
portID := d.Get("port_id").(string)
|
||||||
|
updateOpts.PortID = &portID
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Update Options: %#v", updateOpts)
|
log.Printf("[DEBUG] Update Options: %#v", updateOpts)
|
||||||
|
@ -259,26 +260,20 @@ func waitForFloatingIPDelete(networkingClient *gophercloud.ServiceClient, fId st
|
||||||
|
|
||||||
f, err := floatingips.Get(networkingClient, fId).Extract()
|
f, err := floatingips.Get(networkingClient, fId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return f, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Floating IP %s", fId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Floating IP %s", fId)
|
||||||
return f, "DELETED", nil
|
return f, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return f, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = floatingips.Delete(networkingClient, fId).ExtractErr()
|
err = floatingips.Delete(networkingClient, fId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return f, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Floating IP %s", fId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Floating IP %s", fId)
|
||||||
return f, "DELETED", nil
|
return f, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return f, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Floating IP %s still active.\n", fId)
|
log.Printf("[DEBUG] OpenStack Floating IP %s still active.\n", fId)
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2FloatingIP_basic(t *testing.T) {
|
func TestAccNetworkingV2FloatingIP_basic(t *testing.T) {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingNetworkV2() *schema.Resource {
|
func resourceNetworkingNetworkV2() *schema.Resource {
|
||||||
|
@ -62,41 +62,6 @@ func resourceNetworkingNetworkV2() *schema.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NetworkCreateOpts contains all teh values needed to create a new network.
|
|
||||||
type NetworkCreateOpts struct {
|
|
||||||
AdminStateUp *bool
|
|
||||||
Name string
|
|
||||||
Shared *bool
|
|
||||||
TenantID string
|
|
||||||
ValueSpecs map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToNetworkCreateMpa casts a networkCreateOpts struct to a map.
|
|
||||||
func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
|
|
||||||
n := make(map[string]interface{})
|
|
||||||
|
|
||||||
if opts.AdminStateUp != nil {
|
|
||||||
n["admin_state_up"] = &opts.AdminStateUp
|
|
||||||
}
|
|
||||||
if opts.Name != "" {
|
|
||||||
n["name"] = opts.Name
|
|
||||||
}
|
|
||||||
if opts.Shared != nil {
|
|
||||||
n["shared"] = &opts.Shared
|
|
||||||
}
|
|
||||||
if opts.TenantID != "" {
|
|
||||||
n["tenant_id"] = opts.TenantID
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.ValueSpecs != nil {
|
|
||||||
for k, v := range opts.ValueSpecs {
|
|
||||||
n[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[string]interface{}{"network": n}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error {
|
func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
||||||
|
@ -105,9 +70,11 @@ func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
createOpts := NetworkCreateOpts{
|
createOpts := NetworkCreateOpts{
|
||||||
|
networks.CreateOpts{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
ValueSpecs: networkValueSpecs(d),
|
},
|
||||||
|
MapValueSpecs(d),
|
||||||
}
|
}
|
||||||
|
|
||||||
asuRaw := d.Get("admin_state_up").(string)
|
asuRaw := d.Get("admin_state_up").(string)
|
||||||
|
@ -165,7 +132,7 @@ func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) e
|
||||||
return CheckDeleted(d, err, "network")
|
return CheckDeleted(d, err, "network")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived Network %s: %+v", d.Id(), n)
|
log.Printf("[DEBUG] Retrieved Network %s: %+v", d.Id(), n)
|
||||||
|
|
||||||
d.Set("name", n.Name)
|
d.Set("name", n.Name)
|
||||||
d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp))
|
d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp))
|
||||||
|
@ -264,37 +231,28 @@ func waitForNetworkDelete(networkingClient *gophercloud.ServiceClient, networkId
|
||||||
|
|
||||||
n, err := networks.Get(networkingClient, networkId).Extract()
|
n, err := networks.Get(networkingClient, networkId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return n, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId)
|
||||||
return n, "DELETED", nil
|
return n, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return n, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = networks.Delete(networkingClient, networkId).ExtractErr()
|
err = networks.Delete(networkingClient, networkId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return n, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId)
|
||||||
return n, "DELETED", nil
|
return n, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
return n, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Network %s still active.\n", networkId)
|
log.Printf("[DEBUG] OpenStack Network %s still active.\n", networkId)
|
||||||
return n, "ACTIVE", nil
|
return n, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func networkValueSpecs(d *schema.ResourceData) map[string]string {
|
|
||||||
m := make(map[string]string)
|
|
||||||
for key, val := range d.Get("value_specs").(map[string]interface{}) {
|
|
||||||
m[key] = val.(string)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,12 +8,12 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups"
|
||||||
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2Network_basic(t *testing.T) {
|
func TestAccNetworkingV2Network_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingPortV2() *schema.Resource {
|
func resourceNetworkingPortV2() *schema.Resource {
|
||||||
|
@ -175,7 +175,7 @@ func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) erro
|
||||||
return CheckDeleted(d, err, "port")
|
return CheckDeleted(d, err, "port")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived Port %s: %+v", d.Id(), p)
|
log.Printf("[DEBUG] Retrieved Port %s: %+v", d.Id(), p)
|
||||||
|
|
||||||
d.Set("name", p.Name)
|
d.Set("name", p.Name)
|
||||||
d.Set("admin_state_up", p.AdminStateUp)
|
d.Set("admin_state_up", p.AdminStateUp)
|
||||||
|
@ -359,26 +359,20 @@ func waitForNetworkPortDelete(networkingClient *gophercloud.ServiceClient, portI
|
||||||
|
|
||||||
p, err := ports.Get(networkingClient, portId).Extract()
|
p, err := ports.Get(networkingClient, portId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return p, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
|
||||||
return p, "DELETED", nil
|
return p, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return p, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ports.Delete(networkingClient, portId).ExtractErr()
|
err = ports.Delete(networkingClient, portId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return p, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
|
||||||
return p, "DELETED", nil
|
return p, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return p, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
|
log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2Port_basic(t *testing.T) {
|
func TestAccNetworkingV2Port_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,9 +8,9 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingRouterInterfaceV2() *schema.Resource {
|
func resourceNetworkingRouterInterfaceV2() *schema.Resource {
|
||||||
|
@ -52,7 +52,7 @@ func resourceNetworkingRouterInterfaceV2Create(d *schema.ResourceData, meta inte
|
||||||
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
createOpts := routers.InterfaceOpts{
|
createOpts := routers.AddInterfaceOpts{
|
||||||
SubnetID: d.Get("subnet_id").(string),
|
SubnetID: d.Get("subnet_id").(string),
|
||||||
PortID: d.Get("port_id").(string),
|
PortID: d.Get("port_id").(string),
|
||||||
}
|
}
|
||||||
|
@ -91,19 +91,15 @@ func resourceNetworkingRouterInterfaceV2Read(d *schema.ResourceData, meta interf
|
||||||
|
|
||||||
n, err := ports.Get(networkingClient, d.Id()).Extract()
|
n, err := ports.Get(networkingClient, d.Id()).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpError.Actual == 404 {
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err)
|
return fmt.Errorf("Error retrieving OpenStack Neutron Router Interface: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived Router Interface %s: %+v", d.Id(), n)
|
log.Printf("[DEBUG] Retrieved Router Interface %s: %+v", d.Id(), n)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -150,38 +146,39 @@ func waitForRouterInterfaceDelete(networkingClient *gophercloud.ServiceClient, d
|
||||||
routerId := d.Get("router_id").(string)
|
routerId := d.Get("router_id").(string)
|
||||||
routerInterfaceId := d.Id()
|
routerInterfaceId := d.Id()
|
||||||
|
|
||||||
log.Printf("[DEBUG] Attempting to delete OpenStack Router Interface %s.\n", routerInterfaceId)
|
log.Printf("[DEBUG] Attempting to delete OpenStack Router Interface %s.", routerInterfaceId)
|
||||||
|
|
||||||
removeOpts := routers.InterfaceOpts{
|
removeOpts := routers.RemoveInterfaceOpts{
|
||||||
SubnetID: d.Get("subnet_id").(string),
|
SubnetID: d.Get("subnet_id").(string),
|
||||||
PortID: d.Get("port_id").(string),
|
PortID: d.Get("port_id").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := ports.Get(networkingClient, routerInterfaceId).Extract()
|
r, err := ports.Get(networkingClient, routerInterfaceId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Router Interface %s", routerInterfaceId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Router Interface %s", routerInterfaceId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = routers.RemoveInterface(networkingClient, routerId, removeOpts).Extract()
|
_, err = routers.RemoveInterface(networkingClient, routerId, removeOpts).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
log.Printf("[DEBUG] Successfully deleted OpenStack Router Interface %s.", routerInterfaceId)
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Router Interface %s", routerInterfaceId)
|
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
}
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
log.Printf("[DEBUG] OpenStack Router Interface %s still active.\n", routerInterfaceId)
|
log.Printf("[DEBUG] Router Interface %s is still in use.", routerInterfaceId)
|
||||||
|
return r, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, "ACTIVE", err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] OpenStack Router Interface %s is still active.", routerInterfaceId)
|
||||||
return r, "ACTIVE", nil
|
return r, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2RouterInterface_basic_subnet(t *testing.T) {
|
func TestAccNetworkingV2RouterInterface_basic_subnet(t *testing.T) {
|
||||||
|
|
|
@ -6,8 +6,8 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingRouterRouteV2() *schema.Resource {
|
func resourceNetworkingRouterRouteV2() *schema.Resource {
|
||||||
|
@ -59,15 +59,11 @@ func resourceNetworkingRouterRouteV2Create(d *schema.ResourceData, meta interfac
|
||||||
|
|
||||||
n, err := routers.Get(networkingClient, routerId).Extract()
|
n, err := routers.Get(networkingClient, routerId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpError.Actual == 404 {
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,15 +117,11 @@ func resourceNetworkingRouterRouteV2Read(d *schema.ResourceData, meta interface{
|
||||||
|
|
||||||
n, err := routers.Get(networkingClient, routerId).Extract()
|
n, err := routers.Get(networkingClient, routerId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpError.Actual == 404 {
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,14 +160,10 @@ func resourceNetworkingRouterRouteV2Delete(d *schema.ResourceData, meta interfac
|
||||||
|
|
||||||
n, err := routers.Get(networkingClient, routerId).Extract()
|
n, err := routers.Get(networkingClient, routerId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpError.Actual == 404 {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2RouterRoute_basic(t *testing.T) {
|
func TestAccNetworkingV2RouterRoute_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingRouterV2() *schema.Resource {
|
func resourceNetworkingRouterV2() *schema.Resource {
|
||||||
|
@ -63,50 +63,6 @@ func resourceNetworkingRouterV2() *schema.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// routerCreateOpts contains all the values needed to create a new router. There are
|
|
||||||
// no required values.
|
|
||||||
type RouterCreateOpts struct {
|
|
||||||
Name string
|
|
||||||
AdminStateUp *bool
|
|
||||||
Distributed *bool
|
|
||||||
TenantID string
|
|
||||||
GatewayInfo *routers.GatewayInfo
|
|
||||||
ValueSpecs map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToRouterCreateMap casts a routerCreateOpts struct to a map.
|
|
||||||
func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
|
|
||||||
r := make(map[string]interface{})
|
|
||||||
|
|
||||||
if gophercloud.MaybeString(opts.Name) != nil {
|
|
||||||
r["name"] = opts.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.AdminStateUp != nil {
|
|
||||||
r["admin_state_up"] = opts.AdminStateUp
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Distributed != nil {
|
|
||||||
r["distributed"] = opts.Distributed
|
|
||||||
}
|
|
||||||
|
|
||||||
if gophercloud.MaybeString(opts.TenantID) != nil {
|
|
||||||
r["tenant_id"] = opts.TenantID
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.GatewayInfo != nil {
|
|
||||||
r["external_gateway_info"] = opts.GatewayInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.ValueSpecs != nil {
|
|
||||||
for k, v := range opts.ValueSpecs {
|
|
||||||
r[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[string]interface{}{"router": r}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) error {
|
func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
||||||
|
@ -115,9 +71,11 @@ func resourceNetworkingRouterV2Create(d *schema.ResourceData, meta interface{})
|
||||||
}
|
}
|
||||||
|
|
||||||
createOpts := RouterCreateOpts{
|
createOpts := RouterCreateOpts{
|
||||||
|
routers.CreateOpts{
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
ValueSpecs: routerValueSpecs(d),
|
},
|
||||||
|
MapValueSpecs(d),
|
||||||
}
|
}
|
||||||
|
|
||||||
if asuRaw, ok := d.GetOk("admin_state_up"); ok {
|
if asuRaw, ok := d.GetOk("admin_state_up"); ok {
|
||||||
|
@ -171,19 +129,15 @@ func resourceNetworkingRouterV2Read(d *schema.ResourceData, meta interface{}) er
|
||||||
|
|
||||||
n, err := routers.Get(networkingClient, d.Id()).Extract()
|
n, err := routers.Get(networkingClient, d.Id()).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if httpError.Actual == 404 {
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
return fmt.Errorf("Error retrieving OpenStack Neutron Router: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived Router %s: %+v", d.Id(), n)
|
log.Printf("[DEBUG] Retrieved Router %s: %+v", d.Id(), n)
|
||||||
|
|
||||||
d.Set("name", n.Name)
|
d.Set("name", n.Name)
|
||||||
d.Set("admin_state_up", n.AdminStateUp)
|
d.Set("admin_state_up", n.AdminStateUp)
|
||||||
|
@ -276,37 +230,23 @@ func waitForRouterDelete(networkingClient *gophercloud.ServiceClient, routerId s
|
||||||
|
|
||||||
r, err := routers.Get(networkingClient, routerId).Extract()
|
r, err := routers.Get(networkingClient, routerId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Router %s", routerId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Router %s", routerId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = routers.Delete(networkingClient, routerId).ExtractErr()
|
err = routers.Delete(networkingClient, routerId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Router %s", routerId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Router %s", routerId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Router %s still active.\n", routerId)
|
log.Printf("[DEBUG] OpenStack Router %s still active.\n", routerId)
|
||||||
return r, "ACTIVE", nil
|
return r, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func routerValueSpecs(d *schema.ResourceData) map[string]string {
|
|
||||||
m := make(map[string]string)
|
|
||||||
for key, val := range d.Get("value_specs").(map[string]interface{}) {
|
|
||||||
m[key] = val.(string)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2Router_basic(t *testing.T) {
|
func TestAccNetworkingV2Router_basic(t *testing.T) {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingSecGroupRuleV2() *schema.Resource {
|
func resourceNetworkingSecGroupRuleV2() *schema.Resource {
|
||||||
|
@ -106,17 +106,29 @@ func resourceNetworkingSecGroupRuleV2Create(d *schema.ResourceData, meta interfa
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := rules.CreateOpts{
|
opts := rules.CreateOpts{
|
||||||
Direction: d.Get("direction").(string),
|
|
||||||
EtherType: d.Get("ethertype").(string),
|
|
||||||
SecGroupID: d.Get("security_group_id").(string),
|
SecGroupID: d.Get("security_group_id").(string),
|
||||||
PortRangeMin: d.Get("port_range_min").(int),
|
PortRangeMin: d.Get("port_range_min").(int),
|
||||||
PortRangeMax: d.Get("port_range_max").(int),
|
PortRangeMax: d.Get("port_range_max").(int),
|
||||||
Protocol: d.Get("protocol").(string),
|
|
||||||
RemoteGroupID: d.Get("remote_group_id").(string),
|
RemoteGroupID: d.Get("remote_group_id").(string),
|
||||||
RemoteIPPrefix: d.Get("remote_ip_prefix").(string),
|
RemoteIPPrefix: d.Get("remote_ip_prefix").(string),
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("direction"); ok {
|
||||||
|
direction := resourceNetworkingSecGroupRuleV2DetermineDirection(v.(string))
|
||||||
|
opts.Direction = direction
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ethertype"); ok {
|
||||||
|
ethertype := resourceNetworkingSecGroupRuleV2DetermineEtherType(v.(string))
|
||||||
|
opts.EtherType = ethertype
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("protocol"); ok {
|
||||||
|
protocol := resourceNetworkingSecGroupRuleV2DetermineProtocol(v.(string))
|
||||||
|
opts.Protocol = protocol
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Create OpenStack Neutron security group: %#v", opts)
|
log.Printf("[DEBUG] Create OpenStack Neutron security group: %#v", opts)
|
||||||
|
|
||||||
security_group_rule, err := rules.Create(networkingClient, opts).Extract()
|
security_group_rule, err := rules.Create(networkingClient, opts).Extract()
|
||||||
|
@ -185,32 +197,64 @@ func resourceNetworkingSecGroupRuleV2Delete(d *schema.ResourceData, meta interfa
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceNetworkingSecGroupRuleV2DetermineDirection(v string) rules.RuleDirection {
|
||||||
|
var direction rules.RuleDirection
|
||||||
|
switch v {
|
||||||
|
case "ingress":
|
||||||
|
direction = rules.DirIngress
|
||||||
|
case "egress":
|
||||||
|
direction = rules.DirEgress
|
||||||
|
}
|
||||||
|
|
||||||
|
return direction
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceNetworkingSecGroupRuleV2DetermineEtherType(v string) rules.RuleEtherType {
|
||||||
|
var etherType rules.RuleEtherType
|
||||||
|
switch v {
|
||||||
|
case "IPv4":
|
||||||
|
etherType = rules.EtherType4
|
||||||
|
case "IPv6":
|
||||||
|
etherType = rules.EtherType6
|
||||||
|
}
|
||||||
|
|
||||||
|
return etherType
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceNetworkingSecGroupRuleV2DetermineProtocol(v string) rules.RuleProtocol {
|
||||||
|
var protocol rules.RuleProtocol
|
||||||
|
switch v {
|
||||||
|
case "tcp":
|
||||||
|
protocol = rules.ProtocolTCP
|
||||||
|
case "udp":
|
||||||
|
protocol = rules.ProtocolUDP
|
||||||
|
case "icmp":
|
||||||
|
protocol = rules.ProtocolICMP
|
||||||
|
}
|
||||||
|
|
||||||
|
return protocol
|
||||||
|
}
|
||||||
|
|
||||||
func waitForSecGroupRuleDelete(networkingClient *gophercloud.ServiceClient, secGroupRuleId string) resource.StateRefreshFunc {
|
func waitForSecGroupRuleDelete(networkingClient *gophercloud.ServiceClient, secGroupRuleId string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
log.Printf("[DEBUG] Attempting to delete OpenStack Security Group Rule %s.\n", secGroupRuleId)
|
log.Printf("[DEBUG] Attempting to delete OpenStack Security Group Rule %s.\n", secGroupRuleId)
|
||||||
|
|
||||||
r, err := rules.Get(networkingClient, secGroupRuleId).Extract()
|
r, err := rules.Get(networkingClient, secGroupRuleId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group Rule %s", secGroupRuleId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group Rule %s", secGroupRuleId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = rules.Delete(networkingClient, secGroupRuleId).ExtractErr()
|
err = rules.Delete(networkingClient, secGroupRuleId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group Rule %s", secGroupRuleId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group Rule %s", secGroupRuleId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Neutron Security Group Rule %s still active.\n", secGroupRuleId)
|
log.Printf("[DEBUG] OpenStack Neutron Security Group Rule %s still active.\n", secGroupRuleId)
|
||||||
|
|
|
@ -7,8 +7,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/rules"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2SecGroupRule_basic(t *testing.T) {
|
func TestAccNetworkingV2SecGroupRule_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingSecGroupV2() *schema.Resource {
|
func resourceNetworkingSecGroupV2() *schema.Resource {
|
||||||
|
@ -131,26 +131,20 @@ func waitForSecGroupDelete(networkingClient *gophercloud.ServiceClient, secGroup
|
||||||
|
|
||||||
r, err := groups.Get(networkingClient, secGroupId).Extract()
|
r, err := groups.Get(networkingClient, secGroupId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group %s", secGroupId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group %s", secGroupId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = groups.Delete(networkingClient, secGroupId).ExtractErr()
|
err = groups.Delete(networkingClient, secGroupId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return r, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group %s", secGroupId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Neutron Security Group %s", secGroupId)
|
||||||
return r, "DELETED", nil
|
return r, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return r, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Neutron Security Group %s still active.\n", secGroupId)
|
log.Printf("[DEBUG] OpenStack Neutron Security Group %s still active.\n", secGroupId)
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/groups"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/security/groups"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2SecGroup_basic(t *testing.T) {
|
func TestAccNetworkingV2SecGroup_basic(t *testing.T) {
|
||||||
|
|
|
@ -8,8 +8,8 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud"
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceNetworkingSubnetV2() *schema.Resource {
|
func resourceNetworkingSubnetV2() *schema.Resource {
|
||||||
|
@ -123,82 +123,6 @@ func resourceNetworkingSubnetV2() *schema.Resource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubnetCreateOpts represents the attributes used when creating a new subnet.
|
|
||||||
type SubnetCreateOpts struct {
|
|
||||||
// Required
|
|
||||||
NetworkID string
|
|
||||||
CIDR string
|
|
||||||
// Optional
|
|
||||||
Name string
|
|
||||||
TenantID string
|
|
||||||
AllocationPools []subnets.AllocationPool
|
|
||||||
GatewayIP string
|
|
||||||
NoGateway bool
|
|
||||||
IPVersion int
|
|
||||||
EnableDHCP *bool
|
|
||||||
DNSNameservers []string
|
|
||||||
HostRoutes []subnets.HostRoute
|
|
||||||
ValueSpecs map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSubnetCreateMap casts a CreateOpts struct to a map.
|
|
||||||
func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
|
|
||||||
s := make(map[string]interface{})
|
|
||||||
|
|
||||||
if opts.NetworkID == "" {
|
|
||||||
return nil, fmt.Errorf("A network ID is required")
|
|
||||||
}
|
|
||||||
if opts.CIDR == "" {
|
|
||||||
return nil, fmt.Errorf("A valid CIDR is required")
|
|
||||||
}
|
|
||||||
if opts.IPVersion != 0 && opts.IPVersion != subnets.IPv4 && opts.IPVersion != subnets.IPv6 {
|
|
||||||
return nil, fmt.Errorf("An IP type must either be 4 or 6")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both GatewayIP and NoGateway should not be set
|
|
||||||
if opts.GatewayIP != "" && opts.NoGateway {
|
|
||||||
return nil, fmt.Errorf("Both disabling the gateway and specifying a gateway is not allowed")
|
|
||||||
}
|
|
||||||
|
|
||||||
s["network_id"] = opts.NetworkID
|
|
||||||
s["cidr"] = opts.CIDR
|
|
||||||
|
|
||||||
if opts.EnableDHCP != nil {
|
|
||||||
s["enable_dhcp"] = &opts.EnableDHCP
|
|
||||||
}
|
|
||||||
if opts.Name != "" {
|
|
||||||
s["name"] = opts.Name
|
|
||||||
}
|
|
||||||
if opts.GatewayIP != "" {
|
|
||||||
s["gateway_ip"] = opts.GatewayIP
|
|
||||||
} else if opts.NoGateway {
|
|
||||||
s["gateway_ip"] = nil
|
|
||||||
}
|
|
||||||
if opts.TenantID != "" {
|
|
||||||
s["tenant_id"] = opts.TenantID
|
|
||||||
}
|
|
||||||
if opts.IPVersion != 0 {
|
|
||||||
s["ip_version"] = opts.IPVersion
|
|
||||||
}
|
|
||||||
if len(opts.AllocationPools) != 0 {
|
|
||||||
s["allocation_pools"] = opts.AllocationPools
|
|
||||||
}
|
|
||||||
if len(opts.DNSNameservers) != 0 {
|
|
||||||
s["dns_nameservers"] = opts.DNSNameservers
|
|
||||||
}
|
|
||||||
if len(opts.HostRoutes) != 0 {
|
|
||||||
s["host_routes"] = opts.HostRoutes
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.ValueSpecs != nil {
|
|
||||||
for k, v := range opts.ValueSpecs {
|
|
||||||
s[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map[string]interface{}{"subnet": s}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) error {
|
func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) error {
|
||||||
config := meta.(*Config)
|
config := meta.(*Config)
|
||||||
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
|
||||||
|
@ -206,35 +130,48 @@ func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{})
|
||||||
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := d.GetOk("gateway_ip"); ok {
|
|
||||||
if _, ok2 := d.GetOk("no_gateway"); ok2 {
|
|
||||||
return fmt.Errorf("Both gateway_ip and no_gateway cannot be set.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enableDHCP := d.Get("enable_dhcp").(bool)
|
|
||||||
|
|
||||||
createOpts := SubnetCreateOpts{
|
createOpts := SubnetCreateOpts{
|
||||||
|
subnets.CreateOpts{
|
||||||
NetworkID: d.Get("network_id").(string),
|
NetworkID: d.Get("network_id").(string),
|
||||||
CIDR: d.Get("cidr").(string),
|
CIDR: d.Get("cidr").(string),
|
||||||
Name: d.Get("name").(string),
|
Name: d.Get("name").(string),
|
||||||
TenantID: d.Get("tenant_id").(string),
|
TenantID: d.Get("tenant_id").(string),
|
||||||
AllocationPools: resourceSubnetAllocationPoolsV2(d),
|
AllocationPools: resourceSubnetAllocationPoolsV2(d),
|
||||||
GatewayIP: d.Get("gateway_ip").(string),
|
|
||||||
NoGateway: d.Get("no_gateway").(bool),
|
|
||||||
IPVersion: d.Get("ip_version").(int),
|
|
||||||
DNSNameservers: resourceSubnetDNSNameserversV2(d),
|
DNSNameservers: resourceSubnetDNSNameserversV2(d),
|
||||||
HostRoutes: resourceSubnetHostRoutesV2(d),
|
HostRoutes: resourceSubnetHostRoutesV2(d),
|
||||||
EnableDHCP: &enableDHCP,
|
EnableDHCP: nil,
|
||||||
ValueSpecs: subnetValueSpecs(d),
|
},
|
||||||
|
MapValueSpecs(d),
|
||||||
|
}
|
||||||
|
|
||||||
|
noGateway := d.Get("no_gateway").(bool)
|
||||||
|
gatewayIP := d.Get("gateway_ip").(string)
|
||||||
|
|
||||||
|
if gatewayIP != "" && noGateway {
|
||||||
|
return fmt.Errorf("Both gateway_ip and no_gateway cannot be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if gatewayIP != "" {
|
||||||
|
createOpts.GatewayIP = &gatewayIP
|
||||||
|
}
|
||||||
|
|
||||||
|
if noGateway {
|
||||||
|
disableGateway := ""
|
||||||
|
createOpts.GatewayIP = &disableGateway
|
||||||
|
}
|
||||||
|
|
||||||
|
enableDHCP := d.Get("enable_dhcp").(bool)
|
||||||
|
createOpts.EnableDHCP = &enableDHCP
|
||||||
|
|
||||||
|
if v, ok := d.GetOk("ip_version"); ok {
|
||||||
|
ipVersion := resourceNetworkingSubnetV2DetermineIPVersion(v.(int))
|
||||||
|
createOpts.IPVersion = ipVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Create Options: %#v", createOpts)
|
|
||||||
s, err := subnets.Create(networkingClient, createOpts).Extract()
|
s, err := subnets.Create(networkingClient, createOpts).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err)
|
return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err)
|
||||||
}
|
}
|
||||||
log.Printf("[INFO] Subnet ID: %s", s.ID)
|
|
||||||
|
|
||||||
log.Printf("[DEBUG] Waiting for Subnet (%s) to become available", s.ID)
|
log.Printf("[DEBUG] Waiting for Subnet (%s) to become available", s.ID)
|
||||||
stateConf := &resource.StateChangeConf{
|
stateConf := &resource.StateChangeConf{
|
||||||
|
@ -249,6 +186,7 @@ func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
d.SetId(s.ID)
|
d.SetId(s.ID)
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Created Subnet %s: %#v", s.ID, s)
|
||||||
return resourceNetworkingSubnetV2Read(d, meta)
|
return resourceNetworkingSubnetV2Read(d, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,9 +202,9 @@ func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) er
|
||||||
return CheckDeleted(d, err, "subnet")
|
return CheckDeleted(d, err, "subnet")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] Retreived Subnet %s: %+v", d.Id(), s)
|
log.Printf("[DEBUG] Retrieved Subnet %s: %#v", d.Id(), s)
|
||||||
|
|
||||||
d.Set("newtork_id", s.NetworkID)
|
d.Set("network_id", s.NetworkID)
|
||||||
d.Set("cidr", s.CIDR)
|
d.Set("cidr", s.CIDR)
|
||||||
d.Set("ip_version", s.IPVersion)
|
d.Set("ip_version", s.IPVersion)
|
||||||
d.Set("name", s.Name)
|
d.Set("name", s.Name)
|
||||||
|
@ -290,23 +228,38 @@ func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{})
|
||||||
|
|
||||||
// Check if both gateway_ip and no_gateway are set
|
// Check if both gateway_ip and no_gateway are set
|
||||||
if _, ok := d.GetOk("gateway_ip"); ok {
|
if _, ok := d.GetOk("gateway_ip"); ok {
|
||||||
if _, ok2 := d.GetOk("no_gateway"); ok2 {
|
noGateway := d.Get("no_gateway").(bool)
|
||||||
|
if noGateway {
|
||||||
return fmt.Errorf("Both gateway_ip and no_gateway cannot be set.")
|
return fmt.Errorf("Both gateway_ip and no_gateway cannot be set.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateOpts subnets.UpdateOpts
|
var updateOpts subnets.UpdateOpts
|
||||||
|
|
||||||
|
noGateway := d.Get("no_gateway").(bool)
|
||||||
|
gatewayIP := d.Get("gateway_ip").(string)
|
||||||
|
|
||||||
|
if gatewayIP != "" && noGateway {
|
||||||
|
return fmt.Errorf("Both gateway_ip and no_gateway cannot be set")
|
||||||
|
}
|
||||||
|
|
||||||
if d.HasChange("name") {
|
if d.HasChange("name") {
|
||||||
updateOpts.Name = d.Get("name").(string)
|
updateOpts.Name = d.Get("name").(string)
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("gateway_ip") {
|
if d.HasChange("gateway_ip") {
|
||||||
updateOpts.GatewayIP = d.Get("gateway_ip").(string)
|
updateOpts.GatewayIP = nil
|
||||||
|
if v, ok := d.GetOk("gateway_ip"); ok {
|
||||||
|
gatewayIP := v.(string)
|
||||||
|
updateOpts.GatewayIP = &gatewayIP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("no_gateway") {
|
if d.HasChange("no_gateway") {
|
||||||
updateOpts.NoGateway = d.Get("no_gateway").(bool)
|
if d.Get("no_gateway").(bool) {
|
||||||
|
gatewayIP := ""
|
||||||
|
updateOpts.GatewayIP = &gatewayIP
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if d.HasChange("dns_nameservers") {
|
if d.HasChange("dns_nameservers") {
|
||||||
|
@ -392,6 +345,18 @@ func resourceSubnetHostRoutesV2(d *schema.ResourceData) []subnets.HostRoute {
|
||||||
return hr
|
return hr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resourceNetworkingSubnetV2DetermineIPVersion(v int) gophercloud.IPVersion {
|
||||||
|
var ipVersion gophercloud.IPVersion
|
||||||
|
switch v {
|
||||||
|
case 4:
|
||||||
|
ipVersion = gophercloud.IPv4
|
||||||
|
case 6:
|
||||||
|
ipVersion = gophercloud.IPv6
|
||||||
|
}
|
||||||
|
|
||||||
|
return ipVersion
|
||||||
|
}
|
||||||
|
|
||||||
func waitForSubnetActive(networkingClient *gophercloud.ServiceClient, subnetId string) resource.StateRefreshFunc {
|
func waitForSubnetActive(networkingClient *gophercloud.ServiceClient, subnetId string) resource.StateRefreshFunc {
|
||||||
return func() (interface{}, string, error) {
|
return func() (interface{}, string, error) {
|
||||||
s, err := subnets.Get(networkingClient, subnetId).Extract()
|
s, err := subnets.Get(networkingClient, subnetId).Extract()
|
||||||
|
@ -410,37 +375,28 @@ func waitForSubnetDelete(networkingClient *gophercloud.ServiceClient, subnetId s
|
||||||
|
|
||||||
s, err := subnets.Get(networkingClient, subnetId).Extract()
|
s, err := subnets.Get(networkingClient, subnetId).Extract()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return s, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId)
|
||||||
return s, "DELETED", nil
|
return s, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
return s, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = subnets.Delete(networkingClient, subnetId).ExtractErr()
|
err = subnets.Delete(networkingClient, subnetId).ExtractErr()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return s, "ACTIVE", err
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId)
|
log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId)
|
||||||
return s, "DELETED", nil
|
return s, "DELETED", nil
|
||||||
}
|
}
|
||||||
|
if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
|
||||||
|
if errCode.Actual == 409 {
|
||||||
|
return s, "ACTIVE", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, "ACTIVE", err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] OpenStack Subnet %s still active.\n", subnetId)
|
log.Printf("[DEBUG] OpenStack Subnet %s still active.\n", subnetId)
|
||||||
return s, "ACTIVE", nil
|
return s, "ACTIVE", nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func subnetValueSpecs(d *schema.ResourceData) map[string]string {
|
|
||||||
m := make(map[string]string)
|
|
||||||
for key, val := range d.Get("value_specs").(map[string]interface{}) {
|
|
||||||
m[key] = val.(string)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
|
||||||
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccNetworkingV2Subnet_basic(t *testing.T) {
|
func TestAccNetworkingV2Subnet_basic(t *testing.T) {
|
||||||
|
@ -27,7 +27,7 @@ func TestAccNetworkingV2Subnet_basic(t *testing.T) {
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: testAccNetworkingV2Subnet_update,
|
Config: testAccNetworkingV2Subnet_update,
|
||||||
Check: resource.ComposeTestCheckFunc(
|
Check: resource.ComposeTestCheckFunc(
|
||||||
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "name", "tf-test-subnet"),
|
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "name", "subnet_1"),
|
||||||
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "gateway_ip", "192.168.199.1"),
|
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "gateway_ip", "192.168.199.1"),
|
||||||
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "enable_dhcp", "true"),
|
resource.TestCheckResourceAttr("openstack_networking_subnet_v2.subnet_1", "enable_dhcp", "true"),
|
||||||
),
|
),
|
||||||
|
@ -174,6 +174,10 @@ var testAccNetworkingV2Subnet_basic = fmt.Sprintf(`
|
||||||
resource "openstack_networking_subnet_v2" "subnet_1" {
|
resource "openstack_networking_subnet_v2" "subnet_1" {
|
||||||
network_id = "${openstack_networking_network_v2.network_1.id}"
|
network_id = "${openstack_networking_network_v2.network_1.id}"
|
||||||
cidr = "192.168.199.0/24"
|
cidr = "192.168.199.0/24"
|
||||||
|
allocation_pools {
|
||||||
|
start = "192.168.199.100"
|
||||||
|
end = "192.168.199.200"
|
||||||
|
}
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
var testAccNetworkingV2Subnet_update = fmt.Sprintf(`
|
var testAccNetworkingV2Subnet_update = fmt.Sprintf(`
|
||||||
|
@ -183,10 +187,14 @@ var testAccNetworkingV2Subnet_update = fmt.Sprintf(`
|
||||||
}
|
}
|
||||||
|
|
||||||
resource "openstack_networking_subnet_v2" "subnet_1" {
|
resource "openstack_networking_subnet_v2" "subnet_1" {
|
||||||
name = "tf-test-subnet"
|
name = "subnet_1"
|
||||||
network_id = "${openstack_networking_network_v2.network_1.id}"
|
network_id = "${openstack_networking_network_v2.network_1.id}"
|
||||||
cidr = "192.168.199.0/24"
|
cidr = "192.168.199.0/24"
|
||||||
gateway_ip = "192.168.199.1"
|
gateway_ip = "192.168.199.1"
|
||||||
|
allocation_pools {
|
||||||
|
start = "192.168.199.100"
|
||||||
|
end = "192.168.199.200"
|
||||||
|
}
|
||||||
}`)
|
}`)
|
||||||
|
|
||||||
var testAccNetworkingV2Subnet_enableDHCP = fmt.Sprintf(`
|
var testAccNetworkingV2Subnet_enableDHCP = fmt.Sprintf(`
|
||||||
|
|
|
@ -4,8 +4,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func resourceObjectStorageContainerV1() *schema.Resource {
|
func resourceObjectStorageContainerV1() *schema.Resource {
|
||||||
|
|
|
@ -4,9 +4,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/objectstorage/v1/containers"
|
||||||
"github.com/hashicorp/terraform/helper/resource"
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/rackspace/gophercloud/openstack/objectstorage/v1/containers"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAccObjectStorageV1Container_basic(t *testing.T) {
|
func TestAccObjectStorageV1Container_basic(t *testing.T) {
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NetworkCreateOpts represents the attributes used when creating a new network.
|
||||||
|
type NetworkCreateOpts struct {
|
||||||
|
networks.CreateOpts
|
||||||
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToNetworkCreateMap casts a CreateOpts struct to a map.
|
||||||
|
// It overrides networks.ToNetworkCreateMap to add the ValueSpecs field.
|
||||||
|
func (opts NetworkCreateOpts) ToNetworkCreateMap() (map[string]interface{}, error) {
|
||||||
|
return BuildRequest(opts, "network")
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterCreateOpts represents the attributes used when creating a new router.
|
||||||
|
type RouterCreateOpts struct {
|
||||||
|
routers.CreateOpts
|
||||||
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRouterCreateMap casts a CreateOpts struct to a map.
|
||||||
|
// It overrides routers.ToRouterCreateMap to add the ValueSpecs field.
|
||||||
|
func (opts RouterCreateOpts) ToRouterCreateMap() (map[string]interface{}, error) {
|
||||||
|
return BuildRequest(opts, "router")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubnetCreateOpts represents the attributes used when creating a new subnet.
|
||||||
|
type SubnetCreateOpts struct {
|
||||||
|
subnets.CreateOpts
|
||||||
|
ValueSpecs map[string]string `json:"value_specs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSubnetCreateMap casts a CreateOpts struct to a map.
|
||||||
|
// It overrides subnets.ToSubnetCreateMap to add the ValueSpecs field.
|
||||||
|
func (opts SubnetCreateOpts) ToSubnetCreateMap() (map[string]interface{}, error) {
|
||||||
|
b, err := BuildRequest(opts, "subnet")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if m := b["subnet"].(map[string]interface{}); m["gateway_ip"] == "" {
|
||||||
|
m["gateway_ip"] = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
|
@ -3,20 +3,44 @@ package openstack
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
"github.com/hashicorp/terraform/helper/schema"
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
"github.com/rackspace/gophercloud"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so,
|
// CheckDeleted checks the error to see if it's a 404 (Not Found) and, if so,
|
||||||
// sets the resource ID to the empty string instead of throwing an error.
|
// sets the resource ID to the empty string instead of throwing an error.
|
||||||
func CheckDeleted(d *schema.ResourceData, err error, msg string) error {
|
func CheckDeleted(d *schema.ResourceData, err error, msg string) error {
|
||||||
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
|
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("%s: %s", msg, err)
|
|
||||||
}
|
|
||||||
if errCode.Actual == 404 {
|
|
||||||
d.SetId("")
|
d.SetId("")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("%s: %s", msg, err)
|
return fmt.Errorf("%s: %s", msg, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapValueSpecs converts ResourceData into a map
|
||||||
|
func MapValueSpecs(d *schema.ResourceData) map[string]string {
|
||||||
|
m := make(map[string]string)
|
||||||
|
for key, val := range d.Get("value_specs").(map[string]interface{}) {
|
||||||
|
m[key] = val.(string)
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// BuildRequest takes an opts struct and builds a request body for
|
||||||
|
// Gophercloud to execute
|
||||||
|
func BuildRequest(opts interface{}, parent string) (map[string]interface{}, error) {
|
||||||
|
b, err := gophercloud.BuildRequestBody(opts, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if b["value_specs"] != nil {
|
||||||
|
for k, v := range b["value_specs"].(map[string]interface{}) {
|
||||||
|
b[k] = v
|
||||||
|
}
|
||||||
|
delete(b, "value_specs")
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{parent: b}, nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,235 @@
|
||||||
|
# Contributing to Gophercloud
|
||||||
|
|
||||||
|
- [Getting started](#getting-started)
|
||||||
|
- [Tests](#tests)
|
||||||
|
- [Style guide](#basic-style-guide)
|
||||||
|
- [3 ways to get involved](#5-ways-to-get-involved)
|
||||||
|
|
||||||
|
## Setting up your git workspace
|
||||||
|
|
||||||
|
As a contributor you will need to setup your workspace in a slightly different
|
||||||
|
way than just downloading it. Here are the basic installation instructions:
|
||||||
|
|
||||||
|
1. Configure your `$GOPATH` and run `go get` as described in the main
|
||||||
|
[README](/README.md#how-to-install) but add `-tags "fixtures acceptance"` to
|
||||||
|
get dependencies for unit and acceptance tests.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get -tags "fixtures acceptance" github.com/gophercloud/gophercloud
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Move into the directory that houses your local repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ${GOPATH}/src/github.com/gophercloud/gophercloud
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Fork the `gophercloud/gophercloud` repository and update your remote refs. You
|
||||||
|
will need to rename the `origin` remote branch to `upstream`, and add your
|
||||||
|
fork as `origin` instead:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote rename origin upstream
|
||||||
|
git remote add origin git@github.com:<my_username>/gophercloud.git
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Checkout the latest development branch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout master
|
||||||
|
```
|
||||||
|
|
||||||
|
5. If you're working on something (discussed more in detail below), you will
|
||||||
|
need to checkout a new feature branch:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout -b my-new-feature
|
||||||
|
```
|
||||||
|
|
||||||
|
Another thing to bear in mind is that you will need to add a few extra
|
||||||
|
environment variables for acceptance tests - this is documented in our
|
||||||
|
[acceptance tests readme](/acceptance).
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
When working on a new or existing feature, testing will be the backbone of your
|
||||||
|
work since it helps uncover and prevent regressions in the codebase. There are
|
||||||
|
two types of test we use in Gophercloud: unit tests and acceptance tests, which
|
||||||
|
are both described below.
|
||||||
|
|
||||||
|
### Unit tests
|
||||||
|
|
||||||
|
Unit tests are the fine-grained tests that establish and ensure the behavior
|
||||||
|
of individual units of functionality. We usually test on an
|
||||||
|
operation-by-operation basis (an operation typically being an API action) with
|
||||||
|
the use of mocking to set up explicit expectations. Each operation will set up
|
||||||
|
its HTTP response expectation, and then test how the system responds when fed
|
||||||
|
this controlled, pre-determined input.
|
||||||
|
|
||||||
|
To make life easier, we've introduced a bunch of test helpers to simplify the
|
||||||
|
process of testing expectations with assertions:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud/testhelper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t *testing.T) {
|
||||||
|
result, err := Operation()
|
||||||
|
|
||||||
|
testhelper.AssertEquals(t, "foo", result.Bar)
|
||||||
|
testhelper.AssertNoErr(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSomethingElse(t *testing.T) {
|
||||||
|
testhelper.CheckEquals(t, "expected", "actual")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`AssertEquals` and `AssertNoErr` will throw a fatal error if a value does not
|
||||||
|
match an expected value or if an error has been declared, respectively. You can
|
||||||
|
also use `CheckEquals` and `CheckNoErr` for the same purpose; the only difference
|
||||||
|
being that `t.Errorf` is raised rather than `t.Fatalf`.
|
||||||
|
|
||||||
|
Here is a truncated example of mocked HTTP responses:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
th "github.com/gophercloud/gophercloud/testhelper"
|
||||||
|
fake "github.com/gophercloud/gophercloud/testhelper/client"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGet(t *testing.T) {
|
||||||
|
// Setup the HTTP request multiplexer and server
|
||||||
|
th.SetupHTTP()
|
||||||
|
defer th.TeardownHTTP()
|
||||||
|
|
||||||
|
th.Mux.HandleFunc("/networks/d32019d3-bc6e-4319-9c1d-6722fc136a22", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Test we're using the correct HTTP method
|
||||||
|
th.TestMethod(t, r, "GET")
|
||||||
|
|
||||||
|
// Test we're setting the auth token
|
||||||
|
th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
|
||||||
|
|
||||||
|
// Set the appropriate headers for our mocked response
|
||||||
|
w.Header().Add("Content-Type", "application/json")
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
// Set the HTTP body
|
||||||
|
fmt.Fprintf(w, `
|
||||||
|
{
|
||||||
|
"network": {
|
||||||
|
"status": "ACTIVE",
|
||||||
|
"name": "private-network",
|
||||||
|
"admin_state_up": true,
|
||||||
|
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
|
||||||
|
"shared": true,
|
||||||
|
"id": "d32019d3-bc6e-4319-9c1d-6722fc136a22"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Call our API operation
|
||||||
|
network, err := networks.Get(fake.ServiceClient(), "d32019d3-bc6e-4319-9c1d-6722fc136a22").Extract()
|
||||||
|
|
||||||
|
// Assert no errors and equality
|
||||||
|
th.AssertNoErr(t, err)
|
||||||
|
th.AssertEquals(t, n.Status, "ACTIVE")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Acceptance tests
|
||||||
|
|
||||||
|
As we've already mentioned, unit tests have a very narrow and confined focus -
|
||||||
|
they test small units of behavior. Acceptance tests on the other hand have a
|
||||||
|
far larger scope: they are fully functional tests that test the entire API of a
|
||||||
|
service in one fell swoop. They don't care about unit isolation or mocking
|
||||||
|
expectations, they instead do a full run-through and consequently test how the
|
||||||
|
entire system _integrates_ together. When an API satisfies expectations, it
|
||||||
|
proves by default that the requirements for a contract have been met.
|
||||||
|
|
||||||
|
Please be aware that acceptance tests will hit a live API - and may incur
|
||||||
|
service charges from your provider. Although most tests handle their own
|
||||||
|
teardown procedures, it is always worth manually checking that resources are
|
||||||
|
deleted after the test suite finishes.
|
||||||
|
|
||||||
|
### Running tests
|
||||||
|
|
||||||
|
To run all tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags fixtures ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
To run all tests with verbose output:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -v -tags fixtures ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
To run tests that match certain [build tags]():
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -tags "fixtures foo bar" ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
To run tests for a particular sub-package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ./path/to/package && go test -tags fixtures .
|
||||||
|
```
|
||||||
|
|
||||||
|
## Style guide
|
||||||
|
|
||||||
|
See [here](/STYLEGUIDE.md)
|
||||||
|
|
||||||
|
## 3 ways to get involved
|
||||||
|
|
||||||
|
There are five main ways you can get involved in our open-source project, and
|
||||||
|
each is described briefly below. Once you've made up your mind and decided on
|
||||||
|
your fix, you will need to follow the same basic steps that all submissions are
|
||||||
|
required to adhere to:
|
||||||
|
|
||||||
|
1. [fork](https://help.github.com/articles/fork-a-repo/) the `gophercloud/gophercloud` repository
|
||||||
|
2. checkout a [new branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches)
|
||||||
|
3. submit your branch as a [pull request](https://help.github.com/articles/creating-a-pull-request/)
|
||||||
|
|
||||||
|
### 1. Fixing bugs
|
||||||
|
|
||||||
|
If you want to start fixing open bugs, we'd really appreciate that! Bug fixing
|
||||||
|
is central to any project. The best way to get started is by heading to our
|
||||||
|
[bug tracker](https://github.com/gophercloud/gophercloud/issues) and finding open
|
||||||
|
bugs that you think nobody is working on. It might be useful to comment on the
|
||||||
|
thread to see the current state of the issue and if anybody has made any
|
||||||
|
breakthroughs on it so far.
|
||||||
|
|
||||||
|
### 2. Improving documentation
|
||||||
|
The best source of documentation is on [godoc.org](http://godoc.org). It is
|
||||||
|
automatically generated from the source code.
|
||||||
|
|
||||||
|
If you feel that a certain section could be improved - whether it's to clarify
|
||||||
|
ambiguity, correct a technical mistake, or to fix a grammatical error - please
|
||||||
|
feel entitled to do so! We welcome doc pull requests with the same childlike
|
||||||
|
enthusiasm as any other contribution!
|
||||||
|
|
||||||
|
###3. Working on a new feature
|
||||||
|
|
||||||
|
If you've found something we've left out, definitely feel free to start work on
|
||||||
|
introducing that feature. It's always useful to open an issue or submit a pull
|
||||||
|
request early on to indicate your intent to a core contributor - this enables
|
||||||
|
quick/early feedback and can help steer you in the right direction by avoiding
|
||||||
|
known issues. It might also help you avoid losing time implementing something
|
||||||
|
that might not ever work. One tip is to prefix your Pull Request issue title
|
||||||
|
with [wip] - then people know it's a work in progress.
|
||||||
|
|
||||||
|
You must ensure that all of your work is well tested - both in terms of unit
|
||||||
|
and acceptance tests. Untested code will not be merged because it introduces
|
||||||
|
too much of a risk to end-users.
|
||||||
|
|
||||||
|
Happy hacking!
|
|
@ -0,0 +1,191 @@
|
||||||
|
Copyright 2012-2013 Rackspace, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
||||||
|
this file except in compliance with the License. You may obtain a copy of the
|
||||||
|
License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software distributed
|
||||||
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||||
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations under the License.
|
||||||
|
|
||||||
|
------
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Compute
|
||||||
|
|
||||||
|
## Floating IPs
|
||||||
|
|
||||||
|
* `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingip` is now `github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips`
|
||||||
|
* `floatingips.Associate` and `floatingips.Disassociate` have been removed.
|
||||||
|
* `floatingips.DisassociateOpts` is now required to disassociate a Floating IP.
|
||||||
|
|
||||||
|
## Security Groups
|
||||||
|
|
||||||
|
* `secgroups.AddServerToGroup` is now `secgroups.AddServer`.
|
||||||
|
* `secgroups.RemoveServerFromGroup` is now `secgroups.RemoveServer`.
|
||||||
|
|
||||||
|
## Servers
|
||||||
|
|
||||||
|
* `servers.Reboot` now requires a `servers.RebootOpts` struct:
|
||||||
|
|
||||||
|
```golang
|
||||||
|
rebootOpts := &servers.RebootOpts{
|
||||||
|
Type: servers.SoftReboot,
|
||||||
|
}
|
||||||
|
res := servers.Reboot(client, server.ID, rebootOpts)
|
||||||
|
```
|
||||||
|
|
||||||
|
# Identity
|
||||||
|
|
||||||
|
## V3
|
||||||
|
|
||||||
|
### Tokens
|
||||||
|
|
||||||
|
* `Token.ExpiresAt` is now of type `gophercloud.JSONRFC3339Milli` instead of
|
||||||
|
`time.Time`
|
|
@ -0,0 +1,139 @@
|
||||||
|
# Gophercloud: an OpenStack SDK for Go
|
||||||
|
[![Build Status](https://travis-ci.org/gophercloud/gophercloud.svg?branch=master)](https://travis-ci.org/gophercloud/gophercloud)
|
||||||
|
[![Coverage Status](https://coveralls.io/repos/github/gophercloud/gophercloud/badge.svg?branch=master)](https://coveralls.io/github/gophercloud/gophercloud?branch=master)
|
||||||
|
|
||||||
|
Gophercloud is an OpenStack Go SDK.
|
||||||
|
|
||||||
|
## Useful links
|
||||||
|
|
||||||
|
* [Reference documentation](http://godoc.org/github.com/gophercloud/gophercloud)
|
||||||
|
* [Effective Go](https://golang.org/doc/effective_go.html)
|
||||||
|
|
||||||
|
## How to install
|
||||||
|
|
||||||
|
Before installing, you need to ensure that your [GOPATH environment variable](https://golang.org/doc/code.html#GOPATH)
|
||||||
|
is pointing to an appropriate directory where you want to install Gophercloud:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir $HOME/go
|
||||||
|
export GOPATH=$HOME/go
|
||||||
|
```
|
||||||
|
|
||||||
|
To protect yourself against changes in your dependencies, we highly recommend choosing a
|
||||||
|
[dependency management solution](https://github.com/golang/go/wiki/PackageManagementTools) for
|
||||||
|
your projects, such as [godep](https://github.com/tools/godep). Once this is set up, you can install
|
||||||
|
Gophercloud as a dependency like so:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go get github.com/gophercloud/gophercloud
|
||||||
|
|
||||||
|
# Edit your code to import relevant packages from "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
godep save ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
This will install all the source files you need into a `Godeps/_workspace` directory, which is
|
||||||
|
referenceable from your own source files when you use the `godep go` command.
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
### Credentials
|
||||||
|
|
||||||
|
Because you'll be hitting an API, you will need to retrieve your OpenStack
|
||||||
|
credentials and either store them as environment variables or in your local Go
|
||||||
|
files. The first method is recommended because it decouples credential
|
||||||
|
information from source code, allowing you to push the latter to your version
|
||||||
|
control system without any security risk.
|
||||||
|
|
||||||
|
You will need to retrieve the following:
|
||||||
|
|
||||||
|
* username
|
||||||
|
* password
|
||||||
|
* a valid Keystone identity URL
|
||||||
|
|
||||||
|
For users that have the OpenStack dashboard installed, there's a shortcut. If
|
||||||
|
you visit the `project/access_and_security` path in Horizon and click on the
|
||||||
|
"Download OpenStack RC File" button at the top right hand corner, you will
|
||||||
|
download a bash file that exports all of your access details to environment
|
||||||
|
variables. To execute the file, run `source admin-openrc.sh` and you will be
|
||||||
|
prompted for your password.
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
|
||||||
|
Once you have access to your credentials, you can begin plugging them into
|
||||||
|
Gophercloud. The next step is authentication, and this is handled by a base
|
||||||
|
"Provider" struct. To get one, you can either pass in your credentials
|
||||||
|
explicitly, or tell Gophercloud to use environment variables:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Option 1: Pass in the values yourself
|
||||||
|
opts := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
|
||||||
|
Username: "{username}",
|
||||||
|
Password: "{password}",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option 2: Use a utility function to retrieve all your environment variables
|
||||||
|
opts, err := openstack.AuthOptionsFromEnv()
|
||||||
|
```
|
||||||
|
|
||||||
|
Once you have the `opts` variable, you can pass it in and get back a
|
||||||
|
`ProviderClient` struct:
|
||||||
|
|
||||||
|
```go
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
```
|
||||||
|
|
||||||
|
The `ProviderClient` is the top-level client that all of your OpenStack services
|
||||||
|
derive from. The provider contains all of the authentication details that allow
|
||||||
|
your Go code to access the API - such as the base URL and token ID.
|
||||||
|
|
||||||
|
### Provision a server
|
||||||
|
|
||||||
|
Once we have a base Provider, we inject it as a dependency into each OpenStack
|
||||||
|
service. In order to work with the Compute API, we need a Compute service
|
||||||
|
client; which can be created like so:
|
||||||
|
|
||||||
|
```go
|
||||||
|
client, err := openstack.NewComputeV2(provider, gophercloud.EndpointOpts{
|
||||||
|
Region: os.Getenv("OS_REGION_NAME"),
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
We then use this `client` for any Compute API operation we want. In our case,
|
||||||
|
we want to provision a new server - so we invoke the `Create` method and pass
|
||||||
|
in the flavor ID (hardware specification) and image ID (operating system) we're
|
||||||
|
interested in:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
|
||||||
|
server, err := servers.Create(client, servers.CreateOpts{
|
||||||
|
Name: "My new server!",
|
||||||
|
FlavorRef: "flavor_id",
|
||||||
|
ImageRef: "image_id",
|
||||||
|
}).Extract()
|
||||||
|
```
|
||||||
|
|
||||||
|
The above code sample creates a new server with the parameters, and embodies the
|
||||||
|
new resource in the `server` variable (a
|
||||||
|
[`servers.Server`](http://godoc.org/github.com/gophercloud/gophercloud) struct).
|
||||||
|
|
||||||
|
## Backwards-Compatibility Guarantees
|
||||||
|
|
||||||
|
None. Vendor it and write tests covering the parts you use.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
See the [contributing guide](./CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## Help and feedback
|
||||||
|
|
||||||
|
If you're struggling with something or have spotted a potential bug, feel free
|
||||||
|
to submit an issue to our [bug tracker](/issues).
|
|
@ -0,0 +1,68 @@
|
||||||
|
|
||||||
|
## On Pull Requests
|
||||||
|
|
||||||
|
- Before you start a PR there needs to be a Github issue and a discussion about it
|
||||||
|
on that issue with a core contributor, even if it's just a 'SGTM'.
|
||||||
|
|
||||||
|
- A PR's description must reference the issue it closes with a `For <ISSUE NUMBER>` (e.g. For #293).
|
||||||
|
|
||||||
|
- A PR's description must contain link(s) to the line(s) in the OpenStack
|
||||||
|
source code (on Github) that prove(s) the PR code to be valid. Links to documentation
|
||||||
|
are not good enough. The link(s) should be to a non-`master` branch. For example,
|
||||||
|
a pull request implementing the creation of a Neutron v2 subnet might put the
|
||||||
|
following link in the description:
|
||||||
|
|
||||||
|
https://github.com/openstack/neutron/blob/stable/mitaka/neutron/api/v2/attributes.py#L749
|
||||||
|
|
||||||
|
From that link, a reviewer (or user) can verify the fields in the request/response
|
||||||
|
objects in the PR.
|
||||||
|
|
||||||
|
- A PR that is in-progress should have `[wip]` in front of the PR's title. When
|
||||||
|
ready for review, remove the `[wip]` and ping a core contributor with an `@`.
|
||||||
|
|
||||||
|
- A PR should be small. Even if you intend on implementing an entire
|
||||||
|
service, a PR should only be one route of that service
|
||||||
|
(e.g. create server or get server, but not both).
|
||||||
|
|
||||||
|
- Unless explicitly asked, do not squash commits in the middle of a review; only
|
||||||
|
append. It makes it difficult for the reviewer to see what's changed from one
|
||||||
|
review to the next.
|
||||||
|
|
||||||
|
## On Code
|
||||||
|
|
||||||
|
- In re design: follow as closely as is reasonable the code already in the library.
|
||||||
|
Most operations (e.g. create, delete) admit the same design.
|
||||||
|
|
||||||
|
- Unit tests and acceptance (integration) tests must be written to cover each PR.
|
||||||
|
Tests for operations with several options (e.g. list, create) should include all
|
||||||
|
the options in the tests. This will allow users to verify an operation on their
|
||||||
|
own infrastructure and see an example of usage.
|
||||||
|
|
||||||
|
- If in doubt, ask in-line on the PR.
|
||||||
|
|
||||||
|
### File Structure
|
||||||
|
|
||||||
|
- The following should be used in most cases:
|
||||||
|
|
||||||
|
- `requests.go`: contains all the functions that make HTTP requests and the
|
||||||
|
types associated with the HTTP request (parameters for URL, body, etc)
|
||||||
|
- `results.go`: contains all the response objects and their methods
|
||||||
|
- `urls.go`: contains the endpoints to which the requests are made
|
||||||
|
|
||||||
|
### Naming
|
||||||
|
|
||||||
|
- For methods on a type in `response.go`, the receiver should be named `r` and the
|
||||||
|
variable into which it will be unmarshalled `s`.
|
||||||
|
|
||||||
|
- Functions in `requests.go`, with the exception of functions that return a
|
||||||
|
`pagination.Pager`, should be named returns of the name `r`.
|
||||||
|
|
||||||
|
- Functions in `requests.go` that accept request bodies should accept as their
|
||||||
|
last parameter an `interface` named `<Action>OptsBuilder` (eg `CreateOptsBuilder`).
|
||||||
|
This `interface` should have at the least a method named `To<Resource><Action>Map`
|
||||||
|
(eg `ToPortCreateMap`).
|
||||||
|
|
||||||
|
- Functions in `requests.go` that accept query strings should accept as their
|
||||||
|
last parameter an `interface` named `<Action>OptsBuilder` (eg `ListOptsBuilder`).
|
||||||
|
This `interface` should have at the least a method named `To<Resource><Action>Query`
|
||||||
|
(eg `ToServerListQuery`).
|
|
@ -0,0 +1,331 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
/*
|
||||||
|
AuthOptions stores information needed to authenticate to an OpenStack cluster.
|
||||||
|
You can populate one manually, or use a provider's AuthOptionsFromEnv() function
|
||||||
|
to read relevant information from the standard environment variables. Pass one
|
||||||
|
to a provider's AuthenticatedClient function to authenticate and obtain a
|
||||||
|
ProviderClient representing an active session on that provider.
|
||||||
|
|
||||||
|
Its fields are the union of those recognized by each identity implementation and
|
||||||
|
provider.
|
||||||
|
*/
|
||||||
|
type AuthOptions struct {
|
||||||
|
// IdentityEndpoint specifies the HTTP endpoint that is required to work with
|
||||||
|
// the Identity API of the appropriate version. While it's ultimately needed by
|
||||||
|
// all of the identity services, it will often be populated by a provider-level
|
||||||
|
// function.
|
||||||
|
IdentityEndpoint string `json:"-"`
|
||||||
|
|
||||||
|
// Username is required if using Identity V2 API. Consult with your provider's
|
||||||
|
// control panel to discover your account's username. In Identity V3, either
|
||||||
|
// UserID or a combination of Username and DomainID or DomainName are needed.
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
UserID string `json:"id,omitempty"`
|
||||||
|
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
|
||||||
|
// At most one of DomainID and DomainName must be provided if using Username
|
||||||
|
// with Identity V3. Otherwise, either are optional.
|
||||||
|
DomainID string `json:"id,omitempty"`
|
||||||
|
DomainName string `json:"name,omitempty"`
|
||||||
|
|
||||||
|
// The TenantID and TenantName fields are optional for the Identity V2 API.
|
||||||
|
// Some providers allow you to specify a TenantName instead of the TenantId.
|
||||||
|
// Some require both. Your provider's authentication policies will determine
|
||||||
|
// how these fields influence authentication.
|
||||||
|
TenantID string `json:"tenantId,omitempty"`
|
||||||
|
TenantName string `json:"tenantName,omitempty"`
|
||||||
|
|
||||||
|
// AllowReauth should be set to true if you grant permission for Gophercloud to
|
||||||
|
// cache your credentials in memory, and to allow Gophercloud to attempt to
|
||||||
|
// re-authenticate automatically if/when your token expires. If you set it to
|
||||||
|
// false, it will not cache these settings, but re-authentication will not be
|
||||||
|
// possible. This setting defaults to false.
|
||||||
|
//
|
||||||
|
// NOTE: The reauth function will try to re-authenticate endlessly if left unchecked.
|
||||||
|
// The way to limit the number of attempts is to provide a custom HTTP client to the provider client
|
||||||
|
// and provide a transport that implements the RoundTripper interface and stores the number of failed retries.
|
||||||
|
// For an example of this, see here: https://github.com/rackspace/rack/blob/1.0.0/auth/clients.go#L311
|
||||||
|
AllowReauth bool `json:"-"`
|
||||||
|
|
||||||
|
// TokenID allows users to authenticate (possibly as another user) with an
|
||||||
|
// authentication token ID.
|
||||||
|
TokenID string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToTokenV2CreateMap allows AuthOptions to satisfy the AuthOptionsBuilder
|
||||||
|
// interface in the v2 tokens package
|
||||||
|
func (opts AuthOptions) ToTokenV2CreateMap() (map[string]interface{}, error) {
|
||||||
|
// Populate the request map.
|
||||||
|
authMap := make(map[string]interface{})
|
||||||
|
|
||||||
|
if opts.Username != "" {
|
||||||
|
if opts.Password != "" {
|
||||||
|
authMap["passwordCredentials"] = map[string]interface{}{
|
||||||
|
"username": opts.Username,
|
||||||
|
"password": opts.Password,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, ErrMissingInput{Argument: "Password"}
|
||||||
|
}
|
||||||
|
} else if opts.TokenID != "" {
|
||||||
|
authMap["token"] = map[string]interface{}{
|
||||||
|
"id": opts.TokenID,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return nil, ErrMissingInput{Argument: "Username"}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.TenantID != "" {
|
||||||
|
authMap["tenantId"] = opts.TenantID
|
||||||
|
}
|
||||||
|
if opts.TenantName != "" {
|
||||||
|
authMap["tenantName"] = opts.TenantName
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]interface{}{"auth": authMap}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *AuthOptions) ToTokenV3CreateMap(scope map[string]interface{}) (map[string]interface{}, error) {
|
||||||
|
type domainReq struct {
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type projectReq struct {
|
||||||
|
Domain *domainReq `json:"domain,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type userReq struct {
|
||||||
|
ID *string `json:"id,omitempty"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
Domain *domainReq `json:"domain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type passwordReq struct {
|
||||||
|
User userReq `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenReq struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type identityReq struct {
|
||||||
|
Methods []string `json:"methods"`
|
||||||
|
Password *passwordReq `json:"password,omitempty"`
|
||||||
|
Token *tokenReq `json:"token,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type authReq struct {
|
||||||
|
Identity identityReq `json:"identity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
Auth authReq `json:"auth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the request structure based on the provided arguments. Create and return an error
|
||||||
|
// if insufficient or incompatible information is present.
|
||||||
|
var req request
|
||||||
|
|
||||||
|
// Test first for unrecognized arguments.
|
||||||
|
if opts.TenantID != "" {
|
||||||
|
return nil, ErrTenantIDProvided{}
|
||||||
|
}
|
||||||
|
if opts.TenantName != "" {
|
||||||
|
return nil, ErrTenantNameProvided{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Password == "" {
|
||||||
|
if opts.TokenID != "" {
|
||||||
|
// Because we aren't using password authentication, it's an error to also provide any of the user-based authentication
|
||||||
|
// parameters.
|
||||||
|
if opts.Username != "" {
|
||||||
|
return nil, ErrUsernameWithToken{}
|
||||||
|
}
|
||||||
|
if opts.UserID != "" {
|
||||||
|
return nil, ErrUserIDWithToken{}
|
||||||
|
}
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
return nil, ErrDomainIDWithToken{}
|
||||||
|
}
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
return nil, ErrDomainNameWithToken{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the request for Token authentication.
|
||||||
|
req.Auth.Identity.Methods = []string{"token"}
|
||||||
|
req.Auth.Identity.Token = &tokenReq{
|
||||||
|
ID: opts.TokenID,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If no password or token ID are available, authentication can't continue.
|
||||||
|
return nil, ErrMissingPassword{}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Password authentication.
|
||||||
|
req.Auth.Identity.Methods = []string{"password"}
|
||||||
|
|
||||||
|
// At least one of Username and UserID must be specified.
|
||||||
|
if opts.Username == "" && opts.UserID == "" {
|
||||||
|
return nil, ErrUsernameOrUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.Username != "" {
|
||||||
|
// If Username is provided, UserID may not be provided.
|
||||||
|
if opts.UserID != "" {
|
||||||
|
return nil, ErrUsernameOrUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Either DomainID or DomainName must also be specified.
|
||||||
|
if opts.DomainID == "" && opts.DomainName == "" {
|
||||||
|
return nil, ErrDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
return nil, ErrDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the request for Username and Password authentication with a DomainID.
|
||||||
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Password: opts.Password,
|
||||||
|
Domain: &domainReq{ID: &opts.DomainID},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
// Configure the request for Username and Password authentication with a DomainName.
|
||||||
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{
|
||||||
|
Name: &opts.Username,
|
||||||
|
Password: opts.Password,
|
||||||
|
Domain: &domainReq{Name: &opts.DomainName},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.UserID != "" {
|
||||||
|
// If UserID is specified, neither DomainID nor DomainName may be.
|
||||||
|
if opts.DomainID != "" {
|
||||||
|
return nil, ErrDomainIDWithUserID{}
|
||||||
|
}
|
||||||
|
if opts.DomainName != "" {
|
||||||
|
return nil, ErrDomainNameWithUserID{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure the request for UserID and Password authentication.
|
||||||
|
req.Auth.Identity.Password = &passwordReq{
|
||||||
|
User: userReq{ID: &opts.UserID, Password: opts.Password},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := BuildRequestBody(req, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(scope) != 0 {
|
||||||
|
b["auth"].(map[string]interface{})["scope"] = scope
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *AuthOptions) ToTokenV3ScopeMap() (map[string]interface{}, error) {
|
||||||
|
|
||||||
|
var scope struct {
|
||||||
|
ProjectID string
|
||||||
|
ProjectName string
|
||||||
|
DomainID string
|
||||||
|
DomainName string
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.TenantID != "" {
|
||||||
|
scope.ProjectID = opts.TenantID
|
||||||
|
opts.TenantID = ""
|
||||||
|
opts.TenantName = ""
|
||||||
|
} else {
|
||||||
|
if opts.TenantName != "" {
|
||||||
|
scope.ProjectName = opts.TenantName
|
||||||
|
scope.DomainID = opts.DomainID
|
||||||
|
scope.DomainName = opts.DomainName
|
||||||
|
}
|
||||||
|
opts.TenantName = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if scope.ProjectName != "" {
|
||||||
|
// ProjectName provided: either DomainID or DomainName must also be supplied.
|
||||||
|
// ProjectID may not be supplied.
|
||||||
|
if scope.DomainID == "" && scope.DomainName == "" {
|
||||||
|
return nil, ErrScopeDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
if scope.ProjectID != "" {
|
||||||
|
return nil, ErrScopeProjectIDOrProjectName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scope.DomainID != "" {
|
||||||
|
// ProjectName + DomainID
|
||||||
|
return map[string]interface{}{
|
||||||
|
"project": map[string]interface{}{
|
||||||
|
"name": &scope.ProjectName,
|
||||||
|
"domain": map[string]interface{}{"id": &scope.DomainID},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if scope.DomainName != "" {
|
||||||
|
// ProjectName + DomainName
|
||||||
|
return map[string]interface{}{
|
||||||
|
"project": map[string]interface{}{
|
||||||
|
"name": &scope.ProjectName,
|
||||||
|
"domain": map[string]interface{}{"name": &scope.DomainName},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
} else if scope.ProjectID != "" {
|
||||||
|
// ProjectID provided. ProjectName, DomainID, and DomainName may not be provided.
|
||||||
|
if scope.DomainID != "" {
|
||||||
|
return nil, ErrScopeProjectIDAlone{}
|
||||||
|
}
|
||||||
|
if scope.DomainName != "" {
|
||||||
|
return nil, ErrScopeProjectIDAlone{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProjectID
|
||||||
|
return map[string]interface{}{
|
||||||
|
"project": map[string]interface{}{
|
||||||
|
"id": &scope.ProjectID,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
} else if scope.DomainID != "" {
|
||||||
|
// DomainID provided. ProjectID, ProjectName, and DomainName may not be provided.
|
||||||
|
if scope.DomainName != "" {
|
||||||
|
return nil, ErrScopeDomainIDOrDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DomainID
|
||||||
|
return map[string]interface{}{
|
||||||
|
"domain": map[string]interface{}{
|
||||||
|
"id": &scope.DomainID,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
} else if scope.DomainName != "" {
|
||||||
|
return nil, ErrScopeDomainName{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts AuthOptions) CanReauth() bool {
|
||||||
|
return opts.AllowReauth
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
Package gophercloud provides a multi-vendor interface to OpenStack-compatible
|
||||||
|
clouds. The library has a three-level hierarchy: providers, services, and
|
||||||
|
resources.
|
||||||
|
|
||||||
|
Provider structs represent the service providers that offer and manage a
|
||||||
|
collection of services. Examples of providers include: OpenStack, Rackspace,
|
||||||
|
HP. These are defined like so:
|
||||||
|
|
||||||
|
opts := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: "https://my-openstack.com:5000/v2.0",
|
||||||
|
Username: "{username}",
|
||||||
|
Password: "{password}",
|
||||||
|
TenantID: "{tenant_id}",
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := openstack.AuthenticatedClient(opts)
|
||||||
|
|
||||||
|
Service structs are specific to a provider and handle all of the logic and
|
||||||
|
operations for a particular OpenStack service. Examples of services include:
|
||||||
|
Compute, Object Storage, Block Storage. In order to define one, you need to
|
||||||
|
pass in the parent provider, like so:
|
||||||
|
|
||||||
|
opts := gophercloud.EndpointOpts{Region: "RegionOne"}
|
||||||
|
|
||||||
|
client := openstack.NewComputeV2(provider, opts)
|
||||||
|
|
||||||
|
Resource structs are the domain models that services make use of in order
|
||||||
|
to work with and represent the state of API resources:
|
||||||
|
|
||||||
|
server, err := servers.Get(client, "{serverId}").Extract()
|
||||||
|
|
||||||
|
Intermediate Result structs are returned for API operations, which allow
|
||||||
|
generic access to the HTTP headers, response body, and any errors associated
|
||||||
|
with the network transaction. To turn a result into a usable resource struct,
|
||||||
|
you must call the Extract method which is chained to the response, or an
|
||||||
|
Extract function from an applicable extension:
|
||||||
|
|
||||||
|
result := servers.Get(client, "{serverId}")
|
||||||
|
|
||||||
|
// Attempt to extract the disk configuration from the OS-DCF disk config
|
||||||
|
// extension:
|
||||||
|
config, err := diskconfig.ExtractGet(result)
|
||||||
|
|
||||||
|
All requests that enumerate a collection return a Pager struct that is used to
|
||||||
|
iterate through the results one page at a time. Use the EachPage method on that
|
||||||
|
Pager to handle each successive Page in a closure, then use the appropriate
|
||||||
|
extraction method from that request's package to interpret that Page as a slice
|
||||||
|
of results:
|
||||||
|
|
||||||
|
err := servers.List(client, nil).EachPage(func (page pagination.Page) (bool, error) {
|
||||||
|
s, err := servers.ExtractServers(page)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle the []servers.Server slice.
|
||||||
|
|
||||||
|
// Return "false" or an error to prematurely stop fetching new pages.
|
||||||
|
return true, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
This top-level package contains utility functions and data types that are used
|
||||||
|
throughout the provider and service packages. Of particular note for end users
|
||||||
|
are the AuthOptions and EndpointOpts structs.
|
||||||
|
*/
|
||||||
|
package gophercloud
|
|
@ -0,0 +1,76 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
// Availability indicates to whom a specific service endpoint is accessible:
|
||||||
|
// the internet at large, internal networks only, or only to administrators.
|
||||||
|
// Different identity services use different terminology for these. Identity v2
|
||||||
|
// lists them as different kinds of URLs within the service catalog ("adminURL",
|
||||||
|
// "internalURL", and "publicURL"), while v3 lists them as "Interfaces" in an
|
||||||
|
// endpoint's response.
|
||||||
|
type Availability string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AvailabilityAdmin indicates that an endpoint is only available to
|
||||||
|
// administrators.
|
||||||
|
AvailabilityAdmin Availability = "admin"
|
||||||
|
|
||||||
|
// AvailabilityPublic indicates that an endpoint is available to everyone on
|
||||||
|
// the internet.
|
||||||
|
AvailabilityPublic Availability = "public"
|
||||||
|
|
||||||
|
// AvailabilityInternal indicates that an endpoint is only available within
|
||||||
|
// the cluster's internal network.
|
||||||
|
AvailabilityInternal Availability = "internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// EndpointOpts specifies search criteria used by queries against an
|
||||||
|
// OpenStack service catalog. The options must contain enough information to
|
||||||
|
// unambiguously identify one, and only one, endpoint within the catalog.
|
||||||
|
//
|
||||||
|
// Usually, these are passed to service client factory functions in a provider
|
||||||
|
// package, like "rackspace.NewComputeV2()".
|
||||||
|
type EndpointOpts struct {
|
||||||
|
// Type [required] is the service type for the client (e.g., "compute",
|
||||||
|
// "object-store"). Generally, this will be supplied by the service client
|
||||||
|
// function, but a user-given value will be honored if provided.
|
||||||
|
Type string
|
||||||
|
|
||||||
|
// Name [optional] is the service name for the client (e.g., "nova") as it
|
||||||
|
// appears in the service catalog. Services can have the same Type but a
|
||||||
|
// different Name, which is why both Type and Name are sometimes needed.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// Region [required] is the geographic region in which the endpoint resides,
|
||||||
|
// generally specifying which datacenter should house your resources.
|
||||||
|
// Required only for services that span multiple regions.
|
||||||
|
Region string
|
||||||
|
|
||||||
|
// Availability [optional] is the visibility of the endpoint to be returned.
|
||||||
|
// Valid types include the constants AvailabilityPublic, AvailabilityInternal,
|
||||||
|
// or AvailabilityAdmin from this package.
|
||||||
|
//
|
||||||
|
// Availability is not required, and defaults to AvailabilityPublic. Not all
|
||||||
|
// providers or services offer all Availability options.
|
||||||
|
Availability Availability
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
EndpointLocator is an internal function to be used by provider implementations.
|
||||||
|
|
||||||
|
It provides an implementation that locates a single endpoint from a service
|
||||||
|
catalog for a specific ProviderClient based on user-provided EndpointOpts. The
|
||||||
|
provider then uses it to discover related ServiceClients.
|
||||||
|
*/
|
||||||
|
type EndpointLocator func(EndpointOpts) (string, error)
|
||||||
|
|
||||||
|
// ApplyDefaults is an internal method to be used by provider implementations.
|
||||||
|
//
|
||||||
|
// It sets EndpointOpts fields if not already set, including a default type.
|
||||||
|
// Currently, EndpointOpts.Availability defaults to the public endpoint.
|
||||||
|
func (eo *EndpointOpts) ApplyDefaults(t string) {
|
||||||
|
if eo.Type == "" {
|
||||||
|
eo.Type = t
|
||||||
|
}
|
||||||
|
if eo.Availability == "" {
|
||||||
|
eo.Availability = AvailabilityPublic
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,408 @@
|
||||||
|
package gophercloud
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// BaseError is an error type that all other error types embed.
|
||||||
|
type BaseError struct {
|
||||||
|
DefaultErrString string
|
||||||
|
Info string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e BaseError) Error() string {
|
||||||
|
e.DefaultErrString = "An error occurred while executing a Gophercloud request."
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e BaseError) choseErrString() string {
|
||||||
|
if e.Info != "" {
|
||||||
|
return e.Info
|
||||||
|
}
|
||||||
|
return e.DefaultErrString
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingInput is the error when input is required in a particular
|
||||||
|
// situation but not provided by the user
|
||||||
|
type ErrMissingInput struct {
|
||||||
|
BaseError
|
||||||
|
Argument string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMissingInput) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Missing input for argument [%s]", e.Argument)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrInvalidInput is an error type used for most non-HTTP Gophercloud errors.
|
||||||
|
type ErrInvalidInput struct {
|
||||||
|
ErrMissingInput
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrInvalidInput) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Invalid input provided for argument [%s]: [%+v]", e.Argument, e.Value)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnexpectedResponseCode is returned by the Request method when a response code other than
|
||||||
|
// those listed in OkCodes is encountered.
|
||||||
|
type ErrUnexpectedResponseCode struct {
|
||||||
|
BaseError
|
||||||
|
URL string
|
||||||
|
Method string
|
||||||
|
Expected []int
|
||||||
|
Actual int
|
||||||
|
Body []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrUnexpectedResponseCode) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf(
|
||||||
|
"Expected HTTP response code %v when accessing [%s %s], but got %d instead\n%s",
|
||||||
|
e.Expected, e.Method, e.URL, e.Actual, e.Body,
|
||||||
|
)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault400 is the default error type returned on a 400 HTTP response code.
|
||||||
|
type ErrDefault400 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault401 is the default error type returned on a 401 HTTP response code.
|
||||||
|
type ErrDefault401 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault404 is the default error type returned on a 404 HTTP response code.
|
||||||
|
type ErrDefault404 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault405 is the default error type returned on a 405 HTTP response code.
|
||||||
|
type ErrDefault405 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault408 is the default error type returned on a 408 HTTP response code.
|
||||||
|
type ErrDefault408 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault429 is the default error type returned on a 429 HTTP response code.
|
||||||
|
type ErrDefault429 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault500 is the default error type returned on a 500 HTTP response code.
|
||||||
|
type ErrDefault500 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDefault503 is the default error type returned on a 503 HTTP response code.
|
||||||
|
type ErrDefault503 struct {
|
||||||
|
ErrUnexpectedResponseCode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrDefault400) Error() string {
|
||||||
|
return "Invalid request due to incorrect syntax or missing required parameters."
|
||||||
|
}
|
||||||
|
func (e ErrDefault401) Error() string {
|
||||||
|
return "Authentication failed"
|
||||||
|
}
|
||||||
|
func (e ErrDefault404) Error() string {
|
||||||
|
return "Resource not found"
|
||||||
|
}
|
||||||
|
func (e ErrDefault405) Error() string {
|
||||||
|
return "Method not allowed"
|
||||||
|
}
|
||||||
|
func (e ErrDefault408) Error() string {
|
||||||
|
return "The server timed out waiting for the request"
|
||||||
|
}
|
||||||
|
func (e ErrDefault429) Error() string {
|
||||||
|
return "Too many requests have been sent in a given amount of time. Pause" +
|
||||||
|
" requests, wait up to one minute, and try again."
|
||||||
|
}
|
||||||
|
func (e ErrDefault500) Error() string {
|
||||||
|
return "Internal Server Error"
|
||||||
|
}
|
||||||
|
func (e ErrDefault503) Error() string {
|
||||||
|
return "The service is currently unable to handle the request due to a temporary" +
|
||||||
|
" overloading or maintenance. This is a temporary condition. Try again later."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err400er is the interface resource error types implement to override the error message
|
||||||
|
// from a 400 error.
|
||||||
|
type Err400er interface {
|
||||||
|
Error400(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err401er is the interface resource error types implement to override the error message
|
||||||
|
// from a 401 error.
|
||||||
|
type Err401er interface {
|
||||||
|
Error401(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err404er is the interface resource error types implement to override the error message
|
||||||
|
// from a 404 error.
|
||||||
|
type Err404er interface {
|
||||||
|
Error404(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err405er is the interface resource error types implement to override the error message
|
||||||
|
// from a 405 error.
|
||||||
|
type Err405er interface {
|
||||||
|
Error405(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err408er is the interface resource error types implement to override the error message
|
||||||
|
// from a 408 error.
|
||||||
|
type Err408er interface {
|
||||||
|
Error408(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err429er is the interface resource error types implement to override the error message
|
||||||
|
// from a 429 error.
|
||||||
|
type Err429er interface {
|
||||||
|
Error429(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err500er is the interface resource error types implement to override the error message
|
||||||
|
// from a 500 error.
|
||||||
|
type Err500er interface {
|
||||||
|
Error500(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err503er is the interface resource error types implement to override the error message
|
||||||
|
// from a 503 error.
|
||||||
|
type Err503er interface {
|
||||||
|
Error503(ErrUnexpectedResponseCode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTimeOut is the error type returned when an operations times out.
|
||||||
|
type ErrTimeOut struct {
|
||||||
|
BaseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrTimeOut) Error() string {
|
||||||
|
e.DefaultErrString = "A time out occurred"
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnableToReauthenticate is the error type returned when reauthentication fails.
|
||||||
|
type ErrUnableToReauthenticate struct {
|
||||||
|
BaseError
|
||||||
|
ErrOriginal error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrUnableToReauthenticate) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Unable to re-authenticate: %s", e.ErrOriginal)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrErrorAfterReauthentication is the error type returned when reauthentication
|
||||||
|
// succeeds, but an error occurs afterword (usually an HTTP error).
|
||||||
|
type ErrErrorAfterReauthentication struct {
|
||||||
|
BaseError
|
||||||
|
ErrOriginal error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrErrorAfterReauthentication) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Successfully re-authenticated, but got error executing request: %s", e.ErrOriginal)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrServiceNotFound is returned when no service in a service catalog matches
|
||||||
|
// the provided EndpointOpts. This is generally returned by provider service
|
||||||
|
// factory methods like "NewComputeV2()" and can mean that a service is not
|
||||||
|
// enabled for your account.
|
||||||
|
type ErrServiceNotFound struct {
|
||||||
|
BaseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrServiceNotFound) Error() string {
|
||||||
|
e.DefaultErrString = "No suitable service could be found in the service catalog."
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrEndpointNotFound is returned when no available endpoints match the
|
||||||
|
// provided EndpointOpts. This is also generally returned by provider service
|
||||||
|
// factory methods, and usually indicates that a region was specified
|
||||||
|
// incorrectly.
|
||||||
|
type ErrEndpointNotFound struct {
|
||||||
|
BaseError
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrEndpointNotFound) Error() string {
|
||||||
|
e.DefaultErrString = "No suitable endpoint could be found in the service catalog."
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrResourceNotFound is the error when trying to retrieve a resource's
|
||||||
|
// ID by name and the resource doesn't exist.
|
||||||
|
type ErrResourceNotFound struct {
|
||||||
|
BaseError
|
||||||
|
Name string
|
||||||
|
ResourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrResourceNotFound) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Unable to find %s with name %s", e.ResourceType, e.Name)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMultipleResourcesFound is the error when trying to retrieve a resource's
|
||||||
|
// ID by name and multiple resources have the user-provided name.
|
||||||
|
type ErrMultipleResourcesFound struct {
|
||||||
|
BaseError
|
||||||
|
Name string
|
||||||
|
Count int
|
||||||
|
ResourceType string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrMultipleResourcesFound) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Found %d %ss matching %s", e.Count, e.ResourceType, e.Name)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnexpectedType is the error when an unexpected type is encountered
|
||||||
|
type ErrUnexpectedType struct {
|
||||||
|
BaseError
|
||||||
|
Expected string
|
||||||
|
Actual string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e ErrUnexpectedType) Error() string {
|
||||||
|
e.DefaultErrString = fmt.Sprintf("Expected %s but got %s", e.Expected, e.Actual)
|
||||||
|
return e.choseErrString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func unacceptedAttributeErr(attribute string) string {
|
||||||
|
return fmt.Sprintf("The base Identity V3 API does not accept authentication by %s", attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func redundantWithTokenErr(attribute string) string {
|
||||||
|
return fmt.Sprintf("%s may not be provided when authenticating with a TokenID", attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
func redundantWithUserID(attribute string) string {
|
||||||
|
return fmt.Sprintf("%s may not be provided when authenticating with a UserID", attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrAPIKeyProvided indicates that an APIKey was provided but can't be used.
|
||||||
|
type ErrAPIKeyProvided struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrAPIKeyProvided) Error() string {
|
||||||
|
return unacceptedAttributeErr("APIKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTenantIDProvided indicates that a TenantID was provided but can't be used.
|
||||||
|
type ErrTenantIDProvided struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrTenantIDProvided) Error() string {
|
||||||
|
return unacceptedAttributeErr("TenantID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTenantNameProvided indicates that a TenantName was provided but can't be used.
|
||||||
|
type ErrTenantNameProvided struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrTenantNameProvided) Error() string {
|
||||||
|
return unacceptedAttributeErr("TenantName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUsernameWithToken indicates that a Username was provided, but token authentication is being used instead.
|
||||||
|
type ErrUsernameWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrUsernameWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("Username")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUserIDWithToken indicates that a UserID was provided, but token authentication is being used instead.
|
||||||
|
type ErrUserIDWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrUserIDWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("UserID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainIDWithToken indicates that a DomainID was provided, but token authentication is being used instead.
|
||||||
|
type ErrDomainIDWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainIDWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("DomainID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainNameWithToken indicates that a DomainName was provided, but token authentication is being used instead.s
|
||||||
|
type ErrDomainNameWithToken struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainNameWithToken) Error() string {
|
||||||
|
return redundantWithTokenErr("DomainName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUsernameOrUserID indicates that neither username nor userID are specified, or both are at once.
|
||||||
|
type ErrUsernameOrUserID struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrUsernameOrUserID) Error() string {
|
||||||
|
return "Exactly one of Username and UserID must be provided for password authentication"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainIDWithUserID indicates that a DomainID was provided, but unnecessary because a UserID is being used.
|
||||||
|
type ErrDomainIDWithUserID struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainIDWithUserID) Error() string {
|
||||||
|
return redundantWithUserID("DomainID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainNameWithUserID indicates that a DomainName was provided, but unnecessary because a UserID is being used.
|
||||||
|
type ErrDomainNameWithUserID struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainNameWithUserID) Error() string {
|
||||||
|
return redundantWithUserID("DomainName")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDomainIDOrDomainName indicates that a username was provided, but no domain to scope it.
|
||||||
|
// It may also indicate that both a DomainID and a DomainName were provided at once.
|
||||||
|
type ErrDomainIDOrDomainName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrDomainIDOrDomainName) Error() string {
|
||||||
|
return "You must provide exactly one of DomainID or DomainName to authenticate by Username"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrMissingPassword indicates that no password was provided and no token is available.
|
||||||
|
type ErrMissingPassword struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrMissingPassword) Error() string {
|
||||||
|
return "You must provide a password to authenticate"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeDomainIDOrDomainName indicates that a domain ID or Name was required in a Scope, but not present.
|
||||||
|
type ErrScopeDomainIDOrDomainName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeDomainIDOrDomainName) Error() string {
|
||||||
|
return "You must provide exactly one of DomainID or DomainName in a Scope with ProjectName"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeProjectIDOrProjectName indicates that both a ProjectID and a ProjectName were provided in a Scope.
|
||||||
|
type ErrScopeProjectIDOrProjectName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeProjectIDOrProjectName) Error() string {
|
||||||
|
return "You must provide at most one of ProjectID or ProjectName in a Scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeProjectIDAlone indicates that a ProjectID was provided with other constraints in a Scope.
|
||||||
|
type ErrScopeProjectIDAlone struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeProjectIDAlone) Error() string {
|
||||||
|
return "ProjectID must be supplied alone in a Scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeDomainName indicates that a DomainName was provided alone in a Scope.
|
||||||
|
type ErrScopeDomainName struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeDomainName) Error() string {
|
||||||
|
return "DomainName must be supplied with a ProjectName or ProjectID in a Scope"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrScopeEmpty indicates that no credentials were provided in a Scope.
|
||||||
|
type ErrScopeEmpty struct{ BaseError }
|
||||||
|
|
||||||
|
func (e ErrScopeEmpty) Error() string {
|
||||||
|
return "You must provide either a Project or Domain in a Scope"
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nilOptions = gophercloud.AuthOptions{}
|
||||||
|
|
||||||
|
// AuthOptionsFromEnv fills out an identity.AuthOptions structure with the settings found on the various OpenStack
|
||||||
|
// OS_* environment variables. The following variables provide sources of truth: OS_AUTH_URL, OS_USERNAME,
|
||||||
|
// OS_PASSWORD, OS_TENANT_ID, and OS_TENANT_NAME. Of these, OS_USERNAME, OS_PASSWORD, and OS_AUTH_URL must
|
||||||
|
// have settings, or an error will result. OS_TENANT_ID and OS_TENANT_NAME are optional.
|
||||||
|
func AuthOptionsFromEnv() (gophercloud.AuthOptions, error) {
|
||||||
|
authURL := os.Getenv("OS_AUTH_URL")
|
||||||
|
username := os.Getenv("OS_USERNAME")
|
||||||
|
userID := os.Getenv("OS_USERID")
|
||||||
|
password := os.Getenv("OS_PASSWORD")
|
||||||
|
tenantID := os.Getenv("OS_TENANT_ID")
|
||||||
|
tenantName := os.Getenv("OS_TENANT_NAME")
|
||||||
|
domainID := os.Getenv("OS_DOMAIN_ID")
|
||||||
|
domainName := os.Getenv("OS_DOMAIN_NAME")
|
||||||
|
|
||||||
|
if authURL == "" {
|
||||||
|
err := gophercloud.ErrMissingInput{Argument: "authURL"}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if username == "" && userID == "" {
|
||||||
|
err := gophercloud.ErrMissingInput{Argument: "username"}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if password == "" {
|
||||||
|
err := gophercloud.ErrMissingInput{Argument: "password"}
|
||||||
|
return nilOptions, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ao := gophercloud.AuthOptions{
|
||||||
|
IdentityEndpoint: authURL,
|
||||||
|
UserID: userID,
|
||||||
|
Username: username,
|
||||||
|
Password: password,
|
||||||
|
TenantID: tenantID,
|
||||||
|
TenantName: tenantName,
|
||||||
|
DomainID: domainID,
|
||||||
|
DomainName: domainName,
|
||||||
|
}
|
||||||
|
|
||||||
|
return ao, nil
|
||||||
|
}
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/doc.go
generated
vendored
Normal file
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Package volumes provides information and interaction with volumes in the
|
||||||
|
// OpenStack Block Storage service. A volume is a detachable block storage
|
||||||
|
// device, akin to a USB hard drive. It can only be attached to one instance at
|
||||||
|
// a time.
|
||||||
|
package volumes
|
167
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go
generated
vendored
Normal file
167
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToVolumeCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts contains options for creating a Volume. This object is passed to
|
||||||
|
// the volumes.Create function. For more information about these parameters,
|
||||||
|
// see the Volume object.
|
||||||
|
type CreateOpts struct {
|
||||||
|
Size int `json:"size" required:"true"`
|
||||||
|
Availability string `json:"availability,omitempty"`
|
||||||
|
Description string `json:"display_description,omitempty"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
Name string `json:"display_name,omitempty"`
|
||||||
|
SnapshotID string `json:"snapshot_id,omitempty"`
|
||||||
|
SourceVolID string `json:"source_volid,omitempty"`
|
||||||
|
ImageID string `json:"imageRef,omitempty"`
|
||||||
|
VolumeType string `json:"volume_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeCreateMap assembles a request body based on the contents of a
|
||||||
|
// CreateOpts.
|
||||||
|
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new Volume based on the values in CreateOpts. To extract
|
||||||
|
// the Volume object from the response, call the Extract method on the
|
||||||
|
// CreateResult.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToVolumeCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 201},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will delete the existing Volume with the provided ID.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the Volume with the provided ID. To extract the Volume object
|
||||||
|
// from the response, call the Extract method on the GetResult.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOptsBuilder allows extensions to add additional parameters to the List
|
||||||
|
// request.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToVolumeListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
|
||||||
|
// function.
|
||||||
|
type ListOpts struct {
|
||||||
|
// admin-only option. Set it to true to see all tenant volumes.
|
||||||
|
AllTenants bool `q:"all_tenants"`
|
||||||
|
// List only volumes that contain Metadata.
|
||||||
|
Metadata map[string]string `q:"metadata"`
|
||||||
|
// List only volumes that have Name as the display name.
|
||||||
|
Name string `q:"display_name"`
|
||||||
|
// List only volumes that have a status of Status.
|
||||||
|
Status string `q:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeListQuery formats a ListOpts into a query string.
|
||||||
|
func (opts ListOpts) ToVolumeListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
return q.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns Volumes optionally limited by the conditions provided in ListOpts.
|
||||||
|
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := listURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToVolumeListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return VolumePage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Update request.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToVolumeUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts contain options for updating an existing Volume. This object is passed
|
||||||
|
// to the volumes.Update function. For more information about the parameters, see
|
||||||
|
// the Volume object.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
Name string `json:"display_name,omitempty"`
|
||||||
|
Description string `json:"display_description,omitempty"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeUpdateMap assembles a request body based on the contents of an
|
||||||
|
// UpdateOpts.
|
||||||
|
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will update the Volume with provided information. To extract the updated
|
||||||
|
// Volume from the response, call the Extract method on the UpdateResult.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||||
|
b, err := opts.ToVolumeUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a server's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
count := 0
|
||||||
|
id := ""
|
||||||
|
pages, err := List(client, nil).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err := ExtractVolumes(pages)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range all {
|
||||||
|
if s.Name == name {
|
||||||
|
count++
|
||||||
|
id = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 0:
|
||||||
|
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
|
||||||
|
case 1:
|
||||||
|
return id, nil
|
||||||
|
default:
|
||||||
|
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
|
||||||
|
}
|
||||||
|
}
|
89
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/results.go
generated
vendored
Normal file
89
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/results.go
generated
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Volume contains all the information associated with an OpenStack Volume.
|
||||||
|
type Volume struct {
|
||||||
|
// Current status of the volume.
|
||||||
|
Status string `json:"status"`
|
||||||
|
// Human-readable display name for the volume.
|
||||||
|
Name string `json:"display_name"`
|
||||||
|
// Instances onto which the volume is attached.
|
||||||
|
Attachments []map[string]interface{} `json:"attachments"`
|
||||||
|
// This parameter is no longer used.
|
||||||
|
AvailabilityZone string `json:"availability_zone"`
|
||||||
|
// Indicates whether this is a bootable volume.
|
||||||
|
Bootable string `json:"bootable"`
|
||||||
|
// The date when this volume was created.
|
||||||
|
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
||||||
|
// Human-readable description for the volume.
|
||||||
|
Description string `json:"display_description"`
|
||||||
|
// The type of volume to create, either SATA or SSD.
|
||||||
|
VolumeType string `json:"volume_type"`
|
||||||
|
// The ID of the snapshot from which the volume was created
|
||||||
|
SnapshotID string `json:"snapshot_id"`
|
||||||
|
// The ID of another block storage volume from which the current volume was created
|
||||||
|
SourceVolID string `json:"source_volid"`
|
||||||
|
// Arbitrary key-value pairs defined by the user.
|
||||||
|
Metadata map[string]string `json:"metadata"`
|
||||||
|
// Unique identifier for the volume.
|
||||||
|
ID string `json:"id"`
|
||||||
|
// Size of the volume in GB.
|
||||||
|
Size int `json:"size"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult contains the response body and error from a Create request.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult contains the response body and error from a Get request.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult contains the response body and error from a Delete request.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// VolumePage is a pagination.pager that is returned from a call to the List function.
|
||||||
|
type VolumePage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if a VolumePage contains no Volumes.
|
||||||
|
func (r VolumePage) IsEmpty() (bool, error) {
|
||||||
|
volumes, err := ExtractVolumes(r)
|
||||||
|
return len(volumes) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
|
||||||
|
func ExtractVolumes(r pagination.Page) ([]Volume, error) {
|
||||||
|
var s struct {
|
||||||
|
Volumes []Volume `json:"volumes"`
|
||||||
|
}
|
||||||
|
err := (r.(VolumePage)).ExtractInto(&s)
|
||||||
|
return s.Volumes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult contains the response body and error from an Update request.
|
||||||
|
type UpdateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will get the Volume object out of the commonResult object.
|
||||||
|
func (r commonResult) Extract() (*Volume, error) {
|
||||||
|
var s struct {
|
||||||
|
Volume *Volume `json:"volume"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Volume, err
|
||||||
|
}
|
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/urls.go
generated
vendored
Normal file
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("volumes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return createURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL("volumes", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(c, id)
|
||||||
|
}
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/util.go
generated
vendored
Normal file
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes/util.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitForStatus will continually poll the resource, checking for a particular
|
||||||
|
// status. It will do this for the amount of seconds defined.
|
||||||
|
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
||||||
|
return gophercloud.WaitFor(secs, func() (bool, error) {
|
||||||
|
current, err := Get(c, id).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Status == status {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go
generated
vendored
Normal file
5
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// Package volumes provides information and interaction with volumes in the
|
||||||
|
// OpenStack Block Storage service. A volume is a detachable block storage
|
||||||
|
// device, akin to a USB hard drive. It can only be attached to one instance at
|
||||||
|
// a time.
|
||||||
|
package volumes
|
182
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go
generated
vendored
Normal file
182
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Create request.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToVolumeCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts contains options for creating a Volume. This object is passed to
|
||||||
|
// the volumes.Create function. For more information about these parameters,
|
||||||
|
// see the Volume object.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// The size of the volume, in GB
|
||||||
|
Size int `json:"size" required:"true"`
|
||||||
|
// The availability zone
|
||||||
|
AvailabilityZone string `json:"availability_zone,omitempty"`
|
||||||
|
// ConsistencyGroupID is the ID of a consistency group
|
||||||
|
ConsistencyGroupID string `json:"consistencygroup_id,omitempty"`
|
||||||
|
// The volume description
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
// One or more metadata key and value pairs to associate with the volume
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
// The volume name
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
// the ID of the existing volume snapshot
|
||||||
|
SnapshotID string `json:"snapshot_id,omitempty"`
|
||||||
|
// SourceReplica is a UUID of an existing volume to replicate with
|
||||||
|
SourceReplica string `json:"source_replica,omitempty"`
|
||||||
|
// the ID of the existing volume
|
||||||
|
SourceVolID string `json:"source_volid,omitempty"`
|
||||||
|
// The ID of the image from which you want to create the volume.
|
||||||
|
// Required to create a bootable volume.
|
||||||
|
ImageID string `json:"imageRef,omitempty"`
|
||||||
|
// The associated volume type
|
||||||
|
VolumeType string `json:"volume_type,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeCreateMap assembles a request body based on the contents of a
|
||||||
|
// CreateOpts.
|
||||||
|
func (opts CreateOpts) ToVolumeCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new Volume based on the values in CreateOpts. To extract
|
||||||
|
// the Volume object from the response, call the Extract method on the
|
||||||
|
// CreateResult.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToVolumeCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{202},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will delete the existing Volume with the provided ID.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get retrieves the Volume with the provided ID. To extract the Volume object
|
||||||
|
// from the response, call the Extract method on the GetResult.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOptsBuilder allows extensions to add additional parameters to the List
|
||||||
|
// request.
|
||||||
|
type ListOptsBuilder interface {
|
||||||
|
ToVolumeListQuery() (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListOpts holds options for listing Volumes. It is passed to the volumes.List
|
||||||
|
// function.
|
||||||
|
type ListOpts struct {
|
||||||
|
// admin-only option. Set it to true to see all tenant volumes.
|
||||||
|
AllTenants bool `q:"all_tenants"`
|
||||||
|
// List only volumes that contain Metadata.
|
||||||
|
Metadata map[string]string `q:"metadata"`
|
||||||
|
// List only volumes that have Name as the display name.
|
||||||
|
Name string `q:"name"`
|
||||||
|
// List only volumes that have a status of Status.
|
||||||
|
Status string `q:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeListQuery formats a ListOpts into a query string.
|
||||||
|
func (opts ListOpts) ToVolumeListQuery() (string, error) {
|
||||||
|
q, err := gophercloud.BuildQueryString(opts)
|
||||||
|
return q.String(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns Volumes optionally limited by the conditions provided in ListOpts.
|
||||||
|
func List(client *gophercloud.ServiceClient, opts ListOptsBuilder) pagination.Pager {
|
||||||
|
url := listURL(client)
|
||||||
|
if opts != nil {
|
||||||
|
query, err := opts.ToVolumeListQuery()
|
||||||
|
if err != nil {
|
||||||
|
return pagination.Pager{Err: err}
|
||||||
|
}
|
||||||
|
url += query
|
||||||
|
}
|
||||||
|
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return VolumePage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOptsBuilder allows extensions to add additional parameters to the
|
||||||
|
// Update request.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToVolumeUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts contain options for updating an existing Volume. This object is passed
|
||||||
|
// to the volumes.Update function. For more information about the parameters, see
|
||||||
|
// the Volume object.
|
||||||
|
type UpdateOpts struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Description string `json:"description,omitempty"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToVolumeUpdateMap assembles a request body based on the contents of an
|
||||||
|
// UpdateOpts.
|
||||||
|
func (opts UpdateOpts) ToVolumeUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "volume")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will update the Volume with provided information. To extract the updated
|
||||||
|
// Volume from the response, call the Extract method on the UpdateResult.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||||
|
b, err := opts.ToVolumeUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(updateURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDFromName is a convienience function that returns a server's ID given its name.
|
||||||
|
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
|
||||||
|
count := 0
|
||||||
|
id := ""
|
||||||
|
pages, err := List(client, nil).AllPages()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
all, err := ExtractVolumes(pages)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range all {
|
||||||
|
if s.Name == name {
|
||||||
|
count++
|
||||||
|
id = s.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch count {
|
||||||
|
case 0:
|
||||||
|
return "", gophercloud.ErrResourceNotFound{Name: name, ResourceType: "volume"}
|
||||||
|
case 1:
|
||||||
|
return id, nil
|
||||||
|
default:
|
||||||
|
return "", gophercloud.ErrMultipleResourcesFound{Name: name, Count: count, ResourceType: "volume"}
|
||||||
|
}
|
||||||
|
}
|
121
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go
generated
vendored
Normal file
121
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/results.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Attachment struct {
|
||||||
|
AttachedAt gophercloud.JSONRFC3339MilliNoZ `json:"attached_at"`
|
||||||
|
AttachmentID string `json:"attachment_id"`
|
||||||
|
Device string `json:"device"`
|
||||||
|
HostName string `json:"host_name"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
ServerID string `json:"server_id"`
|
||||||
|
VolumeID string `json:"volume_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Volume contains all the information associated with an OpenStack Volume.
|
||||||
|
type Volume struct {
|
||||||
|
// Unique identifier for the volume.
|
||||||
|
ID string `json:"id"`
|
||||||
|
// Current status of the volume.
|
||||||
|
Status string `json:"status"`
|
||||||
|
// Size of the volume in GB.
|
||||||
|
Size int `json:"size"`
|
||||||
|
// AvailabilityZone is which availability zone the volume is in.
|
||||||
|
AvailabilityZone string `json:"availability_zone"`
|
||||||
|
// The date when this volume was created.
|
||||||
|
CreatedAt gophercloud.JSONRFC3339MilliNoZ `json:"created_at"`
|
||||||
|
// The date when this volume was last updated
|
||||||
|
UpdatedAt gophercloud.JSONRFC3339MilliNoZ `json:"updated_at"`
|
||||||
|
// Instances onto which the volume is attached.
|
||||||
|
Attachments []Attachment `json:"attachments"`
|
||||||
|
// Human-readable display name for the volume.
|
||||||
|
Name string `json:"name"`
|
||||||
|
// Human-readable description for the volume.
|
||||||
|
Description string `json:"description"`
|
||||||
|
// The type of volume to create, either SATA or SSD.
|
||||||
|
VolumeType string `json:"volume_type"`
|
||||||
|
// The ID of the snapshot from which the volume was created
|
||||||
|
SnapshotID string `json:"snapshot_id"`
|
||||||
|
// The ID of another block storage volume from which the current volume was created
|
||||||
|
SourceVolID string `json:"source_volid"`
|
||||||
|
// Arbitrary key-value pairs defined by the user.
|
||||||
|
Metadata map[string]string `json:"metadata"`
|
||||||
|
// UserID is the id of the user who created the volume.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
// Indicates whether this is a bootable volume.
|
||||||
|
Bootable string `json:"bootable"`
|
||||||
|
// Encrypted denotes if the volume is encrypted.
|
||||||
|
Encrypted bool `json:"encrypted"`
|
||||||
|
// ReplicationStatus is the status of replication.
|
||||||
|
ReplicationStatus string `json:"replication_status"`
|
||||||
|
// ConsistencyGroupID is the consistency group ID.
|
||||||
|
ConsistencyGroupID string `json:"consistencygroup_id"`
|
||||||
|
// Multiattach denotes if the volume is multi-attach capable.
|
||||||
|
Multiattach bool `json:"multiattach"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
THESE BELONG IN EXTENSIONS:
|
||||||
|
// ReplicationDriverData contains data about the replication driver.
|
||||||
|
ReplicationDriverData string `json:"os-volume-replication:driver_data"`
|
||||||
|
// ReplicationExtendedStatus contains extended status about replication.
|
||||||
|
ReplicationExtendedStatus string `json:"os-volume-replication:extended_status"`
|
||||||
|
// TenantID is the id of the project that owns the volume.
|
||||||
|
TenantID string `json:"os-vol-tenant-attr:tenant_id"`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// VolumePage is a pagination.pager that is returned from a call to the List function.
|
||||||
|
type VolumePage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty returns true if a ListResult contains no Volumes.
|
||||||
|
func (r VolumePage) IsEmpty() (bool, error) {
|
||||||
|
volumes, err := ExtractVolumes(r)
|
||||||
|
return len(volumes) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractVolumes extracts and returns Volumes. It is used while iterating over a volumes.List call.
|
||||||
|
func ExtractVolumes(r pagination.Page) ([]Volume, error) {
|
||||||
|
var s struct {
|
||||||
|
Volumes []Volume `json:"volumes"`
|
||||||
|
}
|
||||||
|
err := (r.(VolumePage)).ExtractInto(&s)
|
||||||
|
return s.Volumes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will get the Volume object out of the commonResult object.
|
||||||
|
func (r commonResult) Extract() (*Volume, error) {
|
||||||
|
var s struct {
|
||||||
|
Volume *Volume `json:"volume"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Volume, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult contains the response body and error from a Create request.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult contains the response body and error from a Get request.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult contains the response body and error from an Update request.
|
||||||
|
type UpdateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult contains the response body and error from a Delete request.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go
generated
vendored
Normal file
23
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("volumes")
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("volumes", "detail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL("volumes", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return deleteURL(c, id)
|
||||||
|
}
|
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go
generated
vendored
Normal file
22
vendor/github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes/util.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package volumes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
)
|
||||||
|
|
||||||
|
// WaitForStatus will continually poll the resource, checking for a particular
|
||||||
|
// status. It will do this for the amount of seconds defined.
|
||||||
|
func WaitForStatus(c *gophercloud.ServiceClient, id, status string, secs int) error {
|
||||||
|
return gophercloud.WaitFor(secs, func() (bool, error) {
|
||||||
|
current, err := Get(c, id).Extract()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if current.Status == status {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
package openstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
tokens2 "github.com/gophercloud/gophercloud/openstack/identity/v2/tokens"
|
||||||
|
tokens3 "github.com/gophercloud/gophercloud/openstack/identity/v3/tokens"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
v20 = "v2.0"
|
||||||
|
v30 = "v3.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewClient prepares an unauthenticated ProviderClient instance.
|
||||||
|
// Most users will probably prefer using the AuthenticatedClient function instead.
|
||||||
|
// This is useful if you wish to explicitly control the version of the identity service that's used for authentication explicitly,
|
||||||
|
// for example.
|
||||||
|
func NewClient(endpoint string) (*gophercloud.ProviderClient, error) {
|
||||||
|
u, err := url.Parse(endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hadPath := u.Path != ""
|
||||||
|
u.Path, u.RawQuery, u.Fragment = "", "", ""
|
||||||
|
base := u.String()
|
||||||
|
|
||||||
|
endpoint = gophercloud.NormalizeURL(endpoint)
|
||||||
|
base = gophercloud.NormalizeURL(base)
|
||||||
|
|
||||||
|
if hadPath {
|
||||||
|
return &gophercloud.ProviderClient{
|
||||||
|
IdentityBase: base,
|
||||||
|
IdentityEndpoint: endpoint,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ProviderClient{
|
||||||
|
IdentityBase: base,
|
||||||
|
IdentityEndpoint: "",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticatedClient logs in to an OpenStack cloud found at the identity endpoint specified by options, acquires a token, and
|
||||||
|
// returns a Client instance that's ready to operate.
|
||||||
|
// It first queries the root identity endpoint to determine which versions of the identity service are supported, then chooses
|
||||||
|
// the most recent identity service available to proceed.
|
||||||
|
func AuthenticatedClient(options gophercloud.AuthOptions) (*gophercloud.ProviderClient, error) {
|
||||||
|
client, err := NewClient(options.IdentityEndpoint)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = Authenticate(client, options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate or re-authenticate against the most recent identity service supported at the provided endpoint.
|
||||||
|
func Authenticate(client *gophercloud.ProviderClient, options gophercloud.AuthOptions) error {
|
||||||
|
versions := []*utils.Version{
|
||||||
|
{ID: v20, Priority: 20, Suffix: "/v2.0/"},
|
||||||
|
{ID: v30, Priority: 30, Suffix: "/v3/"},
|
||||||
|
}
|
||||||
|
|
||||||
|
chosen, endpoint, err := utils.ChooseVersion(client, versions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch chosen.ID {
|
||||||
|
case v20:
|
||||||
|
return v2auth(client, endpoint, options, gophercloud.EndpointOpts{})
|
||||||
|
case v30:
|
||||||
|
return v3auth(client, endpoint, &options, gophercloud.EndpointOpts{})
|
||||||
|
default:
|
||||||
|
// The switch statement must be out of date from the versions list.
|
||||||
|
return fmt.Errorf("Unrecognized identity version: %s", chosen.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticateV2 explicitly authenticates against the identity v2 endpoint.
|
||||||
|
func AuthenticateV2(client *gophercloud.ProviderClient, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
|
||||||
|
return v2auth(client, "", options, eo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func v2auth(client *gophercloud.ProviderClient, endpoint string, options gophercloud.AuthOptions, eo gophercloud.EndpointOpts) error {
|
||||||
|
v2Client, err := NewIdentityV2(client, eo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint != "" {
|
||||||
|
v2Client.Endpoint = endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
v2Opts := tokens2.AuthOptions{
|
||||||
|
IdentityEndpoint: options.IdentityEndpoint,
|
||||||
|
Username: options.Username,
|
||||||
|
Password: options.Password,
|
||||||
|
TenantID: options.TenantID,
|
||||||
|
TenantName: options.TenantName,
|
||||||
|
AllowReauth: options.AllowReauth,
|
||||||
|
TokenID: options.TokenID,
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tokens2.Create(v2Client, v2Opts)
|
||||||
|
|
||||||
|
token, err := result.ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err := result.ExtractServiceCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if options.AllowReauth {
|
||||||
|
client.ReauthFunc = func() error {
|
||||||
|
client.TokenID = ""
|
||||||
|
return v2auth(client, endpoint, options, eo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.TokenID = token.ID
|
||||||
|
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||||
|
return V2EndpointURL(catalog, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthenticateV3 explicitly authenticates against the identity v3 service.
|
||||||
|
func AuthenticateV3(client *gophercloud.ProviderClient, options tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
|
||||||
|
return v3auth(client, "", options, eo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func v3auth(client *gophercloud.ProviderClient, endpoint string, opts tokens3.AuthOptionsBuilder, eo gophercloud.EndpointOpts) error {
|
||||||
|
// Override the generated service endpoint with the one returned by the version endpoint.
|
||||||
|
v3Client, err := NewIdentityV3(client, eo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoint != "" {
|
||||||
|
v3Client.Endpoint = endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
result := tokens3.Create(v3Client, opts)
|
||||||
|
|
||||||
|
token, err := result.ExtractToken()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
catalog, err := result.ExtractServiceCatalog()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client.TokenID = token.ID
|
||||||
|
|
||||||
|
if opts.CanReauth() {
|
||||||
|
client.ReauthFunc = func() error {
|
||||||
|
client.TokenID = ""
|
||||||
|
return v3auth(client, endpoint, opts, eo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.EndpointLocator = func(opts gophercloud.EndpointOpts) (string, error) {
|
||||||
|
return V3EndpointURL(catalog, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIdentityV2 creates a ServiceClient that may be used to interact with the v2 identity service.
|
||||||
|
func NewIdentityV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
endpoint := client.IdentityBase + "v2.0/"
|
||||||
|
var err error
|
||||||
|
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||||
|
eo.ApplyDefaults("identity")
|
||||||
|
endpoint, err = client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{
|
||||||
|
ProviderClient: client,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIdentityV3 creates a ServiceClient that may be used to access the v3 identity service.
|
||||||
|
func NewIdentityV3(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
endpoint := client.IdentityBase + "v3/"
|
||||||
|
var err error
|
||||||
|
if !reflect.DeepEqual(eo, gophercloud.EndpointOpts{}) {
|
||||||
|
eo.ApplyDefaults("identity")
|
||||||
|
endpoint, err = client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &gophercloud.ServiceClient{
|
||||||
|
ProviderClient: client,
|
||||||
|
Endpoint: endpoint,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewObjectStorageV1 creates a ServiceClient that may be used with the v1 object storage package.
|
||||||
|
func NewObjectStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("object-store")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewComputeV2 creates a ServiceClient that may be used with the v2 compute package.
|
||||||
|
func NewComputeV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("compute")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewNetworkV2 creates a ServiceClient that may be used with the v2 network package.
|
||||||
|
func NewNetworkV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("network")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{
|
||||||
|
ProviderClient: client,
|
||||||
|
Endpoint: url,
|
||||||
|
ResourceBase: url + "v2.0/",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockStorageV1 creates a ServiceClient that may be used to access the v1 block storage service.
|
||||||
|
func NewBlockStorageV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("volume")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBlockStorageV2 creates a ServiceClient that may be used to access the v2 block storage service.
|
||||||
|
func NewBlockStorageV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("volumev2")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSharedFileSystemV2 creates a ServiceClient that may be used to access the v2 shared file system service.
|
||||||
|
func NewSharedFileSystemV2(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("sharev2")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCDNV1 creates a ServiceClient that may be used to access the OpenStack v1
|
||||||
|
// CDN service.
|
||||||
|
func NewCDNV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("cdn")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOrchestrationV1 creates a ServiceClient that may be used to access the v1 orchestration service.
|
||||||
|
func NewOrchestrationV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("orchestration")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDBV1 creates a ServiceClient that may be used to access the v1 DB service.
|
||||||
|
func NewDBV1(client *gophercloud.ProviderClient, eo gophercloud.EndpointOpts) (*gophercloud.ServiceClient, error) {
|
||||||
|
eo.ApplyDefaults("database")
|
||||||
|
url, err := client.EndpointLocator(eo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &gophercloud.ServiceClient{ProviderClient: client, Endpoint: url}, nil
|
||||||
|
}
|
120
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go
generated
vendored
Normal file
120
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
package bootfromvolume
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// DestinationType represents the type of medium being used as the
|
||||||
|
// destination of the bootable device.
|
||||||
|
DestinationType string
|
||||||
|
|
||||||
|
// SourceType represents the type of medium being used as the source of the
|
||||||
|
// bootable device.
|
||||||
|
SourceType string
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DestinationLocal DestinationType is for using an ephemeral disk as the
|
||||||
|
// destination.
|
||||||
|
DestinationLocal DestinationType = "local"
|
||||||
|
|
||||||
|
// DestinationVolume DestinationType is for using a volume as the destination.
|
||||||
|
DestinationVolume DestinationType = "volume"
|
||||||
|
|
||||||
|
// SourceBlank SourceType is for a "blank" or empty source.
|
||||||
|
SourceBlank SourceType = "blank"
|
||||||
|
|
||||||
|
// SourceImage SourceType is for using images as the source of a block device.
|
||||||
|
SourceImage SourceType = "image"
|
||||||
|
|
||||||
|
// SourceSnapshot SourceType is for using a volume snapshot as the source of
|
||||||
|
// a block device.
|
||||||
|
SourceSnapshot SourceType = "snapshot"
|
||||||
|
|
||||||
|
// SourceVolume SourceType is for using a volume as the source of block
|
||||||
|
// device.
|
||||||
|
SourceVolume SourceType = "volume"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BlockDevice is a structure with options for creating block devices in a
|
||||||
|
// server. The block device may be created from an image, snapshot, new volume,
|
||||||
|
// or existing volume. The destination may be a new volume, existing volume
|
||||||
|
// which will be attached to the instance, ephemeral disk, or boot device.
|
||||||
|
type BlockDevice struct {
|
||||||
|
// SourceType must be one of: "volume", "snapshot", "image", or "blank".
|
||||||
|
SourceType SourceType `json:"source_type" required:"true"`
|
||||||
|
|
||||||
|
// UUID is the unique identifier for the existing volume, snapshot, or
|
||||||
|
// image (see above).
|
||||||
|
UUID string `json:"uuid,omitempty"`
|
||||||
|
|
||||||
|
// BootIndex is the boot index. It defaults to 0.
|
||||||
|
BootIndex int `json:"boot_index"`
|
||||||
|
|
||||||
|
// DeleteOnTermination specifies whether or not to delete the attached volume
|
||||||
|
// when the server is deleted. Defaults to `false`.
|
||||||
|
DeleteOnTermination bool `json:"delete_on_termination"`
|
||||||
|
|
||||||
|
// DestinationType is the type that gets created. Possible values are "volume"
|
||||||
|
// and "local".
|
||||||
|
DestinationType DestinationType `json:"destination_type,omitempty"`
|
||||||
|
|
||||||
|
// GuestFormat specifies the format of the block device.
|
||||||
|
GuestFormat string `json:"guest_format,omitempty"`
|
||||||
|
|
||||||
|
// VolumeSize is the size of the volume to create (in gigabytes). This can be
|
||||||
|
// omitted for existing volumes.
|
||||||
|
VolumeSize int `json:"volume_size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsExt is a structure that extends the server `CreateOpts` structure
|
||||||
|
// by allowing for a block device mapping.
|
||||||
|
type CreateOptsExt struct {
|
||||||
|
servers.CreateOptsBuilder
|
||||||
|
BlockDevice []BlockDevice `json:"block_device_mapping_v2,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateMap adds the block device mapping option to the base server
|
||||||
|
// creation options.
|
||||||
|
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.BlockDevice) == 0 {
|
||||||
|
err := gophercloud.ErrMissingInput{}
|
||||||
|
err.Argument = "bootfromvolume.CreateOptsExt.BlockDevice"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
serverMap := base["server"].(map[string]interface{})
|
||||||
|
|
||||||
|
blockDevice := make([]map[string]interface{}, len(opts.BlockDevice))
|
||||||
|
|
||||||
|
for i, bd := range opts.BlockDevice {
|
||||||
|
b, err := gophercloud.BuildRequestBody(bd, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
blockDevice[i] = b
|
||||||
|
}
|
||||||
|
serverMap["block_device_mapping_v2"] = blockDevice
|
||||||
|
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create requests the creation of a server from the given block device mapping.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts servers.CreateOptsBuilder) (r servers.CreateResult) {
|
||||||
|
b, err := opts.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200, 202},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
10
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/results.go
generated
vendored
Normal file
10
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/results.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package bootfromvolume
|
||||||
|
|
||||||
|
import (
|
||||||
|
os "github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateResult temporarily contains the response from a Create call.
|
||||||
|
type CreateResult struct {
|
||||||
|
os.CreateResult
|
||||||
|
}
|
7
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/urls.go
generated
vendored
Normal file
7
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package bootfromvolume
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL("os-volumes_boot")
|
||||||
|
}
|
3
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go
generated
vendored
Normal file
3
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package floatingips provides the ability to manage floating ips through
|
||||||
|
// nova-network
|
||||||
|
package floatingips
|
112
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go
generated
vendored
Normal file
112
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
package floatingips
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// List returns a Pager that allows you to iterate over a collection of FloatingIPs.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
||||||
|
return FloatingIPPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
|
||||||
|
// CreateOpts struct in this package does.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToFloatingIPCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts specifies a Floating IP allocation request
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Pool is the pool of floating IPs to allocate one from
|
||||||
|
Pool string `json:"pool" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloatingIPCreateMap constructs a request body from CreateOpts.
|
||||||
|
func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create requests the creation of a new floating IP
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToFloatingIPCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns data about a previously created FloatingIP.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete requests the deletion of a previous allocated FloatingIP.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssociateOptsBuilder is the interface types must satfisfy to be used as
|
||||||
|
// Associate options
|
||||||
|
type AssociateOptsBuilder interface {
|
||||||
|
ToFloatingIPAssociateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssociateOpts specifies the required information to associate a floating IP with an instance
|
||||||
|
type AssociateOpts struct {
|
||||||
|
// FloatingIP is the floating IP to associate with an instance
|
||||||
|
FloatingIP string `json:"address" required:"true"`
|
||||||
|
// FixedIP is an optional fixed IP address of the server
|
||||||
|
FixedIP string `json:"fixed_address,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloatingIPAssociateMap constructs a request body from AssociateOpts.
|
||||||
|
func (opts AssociateOpts) ToFloatingIPAssociateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "addFloatingIp")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssociateInstance pairs an allocated floating IP with an instance.
|
||||||
|
func AssociateInstance(client *gophercloud.ServiceClient, serverID string, opts AssociateOptsBuilder) (r AssociateResult) {
|
||||||
|
b, err := opts.ToFloatingIPAssociateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(associateURL(client, serverID), b, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisassociateOptsBuilder is the interface types must satfisfy to be used as
|
||||||
|
// Disassociate options
|
||||||
|
type DisassociateOptsBuilder interface {
|
||||||
|
ToFloatingIPDisassociateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisassociateOpts specifies the required information to disassociate a floating IP with an instance
|
||||||
|
type DisassociateOpts struct {
|
||||||
|
FloatingIP string `json:"address" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFloatingIPDisassociateMap constructs a request body from AssociateOpts.
|
||||||
|
func (opts DisassociateOpts) ToFloatingIPDisassociateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "removeFloatingIp")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisassociateInstance decouples an allocated floating IP from an instance
|
||||||
|
func DisassociateInstance(client *gophercloud.ServiceClient, serverID string, opts DisassociateOptsBuilder) (r DisassociateResult) {
|
||||||
|
b, err := opts.ToFloatingIPDisassociateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(disassociateURL(client, serverID), b, nil, nil)
|
||||||
|
return
|
||||||
|
}
|
91
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go
generated
vendored
Normal file
91
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/results.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package floatingips
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A FloatingIP is an IP that can be associated with an instance
|
||||||
|
type FloatingIP struct {
|
||||||
|
// ID is a unique ID of the Floating IP
|
||||||
|
ID string `json:"id"`
|
||||||
|
|
||||||
|
// FixedIP is the IP of the instance related to the Floating IP
|
||||||
|
FixedIP string `json:"fixed_ip,omitempty"`
|
||||||
|
|
||||||
|
// InstanceID is the ID of the instance that is using the Floating IP
|
||||||
|
InstanceID string `json:"instance_id"`
|
||||||
|
|
||||||
|
// IP is the actual Floating IP
|
||||||
|
IP string `json:"ip"`
|
||||||
|
|
||||||
|
// Pool is the pool of floating IPs that this floating IP belongs to
|
||||||
|
Pool string `json:"pool"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatingIPPage stores a single, only page of FloatingIPs
|
||||||
|
// results from a List call.
|
||||||
|
type FloatingIPPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a FloatingIPsPage is empty.
|
||||||
|
func (page FloatingIPPage) IsEmpty() (bool, error) {
|
||||||
|
va, err := ExtractFloatingIPs(page)
|
||||||
|
return len(va) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractFloatingIPs interprets a page of results as a slice of
|
||||||
|
// FloatingIPs.
|
||||||
|
func ExtractFloatingIPs(r pagination.Page) ([]FloatingIP, error) {
|
||||||
|
var s struct {
|
||||||
|
FloatingIPs []FloatingIP `json:"floating_ips"`
|
||||||
|
}
|
||||||
|
err := (r.(FloatingIPPage)).ExtractInto(&s)
|
||||||
|
return s.FloatingIPs, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FloatingIPResult is the raw result from a FloatingIP request.
|
||||||
|
type FloatingIPResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract is a method that attempts to interpret any FloatingIP resource
|
||||||
|
// response as a FloatingIP struct.
|
||||||
|
func (r FloatingIPResult) Extract() (*FloatingIP, error) {
|
||||||
|
var s struct {
|
||||||
|
FloatingIP *FloatingIP `json:"floating_ip"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.FloatingIP, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
|
||||||
|
// as a FloatingIP.
|
||||||
|
type CreateResult struct {
|
||||||
|
FloatingIPResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||||
|
// as a FloatingIP.
|
||||||
|
type GetResult struct {
|
||||||
|
FloatingIPResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
|
||||||
|
// the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssociateResult is the response from a Delete operation. Call its Extract method to determine if
|
||||||
|
// the call succeeded or failed.
|
||||||
|
type AssociateResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisassociateResult is the response from a Delete operation. Call its Extract method to determine if
|
||||||
|
// the call succeeded or failed.
|
||||||
|
type DisassociateResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
37
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go
generated
vendored
Normal file
37
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/floatingips/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package floatingips
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
const resourcePath = "os-floating-ips"
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL(resourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return c.ServiceURL(resourcePath, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(c *gophercloud.ServiceClient, id string) string {
|
||||||
|
return getURL(c, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func serverURL(c *gophercloud.ServiceClient, serverID string) string {
|
||||||
|
return c.ServiceURL("servers/" + serverID + "/action")
|
||||||
|
}
|
||||||
|
|
||||||
|
func associateURL(c *gophercloud.ServiceClient, serverID string) string {
|
||||||
|
return serverURL(c, serverID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disassociateURL(c *gophercloud.ServiceClient, serverID string) string {
|
||||||
|
return serverURL(c, serverID)
|
||||||
|
}
|
3
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go
generated
vendored
Normal file
3
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package keypairs provides information and interaction with the Keypairs
|
||||||
|
// extension for the OpenStack Compute service.
|
||||||
|
package keypairs
|
84
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go
generated
vendored
Normal file
84
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package keypairs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateOptsExt adds a KeyPair option to the base CreateOpts.
|
||||||
|
type CreateOptsExt struct {
|
||||||
|
servers.CreateOptsBuilder
|
||||||
|
KeyName string `json:"key_name,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateMap adds the key_name and, optionally, key_data options to
|
||||||
|
// the base server creation options.
|
||||||
|
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.KeyName == "" {
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
serverMap := base["server"].(map[string]interface{})
|
||||||
|
serverMap["key_name"] = opts.KeyName
|
||||||
|
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// List returns a Pager that allows you to iterate over a collection of KeyPairs.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return pagination.NewPager(client, listURL(client), func(r pagination.PageResult) pagination.Page {
|
||||||
|
return KeyPairPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder describes struct types that can be accepted by the Create call. Notable, the
|
||||||
|
// CreateOpts struct in this package does.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToKeyPairCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts specifies keypair creation or import parameters.
|
||||||
|
type CreateOpts struct {
|
||||||
|
// Name is a friendly name to refer to this KeyPair in other services.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
// PublicKey [optional] is a pregenerated OpenSSH-formatted public key. If provided, this key
|
||||||
|
// will be imported and no new key will be created.
|
||||||
|
PublicKey string `json:"public_key,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToKeyPairCreateMap constructs a request body from CreateOpts.
|
||||||
|
func (opts CreateOpts) ToKeyPairCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "keypair")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create requests the creation of a new keypair on the server, or to import a pre-existing
|
||||||
|
// keypair.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToKeyPairCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(createURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get returns public data about a previously uploaded KeyPair.
|
||||||
|
func Get(client *gophercloud.ServiceClient, name string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(getURL(client, name), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete requests the deletion of a previous stored KeyPair from the server.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, name string) (r DeleteResult) {
|
||||||
|
_, r.Err = client.Delete(deleteURL(client, name), nil)
|
||||||
|
return
|
||||||
|
}
|
86
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go
generated
vendored
Normal file
86
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/results.go
generated
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
package keypairs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyPair is an SSH key known to the OpenStack cluster that is available to be injected into
|
||||||
|
// servers.
|
||||||
|
type KeyPair struct {
|
||||||
|
// Name is used to refer to this keypair from other services within this region.
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
// Fingerprint is a short sequence of bytes that can be used to authenticate or validate a longer
|
||||||
|
// public key.
|
||||||
|
Fingerprint string `json:"fingerprint"`
|
||||||
|
|
||||||
|
// PublicKey is the public key from this pair, in OpenSSH format. "ssh-rsa AAAAB3Nz..."
|
||||||
|
PublicKey string `json:"public_key"`
|
||||||
|
|
||||||
|
// PrivateKey is the private key from this pair, in PEM format.
|
||||||
|
// "-----BEGIN RSA PRIVATE KEY-----\nMIICXA..." It is only present if this keypair was just
|
||||||
|
// returned from a Create call
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
|
||||||
|
// UserID is the user who owns this keypair.
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyPairPage stores a single, only page of KeyPair results from a List call.
|
||||||
|
type KeyPairPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a KeyPairPage is empty.
|
||||||
|
func (page KeyPairPage) IsEmpty() (bool, error) {
|
||||||
|
ks, err := ExtractKeyPairs(page)
|
||||||
|
return len(ks) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractKeyPairs interprets a page of results as a slice of KeyPairs.
|
||||||
|
func ExtractKeyPairs(r pagination.Page) ([]KeyPair, error) {
|
||||||
|
type pair struct {
|
||||||
|
KeyPair KeyPair `json:"keypair"`
|
||||||
|
}
|
||||||
|
var s struct {
|
||||||
|
KeyPairs []pair `json:"keypairs"`
|
||||||
|
}
|
||||||
|
err := (r.(KeyPairPage)).ExtractInto(&s)
|
||||||
|
results := make([]KeyPair, len(s.KeyPairs))
|
||||||
|
for i, pair := range s.KeyPairs {
|
||||||
|
results[i] = pair.KeyPair
|
||||||
|
}
|
||||||
|
return results, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type keyPairResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract is a method that attempts to interpret any KeyPair resource response as a KeyPair struct.
|
||||||
|
func (r keyPairResult) Extract() (*KeyPair, error) {
|
||||||
|
var s struct {
|
||||||
|
KeyPair *KeyPair `json:"keypair"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.KeyPair, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult is the response from a Create operation. Call its Extract method to interpret it
|
||||||
|
// as a KeyPair.
|
||||||
|
type CreateResult struct {
|
||||||
|
keyPairResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult is the response from a Get operation. Call its Extract method to interpret it
|
||||||
|
// as a KeyPair.
|
||||||
|
type GetResult struct {
|
||||||
|
keyPairResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteResult is the response from a Delete operation. Call its Extract method to determine if
|
||||||
|
// the call succeeded or failed.
|
||||||
|
type DeleteResult struct {
|
||||||
|
gophercloud.ErrResult
|
||||||
|
}
|
25
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go
generated
vendored
Normal file
25
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs/urls.go
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package keypairs
|
||||||
|
|
||||||
|
import "github.com/gophercloud/gophercloud"
|
||||||
|
|
||||||
|
const resourcePath = "os-keypairs"
|
||||||
|
|
||||||
|
func resourceURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return c.ServiceURL(resourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createURL(c *gophercloud.ServiceClient) string {
|
||||||
|
return resourceURL(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getURL(c *gophercloud.ServiceClient, name string) string {
|
||||||
|
return c.ServiceURL(resourcePath, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteURL(c *gophercloud.ServiceClient, name string) string {
|
||||||
|
return getURL(c, name)
|
||||||
|
}
|
3
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints/doc.go
generated
vendored
Normal file
3
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints/doc.go
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
// Package schedulerhints enables instances to provide the OpenStack scheduler
|
||||||
|
// hints about where they should be placed in the cloud.
|
||||||
|
package schedulerhints
|
148
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests.go
generated
vendored
Normal file
148
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/schedulerhints/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
package schedulerhints
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SchedulerHints represents a set of scheduling hints that are passed to the
|
||||||
|
// OpenStack scheduler
|
||||||
|
type SchedulerHints struct {
|
||||||
|
// Group specifies a Server Group to place the instance in.
|
||||||
|
Group string
|
||||||
|
// DifferentHost will place the instance on a compute node that does not
|
||||||
|
// host the given instances.
|
||||||
|
DifferentHost []string
|
||||||
|
// SameHost will place the instance on a compute node that hosts the given
|
||||||
|
// instances.
|
||||||
|
SameHost []string
|
||||||
|
// Query is a conditional statement that results in compute nodes able to
|
||||||
|
// host the instance.
|
||||||
|
Query []interface{}
|
||||||
|
// TargetCell specifies a cell name where the instance will be placed.
|
||||||
|
TargetCell string `json:"target_cell,omitempty"`
|
||||||
|
// BuildNearHostIP specifies a subnet of compute nodes to host the instance.
|
||||||
|
BuildNearHostIP string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsBuilder builds the scheduler hints into a serializable format.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToServerSchedulerHintsCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerSchedulerHintsMap builds the scheduler hints into a serializable format.
|
||||||
|
func (opts SchedulerHints) ToServerSchedulerHintsCreateMap() (map[string]interface{}, error) {
|
||||||
|
sh := make(map[string]interface{})
|
||||||
|
|
||||||
|
uuidRegex, _ := regexp.Compile("^[a-z0-9]{8}-[a-z0-9]{4}-[1-5][a-z0-9]{3}-[a-z0-9]{4}-[a-z0-9]{12}$")
|
||||||
|
|
||||||
|
if opts.Group != "" {
|
||||||
|
if !uuidRegex.MatchString(opts.Group) {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "schedulerhints.SchedulerHints.Group"
|
||||||
|
err.Value = opts.Group
|
||||||
|
err.Info = "Group must be a UUID"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sh["group"] = opts.Group
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.DifferentHost) > 0 {
|
||||||
|
for _, diffHost := range opts.DifferentHost {
|
||||||
|
if !uuidRegex.MatchString(diffHost) {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "schedulerhints.SchedulerHints.DifferentHost"
|
||||||
|
err.Value = opts.DifferentHost
|
||||||
|
err.Info = "The hosts must be in UUID format."
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sh["different_host"] = opts.DifferentHost
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(opts.SameHost) > 0 {
|
||||||
|
for _, sameHost := range opts.SameHost {
|
||||||
|
if !uuidRegex.MatchString(sameHost) {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "schedulerhints.SchedulerHints.SameHost"
|
||||||
|
err.Value = opts.SameHost
|
||||||
|
err.Info = "The hosts must be in UUID format."
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sh["same_host"] = opts.SameHost
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Query can be something simple like:
|
||||||
|
[">=", "$free_ram_mb", 1024]
|
||||||
|
|
||||||
|
Or more complex like:
|
||||||
|
['and',
|
||||||
|
['>=', '$free_ram_mb', 1024],
|
||||||
|
['>=', '$free_disk_mb', 200 * 1024]
|
||||||
|
]
|
||||||
|
|
||||||
|
Because of the possible complexity, just make sure the length is a minimum of 3.
|
||||||
|
*/
|
||||||
|
if len(opts.Query) > 0 {
|
||||||
|
if len(opts.Query) < 3 {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "schedulerhints.SchedulerHints.Query"
|
||||||
|
err.Value = opts.Query
|
||||||
|
err.Info = "Must be a conditional statement in the format of [op,variable,value]"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sh["query"] = opts.Query
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.TargetCell != "" {
|
||||||
|
sh["target_cell"] = opts.TargetCell
|
||||||
|
}
|
||||||
|
|
||||||
|
if opts.BuildNearHostIP != "" {
|
||||||
|
if _, _, err := net.ParseCIDR(opts.BuildNearHostIP); err != nil {
|
||||||
|
err := gophercloud.ErrInvalidInput{}
|
||||||
|
err.Argument = "schedulerhints.SchedulerHints.BuildNearHostIP"
|
||||||
|
err.Value = opts.BuildNearHostIP
|
||||||
|
err.Info = "Must be a valid subnet in the form 192.168.1.1/24"
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ipParts := strings.Split(opts.BuildNearHostIP, "/")
|
||||||
|
sh["build_near_host_ip"] = ipParts[0]
|
||||||
|
sh["cidr"] = "/" + ipParts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return sh, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOptsExt adds a SchedulerHints option to the base CreateOpts.
|
||||||
|
type CreateOptsExt struct {
|
||||||
|
servers.CreateOptsBuilder
|
||||||
|
// SchedulerHints provides a set of hints to the scheduler.
|
||||||
|
SchedulerHints CreateOptsBuilder
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToServerCreateMap adds the SchedulerHints option to the base server creation options.
|
||||||
|
func (opts CreateOptsExt) ToServerCreateMap() (map[string]interface{}, error) {
|
||||||
|
base, err := opts.CreateOptsBuilder.ToServerCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
schedulerHints, err := opts.SchedulerHints.ToServerSchedulerHintsCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(schedulerHints) == 0 {
|
||||||
|
return base, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
base["os:scheduler_hints"] = schedulerHints
|
||||||
|
|
||||||
|
return base, nil
|
||||||
|
}
|
1
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/doc.go
generated
vendored
Normal file
1
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/doc.go
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
package secgroups
|
171
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go
generated
vendored
Normal file
171
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/requests.go
generated
vendored
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
package secgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
func commonList(client *gophercloud.ServiceClient, url string) pagination.Pager {
|
||||||
|
return pagination.NewPager(client, url, func(r pagination.PageResult) pagination.Page {
|
||||||
|
return SecurityGroupPage{pagination.SinglePageBase(r)}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// List will return a collection of all the security groups for a particular
|
||||||
|
// tenant.
|
||||||
|
func List(client *gophercloud.ServiceClient) pagination.Pager {
|
||||||
|
return commonList(client, rootURL(client))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListByServer will return a collection of all the security groups which are
|
||||||
|
// associated with a particular server.
|
||||||
|
func ListByServer(client *gophercloud.ServiceClient, serverID string) pagination.Pager {
|
||||||
|
return commonList(client, listByServerURL(client, serverID))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GroupOpts is the underlying struct responsible for creating or updating
|
||||||
|
// security groups. It therefore represents the mutable attributes of a
|
||||||
|
// security group.
|
||||||
|
type GroupOpts struct {
|
||||||
|
// the name of your security group.
|
||||||
|
Name string `json:"name" required:"true"`
|
||||||
|
// the description of your security group.
|
||||||
|
Description string `json:"description" required:"true"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateOpts is the struct responsible for creating a security group.
|
||||||
|
type CreateOpts GroupOpts
|
||||||
|
|
||||||
|
// CreateOptsBuilder builds the create options into a serializable format.
|
||||||
|
type CreateOptsBuilder interface {
|
||||||
|
ToSecGroupCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSecGroupCreateMap builds the create options into a serializable format.
|
||||||
|
func (opts CreateOpts) ToSecGroupCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "security_group")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create will create a new security group.
|
||||||
|
func Create(client *gophercloud.ServiceClient, opts CreateOptsBuilder) (r CreateResult) {
|
||||||
|
b, err := opts.ToSecGroupCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(rootURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOpts is the struct responsible for updating an existing security group.
|
||||||
|
type UpdateOpts GroupOpts
|
||||||
|
|
||||||
|
// UpdateOptsBuilder builds the update options into a serializable format.
|
||||||
|
type UpdateOptsBuilder interface {
|
||||||
|
ToSecGroupUpdateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToSecGroupUpdateMap builds the update options into a serializable format.
|
||||||
|
func (opts UpdateOpts) ToSecGroupUpdateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "security_group")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update will modify the mutable properties of a security group, notably its
|
||||||
|
// name and description.
|
||||||
|
func Update(client *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) (r UpdateResult) {
|
||||||
|
b, err := opts.ToSecGroupUpdateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Put(resourceURL(client, id), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will return details for a particular security group.
|
||||||
|
func Get(client *gophercloud.ServiceClient, id string) (r GetResult) {
|
||||||
|
_, r.Err = client.Get(resourceURL(client, id), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete will permanently delete a security group from the project.
|
||||||
|
func Delete(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
|
||||||
|
_, r.Err = client.Delete(resourceURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRuleOpts represents the configuration for adding a new rule to an
|
||||||
|
// existing security group.
|
||||||
|
type CreateRuleOpts struct {
|
||||||
|
// the ID of the group that this rule will be added to.
|
||||||
|
ParentGroupID string `json:"parent_group_id" required:"true"`
|
||||||
|
// the lower bound of the port range that will be opened.
|
||||||
|
FromPort int `json:"from_port"`
|
||||||
|
// the upper bound of the port range that will be opened.
|
||||||
|
ToPort int `json:"to_port"`
|
||||||
|
// the protocol type that will be allowed, e.g. TCP.
|
||||||
|
IPProtocol string `json:"ip_protocol" required:"true"`
|
||||||
|
// ONLY required if FromGroupID is blank. This represents the IP range that
|
||||||
|
// will be the source of network traffic to your security group. Use
|
||||||
|
// 0.0.0.0/0 to allow all IP addresses.
|
||||||
|
CIDR string `json:"cidr,omitempty" or:"FromGroupID"`
|
||||||
|
// ONLY required if CIDR is blank. This value represents the ID of a group
|
||||||
|
// that forwards traffic to the parent group. So, instead of accepting
|
||||||
|
// network traffic from an entire IP range, you can instead refine the
|
||||||
|
// inbound source by an existing security group.
|
||||||
|
FromGroupID string `json:"group_id,omitempty" or:"CIDR"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRuleOptsBuilder builds the create rule options into a serializable format.
|
||||||
|
type CreateRuleOptsBuilder interface {
|
||||||
|
ToRuleCreateMap() (map[string]interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToRuleCreateMap builds the create rule options into a serializable format.
|
||||||
|
func (opts CreateRuleOpts) ToRuleCreateMap() (map[string]interface{}, error) {
|
||||||
|
return gophercloud.BuildRequestBody(opts, "security_group_rule")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRule will add a new rule to an existing security group (whose ID is
|
||||||
|
// specified in CreateRuleOpts). You have the option of controlling inbound
|
||||||
|
// traffic from either an IP range (CIDR) or from another security group.
|
||||||
|
func CreateRule(client *gophercloud.ServiceClient, opts CreateRuleOptsBuilder) (r CreateRuleResult) {
|
||||||
|
b, err := opts.ToRuleCreateMap()
|
||||||
|
if err != nil {
|
||||||
|
r.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, r.Err = client.Post(rootRuleURL(client), b, &r.Body, &gophercloud.RequestOpts{
|
||||||
|
OkCodes: []int{200},
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteRule will permanently delete a rule from a security group.
|
||||||
|
func DeleteRule(client *gophercloud.ServiceClient, id string) (r gophercloud.ErrResult) {
|
||||||
|
_, r.Err = client.Delete(resourceRuleURL(client, id), nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func actionMap(prefix, groupName string) map[string]map[string]string {
|
||||||
|
return map[string]map[string]string{
|
||||||
|
prefix + "SecurityGroup": map[string]string{"name": groupName},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddServer will associate a server and a security group, enforcing the
|
||||||
|
// rules of the group on the server.
|
||||||
|
func AddServer(client *gophercloud.ServiceClient, serverID, groupName string) (r gophercloud.ErrResult) {
|
||||||
|
_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("add", groupName), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// RemoveServer will disassociate a server from a security group.
|
||||||
|
func RemoveServer(client *gophercloud.ServiceClient, serverID, groupName string) (r gophercloud.ErrResult) {
|
||||||
|
_, r.Err = client.Post(serverActionURL(client, serverID), actionMap("remove", groupName), &r.Body, nil)
|
||||||
|
return
|
||||||
|
}
|
127
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/results.go
generated
vendored
Normal file
127
vendor/github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/secgroups/results.go
generated
vendored
Normal file
|
@ -0,0 +1,127 @@
|
||||||
|
package secgroups
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gophercloud/gophercloud"
|
||||||
|
"github.com/gophercloud/gophercloud/pagination"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecurityGroup represents a security group.
|
||||||
|
type SecurityGroup struct {
|
||||||
|
// The unique ID of the group. If Neutron is installed, this ID will be
|
||||||
|
// represented as a string UUID; if Neutron is not installed, it will be a
|
||||||
|
// numeric ID. For the sake of consistency, we always cast it to a string.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// The human-readable name of the group, which needs to be unique.
|
||||||
|
Name string
|
||||||
|
|
||||||
|
// The human-readable description of the group.
|
||||||
|
Description string
|
||||||
|
|
||||||
|
// The rules which determine how this security group operates.
|
||||||
|
Rules []Rule
|
||||||
|
|
||||||
|
// The ID of the tenant to which this security group belongs.
|
||||||
|
TenantID string `json:"tenant_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rule represents a security group rule, a policy which determines how a
|
||||||
|
// security group operates and what inbound traffic it allows in.
|
||||||
|
type Rule struct {
|
||||||
|
// The unique ID. If Neutron is installed, this ID will be
|
||||||
|
// represented as a string UUID; if Neutron is not installed, it will be a
|
||||||
|
// numeric ID. For the sake of consistency, we always cast it to a string.
|
||||||
|
ID string
|
||||||
|
|
||||||
|
// The lower bound of the port range which this security group should open up
|
||||||
|
FromPort int `json:"from_port"`
|
||||||
|
|
||||||
|
// The upper bound of the port range which this security group should open up
|
||||||
|
ToPort int `json:"to_port"`
|
||||||
|
|
||||||
|
// The IP protocol (e.g. TCP) which the security group accepts
|
||||||
|
IPProtocol string `json:"ip_protocol"`
|
||||||
|
|
||||||
|
// The CIDR IP range whose traffic can be received
|
||||||
|
IPRange IPRange `json:"ip_range"`
|
||||||
|
|
||||||
|
// The security group ID to which this rule belongs
|
||||||
|
ParentGroupID string `json:"parent_group_id"`
|
||||||
|
|
||||||
|
// Not documented.
|
||||||
|
Group Group
|
||||||
|
}
|
||||||
|
|
||||||
|
// IPRange represents the IP range whose traffic will be accepted by the
|
||||||
|
// security group.
|
||||||
|
type IPRange struct {
|
||||||
|
CIDR string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Group represents a group.
|
||||||
|
type Group struct {
|
||||||
|
TenantID string `json:"tenant_id"`
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecurityGroupPage is a single page of a SecurityGroup collection.
|
||||||
|
type SecurityGroupPage struct {
|
||||||
|
pagination.SinglePageBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEmpty determines whether or not a page of Security Groups contains any results.
|
||||||
|
func (page SecurityGroupPage) IsEmpty() (bool, error) {
|
||||||
|
users, err := ExtractSecurityGroups(page)
|
||||||
|
return len(users) == 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractSecurityGroups returns a slice of SecurityGroups contained in a single page of results.
|
||||||
|
func ExtractSecurityGroups(r pagination.Page) ([]SecurityGroup, error) {
|
||||||
|
var s struct {
|
||||||
|
SecurityGroups []SecurityGroup `json:"security_groups"`
|
||||||
|
}
|
||||||
|
err := (r.(SecurityGroupPage)).ExtractInto(&s)
|
||||||
|
return s.SecurityGroups, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type commonResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateResult represents the result of a create operation.
|
||||||
|
type CreateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetResult represents the result of a get operation.
|
||||||
|
type GetResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateResult represents the result of an update operation.
|
||||||
|
type UpdateResult struct {
|
||||||
|
commonResult
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will extract a SecurityGroup struct from most responses.
|
||||||
|
func (r commonResult) Extract() (*SecurityGroup, error) {
|
||||||
|
var s struct {
|
||||||
|
SecurityGroup *SecurityGroup `json:"security_group"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.SecurityGroup, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRuleResult represents the result when adding rules to a security group.
|
||||||
|
type CreateRuleResult struct {
|
||||||
|
gophercloud.Result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract will extract a Rule struct from a CreateRuleResult.
|
||||||
|
func (r CreateRuleResult) Extract() (*Rule, error) {
|
||||||
|
var s struct {
|
||||||
|
Rule *Rule `json:"security_group_rule"`
|
||||||
|
}
|
||||||
|
err := r.ExtractInto(&s)
|
||||||
|
return s.Rule, err
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue