rackspace neutron sec groups ops & unit tests
diff --git a/rackspace/networking/v2/security/rules/delegate.go b/rackspace/networking/v2/security/rules/delegate.go
new file mode 100644
index 0000000..23b4b31
--- /dev/null
+++ b/rackspace/networking/v2/security/rules/delegate.go
@@ -0,0 +1,30 @@
+package rules
+
+import (
+ "github.com/rackspace/gophercloud"
+ os "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
+ "github.com/rackspace/gophercloud/pagination"
+)
+
+// List returns a Pager which allows you to iterate over a collection of
+// security group rules. It accepts a ListOpts struct, which allows you to filter
+// and sort the returned collection for greater efficiency.
+func List(c *gophercloud.ServiceClient, opts os.ListOpts) pagination.Pager {
+ return os.List(c, opts)
+}
+
+// Create is an operation which provisions a new security group with default
+// security group rules for the IPv4 and IPv6 ether types.
+func Create(c *gophercloud.ServiceClient, opts os.CreateOpts) os.CreateResult {
+ return os.Create(c, opts)
+}
+
+// Get retrieves a particular security group based on its unique ID.
+func Get(c *gophercloud.ServiceClient, id string) os.GetResult {
+ return os.Get(c, id)
+}
+
+// Delete will permanently delete a particular security group based on its unique ID.
+func Delete(c *gophercloud.ServiceClient, id string) os.DeleteResult {
+ return os.Delete(c, id)
+}
diff --git a/rackspace/networking/v2/security/rules/delegate_test.go b/rackspace/networking/v2/security/rules/delegate_test.go
new file mode 100644
index 0000000..3563fbe
--- /dev/null
+++ b/rackspace/networking/v2/security/rules/delegate_test.go
@@ -0,0 +1,236 @@
+package rules
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ fake "github.com/rackspace/gophercloud/openstack/networking/v2/common"
+ osRules "github.com/rackspace/gophercloud/openstack/networking/v2/extensions/security/rules"
+ "github.com/rackspace/gophercloud/pagination"
+ th "github.com/rackspace/gophercloud/testhelper"
+)
+
+func TestList(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+ {
+ "security_group_rules": [
+ {
+ "direction": "egress",
+ "ethertype": "IPv6",
+ "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+ "port_range_max": null,
+ "port_range_min": null,
+ "protocol": null,
+ "remote_group_id": null,
+ "remote_ip_prefix": null,
+ "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+ "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+ },
+ {
+ "direction": "egress",
+ "ethertype": "IPv4",
+ "id": "93aa42e5-80db-4581-9391-3a608bd0e448",
+ "port_range_max": null,
+ "port_range_min": null,
+ "protocol": null,
+ "remote_group_id": null,
+ "remote_ip_prefix": null,
+ "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+ "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+ }
+ ]
+ }
+ `)
+ })
+
+ count := 0
+
+ List(fake.ServiceClient(), osRules.ListOpts{}).EachPage(func(page pagination.Page) (bool, error) {
+ count++
+ actual, err := osRules.ExtractRules(page)
+ if err != nil {
+ t.Errorf("Failed to extract secrules: %v", err)
+ return false, err
+ }
+
+ expected := []osRules.SecGroupRule{
+ osRules.SecGroupRule{
+ Direction: "egress",
+ EtherType: "IPv6",
+ ID: "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+ PortRangeMax: 0,
+ PortRangeMin: 0,
+ Protocol: "",
+ RemoteGroupID: "",
+ RemoteIPPrefix: "",
+ SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
+ TenantID: "e4f50856753b4dc6afee5fa6b9b6c550",
+ },
+ osRules.SecGroupRule{
+ Direction: "egress",
+ EtherType: "IPv4",
+ ID: "93aa42e5-80db-4581-9391-3a608bd0e448",
+ PortRangeMax: 0,
+ PortRangeMin: 0,
+ Protocol: "",
+ RemoteGroupID: "",
+ RemoteIPPrefix: "",
+ SecGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
+ TenantID: "e4f50856753b4dc6afee5fa6b9b6c550",
+ },
+ }
+
+ th.CheckDeepEquals(t, expected, actual)
+
+ return true, nil
+ })
+
+ if count != 1 {
+ t.Errorf("Expected 1 page, got %d", count)
+ }
+}
+
+func TestCreate(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/security-group-rules", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "POST")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+ th.TestHeader(t, r, "Content-Type", "application/json")
+ th.TestHeader(t, r, "Accept", "application/json")
+ th.TestJSONRequest(t, r, `
+ {
+ "security_group_rule": {
+ "direction": "ingress",
+ "port_range_min": 80,
+ "ethertype": "IPv4",
+ "port_range_max": 80,
+ "protocol": "tcp",
+ "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+ "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a"
+ }
+ }
+ `)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusCreated)
+
+ fmt.Fprintf(w, `
+ {
+ "security_group_rule": {
+ "direction": "ingress",
+ "ethertype": "IPv4",
+ "id": "2bc0accf-312e-429a-956e-e4407625eb62",
+ "port_range_max": 80,
+ "port_range_min": 80,
+ "protocol": "tcp",
+ "remote_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+ "remote_ip_prefix": null,
+ "security_group_id": "a7734e61-b545-452d-a3cd-0189cbd9747a",
+ "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+ }
+ }
+ `)
+ })
+
+ opts := osRules.CreateOpts{
+ Direction: "ingress",
+ PortRangeMin: 80,
+ EtherType: "IPv4",
+ PortRangeMax: 80,
+ Protocol: "tcp",
+ RemoteGroupID: "85cc3048-abc3-43cc-89b3-377341426ac5",
+ SecGroupID: "a7734e61-b545-452d-a3cd-0189cbd9747a",
+ }
+ _, err := Create(fake.ServiceClient(), opts).Extract()
+ th.AssertNoErr(t, err)
+}
+
+func TestRequiredCreateOpts(t *testing.T) {
+ res := Create(fake.ServiceClient(), osRules.CreateOpts{Direction: "something"})
+ if res.Err == nil {
+ t.Fatalf("Expected error, got none")
+ }
+ res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: "something"})
+ if res.Err == nil {
+ t.Fatalf("Expected error, got none")
+ }
+ res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: osRules.Ether4})
+ if res.Err == nil {
+ t.Fatalf("Expected error, got none")
+ }
+ res = Create(fake.ServiceClient(), osRules.CreateOpts{Direction: osRules.DirIngress, EtherType: osRules.Ether4, SecGroupID: "something", Protocol: "foo"})
+ if res.Err == nil {
+ t.Fatalf("Expected error, got none")
+ }
+}
+
+func TestGet(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/security-group-rules/3c0e45ff-adaf-4124-b083-bf390e5482ff", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "GET")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+
+ w.Header().Add("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+
+ fmt.Fprintf(w, `
+ {
+ "security_group_rule": {
+ "direction": "egress",
+ "ethertype": "IPv6",
+ "id": "3c0e45ff-adaf-4124-b083-bf390e5482ff",
+ "port_range_max": null,
+ "port_range_min": null,
+ "protocol": null,
+ "remote_group_id": null,
+ "remote_ip_prefix": null,
+ "security_group_id": "85cc3048-abc3-43cc-89b3-377341426ac5",
+ "tenant_id": "e4f50856753b4dc6afee5fa6b9b6c550"
+ }
+ }
+ `)
+ })
+
+ sr, err := Get(fake.ServiceClient(), "3c0e45ff-adaf-4124-b083-bf390e5482ff").Extract()
+ th.AssertNoErr(t, err)
+
+ th.AssertEquals(t, "egress", sr.Direction)
+ th.AssertEquals(t, "IPv6", sr.EtherType)
+ th.AssertEquals(t, "3c0e45ff-adaf-4124-b083-bf390e5482ff", sr.ID)
+ th.AssertEquals(t, 0, sr.PortRangeMax)
+ th.AssertEquals(t, 0, sr.PortRangeMin)
+ th.AssertEquals(t, "", sr.Protocol)
+ th.AssertEquals(t, "", sr.RemoteGroupID)
+ th.AssertEquals(t, "", sr.RemoteIPPrefix)
+ th.AssertEquals(t, "85cc3048-abc3-43cc-89b3-377341426ac5", sr.SecGroupID)
+ th.AssertEquals(t, "e4f50856753b4dc6afee5fa6b9b6c550", sr.TenantID)
+}
+
+func TestDelete(t *testing.T) {
+ th.SetupHTTP()
+ defer th.TeardownHTTP()
+
+ th.Mux.HandleFunc("/v2.0/security-group-rules/4ec89087-d057-4e2c-911f-60a3b47ee304", func(w http.ResponseWriter, r *http.Request) {
+ th.TestMethod(t, r, "DELETE")
+ th.TestHeader(t, r, "X-Auth-Token", fake.TokenID)
+ w.WriteHeader(http.StatusNoContent)
+ })
+
+ res := Delete(fake.ServiceClient(), "4ec89087-d057-4e2c-911f-60a3b47ee304")
+ th.AssertNoErr(t, res.Err)
+}