Merge pull request #484 from pratikmallya/fix_gc_client

[rfr]Make client return error on JSON decoding error
diff --git a/openstack/networking/v2/extensions/security/groups/requests.go b/openstack/networking/v2/extensions/security/groups/requests.go
index b55fb5d..2712ac1 100644
--- a/openstack/networking/v2/extensions/security/groups/requests.go
+++ b/openstack/networking/v2/extensions/security/groups/requests.go
@@ -45,6 +45,9 @@
 	// Required. Human-readable name for the VIP. Does not have to be unique.
 	Name string
 
+	// Required for admins. Indicates the owner of the VIP.
+	TenantID string
+
 	// Optional. Describes the security group.
 	Description string
 }
@@ -62,6 +65,7 @@
 
 	type secgroup struct {
 		Name        string `json:"name"`
+		TenantID    string `json:"tenant_id,omitempty"`
 		Description string `json:"description,omitempty"`
 	}
 
@@ -71,6 +75,7 @@
 
 	reqBody := request{SecGroup: secgroup{
 		Name:        opts.Name,
+		TenantID:    opts.TenantID,
 		Description: opts.Description,
 	}}
 
diff --git a/openstack/networking/v2/extensions/security/rules/requests.go b/openstack/networking/v2/extensions/security/rules/requests.go
index 0b2d10b..a80ceb3 100644
--- a/openstack/networking/v2/extensions/security/rules/requests.go
+++ b/openstack/networking/v2/extensions/security/rules/requests.go
@@ -99,6 +99,9 @@
 	// attribute matches the specified IP prefix as the source IP address of the
 	// IP packet.
 	RemoteIPPrefix string
+
+	// Required for admins. Indicates the owner of the VIP.
+	TenantID string
 }
 
 // Create is an operation which provisions a new security group with default
@@ -133,6 +136,7 @@
 		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"`
 	}
 
 	type request struct {
@@ -148,6 +152,7 @@
 		Protocol:       opts.Protocol,
 		RemoteGroupID:  opts.RemoteGroupID,
 		RemoteIPPrefix: opts.RemoteIPPrefix,
+		TenantID:       opts.TenantID,
 	}}
 
 	_, res.Err = c.Post(rootURL(c), reqBody, &res.Body, nil)
diff --git a/openstack/networking/v2/subnets/requests.go b/openstack/networking/v2/subnets/requests.go
index 2368bf5..6cde048 100644
--- a/openstack/networking/v2/subnets/requests.go
+++ b/openstack/networking/v2/subnets/requests.go
@@ -202,10 +202,10 @@
 	if opts.GatewayIP != "" {
 		s["gateway_ip"] = opts.GatewayIP
 	}
