ImageService v2: Fixing Create Properties (#264)

* Fix Creating Image Properties

The combination of JSON tags were not rendering the JSON body
correctly and causing a 400 error by the API.

* ImageService v2 Acceptance Tests

* unit tests
diff --git a/acceptance/clients/clients.go b/acceptance/clients/clients.go
index f07754f..8bf4aa3 100644
--- a/acceptance/clients/clients.go
+++ b/acceptance/clients/clients.go
@@ -266,6 +266,25 @@
 	return openstack.NewIdentityV3(client, gophercloud.EndpointOpts{})
 }
 
+// NewImageServiceV2Client returns a *ServiceClient for making calls to the
+// OpenStack Image v2 API. An error will be returned if authentication or
+// client creation was not possible.
+func NewImageServiceV2Client() (*gophercloud.ServiceClient, error) {
+	ao, err := openstack.AuthOptionsFromEnv()
+	if err != nil {
+		return nil, err
+	}
+
+	client, err := openstack.AuthenticatedClient(ao)
+	if err != nil {
+		return nil, err
+	}
+
+	return openstack.NewImageServiceV2(client, gophercloud.EndpointOpts{
+		Region: os.Getenv("OS_REGION_NAME"),
+	})
+}
+
 // NewNetworkV2Client returns a *ServiceClient for making calls to the
 // OpenStack Networking v2 API. An error will be returned if authentication
 // or client creation was not possible.
diff --git a/acceptance/openstack/imageservice/v2/images_test.go b/acceptance/openstack/imageservice/v2/images_test.go
new file mode 100644
index 0000000..aa0390b
--- /dev/null
+++ b/acceptance/openstack/imageservice/v2/images_test.go
@@ -0,0 +1,26 @@
+// +build acceptance imageservice images
+
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud/acceptance/clients"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+)
+
+func TestImagesCreateDestroyEmptyImage(t *testing.T) {
+	client, err := clients.NewImageServiceV2Client()
+	if err != nil {
+		t.Fatalf("Unable to create an image service client: %v", err)
+	}
+
+	image, err := CreateEmptyImage(t, client)
+	if err != nil {
+		t.Fatalf("Unable to create empty image: %v", err)
+	}
+
+	defer DeleteImage(t, client, image)
+
+	tools.PrintResource(t, image)
+}
diff --git a/acceptance/openstack/imageservice/v2/imageservice.go b/acceptance/openstack/imageservice/v2/imageservice.go
new file mode 100644
index 0000000..8aaeeb7
--- /dev/null
+++ b/acceptance/openstack/imageservice/v2/imageservice.go
@@ -0,0 +1,55 @@
+// Package v2 contains common functions for creating imageservice resources
+// for use in acceptance tests. See the `*_test.go` files for example usages.
+package v2
+
+import (
+	"testing"
+
+	"github.com/gophercloud/gophercloud"
+	"github.com/gophercloud/gophercloud/acceptance/tools"
+	"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
+)
+
+// CreateEmptyImage will create an image, but with no actual image data.
+// An error will be returned if an image was unable to be created.
+func CreateEmptyImage(t *testing.T, client *gophercloud.ServiceClient) (*images.Image, error) {
+	var image *images.Image
+
+	name := tools.RandomString("ACPTTEST", 16)
+	t.Logf("Attempting to create image: %s", name)
+
+	protected := false
+	visibility := images.ImageVisibilityPrivate
+	createOpts := &images.CreateOpts{
+		Name:            name,
+		ContainerFormat: "bare",
+		DiskFormat:      "qcow2",
+		MinDisk:         0,
+		MinRAM:          0,
+		Protected:       &protected,
+		Visibility:      &visibility,
+		Properties: map[string]string{
+			"architecture": "x86_64",
+		},
+	}
+
+	image, err := images.Create(client, createOpts).Extract()
+	if err != nil {
+		return image, err
+	}
+
+	t.Logf("Created image %s: %#v", name, image)
+	return image, nil
+}
+
+// DeleteImage deletes an image.
+// A fatal error will occur if the image failed to delete. This works best when
+// used as a deferred function.
+func DeleteImage(t *testing.T, client *gophercloud.ServiceClient, image *images.Image) {
+	err := images.Delete(client, image.ID).ExtractErr()
+	if err != nil {
+		t.Fatalf("Unable to delete image %s: %v", image.ID, err)
+	}
+
+	t.Logf("Deleted image: %s", image.ID)
+}
diff --git a/openstack/imageservice/v2/images/requests.go b/openstack/imageservice/v2/images/requests.go
index 32f09ee..044b5cb 100644
--- a/openstack/imageservice/v2/images/requests.go
+++ b/openstack/imageservice/v2/images/requests.go
@@ -99,7 +99,7 @@
 
 	// properties is a set of properties, if any, that
 	// are associated with the image.
-	Properties map[string]string `json:"-,omitempty"`
+	Properties map[string]string `json:"-"`
 }
 
 // ToImageCreateMap assembles a request body based on the contents of
diff --git a/openstack/imageservice/v2/images/testing/fixtures.go b/openstack/imageservice/v2/images/testing/fixtures.go
index 1754407..10a87b4 100644
--- a/openstack/imageservice/v2/images/testing/fixtures.go
+++ b/openstack/imageservice/v2/images/testing/fixtures.go
@@ -158,6 +158,7 @@
 		th.TestJSONRequest(t, r, `{
 			"id": "e7db3b45-8db7-47ad-8109-3fb55c2c24fd",
 			"name": "Ubuntu 12.10",
+			"architecture": "x86_64",
 			"tags": [
 				"ubuntu",
 				"quantal"
diff --git a/openstack/imageservice/v2/images/testing/requests_test.go b/openstack/imageservice/v2/images/testing/requests_test.go
index fdd8402..0371e4c 100644
--- a/openstack/imageservice/v2/images/testing/requests_test.go
+++ b/openstack/imageservice/v2/images/testing/requests_test.go
@@ -69,6 +69,9 @@
 	actualImage, err := images.Create(fakeclient.ServiceClient(), images.CreateOpts{
 		ID:   id,
 		Name: name,
+		Properties: map[string]string{
+			"architecture": "x86_64",
+		},
 		Tags: []string{"ubuntu", "quantal"},
 	}).Extract()