error types for networks v2
diff --git a/openstack/networking/v2/extensions/layer3/floatingips/requests.go b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
index 155bdbe..54477b8 100644
--- a/openstack/networking/v2/extensions/layer3/floatingips/requests.go
+++ b/openstack/networking/v2/extensions/layer3/floatingips/requests.go
@@ -1,8 +1,6 @@
package floatingips
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -39,6 +37,12 @@
})
}
+// CreateOptsBuilder is the interface type must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+ ToFloatingIPCreateMap() (map[string]interface{}, error)
+}
+
// CreateOpts contains all the values needed to create a new floating IP
// resource. The only required fields are FloatingNetworkID and PortID which
// refer to the external network and internal port respectively.
@@ -50,9 +54,37 @@
TenantID string
}
-var (
- errFloatingNetworkIDRequired = fmt.Errorf("A NetworkID is required")
-)
+// ToFloatingIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToFloatingIPCreateMap() (map[string]interface{}, error) {
+ if opts.FloatingNetworkID == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "floatingips.ToFloatingIPCreateMap"
+ err.Argument = "floatingips.CreateOpts.FloatingNetworkID"
+ return nil, err
+ }
+
+ i := map[string]string{
+ "floating_network_id": opts.FloatingNetworkID,
+ }
+
+ if opts.FloatingIP != "" {
+ i["floating_ip_address"] = opts.FloatingIP
+ }
+ if opts.PortID != "" {
+ i["port_id"] = opts.PortID
+ }
+ if opts.FixedIP != "" {
+ i["fixed_ip_address"] = opts.FixedIP
+ }
+ if opts.TenantID != "" {
+ i["tenant_id"] = opts.TenantID
+ }
+
+ b := make(map[string]interface{})
+ b["floatingip"] = i
+ return b, nil
+}
// Create accepts a CreateOpts struct and uses the values provided to create a
// new floating IP resource. You can create floating IPs on external networks
@@ -78,38 +110,17 @@
// operation will fail and return a 400 error code. If the PortID and FixedIP
// are already associated with another resource, the operation will fail and
// returns a 409 error code.
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
- var res CreateResult
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+ var r CreateResult
- // Validate
- if opts.FloatingNetworkID == "" {
- res.Err = errFloatingNetworkIDRequired
- return res
+ b, err := opts.ToFloatingIPCreateMap()
+ if err != nil {
+ r.Err = err
+ return r
}
- // Define structures
- type floatingIP struct {
- FloatingNetworkID string `json:"floating_network_id"`
- FloatingIP string `json:"floating_ip_address,omitempty"`
- PortID string `json:"port_id,omitempty"`
- FixedIP string `json:"fixed_ip_address,omitempty"`
- TenantID string `json:"tenant_id,omitempty"`
- }
- type request struct {
- FloatingIP floatingIP `json:"floatingip"`
- }
-
- // Populate request body
- reqBody := request{FloatingIP: floatingIP{
- FloatingNetworkID: opts.FloatingNetworkID,
- FloatingIP: opts.FloatingIP,
- PortID: opts.PortID,
- FixedIP: opts.FixedIP,
- TenantID: opts.TenantID,
- }}
-
- _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
- return res
+ _, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+ return r
}
// Get retrieves a particular floating IP resource based on its unique ID.
diff --git a/openstack/networking/v2/extensions/layer3/routers/requests.go b/openstack/networking/v2/extensions/layer3/routers/requests.go
index 4215889..910760f 100644
--- a/openstack/networking/v2/extensions/layer3/routers/requests.go
+++ b/openstack/networking/v2/extensions/layer3/routers/requests.go
@@ -1,8 +1,6 @@
package routers
import (
- "errors"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -153,7 +151,11 @@
return res
}
-var errInvalidInterfaceOpts = errors.New("When adding a router interface you must provide either a subnet ID or a port ID")
+// InterfaceOptsBuilder is what types must satisfy to be used as AddInterface
+// options.
+type InterfaceOptsBuilder interface {
+ ToInterfaceAddMap() (map[string]interface{}, error)
+}
// InterfaceOpts allow you to work with operations that either add or remote
// an internal interface from a router.
@@ -162,6 +164,29 @@
PortID string
}
+// ToInterfaceAddMap allows InterfaceOpts to satisfy the InterfaceOptsBuilder
+// interface
+func (opts InterfaceOpts) ToInterfaceAddMap() (map[string]interface{}, error) {
+ if (opts.SubnetID == "" && opts.PortID == "") || (opts.SubnetID != "" && opts.PortID != "") {
+ err := gophercloud.ErrInvalidInput{}
+ err.Function = "routers.ToInterfaceAddMap"
+ err.Argument = "routers.InterfaceOpts.SubnetID/routers.InterfaceOpts.PortID"
+ err.Info = "When adding a router interface, you must provide one and only" +
+ " one of a subnet ID or a port ID"
+ return nil, err
+ }
+
+ b := make(map[string]interface{})
+ if opts.SubnetID != "" {
+ b["subnet_id"] = opts.SubnetID
+ }
+ if opts.PortID != "" {
+ b["port_id"] = opts.PortID
+ }
+
+ return b, nil
+}
+
// AddInterface attaches a subnet to an internal router interface. You must
// specify either a SubnetID or PortID in the request body. If you specify both,
// the operation will fail and an error will be returned.
@@ -183,27 +208,28 @@
// identifier of a new port created by this operation. After the operation
// completes, the device ID of the port is set to the router ID, and the
// device owner attribute is set to `network:router_interface'.
-func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOpts) InterfaceResult {
- var res InterfaceResult
+func AddInterface(c *gophercloud.ServiceClient, id string, opts InterfaceOptsBuilder) InterfaceResult {
+ var r InterfaceResult
- // Validate
- if (opts.SubnetID == "" && opts.PortID == "") || (opts.SubnetID != "" && opts.PortID != "") {
- res.Err = errInvalidInterfaceOpts
- return res
+ if id == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "routers.AddInterface"
+ err.Argument = "id"
+ r.Err = err
+ return r
}
- type request struct {
- SubnetID string `json:"subnet_id,omitempty"`
- PortID string `json:"port_id,omitempty"`
+ b, err := opts.ToInterfaceAddMap()
+ if err != nil {
+ r.Err = err
+ return r
}
- body := request{SubnetID: opts.SubnetID, PortID: opts.PortID}
-
- _, res.Err = c.Put(addInterfaceURL(c, id), body, &res.Body, &gophercloud.RequestOpts{
+ _, r.Err = c.Put(addInterfaceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200},
})
- return res
+ return r
}
// RemoveInterface removes an internal router interface, which detaches a
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests.go b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
index 3a967bd..d1a7942 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/requests.go
@@ -56,15 +56,11 @@
TypeHTTPS = "HTTPS"
)
-var (
- errValidTypeRequired = fmt.Errorf("A valid Type is required. Supported values are PING, TCP, HTTP and HTTPS")
- errDelayRequired = fmt.Errorf("Delay is required")
- errTimeoutRequired = fmt.Errorf("Timeout is required")
- errMaxRetriesRequired = fmt.Errorf("MaxRetries is required")
- errURLPathRequired = fmt.Errorf("URL path is required")
- errExpectedCodesRequired = fmt.Errorf("ExpectedCodes is required")
- errDelayMustGETimeout = fmt.Errorf("Delay must be greater than or equal to timeout")
-)
+// CreateOptsBuilder is what types must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+ ToLBMonitorCreateMap() (map[string]interface{}, error)
+}
// CreateOpts contains all the values needed to create a new health monitor.
type CreateOpts struct {
@@ -102,6 +98,94 @@
AdminStateUp *bool
}
+// ToLBMonitorCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToLBMonitorCreateMap() (map[string]interface{}, error) {
+ allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
+ if opts.Type == "" || allowed[opts.Type] == false {
+ err := gophercloud.ErrInvalidInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.Type"
+ err.Info = "Supported values are PING, TCP, HTTP and HTTPS"
+ return nil, err
+ }
+ if opts.Delay == 0 {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.Delay"
+ return nil, err
+ }
+ if opts.Timeout == 0 {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.Timeout"
+ return nil, err
+ }
+ if opts.MaxRetries == 0 {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.MaxRetries"
+ return nil, err
+ }
+
+ if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
+ if opts.URLPath == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.URLPath"
+ return nil, err
+ }
+ if opts.ExpectedCodes == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.ExpectedCodes"
+ return nil, err
+ }
+ }
+ if opts.Delay < opts.Timeout {
+ err := gophercloud.ErrInvalidInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout"
+ err.Info = "Delay must be greater than or equal to timeout"
+ return nil, err
+ }
+
+ i := map[string]interface{}{
+ "type": opts.Type,
+ "delay": opts.Delay,
+ "timeout": opts.Timeout,
+ }
+
+ if opts.MaxRetries != 0 {
+ i["max_retries"] = opts.MaxRetries
+ }
+
+ if opts.TenantID != "" {
+ i["tenant_id"] = opts.TenantID
+ }
+
+ if opts.URLPath != "" {
+ i["url_path"] = opts.URLPath
+ }
+
+ if opts.ExpectedCodes != "" {
+ i["expected_codes"] = opts.ExpectedCodes
+ }
+
+ if opts.HTTPMethod != "" {
+ i["http_method"] = opts.HTTPMethod
+ }
+
+ if opts.AdminStateUp != nil {
+ i["admin_state_up"] = &opts.AdminStateUp
+ }
+
+ b := make(map[string]interface{})
+ b["health_monitor"] = i
+
+ return b, nil
+}
+
// Create is an operation which provisions a new health monitor. There are
// different types of monitor you can provision: PING, TCP or HTTP(S). Below
// are examples of how to create each one.
@@ -116,68 +200,17 @@
// CreateOpts{Type: TypeHTTP, Delay: 20, Timeout: 10, MaxRetries: 3,
// HttpMethod: "HEAD", ExpectedCodes: "200"}
//
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
- var res CreateResult
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+ var r CreateResult
- // Validate inputs
- allowed := map[string]bool{TypeHTTP: true, TypeHTTPS: true, TypeTCP: true, TypePING: true}
- if opts.Type == "" || allowed[opts.Type] == false {
- res.Err = errValidTypeRequired
- }
- if opts.Delay == 0 {
- res.Err = errDelayRequired
- }
- if opts.Timeout == 0 {
- res.Err = errTimeoutRequired
- }
- if opts.MaxRetries == 0 {
- res.Err = errMaxRetriesRequired
- }
- if opts.Type == TypeHTTP || opts.Type == TypeHTTPS {
- if opts.URLPath == "" {
- res.Err = errURLPathRequired
- }
- if opts.ExpectedCodes == "" {
- res.Err = errExpectedCodesRequired
- }
- }
- if opts.Delay < opts.Timeout {
- res.Err = errDelayMustGETimeout
- }
- if res.Err != nil {
- return res
+ b, err := opts.ToLBMonitorCreateMap()
+ if err != nil {
+ r.Err = err
+ return r
}
- type monitor struct {
- Type string `json:"type"`
- Delay int `json:"delay"`
- Timeout int `json:"timeout"`
- MaxRetries int `json:"max_retries"`
- TenantID *string `json:"tenant_id,omitempty"`
- URLPath *string `json:"url_path,omitempty"`
- ExpectedCodes *string `json:"expected_codes,omitempty"`
- HTTPMethod *string `json:"http_method,omitempty"`
- AdminStateUp *bool `json:"admin_state_up,omitempty"`
- }
-
- type request struct {
- Monitor monitor `json:"health_monitor"`
- }
-
- reqBody := request{Monitor: monitor{
- Type: opts.Type,
- Delay: opts.Delay,
- Timeout: opts.Timeout,
- MaxRetries: opts.MaxRetries,
- TenantID: gophercloud.MaybeString(opts.TenantID),
- URLPath: gophercloud.MaybeString(opts.URLPath),
- ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
- HTTPMethod: gophercloud.MaybeString(opts.HTTPMethod),
- AdminStateUp: opts.AdminStateUp,
- }}
-
- _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
- return res
+ _, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+ return r
}
// Get retrieves a particular health monitor based on its unique ID.
@@ -187,6 +220,12 @@
return res
}
+// UpdateOptsBuilder is what types must satisfy to be used as Update
+// options.
+type UpdateOptsBuilder interface {
+ ToLBMonitorUpdateMap() (map[string]interface{}, error)
+}
+
// UpdateOpts contains all the values needed to update an existing virtual IP.
// Attributes not listed here but appear in CreateOpts are immutable and cannot
// be updated.
@@ -218,43 +257,71 @@
AdminStateUp *bool
}
-// Update is an operation which modifies the attributes of the specified monitor.
-func Update(c *gophercloud.ServiceClient, id string, opts UpdateOpts) UpdateResult {
- var res UpdateResult
-
+// ToLBMonitorUpdateMap allows UpdateOpts to satisfy the UpdateOptsBuilder
+// interface
+func (opts UpdateOpts) ToLBMonitorUpdateMap() (map[string]interface{}, error) {
if opts.Delay > 0 && opts.Timeout > 0 && opts.Delay < opts.Timeout {
- res.Err = errDelayMustGETimeout
+ err := gophercloud.ErrInvalidInput{}
+ err.Function = "monitors.ToLBMonitorCreateMap"
+ err.Argument = "monitors.CreateOpts.Delay/monitors.CreateOpts.Timeout"
+ err.Value = fmt.Sprintf("%d/%d", opts.Delay, opts.Timeout)
+ err.Info = "Delay must be greater than or equal to timeout"
+ return nil, err
}
- type monitor struct {
- Delay int `json:"delay"`
- Timeout int `json:"timeout"`
- MaxRetries int `json:"max_retries"`
- URLPath *string `json:"url_path,omitempty"`
- ExpectedCodes *string `json:"expected_codes,omitempty"`
- HTTPMethod *string `json:"http_method,omitempty"`
- AdminStateUp *bool `json:"admin_state_up,omitempty"`
+ i := map[string]interface{}{
+ "delay": opts.Delay,
+ "timeout": opts.Timeout,
+ "max_retries": opts.MaxRetries,
}
- type request struct {
- Monitor monitor `json:"health_monitor"`
+ if opts.URLPath != "" {
+ i["url_path"] = opts.URLPath
}
- reqBody := request{Monitor: monitor{
- Delay: opts.Delay,
- Timeout: opts.Timeout,
- MaxRetries: opts.MaxRetries,
- URLPath: gophercloud.MaybeString(opts.URLPath),
- ExpectedCodes: gophercloud.MaybeString(opts.ExpectedCodes),
- HTTPMethod: gophercloud.MaybeString(opts.HTTPMethod),
- AdminStateUp: opts.AdminStateUp,
- }}
+ if opts.ExpectedCodes != "" {
+ i["expected_codes"] = opts.ExpectedCodes
+ }
- _, res.Err = c.Put(resourceURL(c, id), reqBody, &res.Body, &gophercloud.RequestOpts{
+ if opts.HTTPMethod != "" {
+ i["http_method"] = opts.HTTPMethod
+ }
+
+ if opts.AdminStateUp != nil {
+ i["admin_state_up"] = *opts.AdminStateUp
+ }
+
+ b := make(map[string]interface{})
+ b["health_monitor"] = i
+
+ fmt.Printf("b: %+v\n", b)
+
+ return b, nil
+}
+
+// Update is an operation which modifies the attributes of the specified monitor.
+func Update(c *gophercloud.ServiceClient, id string, opts UpdateOptsBuilder) UpdateResult {
+ var r UpdateResult
+
+ if id == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "monitors.Update"
+ err.Argument = "id"
+ r.Err = err
+ return r
+ }
+
+ b, err := opts.ToLBMonitorUpdateMap()
+ if err != nil {
+ r.Err = err
+ return r
+ }
+
+ _, r.Err = c.Put(resourceURL(c, id), b, &r.Body, &gophercloud.RequestOpts{
OkCodes: []int{200, 202},
})
- return res
+ return r
}
// Delete will permanently delete a particular monitor based on its unique ID.
diff --git a/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go b/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
index d4ee6e9..6f1a976 100644
--- a/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
+++ b/openstack/networking/v2/extensions/lbaas/monitors/requests_test.go
@@ -8,6 +8,7 @@
fake "github.com/gophercloud/gophercloud/openstack/networking/v2/common"
"github.com/gophercloud/gophercloud/pagination"
th "github.com/gophercloud/gophercloud/testhelper"
+ "github.com/jrperritt/gophercloud"
)
func TestURLs(t *testing.T) {
@@ -251,11 +252,12 @@
th.TestJSONRequest(t, r, `
{
"health_monitor":{
- "delay": 3,
+ "delay": 30,
"timeout": 20,
"max_retries": 10,
"url_path": "/another_check",
- "expected_codes": "301"
+ "expected_codes": "301",
+ "admin_state_up": true
}
}
`)
@@ -268,7 +270,7 @@
"health_monitor": {
"admin_state_up": true,
"tenant_id": "4fd44f30292945e481c7b8a0c8908869",
- "delay": 3,
+ "delay": 30,
"max_retries": 10,
"http_method": "GET",
"timeout": 20,
@@ -287,11 +289,12 @@
})
_, err := Update(fake.ServiceClient(), "b05e44b5-81f9-4551-b474-711a722698f7", UpdateOpts{
- Delay: 3,
+ Delay: 30,
Timeout: 20,
MaxRetries: 10,
URLPath: "/another_check",
ExpectedCodes: "301",
+ AdminStateUp: gophercloud.Enabled,
}).Extract()
th.AssertNoErr(t, err)
diff --git a/openstack/networking/v2/extensions/lbaas/vips/requests.go b/openstack/networking/v2/extensions/lbaas/vips/requests.go
index 18b2ee7..0f99fe8 100644
--- a/openstack/networking/v2/extensions/lbaas/vips/requests.go
+++ b/openstack/networking/v2/extensions/lbaas/vips/requests.go
@@ -1,8 +1,6 @@
package vips
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -60,13 +58,11 @@
})
}
-var (
- errNameRequired = fmt.Errorf("Name is required")
- errSubnetIDRequried = fmt.Errorf("SubnetID is required")
- errProtocolRequired = fmt.Errorf("Protocol is required")
- errProtocolPortRequired = fmt.Errorf("Protocol port is required")
- errPoolIDRequired = fmt.Errorf("PoolID is required")
-)
+// CreateOptsBuilder is what types must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+ ToVIPCreateMap() (map[string]interface{}, error)
+}
// CreateOpts contains all the values needed to create a new virtual IP.
type CreateOpts struct {
@@ -107,6 +103,72 @@
AdminStateUp *bool
}
+// ToVIPCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToVIPCreateMap() (map[string]interface{}, error) {
+ if opts.Name == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "vips.ToVIPCreateMap"
+ err.Argument = "vips.CreateOpts.Name"
+ return nil, err
+ }
+ if opts.SubnetID == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "vips.ToVIPCreateMap"
+ err.Argument = "vips.CreateOpts.SubnetID"
+ return nil, err
+ }
+ if opts.Protocol == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "vips.ToVIPCreateMap"
+ err.Argument = "vips.CreateOpts.Protocol"
+ return nil, err
+ }
+ if opts.ProtocolPort == 0 {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "vips.ToVIPCreateMap"
+ err.Argument = "vips.CreateOpts.ProtocolPort"
+ return nil, err
+ }
+ if opts.PoolID == "" {
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "vips.ToVIPCreateMap"
+ err.Argument = "vips.CreateOpts.PoolID"
+ return nil, err
+ }
+
+ i := map[string]interface{}{
+ "name": opts.Name,
+ "subnet_id": opts.SubnetID,
+ "protocol": opts.Protocol,
+ "protocol_port": opts.ProtocolPort,
+ "pool_id": opts.PoolID,
+ }
+ if opts.Description != "" {
+ i["description"] = opts.Description
+ }
+ if opts.TenantID != "" {
+ i["tenant_id"] = opts.TenantID
+ }
+ if opts.Address != "" {
+ i["address"] = opts.Address
+ }
+ if opts.Persistence != nil {
+ i["session_persistence"] = opts.Persistence
+ }
+ if opts.ConnLimit != nil {
+ i["connection_limit"] = opts.ConnLimit
+ }
+ if opts.AdminStateUp != nil {
+ i["admin_state_up"] = opts.AdminStateUp
+ }
+
+ b := make(map[string]interface{})
+ b["vip"] = i
+
+ return b, nil
+}
+
// Create is an operation which provisions a new virtual IP based on the
// configuration defined in the CreateOpts struct. Once the request is
// validated and progress has started on the provisioning process, a
@@ -119,67 +181,16 @@
// Users with an admin role can create VIPs on behalf of other tenants by
// specifying a TenantID attribute different than their own.
func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
- var res CreateResult
+ var r CreateResult
- // Validate required opts
- if opts.Name == "" {
- res.Err = errNameRequired
- return res
- }
- if opts.SubnetID == "" {
- res.Err = errSubnetIDRequried
- return res
- }
- if opts.Protocol == "" {
- res.Err = errProtocolRequired
- return res
- }
- if opts.ProtocolPort == 0 {
- res.Err = errProtocolPortRequired
- return res
- }
- if opts.PoolID == "" {
- res.Err = errPoolIDRequired
- return res
+ b, err := opts.ToVIPCreateMap()
+ if err != nil {
+ r.Err = err
+ return r
}
- type vip struct {
- Name string `json:"name"`
- SubnetID string `json:"subnet_id"`
- Protocol string `json:"protocol"`
- ProtocolPort int `json:"protocol_port"`
- PoolID string `json:"pool_id"`
- Description *string `json:"description,omitempty"`
- TenantID *string `json:"tenant_id,omitempty"`
- Address *string `json:"address,omitempty"`
- Persistence *SessionPersistence `json:"session_persistence,omitempty"`
- ConnLimit *int `json:"connection_limit,omitempty"`
- AdminStateUp *bool `json:"admin_state_up,omitempty"`
- }
-
- type request struct {
- VirtualIP vip `json:"vip"`
- }
-
- reqBody := request{VirtualIP: vip{
- Name: opts.Name,
- SubnetID: opts.SubnetID,
- Protocol: opts.Protocol,
- ProtocolPort: opts.ProtocolPort,
- PoolID: opts.PoolID,
- Description: gophercloud.MaybeString(opts.Description),
- TenantID: gophercloud.MaybeString(opts.TenantID),
- Address: gophercloud.MaybeString(opts.Address),
- ConnLimit: opts.ConnLimit,
- AdminStateUp: opts.AdminStateUp,
- }}
-
- if opts.Persistence != nil {
- reqBody.VirtualIP.Persistence = opts.Persistence
- }
-
- _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
- return res
+ _, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+ return r
}
// Get retrieves a particular virtual IP based on its unique ID.
diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go
index f9e252c..9adce66 100644
--- a/openstack/networking/v2/extensions/security/groups/requests.go
+++ b/openstack/networking/v2/extensions/security/groups/requests.go
@@ -1,8 +1,6 @@
package groups
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -36,10 +34,6 @@
})
}
-var (
- errNameRequired = fmt.Errorf("Name is required")
-)
-
// CreateOpts contains all the values needed to create a new security group.
type CreateOpts struct {
// Required. Human-readable name for the VIP. Does not have to be unique.
@@ -59,7 +53,10 @@
// Validate required opts
if opts.Name == "" {
- res.Err = errNameRequired
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "groups.Create"
+ err.Argument = "groups.CreateOpts.Name"
+ res.Err = err
return res
}
@@ -99,33 +96,47 @@
// IDFromName is a convenience function that returns a security group's ID given its name.
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
- securityGroupCount := 0
- securityGroupID := ""
+ count := 0
+ id := ""
if name == "" {
- return "", fmt.Errorf("A security group name must be provided.")
+ err := &gophercloud.ErrMissingInput{}
+ err.Function = "groups.IDFromName"
+ err.Argument = "name"
+ return "", err
}
- pager := List(client, ListOpts{})
- pager.EachPage(func(page pagination.Page) (bool, error) {
- securityGroupList, err := ExtractGroups(page)
- if err != nil {
- return false, err
- }
- for _, s := range securityGroupList {
- if s.Name == name {
- securityGroupCount++
- securityGroupID = s.ID
- }
- }
- return true, nil
- })
+ pages, err := List(client, ListOpts{}).AllPages()
+ if err != nil {
+ return "", err
+ }
- switch securityGroupCount {
+ all, err := ExtractGroups(pages)
+ if err != nil {
+ return "", err
+ }
+
+ for _, s := range all {
+ if s.Name == name {
+ count++
+ id = s.ID
+ }
+ }
+
+ switch count {
case 0:
- return "", fmt.Errorf("Unable to find security group: %s", name)
+ err := &gophercloud.ErrResourceNotFound{}
+ err.Name = name
+ err.ResourceType = "group"
+ err.Function = "groups.IDFromName"
+ return "", err
case 1:
- return securityGroupID, nil
+ return id, nil
default:
- return "", fmt.Errorf("Found %d security groups matching %s", securityGroupCount, name)
+ err := &gophercloud.ErrMultipleResourcesFound{}
+ err.Count = count
+ err.Name = name
+ err.ResourceType = "group"
+ err.Function = "groups.IDFromName"
+ return "", err
}
}
diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go
index 07083c2..2da6227 100644
--- a/openstack/networking/v2/extensions/security/rules/requests.go
+++ b/openstack/networking/v2/extensions/security/rules/requests.go
@@ -1,8 +1,6 @@
package rules
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -43,14 +41,6 @@
})
}
-// Errors
-var (
- errValidDirectionRequired = fmt.Errorf("A valid Direction is required")
- errValidEtherTypeRequired = fmt.Errorf("A valid EtherType is required")
- errSecGroupIDRequired = fmt.Errorf("A valid SecGroupID is required")
- errValidProtocolRequired = fmt.Errorf("A valid Protocol is required")
-)
-
// Constants useful for CreateOpts
const (
DirIngress = "ingress"
@@ -62,6 +52,12 @@
ProtocolICMP = "icmp"
)
+// CreateOptsBuilder is what types must satisfy to be used as Create
+// options.
+type CreateOptsBuilder interface {
+ ToSecGroupRuleCreateMap() (map[string]interface{}, error)
+}
+
// CreateOpts contains all the values needed to create a new security group rule.
type CreateOpts struct {
// Required. Must be either "ingress" or "egress": the direction in which the
@@ -104,59 +100,75 @@
TenantID string
}
-// Create is an operation which adds a new security group rule and associates it
-// with an existing security group (whose ID is specified in CreateOpts).
-func Create(c *gophercloud.ServiceClient, opts CreateOpts) CreateResult {
- var res CreateResult
-
- // Validate required opts
+// ToSecGroupRuleCreateMap allows CreateOpts to satisfy the CreateOptsBuilder
+// interface
+func (opts CreateOpts) ToSecGroupRuleCreateMap() (map[string]interface{}, error) {
if opts.Direction != DirIngress && opts.Direction != DirEgress {
- res.Err = errValidDirectionRequired
- return res
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "rules.ToSecGroupRuleCreateMap"
+ err.Argument = "rules.CreateOpts.Direction"
+ return nil, err
}
if opts.EtherType != Ether4 && opts.EtherType != Ether6 {
- res.Err = errValidEtherTypeRequired
- return res
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "rules.ToSecGroupRuleCreateMap"
+ err.Argument = "rules.CreateOpts.EtherType"
+ return nil, err
}
if opts.SecGroupID == "" {
- res.Err = errSecGroupIDRequired
- return res
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "rules.ToSecGroupRuleCreateMap"
+ err.Argument = "rules.CreateOpts.SecGroupID"
+ return nil, err
}
if opts.Protocol != "" && opts.Protocol != ProtocolTCP && opts.Protocol != ProtocolUDP && opts.Protocol != ProtocolICMP {
- res.Err = errValidProtocolRequired
- return res
+ err := gophercloud.ErrMissingInput{}
+ err.Function = "rules.ToSecGroupRuleCreateMap"
+ err.Argument = "rules.CreateOpts.Protocol"
+ return nil, err
}
- type secrule struct {
- Direction string `json:"direction"`
- EtherType string `json:"ethertype"`
- SecGroupID string `json:"security_group_id"`
- PortRangeMax int `json:"port_range_max,omitempty"`
- PortRangeMin int `json:"port_range_min,omitempty"`
- Protocol string `json:"protocol,omitempty"`
- RemoteGroupID string `json:"remote_group_id,omitempty"`
- RemoteIPPrefix string `json:"remote_ip_prefix,omitempty"`
- TenantID string `json:"tenant_id,omitempty"`
+ i := map[string]interface{}{
+ "direction": opts.Direction,
+ "ethertype": opts.EtherType,
+ "security_group_id": opts.SecGroupID,
+ }
+ if opts.PortRangeMax != 0 {
+ i["port_range_max"] = opts.PortRangeMax
+ }
+ if opts.PortRangeMin != 0 {
+ i["port_range_min"] = opts.PortRangeMin
+ }
+ if opts.Protocol != "" {
+ i["protocol"] = opts.Protocol
+ }
+ if opts.RemoteGroupID != "" {
+ i["remote_group_id"] = opts.RemoteGroupID
+ }
+ if opts.RemoteIPPrefix != "" {
+ i["remote_ip_prefix"] = opts.RemoteIPPrefix
+ }
+ if opts.TenantID != "" {
+ i["tenant_id"] = opts.TenantID
}
- type request struct {
- SecRule secrule `json:"security_group_rule"`
+ b := make(map[string]interface{})
+ b["security_group_rule"] = i
+ return b, nil
+}
+
+// Create is an operation which adds a new security group rule and associates it
+// with an existing security group (whose ID is specified in CreateOpts).
+func Create(c *gophercloud.ServiceClient, opts CreateOptsBuilder) CreateResult {
+ var r CreateResult
+
+ b, err := opts.ToSecGroupRuleCreateMap()
+ if err != nil {
+ r.Err = err
+ return r
}
-
- reqBody := request{SecRule: secrule{
- Direction: opts.Direction,
- EtherType: opts.EtherType,
- SecGroupID: opts.SecGroupID,
- PortRangeMax: opts.PortRangeMax,
- PortRangeMin: opts.PortRangeMin,
- Protocol: opts.Protocol,
- RemoteGroupID: opts.RemoteGroupID,
- RemoteIPPrefix: opts.RemoteIPPrefix,
- TenantID: opts.TenantID,
- }}
-
- _, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
- return res
+ _, r.Err = c.Post(rootURL(c), b, &r.Body, nil)
+ return r
}
// Get retrieves a particular security group rule based on its unique ID.
diff --git a/openstack/networking/v2/networks/requests.go b/openstack/networking/v2/networks/requests.go
index 1cb1878..03fac14 100644
--- a/openstack/networking/v2/networks/requests.go
+++ b/openstack/networking/v2/networks/requests.go
@@ -1,8 +1,6 @@
package networks
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -194,33 +192,47 @@
// IDFromName is a convenience function that returns a network's ID given its name.
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
- networkCount := 0
- networkID := ""
+ count := 0
+ id := ""
if name == "" {
- return "", fmt.Errorf("A network name must be provided.")
+ err := &gophercloud.ErrMissingInput{}
+ err.Function = "networks.IDFromName"
+ err.Argument = "name"
+ return "", err
}
- pager := List(client, nil)
- pager.EachPage(func(page pagination.Page) (bool, error) {
- networkList, err := ExtractNetworks(page)
- if err != nil {
- return false, err
- }
- for _, n := range networkList {
- if n.Name == name {
- networkCount++
- networkID = n.ID
- }
- }
- return true, nil
- })
+ pages, err := List(client, nil).AllPages()
+ if err != nil {
+ return "", err
+ }
- switch networkCount {
+ all, err := ExtractNetworks(pages)
+ if err != nil {
+ return "", err
+ }
+
+ for _, s := range all {
+ if s.Name == name {
+ count++
+ id = s.ID
+ }
+ }
+
+ switch count {
case 0:
- return "", fmt.Errorf("Unable to find network: %s", name)
+ err := &gophercloud.ErrResourceNotFound{}
+ err.Name = name
+ err.ResourceType = "network"
+ err.Function = "networks.IDFromName"
+ return "", err
case 1:
- return networkID, nil
+ return id, nil
default:
- return "", fmt.Errorf("Found %d networks matching %s", networkCount, name)
+ err := &gophercloud.ErrMultipleResourcesFound{}
+ err.Count = count
+ err.Name = name
+ err.ResourceType = "network"
+ err.Function = "networks.IDFromName"
+ return "", err
}
}
diff --git a/openstack/networking/v2/ports/requests.go b/openstack/networking/v2/ports/requests.go
index 4730882..d719f93 100644
--- a/openstack/networking/v2/ports/requests.go
+++ b/openstack/networking/v2/ports/requests.go
@@ -1,8 +1,6 @@
package ports
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -236,33 +234,47 @@
// IDFromName is a convenience function that returns a port's ID given its name.
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
- portCount := 0
- portID := ""
+ count := 0
+ id := ""
if name == "" {
- return "", fmt.Errorf("A port name must be provided.")
+ err := &gophercloud.ErrMissingInput{}
+ err.Function = "ports.IDFromName"
+ err.Argument = "name"
+ return "", err
}
- pager := List(client, nil)
- pager.EachPage(func(page pagination.Page) (bool, error) {
- portList, err := ExtractPorts(page)
- if err != nil {
- return false, err
- }
- for _, p := range portList {
- if p.Name == name {
- portCount++
- portID = p.ID
- }
- }
- return true, nil
- })
+ pages, err := List(client, nil).AllPages()
+ if err != nil {
+ return "", err
+ }
- switch portCount {
+ all, err := ExtractPorts(pages)
+ if err != nil {
+ return "", err
+ }
+
+ for _, s := range all {
+ if s.Name == name {
+ count++
+ id = s.ID
+ }
+ }
+
+ switch count {
case 0:
- return "", fmt.Errorf("Unable to find port: %s", name)
+ err := &gophercloud.ErrResourceNotFound{}
+ err.Name = name
+ err.ResourceType = "port"
+ err.Function = "ports.IDFromName"
+ return "", err
case 1:
- return portID, nil
+ return id, nil
default:
- return "", fmt.Errorf("Found %d ports matching %s", portCount, name)
+ err := &gophercloud.ErrMultipleResourcesFound{}
+ err.Count = count
+ err.Name = name
+ err.ResourceType = "port"
+ err.Function = "ports.IDFromName"
+ return "", err
}
}
diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go
index 78fa9e2..e55a034 100644
--- a/openstack/networking/v2/subnets/requests.go
+++ b/openstack/networking/v2/subnets/requests.go
@@ -1,8 +1,6 @@
package subnets
import (
- "fmt"
-
"github.com/gophercloud/gophercloud"
"github.com/gophercloud/gophercloud/pagination"
)
@@ -239,33 +237,47 @@
// IDFromName is a convenience function that returns a subnet's ID given its name.
func IDFromName(client *gophercloud.ServiceClient, name string) (string, error) {
- subnetCount := 0
- subnetID := ""
+ count := 0
+ id := ""
if name == "" {
- return "", fmt.Errorf("A subnet name must be provided.")
+ err := &gophercloud.ErrMissingInput{}
+ err.Function = "subnets.IDFromName"
+ err.Argument = "name"
+ return "", err
}
- pager := List(client, nil)
- pager.EachPage(func(page pagination.Page) (bool, error) {
- subnetList, err := ExtractSubnets(page)
- if err != nil {
- return false, err
- }
- for _, s := range subnetList {
- if s.Name == name {
- subnetCount++
- subnetID = s.ID
- }
- }
- return true, nil
- })
+ pages, err := List(client, nil).AllPages()
+ if err != nil {
+ return "", err
+ }
- switch subnetCount {
+ all, err := ExtractSubnets(pages)
+ if err != nil {
+ return "", err
+ }
+
+ for _, s := range all {
+ if s.Name == name {
+ count++
+ id = s.ID
+ }
+ }
+
+ switch count {
case 0:
- return "", fmt.Errorf("Unable to find subnet: %s", name)
+ err := &gophercloud.ErrResourceNotFound{}
+ err.Name = name
+ err.ResourceType = "subnet"
+ err.Function = "subnets.IDFromName"
+ return "", err
case 1:
- return subnetID, nil
+ return id, nil
default:
- return "", fmt.Errorf("Found %d subnets matching %s", subnetCount, name)
+ err := &gophercloud.ErrMultipleResourcesFound{}
+ err.Count = count
+ err.Name = name
+ err.ResourceType = "subnet"
+ err.Function = "subnets.IDFromName"
+ return "", err
}
}