github.com/mika/distribution@v2.2.2-0.20160108133430-a75790e3d8e0+incompatible/registry/storage/driver/swift/swift.go (about)

     1  // Package swift provides a storagedriver.StorageDriver implementation to
     2  // store blobs in Openstack Swift object storage.
     3  //
     4  // This package leverages the ncw/swift client library for interfacing with
     5  // Swift.
     6  //
     7  // It supports both TempAuth authentication and Keystone authentication
     8  // (up to version 3).
     9  //
    10  // As Swift has a limit on the size of a single uploaded object (by default
    11  // this is 5GB), the driver makes use of the Swift Large Object Support
    12  // (http://docs.openstack.org/developer/swift/overview_large_objects.html).
    13  // Only one container is used for both manifests and data objects. Manifests
    14  // are stored in the 'files' pseudo directory, data objects are stored under
    15  // 'segments'.
    16  package swift
    17  
    18  import (
    19  	"bytes"
    20  	"crypto/md5"
    21  	"crypto/rand"
    22  	"crypto/sha1"
    23  	"crypto/tls"
    24  	"encoding/hex"
    25  	"fmt"
    26  	"io"
    27  	"io/ioutil"
    28  	"net/http"
    29  	"net/url"
    30  	"strconv"
    31  	"strings"
    32  	"time"
    33  
    34  	"github.com/mitchellh/mapstructure"
    35  	"github.com/ncw/swift"
    36  
    37  	"github.com/docker/distribution/context"
    38  	storagedriver "github.com/docker/distribution/registry/storage/driver"
    39  	"github.com/docker/distribution/registry/storage/driver/base"
    40  	"github.com/docker/distribution/registry/storage/driver/factory"
    41  	"github.com/docker/distribution/version"
    42  )
    43  
    44  const driverName = "swift"
    45  
    46  // defaultChunkSize defines the default size of a segment
    47  const defaultChunkSize = 20 * 1024 * 1024
    48  
    49  // minChunkSize defines the minimum size of a segment
    50  const minChunkSize = 1 << 20
    51  
    52  // readAfterWriteTimeout defines the time we wait before an object appears after having been uploaded
    53  var readAfterWriteTimeout = 15 * time.Second
    54  
    55  // readAfterWriteWait defines the time to sleep between two retries
    56  var readAfterWriteWait = 200 * time.Millisecond
    57  
    58  // Parameters A struct that encapsulates all of the driver parameters after all values have been set
    59  type Parameters struct {
    60  	Username            string
    61  	Password            string
    62  	AuthURL             string
    63  	Tenant              string
    64  	TenantID            string
    65  	Domain              string
    66  	DomainID            string
    67  	TrustID             string
    68  	Region              string
    69  	Container           string
    70  	Prefix              string
    71  	InsecureSkipVerify  bool
    72  	ChunkSize           int
    73  	SecretKey           string
    74  	AccessKey           string
    75  	TempURLContainerKey bool
    76  	TempURLMethods      []string
    77  }
    78  
    79  // swiftInfo maps the JSON structure returned by Swift /info endpoint
    80  type swiftInfo struct {
    81  	Swift struct {
    82  		Version string `mapstructure:"version"`
    83  	}
    84  	Tempurl struct {
    85  		Methods []string `mapstructure:"methods"`
    86  	}
    87  }
    88  
    89  func init() {
    90  	factory.Register(driverName, &swiftDriverFactory{})
    91  }
    92  
    93  // swiftDriverFactory implements the factory.StorageDriverFactory interface
    94  type swiftDriverFactory struct{}
    95  
    96  func (factory *swiftDriverFactory) Create(parameters map[string]interface{}) (storagedriver.StorageDriver, error) {
    97  	return FromParameters(parameters)
    98  }
    99  
   100  type driver struct {
   101  	Conn                swift.Connection
   102  	Container           string
   103  	Prefix              string
   104  	BulkDeleteSupport   bool
   105  	ChunkSize           int
   106  	SecretKey           string
   107  	AccessKey           string
   108  	TempURLContainerKey bool
   109  	TempURLMethods      []string
   110  }
   111  
   112  type baseEmbed struct {
   113  	base.Base
   114  }
   115  
   116  // Driver is a storagedriver.StorageDriver implementation backed by Openstack Swift
   117  // Objects are stored at absolute keys in the provided container.
   118  type Driver struct {
   119  	baseEmbed
   120  }
   121  
   122  // FromParameters constructs a new Driver with a given parameters map
   123  // Required parameters:
   124  // - username
   125  // - password
   126  // - authurl
   127  // - container
   128  func FromParameters(parameters map[string]interface{}) (*Driver, error) {
   129  	params := Parameters{
   130  		ChunkSize:          defaultChunkSize,
   131  		InsecureSkipVerify: false,
   132  	}
   133  
   134  	if err := mapstructure.Decode(parameters, &params); err != nil {
   135  		return nil, err
   136  	}
   137  
   138  	if params.Username == "" {
   139  		return nil, fmt.Errorf("No username parameter provided")
   140  	}
   141  
   142  	if params.Password == "" {
   143  		return nil, fmt.Errorf("No password parameter provided")
   144  	}
   145  
   146  	if params.AuthURL == "" {
   147  		return nil, fmt.Errorf("No authurl parameter provided")
   148  	}
   149  
   150  	if params.Container == "" {
   151  		return nil, fmt.Errorf("No container parameter provided")
   152  	}
   153  
   154  	if params.ChunkSize < minChunkSize {
   155  		return nil, fmt.Errorf("The chunksize %#v parameter should be a number that is larger than or equal to %d", params.ChunkSize, minChunkSize)
   156  	}
   157  
   158  	return New(params)
   159  }
   160  
   161  // New constructs a new Driver with the given Openstack Swift credentials and container name
   162  func New(params Parameters) (*Driver, error) {
   163  	transport := &http.Transport{
   164  		Proxy:               http.ProxyFromEnvironment,
   165  		MaxIdleConnsPerHost: 2048,
   166  		TLSClientConfig:     &tls.Config{InsecureSkipVerify: params.InsecureSkipVerify},
   167  	}
   168  
   169  	ct := swift.Connection{
   170  		UserName:       params.Username,
   171  		ApiKey:         params.Password,
   172  		AuthUrl:        params.AuthURL,
   173  		Region:         params.Region,
   174  		UserAgent:      "distribution/" + version.Version,
   175  		Tenant:         params.Tenant,
   176  		TenantId:       params.TenantID,
   177  		Domain:         params.Domain,
   178  		DomainId:       params.DomainID,
   179  		TrustId:        params.TrustID,
   180  		Transport:      transport,
   181  		ConnectTimeout: 60 * time.Second,
   182  		Timeout:        15 * 60 * time.Second,
   183  	}
   184  	err := ct.Authenticate()
   185  	if err != nil {
   186  		return nil, fmt.Errorf("Swift authentication failed: %s", err)
   187  	}
   188  
   189  	if _, _, err := ct.Container(params.Container); err == swift.ContainerNotFound {
   190  		if err := ct.ContainerCreate(params.Container, nil); err != nil {
   191  			return nil, fmt.Errorf("Failed to create container %s (%s)", params.Container, err)
   192  		}
   193  	} else if err != nil {
   194  		return nil, fmt.Errorf("Failed to retrieve info about container %s (%s)", params.Container, err)
   195  	}
   196  
   197  	d := &driver{
   198  		Conn:           ct,
   199  		Container:      params.Container,
   200  		Prefix:         params.Prefix,
   201  		ChunkSize:      params.ChunkSize,
   202  		TempURLMethods: make([]string, 0),
   203  		AccessKey:      params.AccessKey,
   204  	}
   205  
   206  	info := swiftInfo{}
   207  	if config, err := d.Conn.QueryInfo(); err == nil {
   208  		_, d.BulkDeleteSupport = config["bulk_delete"]
   209  
   210  		if err := mapstructure.Decode(config, &info); err == nil {
   211  			d.TempURLContainerKey = info.Swift.Version >= "2.3.0"
   212  			d.TempURLMethods = info.Tempurl.Methods
   213  		}
   214  	} else {
   215  		d.TempURLContainerKey = params.TempURLContainerKey
   216  		d.TempURLMethods = params.TempURLMethods
   217  	}
   218  
   219  	if len(d.TempURLMethods) > 0 {
   220  		secretKey := params.SecretKey
   221  		if secretKey == "" {
   222  			secretKey, _ = generateSecret()
   223  		}
   224  
   225  		// Since Swift 2.2.2, we can now set secret keys on containers
   226  		// in addition to the account secret keys. Use them in preference.
   227  		if d.TempURLContainerKey {
   228  			_, containerHeaders, err := d.Conn.Container(d.Container)
   229  			if err != nil {
   230  				return nil, fmt.Errorf("Failed to fetch container info %s (%s)", d.Container, err)
   231  			}
   232  
   233  			d.SecretKey = containerHeaders["X-Container-Meta-Temp-Url-Key"]
   234  			if d.SecretKey == "" || (params.SecretKey != "" && d.SecretKey != params.SecretKey) {
   235  				m := swift.Metadata{}
   236  				m["temp-url-key"] = secretKey
   237  				if d.Conn.ContainerUpdate(d.Container, m.ContainerHeaders()); err == nil {
   238  					d.SecretKey = secretKey
   239  				}
   240  			}
   241  		} else {
   242  			// Use the account secret key
   243  			_, accountHeaders, err := d.Conn.Account()
   244  			if err != nil {
   245  				return nil, fmt.Errorf("Failed to fetch account info (%s)", err)
   246  			}
   247  
   248  			d.SecretKey = accountHeaders["X-Account-Meta-Temp-Url-Key"]
   249  			if d.SecretKey == "" || (params.SecretKey != "" && d.SecretKey != params.SecretKey) {
   250  				m := swift.Metadata{}
   251  				m["temp-url-key"] = secretKey
   252  				if err := d.Conn.AccountUpdate(m.AccountHeaders()); err == nil {
   253  					d.SecretKey = secretKey
   254  				}
   255  			}
   256  		}
   257  	}
   258  
   259  	return &Driver{
   260  		baseEmbed: baseEmbed{
   261  			Base: base.Base{
   262  				StorageDriver: d,
   263  			},
   264  		},
   265  	}, nil
   266  }
   267  
   268  // Implement the storagedriver.StorageDriver interface
   269  
   270  func (d *driver) Name() string {
   271  	return driverName
   272  }
   273  
   274  // GetContent retrieves the content stored at "path" as a []byte.
   275  func (d *driver) GetContent(ctx context.Context, path string) ([]byte, error) {
   276  	content, err := d.Conn.ObjectGetBytes(d.Container, d.swiftPath(path))
   277  	if err == swift.ObjectNotFound {
   278  		return nil, storagedriver.PathNotFoundError{Path: path}
   279  	}
   280  	return content, nil
   281  }
   282  
   283  // PutContent stores the []byte content at a location designated by "path".
   284  func (d *driver) PutContent(ctx context.Context, path string, contents []byte) error {
   285  	err := d.Conn.ObjectPutBytes(d.Container, d.swiftPath(path), contents, d.getContentType())
   286  	if err == swift.ObjectNotFound {
   287  		return storagedriver.PathNotFoundError{Path: path}
   288  	}
   289  	return err
   290  }
   291  
   292  // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
   293  // given byte offset.
   294  func (d *driver) ReadStream(ctx context.Context, path string, offset int64) (io.ReadCloser, error) {
   295  	headers := make(swift.Headers)
   296  	headers["Range"] = "bytes=" + strconv.FormatInt(offset, 10) + "-"
   297  
   298  	file, _, err := d.Conn.ObjectOpen(d.Container, d.swiftPath(path), false, headers)
   299  	if err == swift.ObjectNotFound {
   300  		return nil, storagedriver.PathNotFoundError{Path: path}
   301  	}
   302  	if swiftErr, ok := err.(*swift.Error); ok && swiftErr.StatusCode == http.StatusRequestedRangeNotSatisfiable {
   303  		return ioutil.NopCloser(bytes.NewReader(nil)), nil
   304  	}
   305  	return file, err
   306  }
   307  
   308  // WriteStream stores the contents of the provided io.Reader at a
   309  // location designated by the given path. The driver will know it has
   310  // received the full contents when the reader returns io.EOF. The number
   311  // of successfully READ bytes will be returned, even if an error is
   312  // returned. May be used to resume writing a stream by providing a nonzero
   313  // offset. Offsets past the current size will write from the position
   314  // beyond the end of the file.
   315  func (d *driver) WriteStream(ctx context.Context, path string, offset int64, reader io.Reader) (int64, error) {
   316  	var (
   317  		segments      []swift.Object
   318  		multi         io.Reader
   319  		paddingReader io.Reader
   320  		currentLength int64
   321  		cursor        int64
   322  		segmentPath   string
   323  	)
   324  
   325  	partNumber := 1
   326  	chunkSize := int64(d.ChunkSize)
   327  	zeroBuf := make([]byte, d.ChunkSize)
   328  	hash := md5.New()
   329  
   330  	getSegment := func() string {
   331  		return fmt.Sprintf("%s/%016d", segmentPath, partNumber)
   332  	}
   333  
   334  	max := func(a int64, b int64) int64 {
   335  		if a > b {
   336  			return a
   337  		}
   338  		return b
   339  	}
   340  
   341  	createManifest := true
   342  	info, headers, err := d.Conn.Object(d.Container, d.swiftPath(path))
   343  	if err == nil {
   344  		manifest, ok := headers["X-Object-Manifest"]
   345  		if !ok {
   346  			if segmentPath, err = d.swiftSegmentPath(path); err != nil {
   347  				return 0, err
   348  			}
   349  			if err := d.Conn.ObjectMove(d.Container, d.swiftPath(path), d.Container, getSegment()); err != nil {
   350  				return 0, err
   351  			}
   352  			segments = append(segments, info)
   353  		} else {
   354  			_, segmentPath = parseManifest(manifest)
   355  			if segments, err = d.getAllSegments(segmentPath); err != nil {
   356  				return 0, err
   357  			}
   358  			createManifest = false
   359  		}
   360  		currentLength = info.Bytes
   361  	} else if err == swift.ObjectNotFound {
   362  		if segmentPath, err = d.swiftSegmentPath(path); err != nil {
   363  			return 0, err
   364  		}
   365  	} else {
   366  		return 0, err
   367  	}
   368  
   369  	// First, we skip the existing segments that are not modified by this call
   370  	for i := range segments {
   371  		if offset < cursor+segments[i].Bytes {
   372  			break
   373  		}
   374  		cursor += segments[i].Bytes
   375  		hash.Write([]byte(segments[i].Hash))
   376  		partNumber++
   377  	}
   378  
   379  	// We reached the end of the file but we haven't reached 'offset' yet
   380  	// Therefore we add blocks of zeros
   381  	if offset >= currentLength {
   382  		for offset-currentLength >= chunkSize {
   383  			// Insert a block a zero
   384  			headers, err := d.Conn.ObjectPut(d.Container, getSegment(), bytes.NewReader(zeroBuf), false, "", d.getContentType(), nil)
   385  			if err != nil {
   386  				if err == swift.ObjectNotFound {
   387  					return 0, storagedriver.PathNotFoundError{Path: getSegment()}
   388  				}
   389  				return 0, err
   390  			}
   391  			currentLength += chunkSize
   392  			partNumber++
   393  			hash.Write([]byte(headers["Etag"]))
   394  		}
   395  
   396  		cursor = currentLength
   397  		paddingReader = bytes.NewReader(zeroBuf)
   398  	} else if offset-cursor > 0 {
   399  		// Offset is inside the current segment : we need to read the
   400  		// data from the beginning of the segment to offset
   401  		file, _, err := d.Conn.ObjectOpen(d.Container, getSegment(), false, nil)
   402  		if err != nil {
   403  			if err == swift.ObjectNotFound {
   404  				return 0, storagedriver.PathNotFoundError{Path: getSegment()}
   405  			}
   406  			return 0, err
   407  		}
   408  		defer file.Close()
   409  		paddingReader = file
   410  	}
   411  
   412  	readers := []io.Reader{}
   413  	if paddingReader != nil {
   414  		readers = append(readers, io.LimitReader(paddingReader, offset-cursor))
   415  	}
   416  	readers = append(readers, io.LimitReader(reader, chunkSize-(offset-cursor)))
   417  	multi = io.MultiReader(readers...)
   418  
   419  	writeSegment := func(segment string) (finished bool, bytesRead int64, err error) {
   420  		currentSegment, err := d.Conn.ObjectCreate(d.Container, segment, false, "", d.getContentType(), nil)
   421  		if err != nil {
   422  			if err == swift.ObjectNotFound {
   423  				return false, bytesRead, storagedriver.PathNotFoundError{Path: segment}
   424  			}
   425  			return false, bytesRead, err
   426  		}
   427  
   428  		segmentHash := md5.New()
   429  		writer := io.MultiWriter(currentSegment, segmentHash)
   430  
   431  		n, err := io.Copy(writer, multi)
   432  		if err != nil {
   433  			return false, bytesRead, err
   434  		}
   435  
   436  		if n > 0 {
   437  			defer func() {
   438  				closeError := currentSegment.Close()
   439  				if err != nil {
   440  					err = closeError
   441  				}
   442  				hexHash := hex.EncodeToString(segmentHash.Sum(nil))
   443  				hash.Write([]byte(hexHash))
   444  			}()
   445  			bytesRead += n - max(0, offset-cursor)
   446  		}
   447  
   448  		if n < chunkSize {
   449  			// We wrote all the data
   450  			if cursor+n < currentLength {
   451  				// Copy the end of the chunk
   452  				headers := make(swift.Headers)
   453  				headers["Range"] = "bytes=" + strconv.FormatInt(cursor+n, 10) + "-" + strconv.FormatInt(cursor+chunkSize, 10)
   454  				file, _, err := d.Conn.ObjectOpen(d.Container, d.swiftPath(path), false, headers)
   455  				if err != nil {
   456  					if err == swift.ObjectNotFound {
   457  						return false, bytesRead, storagedriver.PathNotFoundError{Path: path}
   458  					}
   459  					return false, bytesRead, err
   460  				}
   461  
   462  				_, copyErr := io.Copy(writer, file)
   463  
   464  				if err := file.Close(); err != nil {
   465  					if err == swift.ObjectNotFound {
   466  						return false, bytesRead, storagedriver.PathNotFoundError{Path: path}
   467  					}
   468  					return false, bytesRead, err
   469  				}
   470  
   471  				if copyErr != nil {
   472  					return false, bytesRead, copyErr
   473  				}
   474  			}
   475  
   476  			return true, bytesRead, nil
   477  		}
   478  
   479  		multi = io.LimitReader(reader, chunkSize)
   480  		cursor += chunkSize
   481  		partNumber++
   482  
   483  		return false, bytesRead, nil
   484  	}
   485  
   486  	finished := false
   487  	read := int64(0)
   488  	bytesRead := int64(0)
   489  	for finished == false {
   490  		finished, read, err = writeSegment(getSegment())
   491  		bytesRead += read
   492  		if err != nil {
   493  			return bytesRead, err
   494  		}
   495  	}
   496  
   497  	for ; partNumber < len(segments); partNumber++ {
   498  		hash.Write([]byte(segments[partNumber].Hash))
   499  	}
   500  
   501  	if createManifest {
   502  		if err := d.createManifest(path, d.Container+"/"+segmentPath); err != nil {
   503  			return 0, err
   504  		}
   505  	}
   506  
   507  	expectedHash := hex.EncodeToString(hash.Sum(nil))
   508  	waitingTime := readAfterWriteWait
   509  	endTime := time.Now().Add(readAfterWriteTimeout)
   510  	for {
   511  		var infos swift.Object
   512  		if infos, _, err = d.Conn.Object(d.Container, d.swiftPath(path)); err == nil {
   513  			if strings.Trim(infos.Hash, "\"") == expectedHash {
   514  				return bytesRead, nil
   515  			}
   516  			err = fmt.Errorf("Timeout expired while waiting for segments of %s to show up", path)
   517  		}
   518  		if time.Now().Add(waitingTime).After(endTime) {
   519  			break
   520  		}
   521  		time.Sleep(waitingTime)
   522  		waitingTime *= 2
   523  	}
   524  
   525  	return bytesRead, err
   526  }
   527  
   528  // Stat retrieves the FileInfo for the given path, including the current size
   529  // in bytes and the creation time.
   530  func (d *driver) Stat(ctx context.Context, path string) (storagedriver.FileInfo, error) {
   531  	swiftPath := d.swiftPath(path)
   532  	opts := &swift.ObjectsOpts{
   533  		Prefix:    swiftPath,
   534  		Delimiter: '/',
   535  	}
   536  
   537  	objects, err := d.Conn.ObjectsAll(d.Container, opts)
   538  	if err != nil {
   539  		if err == swift.ContainerNotFound {
   540  			return nil, storagedriver.PathNotFoundError{Path: path}
   541  		}
   542  		return nil, err
   543  	}
   544  
   545  	fi := storagedriver.FileInfoFields{
   546  		Path: strings.TrimPrefix(strings.TrimSuffix(swiftPath, "/"), d.swiftPath("/")),
   547  	}
   548  
   549  	for _, obj := range objects {
   550  		if obj.PseudoDirectory && obj.Name == swiftPath+"/" {
   551  			fi.IsDir = true
   552  			return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil
   553  		} else if obj.Name == swiftPath {
   554  			// On Swift 1.12, the 'bytes' field is always 0
   555  			// so we need to do a second HEAD request
   556  			info, _, err := d.Conn.Object(d.Container, swiftPath)
   557  			if err != nil {
   558  				if err == swift.ObjectNotFound {
   559  					return nil, storagedriver.PathNotFoundError{Path: path}
   560  				}
   561  				return nil, err
   562  			}
   563  			fi.IsDir = false
   564  			fi.Size = info.Bytes
   565  			fi.ModTime = info.LastModified
   566  			return storagedriver.FileInfoInternal{FileInfoFields: fi}, nil
   567  		}
   568  	}
   569  
   570  	return nil, storagedriver.PathNotFoundError{Path: path}
   571  }
   572  
   573  // List returns a list of the objects that are direct descendants of the given path.
   574  func (d *driver) List(ctx context.Context, path string) ([]string, error) {
   575  	var files []string
   576  
   577  	prefix := d.swiftPath(path)
   578  	if prefix != "" {
   579  		prefix += "/"
   580  	}
   581  
   582  	opts := &swift.ObjectsOpts{
   583  		Prefix:    prefix,
   584  		Delimiter: '/',
   585  	}
   586  
   587  	objects, err := d.Conn.ObjectsAll(d.Container, opts)
   588  	for _, obj := range objects {
   589  		files = append(files, strings.TrimPrefix(strings.TrimSuffix(obj.Name, "/"), d.swiftPath("/")))
   590  	}
   591  
   592  	if err == swift.ContainerNotFound || (len(objects) == 0 && path != "/") {
   593  		return files, storagedriver.PathNotFoundError{Path: path}
   594  	}
   595  	return files, err
   596  }
   597  
   598  // Move moves an object stored at sourcePath to destPath, removing the original
   599  // object.
   600  func (d *driver) Move(ctx context.Context, sourcePath string, destPath string) error {
   601  	_, headers, err := d.Conn.Object(d.Container, d.swiftPath(sourcePath))
   602  	if err == nil {
   603  		if manifest, ok := headers["X-Object-Manifest"]; ok {
   604  			if err = d.createManifest(destPath, manifest); err != nil {
   605  				return err
   606  			}
   607  			err = d.Conn.ObjectDelete(d.Container, d.swiftPath(sourcePath))
   608  		} else {
   609  			err = d.Conn.ObjectMove(d.Container, d.swiftPath(sourcePath), d.Container, d.swiftPath(destPath))
   610  		}
   611  	}
   612  	if err == swift.ObjectNotFound {
   613  		return storagedriver.PathNotFoundError{Path: sourcePath}
   614  	}
   615  	return err
   616  }
   617  
   618  // Delete recursively deletes all objects stored at "path" and its subpaths.
   619  func (d *driver) Delete(ctx context.Context, path string) error {
   620  	opts := swift.ObjectsOpts{
   621  		Prefix: d.swiftPath(path) + "/",
   622  	}
   623  
   624  	objects, err := d.Conn.ObjectsAll(d.Container, &opts)
   625  	if err != nil {
   626  		if err == swift.ContainerNotFound {
   627  			return storagedriver.PathNotFoundError{Path: path}
   628  		}
   629  		return err
   630  	}
   631  
   632  	for _, obj := range objects {
   633  		if obj.PseudoDirectory {
   634  			continue
   635  		}
   636  		if _, headers, err := d.Conn.Object(d.Container, obj.Name); err == nil {
   637  			manifest, ok := headers["X-Object-Manifest"]
   638  			if ok {
   639  				_, prefix := parseManifest(manifest)
   640  				segments, err := d.getAllSegments(prefix)
   641  				if err != nil {
   642  					return err
   643  				}
   644  				objects = append(objects, segments...)
   645  			}
   646  		} else {
   647  			if err == swift.ObjectNotFound {
   648  				return storagedriver.PathNotFoundError{Path: obj.Name}
   649  			}
   650  			return err
   651  		}
   652  	}
   653  
   654  	if d.BulkDeleteSupport && len(objects) > 0 {
   655  		filenames := make([]string, len(objects))
   656  		for i, obj := range objects {
   657  			filenames[i] = obj.Name
   658  		}
   659  		_, err = d.Conn.BulkDelete(d.Container, filenames)
   660  		// Don't fail on ObjectNotFound because eventual consistency
   661  		// makes this situation normal.
   662  		if err != nil && err != swift.Forbidden && err != swift.ObjectNotFound {
   663  			if err == swift.ContainerNotFound {
   664  				return storagedriver.PathNotFoundError{Path: path}
   665  			}
   666  			return err
   667  		}
   668  	} else {
   669  		for _, obj := range objects {
   670  			if err := d.Conn.ObjectDelete(d.Container, obj.Name); err != nil {
   671  				if err == swift.ObjectNotFound {
   672  					return storagedriver.PathNotFoundError{Path: obj.Name}
   673  				}
   674  				return err
   675  			}
   676  		}
   677  	}
   678  
   679  	_, _, err = d.Conn.Object(d.Container, d.swiftPath(path))
   680  	if err == nil {
   681  		if err := d.Conn.ObjectDelete(d.Container, d.swiftPath(path)); err != nil {
   682  			if err == swift.ObjectNotFound {
   683  				return storagedriver.PathNotFoundError{Path: path}
   684  			}
   685  			return err
   686  		}
   687  	} else if err == swift.ObjectNotFound {
   688  		if len(objects) == 0 {
   689  			return storagedriver.PathNotFoundError{Path: path}
   690  		}
   691  	} else {
   692  		return err
   693  	}
   694  	return nil
   695  }
   696  
   697  // URLFor returns a URL which may be used to retrieve the content stored at the given path.
   698  func (d *driver) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) {
   699  	if d.SecretKey == "" {
   700  		return "", storagedriver.ErrUnsupportedMethod{}
   701  	}
   702  
   703  	methodString := "GET"
   704  	method, ok := options["method"]
   705  	if ok {
   706  		if methodString, ok = method.(string); !ok {
   707  			return "", storagedriver.ErrUnsupportedMethod{}
   708  		}
   709  	}
   710  
   711  	if methodString == "HEAD" {
   712  		// A "HEAD" request on a temporary URL is allowed if the
   713  		// signature was generated with "GET", "POST" or "PUT"
   714  		methodString = "GET"
   715  	}
   716  
   717  	supported := false
   718  	for _, method := range d.TempURLMethods {
   719  		if method == methodString {
   720  			supported = true
   721  			break
   722  		}
   723  	}
   724  
   725  	if !supported {
   726  		return "", storagedriver.ErrUnsupportedMethod{}
   727  	}
   728  
   729  	expiresTime := time.Now().Add(20 * time.Minute)
   730  	expires, ok := options["expiry"]
   731  	if ok {
   732  		et, ok := expires.(time.Time)
   733  		if ok {
   734  			expiresTime = et
   735  		}
   736  	}
   737  
   738  	tempURL := d.Conn.ObjectTempUrl(d.Container, d.swiftPath(path), d.SecretKey, methodString, expiresTime)
   739  
   740  	if d.AccessKey != "" {
   741  		// On HP Cloud, the signature must be in the form of tenant_id:access_key:signature
   742  		url, _ := url.Parse(tempURL)
   743  		query := url.Query()
   744  		query.Set("temp_url_sig", fmt.Sprintf("%s:%s:%s", d.Conn.TenantId, d.AccessKey, query.Get("temp_url_sig")))
   745  		url.RawQuery = query.Encode()
   746  		tempURL = url.String()
   747  	}
   748  
   749  	return tempURL, nil
   750  }
   751  
   752  func (d *driver) swiftPath(path string) string {
   753  	return strings.TrimLeft(strings.TrimRight(d.Prefix+"/files"+path, "/"), "/")
   754  }
   755  
   756  func (d *driver) swiftSegmentPath(path string) (string, error) {
   757  	checksum := sha1.New()
   758  	random := make([]byte, 32)
   759  	if _, err := rand.Read(random); err != nil {
   760  		return "", err
   761  	}
   762  	path = hex.EncodeToString(checksum.Sum(append([]byte(path), random...)))
   763  	return strings.TrimLeft(strings.TrimRight(d.Prefix+"/segments/"+path[0:3]+"/"+path[3:], "/"), "/"), nil
   764  }
   765  
   766  func (d *driver) getContentType() string {
   767  	return "application/octet-stream"
   768  }
   769  
   770  func (d *driver) getAllSegments(path string) ([]swift.Object, error) {
   771  	segments, err := d.Conn.ObjectsAll(d.Container, &swift.ObjectsOpts{Prefix: path})
   772  	if err == swift.ContainerNotFound {
   773  		return nil, storagedriver.PathNotFoundError{Path: path}
   774  	}
   775  	return segments, err
   776  }
   777  
   778  func (d *driver) createManifest(path string, segments string) error {
   779  	headers := make(swift.Headers)
   780  	headers["X-Object-Manifest"] = segments
   781  	manifest, err := d.Conn.ObjectCreate(d.Container, d.swiftPath(path), false, "", d.getContentType(), headers)
   782  	if err != nil {
   783  		if err == swift.ObjectNotFound {
   784  			return storagedriver.PathNotFoundError{Path: path}
   785  		}
   786  		return err
   787  	}
   788  	if err := manifest.Close(); err != nil {
   789  		if err == swift.ObjectNotFound {
   790  			return storagedriver.PathNotFoundError{Path: path}
   791  		}
   792  		return err
   793  	}
   794  	return nil
   795  }
   796  
   797  func parseManifest(manifest string) (container string, prefix string) {
   798  	components := strings.SplitN(manifest, "/", 2)
   799  	container = components[0]
   800  	if len(components) > 1 {
   801  		prefix = components[1]
   802  	}
   803  	return container, prefix
   804  }
   805  
   806  func generateSecret() (string, error) {
   807  	var secretBytes [32]byte
   808  	if _, err := rand.Read(secretBytes[:]); err != nil {
   809  		return "", fmt.Errorf("could not generate random bytes for Swift secret key: %v", err)
   810  	}
   811  	return hex.EncodeToString(secretBytes[:]), nil
   812  }