-	if len(opts.DNSNameservers) != 0 {
+	if opts.DNSNameservers != nil {
 		s["dns_nameservers"] = opts.DNSNameservers
 	}
-	if len(opts.HostRoutes) != 0 {
+	if opts.HostRoutes != nil {
 		s["host_routes"] = opts.HostRoutes
 	}
 
diff --git a/openstack/networking/v2/subnets/results.go b/openstack/networking/v2/subnets/results.go
index 1910f17..77b956a 100644
--- a/openstack/networking/v2/subnets/results.go
+++ b/openstack/networking/v2/subnets/results.go
@@ -55,8 +55,8 @@
 // HostRoute represents a route that should be used by devices with IPs from
 // a subnet (not including local subnet route).
 type HostRoute struct {
-	DestinationCIDR string `json:"destination"`
-	NextHop         string `json:"nexthop"`
+	DestinationCIDR string `mapstructure:"destination" json:"destination"`
+	NextHop         string `mapstructure:"nexthop" json:"nexthop"`
 }
 
 // Subnet represents a subnet. See package documentation for a top-level
diff --git a/openstack/networking/v2/subnets/results_test.go b/openstack/networking/v2/subnets/results_test.go
new file mode 100644
index 0000000..d404838
--- /dev/null
+++ b/openstack/networking/v2/subnets/results_test.go
@@ -0,0 +1,54 @@
+package subnets
+
+import (
+	"encoding/json"
+	"github.com/rackspace/gophercloud"
+	th "github.com/rackspace/gophercloud/testhelper"
+	"testing"
+)
+
+func TestHostRoute(t *testing.T) {
+	sejson := []byte(`
+    {"subnet": {
+      "name": "test-subnet",
+      "enable_dhcp": false,
+      "network_id": "3e66c41e-cbbd-4019-9aab-740b7e4150a0",
+      "tenant_id": "f86e123198cf42d19c8854c5f80c2f06",
+      "dns_nameservers": [],
+      "gateway_ip": "172.16.0.1",
+      "ipv6_ra_mode": null,
+      "allocation_pools": [
+        {
+          "start": "172.16.0.2",
+          "end": "172.16.255.254"
+        }
+      ],
+      "host_routes": [
+        {
+          "destination": "172.20.1.0/24",
+		  "nexthop": "172.16.0.2"
+        }
+      ],
+      "ip_version": 4,
+      "ipv6_address_mode": null,
+      "cidr": "172.16.0.0/16",
+      "id": "6dcaa873-7115-41af-9ef5-915f73636e43",
+      "subnetpool_id": null
+  }}
+`)
+
+	var dejson interface{}
+	err := json.Unmarshal(sejson, &dejson)
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+
+	resp := commonResult{gophercloud.Result{Body: dejson}}
+	subnet, err := resp.Extract()
+	if err != nil {
+		t.Fatalf("%s", err)
+	}
+	route := subnet.HostRoutes[0]
+	th.AssertEquals(t, route.NextHop, "172.16.0.2")
+	th.AssertEquals(t, route.DestinationCIDR, "172.20.1.0/24")
+}
diff --git a/openstack/orchestration/v1/stackevents/fixtures.go b/openstack/orchestration/v1/stackevents/fixtures.go
index 016ae00..235787a 100644
--- a/openstack/orchestration/v1/stackevents/fixtures.go
+++ b/openstack/orchestration/v1/stackevents/fixtures.go
@@ -67,7 +67,7 @@
   "events": [
   {
     "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:11Z",
+    "event_time": "2015-02-05T21:33:11",
     "links": [
     {
       "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
@@ -90,7 +90,7 @@
     },
     {
       "resource_name": "hello_world",
-      "event_time": "2015-02-05T21:33:27Z",
+      "event_time": "2015-02-05T21:33:27",
       "links": [
       {
         "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
@@ -184,7 +184,7 @@
   "events": [
   {
     "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:11Z",
+    "event_time": "2015-02-05T21:33:11",
     "links": [
     {
       "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
@@ -207,7 +207,7 @@
     },
     {
       "resource_name": "hello_world",
-      "event_time": "2015-02-05T21:33:27Z",
+      "event_time": "2015-02-05T21:33:27",
       "links": [
       {
         "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
@@ -309,7 +309,7 @@
   "events": [
   {
     "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:11Z",
+    "event_time": "2015-02-05T21:33:11",
     "links": [
     {
       "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/06feb26f-9298-4a9b-8749-9d770e5d577a",
@@ -332,7 +332,7 @@
     },
     {
       "resource_name": "hello_world",
-      "event_time": "2015-02-05T21:33:27Z",
+      "event_time": "2015-02-05T21:33:27",
       "links": [
       {
         "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
@@ -408,7 +408,7 @@
 {
   "event":{
     "resource_name": "hello_world",
-    "event_time": "2015-02-05T21:33:27Z",
+    "event_time": "2015-02-05T21:33:27",
     "links": [
     {
       "href": "http://166.78.160.107:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/5f57cff9-93fc-424e-9f78-df0515e7f48b/resources/hello_world/events/93940999-7d40-44ae-8de4-19624e7b8d18",
diff --git a/openstack/orchestration/v1/stackevents/results.go b/openstack/orchestration/v1/stackevents/results.go
index 3c8f1da..cf9e240 100644
--- a/openstack/orchestration/v1/stackevents/results.go
+++ b/openstack/orchestration/v1/stackevents/results.go
@@ -57,7 +57,7 @@
 	for i, eventRaw := range events {
 		event := eventRaw.(map[string]interface{})
 		if date, ok := event["event_time"]; ok && date != nil {
-			t, err := time.Parse(time.RFC3339, date.(string))
+			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 			if err != nil {
 				return nil, err
 			}
@@ -121,7 +121,7 @@
 	for i, eventRaw := range events {
 		event := eventRaw.(map[string]interface{})
 		if date, ok := event["event_time"]; ok && date != nil {
-			t, err := time.Parse(time.RFC3339, date.(string))
+			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 			if err != nil {
 				return nil, err
 			}
@@ -161,7 +161,7 @@
 	event := r.Body.(map[string]interface{})["event"].(map[string]interface{})
 
 	if date, ok := event["event_time"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
+		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 		if err != nil {
 			return nil, err
 		}
diff --git a/openstack/orchestration/v1/stackresources/fixtures.go b/openstack/orchestration/v1/stackresources/fixtures.go
index 0b930f4..c3c3d3f 100644
--- a/openstack/orchestration/v1/stackresources/fixtures.go
+++ b/openstack/orchestration/v1/stackresources/fixtures.go
@@ -53,7 +53,7 @@
     ],
     "logical_resource_id": "hello_world",
     "resource_status_reason": "state changed",
-    "updated_time": "2015-02-05T21:33:11Z",
+    "updated_time": "2015-02-05T21:33:11",
     "required_by": [],
     "resource_status": "CREATE_IN_PROGRESS",
     "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
@@ -117,7 +117,7 @@
     ],
     "logical_resource_id": "hello_world",
     "resource_status_reason": "state changed",
-    "updated_time": "2015-02-05T21:33:11Z",
+    "updated_time": "2015-02-05T21:33:11",
     "required_by": [],
     "resource_status": "CREATE_IN_PROGRESS",
     "physical_resource_id": "49181cd6-169a-4130-9455-31185bbfc5bf",
@@ -188,7 +188,7 @@
     ],
     "logical_resource_id": "wordpress_instance",
     "resource_status": "CREATE_COMPLETE",
-    "updated_time": "2014-12-10T18:34:35Z",
+    "updated_time": "2014-12-10T18:34:35",
     "required_by": [],
     "resource_status_reason": "state changed",
     "physical_resource_id": "00e3a2fe-c65d-403c-9483-4db9930dd194",
diff --git a/openstack/orchestration/v1/stackresources/results.go b/openstack/orchestration/v1/stackresources/results.go
index ea84db5..df79d58 100644
--- a/openstack/orchestration/v1/stackresources/results.go
+++ b/openstack/orchestration/v1/stackresources/results.go
@@ -48,7 +48,7 @@
 	for i, resourceRaw := range resources {
 		resource := resourceRaw.(map[string]interface{})
 		if date, ok := resource["updated_time"]; ok && date != nil {
-			t, err := time.Parse(time.RFC3339, date.(string))
+			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 			if err != nil {
 				return nil, err
 			}
@@ -109,7 +109,7 @@
 	for i, resourceRaw := range resources {
 		resource := resourceRaw.(map[string]interface{})
 		if date, ok := resource["updated_time"]; ok && date != nil {
-			t, err := time.Parse(time.RFC3339, date.(string))
+			t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 			if err != nil {
 				return nil, err
 			}
@@ -143,7 +143,7 @@
 	resource := r.Body.(map[string]interface{})["resource"].(map[string]interface{})
 
 	if date, ok := resource["updated_time"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
+		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 		if err != nil {
 			return nil, err
 		}
diff --git a/openstack/orchestration/v1/stacks/fixtures.go b/openstack/orchestration/v1/stacks/fixtures.go
index 6d3e959..3a621da 100644
--- a/openstack/orchestration/v1/stacks/fixtures.go
+++ b/openstack/orchestration/v1/stacks/fixtures.go
@@ -95,7 +95,7 @@
     ],
     "stack_status_reason": "Stack CREATE completed successfully",
     "stack_name": "postman_stack",
-    "creation_time": "2015-02-03T20:07:39Z",
+    "creation_time": "2015-02-03T20:07:39",
     "updated_time": null,
     "stack_status": "CREATE_COMPLETE",
     "id": "16ef0584-4458-41eb-87c8-0dc8d5f66c87"
@@ -110,8 +110,8 @@
     ],
     "stack_status_reason": "Stack successfully updated",
     "stack_name": "gophercloud-test-stack-2",
-    "creation_time": "2014-12-11T17:39:16Z",
-    "updated_time": "2014-12-11T17:40:37Z",
+    "creation_time": "2014-12-11T17:39:16",
+    "updated_time": "2014-12-11T17:40:37",
     "stack_status": "UPDATE_COMPLETE",
     "id": "db6977b2-27aa-4775-9ae7-6213212d4ada"
   }
@@ -181,7 +181,7 @@
     "stack_status_reason": "Stack CREATE completed successfully",
     "stack_name": "postman_stack",
     "outputs": [],
-    "creation_time": "2015-02-03T20:07:39Z",
+    "creation_time": "2015-02-03T20:07:39",
     "links": [
     {
       "href": "http://166.76.160.117:8004/v1/98606384f58d4ad0b3db7d0d779549ac/stacks/postman_stack/16ef0584-4458-41eb-87c8-0dc8d5f66c87",
diff --git a/openstack/orchestration/v1/stacks/results.go b/openstack/orchestration/v1/stacks/results.go
index 04d3f8e..dca06e4 100644
--- a/openstack/orchestration/v1/stacks/results.go
+++ b/openstack/orchestration/v1/stacks/results.go
@@ -100,7 +100,7 @@
 		thisStack := (rawStacks[i]).(map[string]interface{})
 
 		if t, ok := thisStack["creation_time"].(string); ok && t != "" {
-			creationTime, err := time.Parse(time.RFC3339, t)
+			creationTime, err := time.Parse(gophercloud.STACK_TIME_FMT, t)
 			if err != nil {
 				return res.Stacks, err
 			}
@@ -108,7 +108,7 @@
 		}
 
 		if t, ok := thisStack["updated_time"].(string); ok && t != "" {
-			updatedTime, err := time.Parse(time.RFC3339, t)
+			updatedTime, err := time.Parse(gophercloud.STACK_TIME_FMT, t)
 			if err != nil {
 				return res.Stacks, err
 			}
@@ -170,7 +170,7 @@
 	b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
 
 	if date, ok := b["creation_time"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
+		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 		if err != nil {
 			return nil, err
 		}
@@ -178,7 +178,7 @@
 	}
 
 	if date, ok := b["updated_time"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
+		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 		if err != nil {
 			return nil, err
 		}
@@ -249,7 +249,7 @@
 	b := r.Body.(map[string]interface{})["stack"].(map[string]interface{})
 
 	if date, ok := b["creation_time"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
+		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 		if err != nil {
 			return nil, err
 		}
@@ -257,7 +257,7 @@
 	}
 
 	if date, ok := b["updated_time"]; ok && date != nil {
-		t, err := time.Parse(time.RFC3339, date.(string))
+		t, err := time.Parse(gophercloud.STACK_TIME_FMT, date.(string))
 		if err != nil {
 			return nil, err
 		}
diff --git a/results.go b/results.go
index 7c86ce4..27fd1b6 100644
--- a/results.go
+++ b/results.go
@@ -113,6 +113,9 @@
 // RFC3339Milli describes a common time format used by some API responses.
 const RFC3339Milli = "2006-01-02T15:04:05.999999Z"
 
+// Time format used in cloud orchestration
+const STACK_TIME_FMT = "2006-01-02T15:04:05"
+
 /*
 Link is an internal type to be used in packages of collection resources that are
 paginated in a certain way.