...

Source file src/github.com/Azure/azure-sdk-for-go/storage/container.go

Documentation: github.com/Azure/azure-sdk-for-go/storage

     1  package storage
     2  
     3  // Copyright (c) Microsoft Corporation. All rights reserved.
     4  // Licensed under the MIT License. See License.txt in the project root for license information.
     5  
     6  import (
     7  	"encoding/xml"
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"net/url"
    12  	"strconv"
    13  	"strings"
    14  	"time"
    15  )
    16  
    17  // Container represents an Azure container.
    18  type Container struct {
    19  	bsc        *BlobStorageClient
    20  	Name       string              `xml:"Name"`
    21  	Properties ContainerProperties `xml:"Properties"`
    22  	Metadata   map[string]string
    23  	sasuri     url.URL
    24  }
    25  
    26  // Client returns the HTTP client used by the Container reference.
    27  func (c *Container) Client() *Client {
    28  	return &c.bsc.client
    29  }
    30  
    31  func (c *Container) buildPath() string {
    32  	return fmt.Sprintf("/%s", c.Name)
    33  }
    34  
    35  // GetURL gets the canonical URL to the container.
    36  // This method does not create a publicly accessible URL if the container
    37  // is private and this method does not check if the blob exists.
    38  func (c *Container) GetURL() string {
    39  	container := c.Name
    40  	if container == "" {
    41  		container = "$root"
    42  	}
    43  	return c.bsc.client.getEndpoint(blobServiceName, pathForResource(container, ""), nil)
    44  }
    45  
    46  // ContainerSASOptions are options to construct a container SAS
    47  // URI.
    48  // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
    49  type ContainerSASOptions struct {
    50  	ContainerSASPermissions
    51  	OverrideHeaders
    52  	SASOptions
    53  }
    54  
    55  // ContainerSASPermissions includes the available permissions for
    56  // a container SAS URI.
    57  type ContainerSASPermissions struct {
    58  	BlobServiceSASPermissions
    59  	List bool
    60  }
    61  
    62  // GetSASURI creates an URL to the container which contains the Shared
    63  // Access Signature with the specified options.
    64  //
    65  // See https://docs.microsoft.com/en-us/rest/api/storageservices/constructing-a-service-sas
    66  func (c *Container) GetSASURI(options ContainerSASOptions) (string, error) {
    67  	uri := c.GetURL()
    68  	signedResource := "c"
    69  	canonicalizedResource, err := c.bsc.client.buildCanonicalizedResource(uri, c.bsc.auth, true)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  
    74  	// build permissions string
    75  	permissions := options.BlobServiceSASPermissions.buildString()
    76  	if options.List {
    77  		permissions += "l"
    78  	}
    79  
    80  	return c.bsc.client.blobAndFileSASURI(options.SASOptions, uri, permissions, canonicalizedResource, signedResource, options.OverrideHeaders)
    81  }
    82  
    83  // ContainerProperties contains various properties of a container returned from
    84  // various endpoints like ListContainers.
    85  type ContainerProperties struct {
    86  	LastModified  string              `xml:"Last-Modified"`
    87  	Etag          string              `xml:"Etag"`
    88  	LeaseStatus   string              `xml:"LeaseStatus"`
    89  	LeaseState    string              `xml:"LeaseState"`
    90  	LeaseDuration string              `xml:"LeaseDuration"`
    91  	PublicAccess  ContainerAccessType `xml:"PublicAccess"`
    92  }
    93  
    94  // ContainerListResponse contains the response fields from
    95  // ListContainers call.
    96  //
    97  // See https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx
    98  type ContainerListResponse struct {
    99  	XMLName    xml.Name    `xml:"EnumerationResults"`
   100  	Xmlns      string      `xml:"xmlns,attr"`
   101  	Prefix     string      `xml:"Prefix"`
   102  	Marker     string      `xml:"Marker"`
   103  	NextMarker string      `xml:"NextMarker"`
   104  	MaxResults int64       `xml:"MaxResults"`
   105  	Containers []Container `xml:"Containers>Container"`
   106  }
   107  
   108  // BlobListResponse contains the response fields from ListBlobs call.
   109  //
   110  // See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx
   111  type BlobListResponse struct {
   112  	XMLName    xml.Name `xml:"EnumerationResults"`
   113  	Xmlns      string   `xml:"xmlns,attr"`
   114  	Prefix     string   `xml:"Prefix"`
   115  	Marker     string   `xml:"Marker"`
   116  	NextMarker string   `xml:"NextMarker"`
   117  	MaxResults int64    `xml:"MaxResults"`
   118  	Blobs      []Blob   `xml:"Blobs>Blob"`
   119  
   120  	// BlobPrefix is used to traverse blobs as if it were a file system.
   121  	// It is returned if ListBlobsParameters.Delimiter is specified.
   122  	// The list here can be thought of as "folders" that may contain
   123  	// other folders or blobs.
   124  	BlobPrefixes []string `xml:"Blobs>BlobPrefix>Name"`
   125  
   126  	// Delimiter is used to traverse blobs as if it were a file system.
   127  	// It is returned if ListBlobsParameters.Delimiter is specified.
   128  	Delimiter string `xml:"Delimiter"`
   129  }
   130  
   131  // IncludeBlobDataset has options to include in a list blobs operation
   132  type IncludeBlobDataset struct {
   133  	Snapshots        bool
   134  	Metadata         bool
   135  	UncommittedBlobs bool
   136  	Copy             bool
   137  }
   138  
   139  // ListBlobsParameters defines the set of customizable
   140  // parameters to make a List Blobs call.
   141  //
   142  // See https://msdn.microsoft.com/en-us/library/azure/dd135734.aspx
   143  type ListBlobsParameters struct {
   144  	Prefix     string
   145  	Delimiter  string
   146  	Marker     string
   147  	Include    *IncludeBlobDataset
   148  	MaxResults uint
   149  	Timeout    uint
   150  	RequestID  string
   151  }
   152  
   153  func (p ListBlobsParameters) getParameters() url.Values {
   154  	out := url.Values{}
   155  
   156  	if p.Prefix != "" {
   157  		out.Set("prefix", p.Prefix)
   158  	}
   159  	if p.Delimiter != "" {
   160  		out.Set("delimiter", p.Delimiter)
   161  	}
   162  	if p.Marker != "" {
   163  		out.Set("marker", p.Marker)
   164  	}
   165  	if p.Include != nil {
   166  		include := []string{}
   167  		include = addString(include, p.Include.Snapshots, "snapshots")
   168  		include = addString(include, p.Include.Metadata, "metadata")
   169  		include = addString(include, p.Include.UncommittedBlobs, "uncommittedblobs")
   170  		include = addString(include, p.Include.Copy, "copy")
   171  		fullInclude := strings.Join(include, ",")
   172  		out.Set("include", fullInclude)
   173  	}
   174  	if p.MaxResults != 0 {
   175  		out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
   176  	}
   177  	if p.Timeout != 0 {
   178  		out.Set("timeout", strconv.FormatUint(uint64(p.Timeout), 10))
   179  	}
   180  
   181  	return out
   182  }
   183  
   184  func addString(datasets []string, include bool, text string) []string {
   185  	if include {
   186  		datasets = append(datasets, text)
   187  	}
   188  	return datasets
   189  }
   190  
   191  // ContainerAccessType defines the access level to the container from a public
   192  // request.
   193  //
   194  // See https://msdn.microsoft.com/en-us/library/azure/dd179468.aspx and "x-ms-
   195  // blob-public-access" header.
   196  type ContainerAccessType string
   197  
   198  // Access options for containers
   199  const (
   200  	ContainerAccessTypePrivate   ContainerAccessType = ""
   201  	ContainerAccessTypeBlob      ContainerAccessType = "blob"
   202  	ContainerAccessTypeContainer ContainerAccessType = "container"
   203  )
   204  
   205  // ContainerAccessPolicy represents each access policy in the container ACL.
   206  type ContainerAccessPolicy struct {
   207  	ID         string
   208  	StartTime  time.Time
   209  	ExpiryTime time.Time
   210  	CanRead    bool
   211  	CanWrite   bool
   212  	CanDelete  bool
   213  }
   214  
   215  // ContainerPermissions represents the container ACLs.
   216  type ContainerPermissions struct {
   217  	AccessType     ContainerAccessType
   218  	AccessPolicies []ContainerAccessPolicy
   219  }
   220  
   221  // ContainerAccessHeader references header used when setting/getting container ACL
   222  const (
   223  	ContainerAccessHeader string = "x-ms-blob-public-access"
   224  )
   225  
   226  // GetBlobReference returns a Blob object for the specified blob name.
   227  func (c *Container) GetBlobReference(name string) *Blob {
   228  	return &Blob{
   229  		Container: c,
   230  		Name:      name,
   231  	}
   232  }
   233  
   234  // CreateContainerOptions includes the options for a create container operation
   235  type CreateContainerOptions struct {
   236  	Timeout   uint
   237  	Access    ContainerAccessType `header:"x-ms-blob-public-access"`
   238  	RequestID string              `header:"x-ms-client-request-id"`
   239  }
   240  
   241  // Create creates a blob container within the storage account
   242  // with given name and access level. Returns error if container already exists.
   243  //
   244  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Container
   245  func (c *Container) Create(options *CreateContainerOptions) error {
   246  	resp, err := c.create(options)
   247  	if err != nil {
   248  		return err
   249  	}
   250  	defer drainRespBody(resp)
   251  	return checkRespCode(resp, []int{http.StatusCreated})
   252  }
   253  
   254  // CreateIfNotExists creates a blob container if it does not exist. Returns
   255  // true if container is newly created or false if container already exists.
   256  func (c *Container) CreateIfNotExists(options *CreateContainerOptions) (bool, error) {
   257  	resp, err := c.create(options)
   258  	if resp != nil {
   259  		defer drainRespBody(resp)
   260  		if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusConflict {
   261  			return resp.StatusCode == http.StatusCreated, nil
   262  		}
   263  	}
   264  	return false, err
   265  }
   266  
   267  func (c *Container) create(options *CreateContainerOptions) (*http.Response, error) {
   268  	query := url.Values{"restype": {"container"}}
   269  	headers := c.bsc.client.getStandardHeaders()
   270  	headers = c.bsc.client.addMetadataToHeaders(headers, c.Metadata)
   271  
   272  	if options != nil {
   273  		query = addTimeout(query, options.Timeout)
   274  		headers = mergeHeaders(headers, headersFromStruct(*options))
   275  	}
   276  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), query)
   277  
   278  	return c.bsc.client.exec(http.MethodPut, uri, headers, nil, c.bsc.auth)
   279  }
   280  
   281  // Exists returns true if a container with given name exists
   282  // on the storage account, otherwise returns false.
   283  func (c *Container) Exists() (bool, error) {
   284  	q := url.Values{"restype": {"container"}}
   285  	var uri string
   286  	if c.bsc.client.isServiceSASClient() {
   287  		q = mergeParams(q, c.sasuri.Query())
   288  		newURI := c.sasuri
   289  		newURI.RawQuery = q.Encode()
   290  		uri = newURI.String()
   291  
   292  	} else {
   293  		uri = c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), q)
   294  	}
   295  	headers := c.bsc.client.getStandardHeaders()
   296  
   297  	resp, err := c.bsc.client.exec(http.MethodHead, uri, headers, nil, c.bsc.auth)
   298  	if resp != nil {
   299  		defer drainRespBody(resp)
   300  		if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound {
   301  			return resp.StatusCode == http.StatusOK, nil
   302  		}
   303  	}
   304  	return false, err
   305  }
   306  
   307  // SetContainerPermissionOptions includes options for a set container permissions operation
   308  type SetContainerPermissionOptions struct {
   309  	Timeout           uint
   310  	LeaseID           string     `header:"x-ms-lease-id"`
   311  	IfModifiedSince   *time.Time `header:"If-Modified-Since"`
   312  	IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
   313  	RequestID         string     `header:"x-ms-client-request-id"`
   314  }
   315  
   316  // SetPermissions sets up container permissions
   317  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Container-ACL
   318  func (c *Container) SetPermissions(permissions ContainerPermissions, options *SetContainerPermissionOptions) error {
   319  	body, length, err := generateContainerACLpayload(permissions.AccessPolicies)
   320  	if err != nil {
   321  		return err
   322  	}
   323  	params := url.Values{
   324  		"restype": {"container"},
   325  		"comp":    {"acl"},
   326  	}
   327  	headers := c.bsc.client.getStandardHeaders()
   328  	headers = addToHeaders(headers, ContainerAccessHeader, string(permissions.AccessType))
   329  	headers["Content-Length"] = strconv.Itoa(length)
   330  
   331  	if options != nil {
   332  		params = addTimeout(params, options.Timeout)
   333  		headers = mergeHeaders(headers, headersFromStruct(*options))
   334  	}
   335  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
   336  
   337  	resp, err := c.bsc.client.exec(http.MethodPut, uri, headers, body, c.bsc.auth)
   338  	if err != nil {
   339  		return err
   340  	}
   341  	defer drainRespBody(resp)
   342  	return checkRespCode(resp, []int{http.StatusOK})
   343  }
   344  
   345  // GetContainerPermissionOptions includes options for a get container permissions operation
   346  type GetContainerPermissionOptions struct {
   347  	Timeout   uint
   348  	LeaseID   string `header:"x-ms-lease-id"`
   349  	RequestID string `header:"x-ms-client-request-id"`
   350  }
   351  
   352  // GetPermissions gets the container permissions as per https://msdn.microsoft.com/en-us/library/azure/dd179469.aspx
   353  // If timeout is 0 then it will not be passed to Azure
   354  // leaseID will only be passed to Azure if populated
   355  func (c *Container) GetPermissions(options *GetContainerPermissionOptions) (*ContainerPermissions, error) {
   356  	params := url.Values{
   357  		"restype": {"container"},
   358  		"comp":    {"acl"},
   359  	}
   360  	headers := c.bsc.client.getStandardHeaders()
   361  
   362  	if options != nil {
   363  		params = addTimeout(params, options.Timeout)
   364  		headers = mergeHeaders(headers, headersFromStruct(*options))
   365  	}
   366  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
   367  
   368  	resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth)
   369  	if err != nil {
   370  		return nil, err
   371  	}
   372  	defer resp.Body.Close()
   373  
   374  	var ap AccessPolicy
   375  	err = xmlUnmarshal(resp.Body, &ap.SignedIdentifiersList)
   376  	if err != nil {
   377  		return nil, err
   378  	}
   379  	return buildAccessPolicy(ap, &resp.Header), nil
   380  }
   381  
   382  func buildAccessPolicy(ap AccessPolicy, headers *http.Header) *ContainerPermissions {
   383  	// containerAccess. Blob, Container, empty
   384  	containerAccess := headers.Get(http.CanonicalHeaderKey(ContainerAccessHeader))
   385  	permissions := ContainerPermissions{
   386  		AccessType:     ContainerAccessType(containerAccess),
   387  		AccessPolicies: []ContainerAccessPolicy{},
   388  	}
   389  
   390  	for _, policy := range ap.SignedIdentifiersList.SignedIdentifiers {
   391  		capd := ContainerAccessPolicy{
   392  			ID:         policy.ID,
   393  			StartTime:  policy.AccessPolicy.StartTime,
   394  			ExpiryTime: policy.AccessPolicy.ExpiryTime,
   395  		}
   396  		capd.CanRead = updatePermissions(policy.AccessPolicy.Permission, "r")
   397  		capd.CanWrite = updatePermissions(policy.AccessPolicy.Permission, "w")
   398  		capd.CanDelete = updatePermissions(policy.AccessPolicy.Permission, "d")
   399  
   400  		permissions.AccessPolicies = append(permissions.AccessPolicies, capd)
   401  	}
   402  	return &permissions
   403  }
   404  
   405  // DeleteContainerOptions includes options for a delete container operation
   406  type DeleteContainerOptions struct {
   407  	Timeout           uint
   408  	LeaseID           string     `header:"x-ms-lease-id"`
   409  	IfModifiedSince   *time.Time `header:"If-Modified-Since"`
   410  	IfUnmodifiedSince *time.Time `header:"If-Unmodified-Since"`
   411  	RequestID         string     `header:"x-ms-client-request-id"`
   412  }
   413  
   414  // Delete deletes the container with given name on the storage
   415  // account. If the container does not exist returns error.
   416  //
   417  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/delete-container
   418  func (c *Container) Delete(options *DeleteContainerOptions) error {
   419  	resp, err := c.delete(options)
   420  	if err != nil {
   421  		return err
   422  	}
   423  	defer drainRespBody(resp)
   424  	return checkRespCode(resp, []int{http.StatusAccepted})
   425  }
   426  
   427  // DeleteIfExists deletes the container with given name on the storage
   428  // account if it exists. Returns true if container is deleted with this call, or
   429  // false if the container did not exist at the time of the Delete Container
   430  // operation.
   431  //
   432  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/delete-container
   433  func (c *Container) DeleteIfExists(options *DeleteContainerOptions) (bool, error) {
   434  	resp, err := c.delete(options)
   435  	if resp != nil {
   436  		defer drainRespBody(resp)
   437  		if resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusNotFound {
   438  			return resp.StatusCode == http.StatusAccepted, nil
   439  		}
   440  	}
   441  	return false, err
   442  }
   443  
   444  func (c *Container) delete(options *DeleteContainerOptions) (*http.Response, error) {
   445  	query := url.Values{"restype": {"container"}}
   446  	headers := c.bsc.client.getStandardHeaders()
   447  
   448  	if options != nil {
   449  		query = addTimeout(query, options.Timeout)
   450  		headers = mergeHeaders(headers, headersFromStruct(*options))
   451  	}
   452  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), query)
   453  
   454  	return c.bsc.client.exec(http.MethodDelete, uri, headers, nil, c.bsc.auth)
   455  }
   456  
   457  // ListBlobs returns an object that contains list of blobs in the container,
   458  // pagination token and other information in the response of List Blobs call.
   459  //
   460  // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Blobs
   461  func (c *Container) ListBlobs(params ListBlobsParameters) (BlobListResponse, error) {
   462  	q := mergeParams(params.getParameters(), url.Values{
   463  		"restype": {"container"},
   464  		"comp":    {"list"},
   465  	})
   466  	var uri string
   467  	if c.bsc.client.isServiceSASClient() {
   468  		q = mergeParams(q, c.sasuri.Query())
   469  		newURI := c.sasuri
   470  		newURI.RawQuery = q.Encode()
   471  		uri = newURI.String()
   472  	} else {
   473  		uri = c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), q)
   474  	}
   475  
   476  	headers := c.bsc.client.getStandardHeaders()
   477  	headers = addToHeaders(headers, "x-ms-client-request-id", params.RequestID)
   478  
   479  	var out BlobListResponse
   480  	resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth)
   481  	if err != nil {
   482  		return out, err
   483  	}
   484  	defer resp.Body.Close()
   485  
   486  	err = xmlUnmarshal(resp.Body, &out)
   487  	for i := range out.Blobs {
   488  		out.Blobs[i].Container = c
   489  	}
   490  	return out, err
   491  }
   492  
   493  // ContainerMetadataOptions includes options for container metadata operations
   494  type ContainerMetadataOptions struct {
   495  	Timeout   uint
   496  	LeaseID   string `header:"x-ms-lease-id"`
   497  	RequestID string `header:"x-ms-client-request-id"`
   498  }
   499  
   500  // SetMetadata replaces the metadata for the specified container.
   501  //
   502  // Some keys may be converted to Camel-Case before sending. All keys
   503  // are returned in lower case by GetBlobMetadata. HTTP header names
   504  // are case-insensitive so case munging should not matter to other
   505  // applications either.
   506  //
   507  // See https://docs.microsoft.com/en-us/rest/api/storageservices/set-container-metadata
   508  func (c *Container) SetMetadata(options *ContainerMetadataOptions) error {
   509  	params := url.Values{
   510  		"comp":    {"metadata"},
   511  		"restype": {"container"},
   512  	}
   513  	headers := c.bsc.client.getStandardHeaders()
   514  	headers = c.bsc.client.addMetadataToHeaders(headers, c.Metadata)
   515  
   516  	if options != nil {
   517  		params = addTimeout(params, options.Timeout)
   518  		headers = mergeHeaders(headers, headersFromStruct(*options))
   519  	}
   520  
   521  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
   522  
   523  	resp, err := c.bsc.client.exec(http.MethodPut, uri, headers, nil, c.bsc.auth)
   524  	if err != nil {
   525  		return err
   526  	}
   527  	defer drainRespBody(resp)
   528  	return checkRespCode(resp, []int{http.StatusOK})
   529  }
   530  
   531  // GetMetadata returns all user-defined metadata for the specified container.
   532  //
   533  // All metadata keys will be returned in lower case. (HTTP header
   534  // names are case-insensitive.)
   535  //
   536  // See https://docs.microsoft.com/en-us/rest/api/storageservices/get-container-metadata
   537  func (c *Container) GetMetadata(options *ContainerMetadataOptions) error {
   538  	params := url.Values{
   539  		"comp":    {"metadata"},
   540  		"restype": {"container"},
   541  	}
   542  	headers := c.bsc.client.getStandardHeaders()
   543  
   544  	if options != nil {
   545  		params = addTimeout(params, options.Timeout)
   546  		headers = mergeHeaders(headers, headersFromStruct(*options))
   547  	}
   548  
   549  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
   550  
   551  	resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth)
   552  	if err != nil {
   553  		return err
   554  	}
   555  	defer drainRespBody(resp)
   556  	if err := checkRespCode(resp, []int{http.StatusOK}); err != nil {
   557  		return err
   558  	}
   559  
   560  	c.writeMetadata(resp.Header)
   561  	return nil
   562  }
   563  
   564  func (c *Container) writeMetadata(h http.Header) {
   565  	c.Metadata = writeMetadata(h)
   566  }
   567  
   568  func generateContainerACLpayload(policies []ContainerAccessPolicy) (io.Reader, int, error) {
   569  	sil := SignedIdentifiers{
   570  		SignedIdentifiers: []SignedIdentifier{},
   571  	}
   572  	for _, capd := range policies {
   573  		permission := capd.generateContainerPermissions()
   574  		signedIdentifier := convertAccessPolicyToXMLStructs(capd.ID, capd.StartTime, capd.ExpiryTime, permission)
   575  		sil.SignedIdentifiers = append(sil.SignedIdentifiers, signedIdentifier)
   576  	}
   577  	return xmlMarshal(sil)
   578  }
   579  
   580  func (capd *ContainerAccessPolicy) generateContainerPermissions() (permissions string) {
   581  	// generate the permissions string (rwd).
   582  	// still want the end user API to have bool flags.
   583  	permissions = ""
   584  
   585  	if capd.CanRead {
   586  		permissions += "r"
   587  	}
   588  
   589  	if capd.CanWrite {
   590  		permissions += "w"
   591  	}
   592  
   593  	if capd.CanDelete {
   594  		permissions += "d"
   595  	}
   596  
   597  	return permissions
   598  }
   599  
   600  // GetProperties updated the properties of the container.
   601  //
   602  // See https://docs.microsoft.com/en-us/rest/api/storageservices/get-container-properties
   603  func (c *Container) GetProperties() error {
   604  	params := url.Values{
   605  		"restype": {"container"},
   606  	}
   607  	headers := c.bsc.client.getStandardHeaders()
   608  
   609  	uri := c.bsc.client.getEndpoint(blobServiceName, c.buildPath(), params)
   610  
   611  	resp, err := c.bsc.client.exec(http.MethodGet, uri, headers, nil, c.bsc.auth)
   612  	if err != nil {
   613  		return err
   614  	}
   615  	defer resp.Body.Close()
   616  	if err := checkRespCode(resp, []int{http.StatusOK}); err != nil {
   617  		return err
   618  	}
   619  
   620  	// update properties
   621  	c.Properties.Etag = resp.Header.Get(headerEtag)
   622  	c.Properties.LeaseStatus = resp.Header.Get("x-ms-lease-status")
   623  	c.Properties.LeaseState = resp.Header.Get("x-ms-lease-state")
   624  	c.Properties.LeaseDuration = resp.Header.Get("x-ms-lease-duration")
   625  	c.Properties.LastModified = resp.Header.Get("Last-Modified")
   626  	c.Properties.PublicAccess = ContainerAccessType(resp.Header.Get(ContainerAccessHeader))
   627  
   628  	return nil
   629  }
   630  

View as plain text