openstack object storage v02.0
diff --git a/openstack/storage/accounts/accounts.go b/openstack/storage/accounts/accounts.go
new file mode 100644
index 0000000..cb72072
--- /dev/null
+++ b/openstack/storage/accounts/accounts.go
@@ -0,0 +1,27 @@
+package accounts
+
+import (
+	"strings"
+)
+
+type UpdateOpts struct {
+	Metadata map[string]string
+	Headers  map[string]string
+}
+
+type GetOpts struct {
+	Headers map[string]string
+}
+
+// GetMetadata is a function that takes a GetResult (of type *perigee.Response)
+// and returns the custom metatdata associated with the account.
+func GetMetadata(gr GetResult) map[string]string {
+	metadata := make(map[string]string)
+	for k, v := range gr.HttpResponse.Header {
+		if strings.HasPrefix(k, "X-Account-Meta-") {
+			key := strings.TrimPrefix(k, "X-Account-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata
+}
diff --git a/openstack/storage/accounts/requests.go b/openstack/storage/accounts/requests.go
new file mode 100644
index 0000000..5c658d7
--- /dev/null
+++ b/openstack/storage/accounts/requests.go
@@ -0,0 +1,50 @@
+package accounts
+
+import (
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud/openstack/storage"
+)
+
+type GetResult *perigee.Response
+
+// Update is a function that creates, updates, or deletes an account's metadata.
+func Update(c *storage.Client, opts UpdateOpts) error {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Account-Meta-"+k] = v
+	}
+
+	url := c.GetAccountURL()
+	_, err = perigee.Request("POST", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return err
+}
+
+// Get is a function that retrieves an account's metadata. To extract just the custom
+// metadata, pass the GetResult response to the GetMetadata function.
+func Get(c *storage.Client, opts GetOpts) (GetResult, error) {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	url := c.GetAccountURL()
+	resp, err := perigee.Request("HEAD", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return resp, err
+}
diff --git a/openstack/storage/client.go b/openstack/storage/client.go
new file mode 100644
index 0000000..94307b0
--- /dev/null
+++ b/openstack/storage/client.go
@@ -0,0 +1,60 @@
+package storage
+
+import (
+	"fmt"
+	"github.com/rackspace/gophercloud/openstack/identity"
+)
+
+// Client is a structure that contains information for communicating with a provider.
+type Client struct {
+	endpoint  string
+	authority identity.AuthResults
+	options   identity.AuthOptions
+	token     *identity.Token
+}
+
+func NewClient(e string, a identity.AuthResults, o identity.AuthOptions) *Client {
+	return &Client{
+		endpoint:  e,
+		authority: a,
+		options:   o,
+	}
+}
+
+func (c *Client) GetAccountURL() string {
+	return fmt.Sprintf("%s", c.endpoint)
+}
+
+func (c *Client) GetContainerURL(container string) string {
+	return fmt.Sprintf("%s/%s", c.endpoint, container)
+}
+
+func (c *Client) GetObjectURL(container, object string) string {
+	return fmt.Sprintf("%s/%s/%s", c.endpoint, container, object)
+}
+
+// GetHeaders is a function that sets the header for token authentication against a client's endpoint.
+func (c *Client) GetHeaders() (map[string]string, error) {
+	t, err := c.getAuthToken()
+	if err != nil {
+		return map[string]string{}, err
+	}
+
+	return map[string]string{
+		"X-Auth-Token": t,
+	}, nil
+}
+
+// getAuthToken is a function that tries to retrieve an authentication token from a client's endpoint.
+func (c *Client) getAuthToken() (string, error) {
+	var err error
+
+	if c.token == nil {
+		c.token, err = identity.GetToken(c.authority)
+		if err != nil {
+			return "", err
+		}
+	}
+
+	return c.token.Id, err
+}
diff --git a/openstack/storage/containers/containers.go b/openstack/storage/containers/containers.go
new file mode 100644
index 0000000..b3fb921
--- /dev/null
+++ b/openstack/storage/containers/containers.go
@@ -0,0 +1,69 @@
+package containers
+
+import (
+	"encoding/json"
+	"strings"
+)
+
+type Container struct {
+	Bytes int
+	Count int
+	Name  string
+}
+
+type ListOpts struct {
+	Full   bool
+	Params map[string]string
+}
+
+type CreateOpts struct {
+	Name     string
+	Metadata map[string]string
+	Headers  map[string]string
+}
+
+type DeleteOpts struct {
+	Name   string
+	Params map[string]string
+}
+
+type UpdateOpts struct {
+	Name     string
+	Metadata map[string]string
+	Headers  map[string]string
+}
+
+type GetOpts struct {
+	Name     string
+	Metadata map[string]string
+}
+
+// GetInfo is a function that takes a ListResult (of type *perigee.Response)
+// and returns the containers' information.
+func GetInfo(lr ListResult) ([]Container, error) {
+	var ci []Container
+	err := json.Unmarshal(lr.JsonResult, &ci)
+	return ci, err
+}
+
+// GetNames is a function that takes a ListResult (of type *perigee.Response)
+// and returns the containers' names.
+func GetNames(lr ListResult) ([]string, error) {
+	jr := string(lr.JsonResult)
+	cns := strings.Split(jr, "\n")
+	cns = cns[:len(cns)-1]
+	return cns, nil
+}
+
+// GetMetadata is a function that takes a GetResult (of type *perigee.Response)
+// and returns the custom metadata associated with the container.
+func GetMetadata(gr GetResult) map[string]string {
+	metadata := make(map[string]string)
+	for k, v := range gr.HttpResponse.Header {
+		if strings.HasPrefix(k, "X-Container-Meta-") {
+			key := strings.TrimPrefix(k, "X-Container-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata
+}
diff --git a/openstack/storage/containers/requests.go b/openstack/storage/containers/requests.go
new file mode 100644
index 0000000..6cdd7c7
--- /dev/null
+++ b/openstack/storage/containers/requests.go
@@ -0,0 +1,127 @@
+package containers
+
+import (
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud/openstack/storage"
+	"github.com/rackspace/gophercloud/openstack/utils"
+)
+
+type ListResult *perigee.Response
+type GetResult *perigee.Response
+
+// 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 GetInfo or GetNames function, respectively.
+func List(c *storage.Client, opts ListOpts) (ListResult, error) {
+	contentType := ""
+
+	h, err := c.GetHeaders()
+	if err != nil {
+		return nil, err
+	}
+
+	query := utils.BuildQuery(opts.Params)
+
+	if !opts.Full {
+		contentType = "text/plain"
+	}
+
+	url := c.GetAccountURL() + query
+	resp, err := perigee.Request("GET", url, perigee.Options{
+		Results:     true,
+		MoreHeaders: h,
+		OkCodes:     []int{200, 204},
+		Accept:      contentType,
+	})
+	return resp, err
+}
+
+// Create is a function that creates a new container.
+func Create(c *storage.Client, opts CreateOpts) (*Container, error) {
+	var ci *Container
+
+	h, err := c.GetHeaders()
+	if err != nil {
+		return new(Container), err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+
+	url := c.GetContainerURL(opts.Name)
+	_, err = perigee.Request("PUT", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{201, 204},
+	})
+	if err == nil {
+		ci = &Container{
+			Name: opts.Name,
+		}
+	}
+	return ci, err
+}
+
+// Delete is a function that deletes a container.
+func Delete(c *storage.Client, opts DeleteOpts) error {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+
+	query := utils.BuildQuery(opts.Params)
+
+	url := c.GetContainerURL(opts.Name) + query
+	_, err = perigee.Request("DELETE", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return err
+}
+
+// Update is a function that creates, updates, or deletes a container's metadata.
+func Update(c *storage.Client, opts UpdateOpts) error {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+
+	url := c.GetContainerURL(opts.Name)
+	_, 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 GetMetadata function.
+func Get(c *storage.Client, opts GetOpts) (GetResult, error) {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Container-Meta-"+k] = v
+	}
+
+	url := c.GetContainerURL(opts.Name)
+	resp, err := perigee.Request("HEAD", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return resp, err
+}
diff --git a/openstack/storage/objects/objects.go b/openstack/storage/objects/objects.go
new file mode 100644
index 0000000..20228e5
--- /dev/null
+++ b/openstack/storage/objects/objects.go
@@ -0,0 +1,102 @@
+package objects
+
+import (
+	"bytes"
+	"encoding/json"
+	"strings"
+)
+
+type Object struct {
+	Name          string
+	Hash          string
+	Bytes         int
+	Content_type  string
+	Last_modified string
+}
+
+type ListOpts struct {
+	Container string
+	Full      bool
+	Params    map[string]string
+}
+
+type DownloadOpts struct {
+	Container string
+	Name      string
+	Headers   map[string]string
+	Params    map[string]string
+}
+
+type CreateOpts struct {
+	Container string
+	Name      string
+	Content   *bytes.Buffer
+	Metadata  map[string]string
+	Headers   map[string]string
+	Params    map[string]string
+}
+
+type CopyOpts struct {
+	Container    string
+	Name         string
+	NewContainer string
+	NewName      string
+	Metadata     map[string]string
+	Headers      map[string]string
+}
+
+type DeleteOpts struct {
+	Container string
+	Name      string
+	Params    map[string]string
+}
+
+type GetOpts struct {
+	Container string
+	Name      string
+	Headers   map[string]string
+	Params    map[string]string
+}
+
+type UpdateOpts struct {
+	Container string
+	Name      string
+	Metadata  map[string]string
+	Headers   map[string]string
+}
+
+// GetInfo is a function that takes a ListResult (of type *perigee.Response)
+// and returns the objects' information.
+func GetInfo(lr ListResult) ([]Object, error) {
+	var oi []Object
+	err := json.Unmarshal(lr.JsonResult, &oi)
+	return oi, err
+}
+
+// GetNames is a function that takes a ListResult (of type *perigee.Response)
+// and returns the objects' names.
+func GetNames(lr ListResult) []string {
+	jr := string(lr.JsonResult)
+	ons := strings.Split(jr, "\n")
+	ons = ons[:len(ons)-1]
+	return ons
+}
+
+// GetContent is a function that takes a DownloadResult (of type *perigee.Response)
+// and returns the object's content.
+func GetContent(dr DownloadResult) []byte {
+	return dr.JsonResult
+}
+
+// GetMetadata is a function that takes a GetResult (of type *perifee.Response)
+// and returns the custom metadata associated with the object.
+func GetMetadata(gr GetResult) map[string]string {
+	metadata := make(map[string]string)
+	for k, v := range gr.HttpResponse.Header {
+		if strings.HasPrefix(k, "X-Object-Meta-") {
+			key := strings.TrimPrefix(k, "X-Object-Meta-")
+			metadata[key] = v[0]
+		}
+	}
+	return metadata
+}
diff --git a/openstack/storage/objects/requests.go b/openstack/storage/objects/requests.go
new file mode 100644
index 0000000..8f645fc
--- /dev/null
+++ b/openstack/storage/objects/requests.go
@@ -0,0 +1,182 @@
+package objects
+
+import (
+	"fmt"
+
+	"github.com/racker/perigee"
+	"github.com/rackspace/gophercloud/openstack/storage"
+	"github.com/rackspace/gophercloud/openstack/utils"
+)
+
+type ListResult *perigee.Response
+type DownloadResult *perigee.Response
+type GetResult *perigee.Response
+
+// List is a function that retrieves all objects in a container. It also returns the details
+// for the container. To extract only the object information or names, pass the ListResult
+// response to the GetInfo or GetNames function, respectively.
+func List(c *storage.Client, opts ListOpts) (ListResult, error) {
+	contentType := ""
+
+	h, err := c.GetHeaders()
+	if err != nil {
+		return nil, err
+	}
+
+	query := utils.BuildQuery(opts.Params)
+
+	if !opts.Full {
+		contentType = "text/plain"
+	}
+
+	url := c.GetContainerURL(opts.Container) + query
+	resp, err := perigee.Request("GET", url, perigee.Options{
+		Results:     true,
+		MoreHeaders: h,
+		OkCodes:     []int{200, 204},
+		Accept:      contentType,
+	})
+	return resp, err
+}
+
+// Download is a function that retrieves the content and metadata for an object.
+// To extract just the content, pass the DownloadResult response to the GetContent
+// function.
+func Download(c *storage.Client, opts DownloadOpts) (DownloadResult, error) {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	query := utils.BuildQuery(opts.Params)
+
+	url := c.GetObjectURL(opts.Container, opts.Name) + query
+	resp, err := perigee.Request("GET", url, perigee.Options{
+		Results:     true,
+		MoreHeaders: h,
+		OkCodes:     []int{200},
+	})
+	return resp, err
+}
+
+// Create is a function that creates a new object or replaces an existing object.
+func Create(c *storage.Client, opts CreateOpts) error {
+	var reqBody []byte
+
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+
+	query := utils.BuildQuery(opts.Params)
+
+	content := opts.Content
+	if content != nil {
+		reqBody = make([]byte, content.Len())
+		_, err = content.Read(reqBody)
+		if err != nil {
+			return err
+		}
+	}
+
+	url := c.GetObjectURL(opts.Container, opts.Name) + query
+	_, err = perigee.Request("PUT", url, perigee.Options{
+		ReqBody:     reqBody,
+		MoreHeaders: h,
+		OkCodes:     []int{201},
+	})
+	return err
+}
+
+// Copy is a function that copies one object to another.
+func Copy(c *storage.Client, opts CopyOpts) error {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+
+	h["Destination"] = fmt.Sprintf("/%s/%s", opts.NewContainer, opts.NewName)
+
+	url := c.GetObjectURL(opts.Container, opts.Name)
+	_, err = perigee.Request("COPY", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{201},
+	})
+	return err
+}
+
+// Delete is a function that deletes an object.
+func Delete(c *storage.Client, opts DeleteOpts) error {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+
+	query := utils.BuildQuery(opts.Params)
+
+	url := c.GetObjectURL(opts.Container, opts.Name) + query
+	_, err = perigee.Request("DELETE", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return err
+}
+
+// Get is a function that retrieves the metadata of an object. To extract just the custom
+// metadata, pass the GetResult response to the GetMetadata function.
+func Get(c *storage.Client, opts GetOpts) (GetResult, error) {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return nil, err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	url := c.GetObjectURL(opts.Container, opts.Name)
+	resp, err := perigee.Request("HEAD", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{204},
+	})
+	return resp, err
+}
+
+// Update is a function that creates, updates, or deletes an object's metadata.
+func Update(c *storage.Client, opts UpdateOpts) error {
+	h, err := c.GetHeaders()
+	if err != nil {
+		return err
+	}
+
+	for k, v := range opts.Headers {
+		h[k] = v
+	}
+
+	for k, v := range opts.Metadata {
+		h["X-Object-Meta-"+k] = v
+	}
+
+	url := c.GetObjectURL(opts.Container, opts.Name)
+	_, err = perigee.Request("POST", url, perigee.Options{
+		MoreHeaders: h,
+		OkCodes:     []int{202, 204},
+	})
+	return err
+}