rename directory from 'storage' to 'objectStorage'; add fix for handling 'text/html' content-type response from 'ListNames'
diff --git a/openstack/objectStorage/v1/containers/doc.go b/openstack/objectStorage/v1/containers/doc.go
new file mode 100644
index 0000000..9b6ac17
--- /dev/null
+++ b/openstack/objectStorage/v1/containers/doc.go
@@ -0,0 +1,5 @@
+/* The containers package defines operations performed on an object-storage container.
+
+Reference: http://developer.openstack.org/api-ref-objectstorage-v1.html#storage_container_services
+*/
+package containers
diff --git a/openstack/objectStorage/v1/containers/requests.go b/openstack/objectStorage/v1/containers/requests.go
new file mode 100644
index 0000000..013c6ff
--- /dev/null
+++ b/openstack/objectStorage/v1/containers/requests.go
@@ -0,0 +1,146 @@
+package containers
+
+import (
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+)
+
+// ListOpts is a structure that holds options for listing containers.
+type ListOpts struct {
+	Full      bool
+	Limit     int    `q:"limit"`
+	Marker    string `q:"marker"`
+	EndMarker string `q:"end_marker"`
+	Format    string `q:"format"`
+	Prefix    string `q:"prefix"`
+	Delimiter []byte `q:"delimiter"`
+}
+
+// List is a function that retrieves all objects in a container. It also returns the details
+// for the account. To extract just the container information or names, pass the ListResult
+// response to the ExtractInfo or ExtractNames function, respectively.
+func List(c *gophercloud.ServiceClient, opts ListOpts) pagination.Pager {
+	var headers map[string]string
+	query, err := gophercloud.BuildQueryString(opts)
+	if err != nil {
+		return pagination.Pager{Err: err}
+	}
+
+	if !opts.Full {
+		headers = map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"}
+	}
+
+	createPage := func(r pagination.LastHTTPResponse) pagination.Page {
+		p := ContainerPage{pagination.MarkerPageBase{LastHTTPResponse: r}}
+		p.MarkerPageBase.Owner = p
+		return p
+	}
+
+	url := accountURL(c) + query
+	pager := pagination.NewPager(c, url, createPage)
+	pager.Headers = headers
+	return pager
+}
+
+// CreateOpts is a structure that holds parameters for creating a container.
+type CreateOpts struct {
+	Metadata          map[string]string
+	ContainerRead     string `h:"X-Container-Read"`
+	ContainerSyncTo   string `h:"X-Container-Sync-To"`
+	ContainerSyncKey  string `h:"X-Container-Sync-Key"`
+	ContainerWrite    string `h:"X-Container-Write"`
+	ContentType       string `h:"Content-Type"`
+	DetectContentType bool   `h:"X-Detect-Content-Type"`
+	IfNoneMatch       string `h:"If-None-Match"`
+	VersionsLocation  string `h:"X-Versions-Location"`
+}
+
+// Create is a function that creates a new container.
+func Create(c *gophercloud.ServiceClient, containerName string, opts CreateOpts) (Container, error) {
+	var container Container
+	h := c.Provider.AuthenticatedHeaders()
+
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return container, err
+	}
+
+	for k, v := range headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+
+	_, err = perigee.Request("PUT", containerURL(c, containerName), perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{201, 204},
+	})
+	if err == nil {
+		container = Container{Name: containerName}
+	}
+	return container, err
+}
+
+// Delete is a function that deletes a container.
+func Delete(c *gophercloud.ServiceClient, containerName string) error {
+	_, err := perigee.Request("DELETE", containerURL(c, containerName), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	return err
+}
+
+// UpdateOpts is a structure that holds parameters for updating, creating, or deleting a
+// container's metadata.
+type UpdateOpts struct {
+	Metadata               map[string]string
+	ContainerRead          string `h:"X-Container-Read"`
+	ContainerSyncTo        string `h:"X-Container-Sync-To"`
+	ContainerSyncKey       string `h:"X-Container-Sync-Key"`
+	ContainerWrite         string `h:"X-Container-Write"`
+	ContentType            string `h:"Content-Type"`
+	DetectContentType      bool   `h:"X-Detect-Content-Type"`
+	RemoveVersionsLocation string `h:"X-Remove-Versions-Location"`
+	VersionsLocation       string `h:"X-Versions-Location"`
+}
+
+// Update is a function that creates, updates, or deletes a container's metadata.
+func Update(c *gophercloud.ServiceClient, containerName string, opts UpdateOpts) error {
+	h := c.Provider.AuthenticatedHeaders()
+
+	headers, err := gophercloud.BuildHeaders(opts)
+	if err != nil {
+		return err
+	}
+
+	for k, v := range headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+
+	url := containerURL(c, containerName)
+	_, err = perigee.Request("POST", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return err
+}
+
+// Get is a function that retrieves the metadata of a container. To extract just the custom
+// metadata, pass the GetResult response to the ExtractMetadata function.
+func Get(c *gophercloud.ServiceClient, containerName string) GetResult {
+	var gr GetResult
+	resp, err := perigee.Request("HEAD", containerURL(c, containerName), perigee.Options{
+		MoreHeaders: c.Provider.AuthenticatedHeaders(),
+		OkCodes:     []int{204},
+	})
+	gr.Err = err
+	gr.Resp = &resp.HttpResponse
+	return gr
+}
diff --git a/openstack/objectStorage/v1/containers/requests_test.go b/openstack/objectStorage/v1/containers/requests_test.go
new file mode 100644
index 0000000..1c9ee11
--- /dev/null
+++ b/openstack/objectStorage/v1/containers/requests_test.go
@@ -0,0 +1,198 @@
+package containers
+
+import (
+	"fmt"
+	"net/http"
+	"testing"
+
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+	"github.com/rackspace/gophercloud/testhelper"
+)
+
+const (
+	tokenId = "abcabcabcabc"
+)
+
+var metadata = map[string]string{"gophercloud-test": "containers"}
+
+func serviceClient() *gophercloud.ServiceClient {
+	return &gophercloud.ServiceClient{
+		Provider: &gophercloud.ProviderClient{TokenID: tokenId},
+		Endpoint: testhelper.Endpoint(),
+	}
+}
+
+func TestListContainerInfo(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+
+		w.Header().Add("Content-Type", "application/json")
+		r.ParseForm()
+		fmt.Printf("r: %+v\n", r)
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, `[
+				{
+					"count": 0,
+					"bytes": 0,
+					"name": "janeausten"
+				},
+				{
+					"count": 1,
+					"bytes": 14,
+					"name": "marktwain"
+				}
+			]`)
+		case "marktwain":
+			fmt.Fprintf(w, `[]`)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	client := serviceClient()
+	List(client, ListOpts{Full: true}).EachPage(func(page pagination.Page) (bool, error) {
+		actual, err := ExtractInfo(page)
+		if err != nil {
+			t.Errorf("Failed to extract container info: %v", err)
+			return false, err
+		}
+
+		expected := []Container{
+			Container{
+				Count: 0,
+				Bytes: 0,
+				Name:  "janeausten",
+			},
+			Container{
+				Count: 1,
+				Bytes: 14,
+				Name:  "marktwain",
+			},
+		}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+}
+
+func TestListContainerNames(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "GET")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
+		testhelper.TestHeader(t, r, "Accept", "text/plain")
+
+		w.Header().Set("Content-Type", "text/plain")
+		r.ParseForm()
+		marker := r.Form.Get("marker")
+		switch marker {
+		case "":
+			fmt.Fprintf(w, "janeausten\nmarktwain\n")
+		case "marktwain":
+			fmt.Fprintf(w, ``)
+		default:
+			t.Fatalf("Unexpected marker: [%s]", marker)
+		}
+	})
+
+	client := serviceClient()
+	List(client, ListOpts{Full: false}).EachPage(func(page pagination.Page) (bool, error) {
+		actual, err := ExtractNames(page)
+		if err != nil {
+			t.Errorf("Failed to extract container names: %v", err)
+			return false, err
+		}
+
+		expected := []string{"janeausten", "marktwain"}
+
+		testhelper.CheckDeepEquals(t, expected, actual)
+
+		return true, nil
+	})
+}
+
+func TestCreateContainer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "PUT")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	client := serviceClient()
+	_, err := Create(client, "testContainer", CreateOpts{})
+	if err != nil {
+		t.Fatalf("Unexpected error creating container: %v", err)
+	}
+}
+
+func TestDeleteContainer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "DELETE")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	client := serviceClient()
+	err := Delete(client, "testContainer")
+	if err != nil {
+		t.Fatalf("Unexpected error deleting container: %v", err)
+	}
+}
+
+func TestUpateContainer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "POST")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+	})
+
+	client := serviceClient()
+	err := Update(client, "testContainer", UpdateOpts{})
+	if err != nil {
+		t.Fatalf("Unexpected error updating container metadata: %v", err)
+	}
+}
+
+func TestGetContainer(t *testing.T) {
+	testhelper.SetupHTTP()
+	defer testhelper.TeardownHTTP()
+
+	testhelper.Mux.HandleFunc("/testContainer", func(w http.ResponseWriter, r *http.Request) {
+		testhelper.TestMethod(t, r, "HEAD")
+		testhelper.TestHeader(t, r, "X-Auth-Token", tokenId)
+		testhelper.TestHeader(t, r, "Accept", "application/json")
+		w.WriteHeader(http.StatusNoContent)
+		fmt.Fprintf(w, `
+		
+		`)
+	})
+
+	client := serviceClient()
+	_, err := Get(client, "testContainer").ExtractMetadata()
+	if err != nil {
+		t.Fatalf("Unexpected error getting container metadata: %v", err)
+	}
+}
diff --git a/openstack/objectStorage/v1/containers/results.go b/openstack/objectStorage/v1/containers/results.go
new file mode 100644
index 0000000..5df99ed
--- /dev/null
+++ b/openstack/objectStorage/v1/containers/results.go
@@ -0,0 +1,142 @@
+package containers
+
+import (
+	"fmt"
+	"github.com/mitchellh/mapstructure"
+	"github.com/rackspace/gophercloud"
+	"github.com/rackspace/gophercloud/pagination"
+	"net/http"
+	"strings"
+)
+
+type Container struct {
+	Bytes int    `json:"bytes" mapstructure:"bytes"`
+	Count int    `json:"count" mapstructure:"count"`
+	Name  string `json:"name"  mapstructure:"name"`
+}
+
+type commonResult struct {
+	gophercloud.CommonResult
+}
+
+func (r GetResult) Extract() (*Container, error) {
+	if r.Err != nil {
+		return nil, r.Err
+	}
+
+	var res struct {
+		Container *Container
+	}
+
+	err := mapstructure.Decode(r.Resp, &res)
+	if err != nil {
+		return nil, fmt.Errorf("Error decoding Object Storage Container: %v", err)
+	}
+
+	return res.Container, nil
+}
+
+type CreateResult struct {
+	commonResult
+}
+
+// GetResult represents the result of a get operation.
+type GetResult struct {
+	Resp *http.Response
+	Err  error
+}
+
+// UpdateResult represents the result of an update operation.
+type UpdateResult commonResult
+
+// DeleteResult represents the result of a delete operation.
+type DeleteResult commonResult
+
+// ListResult is a *http.Response that is returned from a call to the List function.
+type ContainerPage struct {
+	pagination.MarkerPageBase
+}
+
+// IsEmpty returns true if a ListResult contains no container names.
+func (r ContainerPage) IsEmpty() (bool, error) {
+	names, err := ExtractNames(r)
+	if err != nil {
+		return true, err
+	}
+	return len(names) == 0, nil
+}
+
+// LastMarker returns the last container name in a ListResult.
+func (r ContainerPage) LastMarker() (string, error) {
+	names, err := ExtractNames(r)
+	if err != nil {
+		return "", err
+	}
+	if len(names) == 0 {
+		return "", nil
+	}
+	return names[len(names)-1], nil
+}
+
+// ExtractInfo is a function that takes a ListResult and returns the containers' information.
+func ExtractInfo(page pagination.Page) ([]Container, error) {
+	untyped := page.(ContainerPage).Body.([]interface{})
+	results := make([]Container, len(untyped))
+	for index, each := range untyped {
+		container := each.(map[string]interface{})
+		err := mapstructure.Decode(container, &results[index])
+		if err != nil {
+			return results, err
+		}
+	}
+	return results, nil
+}
+
+// ExtractNames is a function that takes a ListResult and returns the containers' names.
+func ExtractNames(page pagination.Page) ([]string, error) {
+	casted := page.(ContainerPage)
+	ct := casted.Header.Get("Content-Type")
+
+	switch {
+	case strings.HasPrefix(ct, "application/json"):
+		parsed, err := ExtractInfo(page)
+		if err != nil {
+			return nil, err
+		}
+
+		names := make([]string, 0, len(parsed))
+		for _, container := range parsed {
+			names = append(names, container.Name)
+		}
+		return names, nil
+	case strings.HasPrefix(ct, "text/plain"):
+		names := make([]string, 0, 50)
+
+		body := string(page.(ContainerPage).Body.([]uint8))
+		for _, name := range strings.Split(body, "\n") {
+			if len(name) > 0 {
+				names = append(names, name)
+			}
+		}
+
+		return names, nil
+	default:
+		return nil, fmt.Errorf("Cannot extract names from response with content-type: [%s]", ct)
+	}
+}
+
+// ExtractMetadata is a function that takes a GetResult (of type *http.Response)
+// and returns the custom metadata associated with the container.
+func (gr GetResult) ExtractMetadata() (map[string]string, error) {
+	if gr.Err != nil {
+		return nil, gr.Err
+	}
+	metadata := make(map[string]string)
+	for k, v := range gr.Resp.Header {
+		if strings.HasPrefix(k, "X-Container-Meta-") {
+			key := strings.TrimPrefix(k, "X-Container-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata, nil
+}
diff --git a/openstack/objectStorage/v1/containers/urls.go b/openstack/objectStorage/v1/containers/urls.go
new file mode 100644
index 0000000..2a06f95
--- /dev/null
+++ b/openstack/objectStorage/v1/containers/urls.go
@@ -0,0 +1,13 @@
+package containers
+
+import "github.com/rackspace/gophercloud"
+
+// accountURL returns the URI used to list Containers.
+func accountURL(c *gophercloud.ServiceClient) string {
+	return c.Endpoint
+}
+
+// containerURL returns the URI for making Container requests.
+func containerURL(c *gophercloud.ServiceClient, container string) string {
+	return c.ServiceURL(container)
+}
diff --git a/openstack/objectStorage/v1/containers/urls_test.go b/openstack/objectStorage/v1/containers/urls_test.go
new file mode 100644
index 0000000..da37bf6
--- /dev/null
+++ b/openstack/objectStorage/v1/containers/urls_test.go
@@ -0,0 +1,29 @@
+package containers
+
+import (
+	"testing"
+	"github.com/rackspace/gophercloud"
+)
+
+func TestAccountURL(t *testing.T) {
+	client := gophercloud.ServiceClient{
+		Endpoint: "http://localhost:5000/v1/",
+	}
+	expected := "http://localhost:5000/v1/"
+	actual := accountURL(&client)
+	if actual != expected {
+		t.Errorf("Unexpected service URL generated: [%s]", actual)
+	}
+
+}
+
+func TestContainerURL(t *testing.T) {
+	client := gophercloud.ServiceClient{
+		Endpoint: "http://localhost:5000/v1/",
+	}
+	expected := "http://localhost:5000/v1/testContainer"
+	actual := containerURL(&client, "testContainer")
+	if actual != expected {
+		t.Errorf("Unexpected service URL generated: [%s]", actual)
+	}
+}