1package storage 2 3// Copyright 2017 Microsoft Corporation 4// 5// Licensed under the Apache License, Version 2.0 (the "License"); 6// you may not use this file except in compliance with the License. 7// You may obtain a copy of the License at 8// 9// http://www.apache.org/licenses/LICENSE-2.0 10// 11// Unless required by applicable law or agreed to in writing, software 12// distributed under the License is distributed on an "AS IS" BASIS, 13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14// See the License for the specific language governing permissions and 15// limitations under the License. 16 17import ( 18 "fmt" 19 "net/http" 20 "net/url" 21 "strconv" 22) 23 24// Share represents an Azure file share. 25type Share struct { 26 fsc *FileServiceClient 27 Name string `xml:"Name"` 28 Properties ShareProperties `xml:"Properties"` 29 Metadata map[string]string 30} 31 32// ShareProperties contains various properties of a share. 33type ShareProperties struct { 34 LastModified string `xml:"Last-Modified"` 35 Etag string `xml:"Etag"` 36 Quota int `xml:"Quota"` 37} 38 39// builds the complete path for this share object. 40func (s *Share) buildPath() string { 41 return fmt.Sprintf("/%s", s.Name) 42} 43 44// Create this share under the associated account. 45// If a share with the same name already exists, the operation fails. 46// 47// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Share 48func (s *Share) Create(options *FileRequestOptions) error { 49 extraheaders := map[string]string{} 50 if s.Properties.Quota > 0 { 51 extraheaders["x-ms-share-quota"] = strconv.Itoa(s.Properties.Quota) 52 } 53 54 params := prepareOptions(options) 55 headers, err := s.fsc.createResource(s.buildPath(), resourceShare, params, mergeMDIntoExtraHeaders(s.Metadata, extraheaders), []int{http.StatusCreated}) 56 if err != nil { 57 return err 58 } 59 60 s.updateEtagAndLastModified(headers) 61 return nil 62} 63 64// CreateIfNotExists creates this share under the associated account if 65// it does not exist. Returns true if the share is newly created or false if 66// the share already exists. 67// 68// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Create-Share 69func (s *Share) CreateIfNotExists(options *FileRequestOptions) (bool, error) { 70 extraheaders := map[string]string{} 71 if s.Properties.Quota > 0 { 72 extraheaders["x-ms-share-quota"] = strconv.Itoa(s.Properties.Quota) 73 } 74 75 params := prepareOptions(options) 76 resp, err := s.fsc.createResourceNoClose(s.buildPath(), resourceShare, params, extraheaders) 77 if resp != nil { 78 defer drainRespBody(resp) 79 if resp.StatusCode == http.StatusCreated || resp.StatusCode == http.StatusConflict { 80 if resp.StatusCode == http.StatusCreated { 81 s.updateEtagAndLastModified(resp.Header) 82 return true, nil 83 } 84 return false, s.FetchAttributes(nil) 85 } 86 } 87 88 return false, err 89} 90 91// Delete marks this share for deletion. The share along with any files 92// and directories contained within it are later deleted during garbage 93// collection. If the share does not exist the operation fails 94// 95// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Share 96func (s *Share) Delete(options *FileRequestOptions) error { 97 return s.fsc.deleteResource(s.buildPath(), resourceShare, options) 98} 99 100// DeleteIfExists operation marks this share for deletion if it exists. 101// 102// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Delete-Share 103func (s *Share) DeleteIfExists(options *FileRequestOptions) (bool, error) { 104 resp, err := s.fsc.deleteResourceNoClose(s.buildPath(), resourceShare, options) 105 if resp != nil { 106 defer drainRespBody(resp) 107 if resp.StatusCode == http.StatusAccepted || resp.StatusCode == http.StatusNotFound { 108 return resp.StatusCode == http.StatusAccepted, nil 109 } 110 } 111 return false, err 112} 113 114// Exists returns true if this share already exists 115// on the storage account, otherwise returns false. 116func (s *Share) Exists() (bool, error) { 117 exists, headers, err := s.fsc.resourceExists(s.buildPath(), resourceShare) 118 if exists { 119 s.updateEtagAndLastModified(headers) 120 s.updateQuota(headers) 121 } 122 return exists, err 123} 124 125// FetchAttributes retrieves metadata and properties for this share. 126// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-share-properties 127func (s *Share) FetchAttributes(options *FileRequestOptions) error { 128 params := prepareOptions(options) 129 headers, err := s.fsc.getResourceHeaders(s.buildPath(), compNone, resourceShare, params, http.MethodHead) 130 if err != nil { 131 return err 132 } 133 134 s.updateEtagAndLastModified(headers) 135 s.updateQuota(headers) 136 s.Metadata = getMetadataFromHeaders(headers) 137 138 return nil 139} 140 141// GetRootDirectoryReference returns a Directory object at the root of this share. 142func (s *Share) GetRootDirectoryReference() *Directory { 143 return &Directory{ 144 fsc: s.fsc, 145 share: s, 146 } 147} 148 149// ServiceClient returns the FileServiceClient associated with this share. 150func (s *Share) ServiceClient() *FileServiceClient { 151 return s.fsc 152} 153 154// SetMetadata replaces the metadata for this share. 155// 156// Some keys may be converted to Camel-Case before sending. All keys 157// are returned in lower case by GetShareMetadata. HTTP header names 158// are case-insensitive so case munging should not matter to other 159// applications either. 160// 161// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-share-metadata 162func (s *Share) SetMetadata(options *FileRequestOptions) error { 163 headers, err := s.fsc.setResourceHeaders(s.buildPath(), compMetadata, resourceShare, mergeMDIntoExtraHeaders(s.Metadata, nil), options) 164 if err != nil { 165 return err 166 } 167 168 s.updateEtagAndLastModified(headers) 169 return nil 170} 171 172// SetProperties sets system properties for this share. 173// 174// Some keys may be converted to Camel-Case before sending. All keys 175// are returned in lower case by SetShareProperties. HTTP header names 176// are case-insensitive so case munging should not matter to other 177// applications either. 178// 179// See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/Set-Share-Properties 180func (s *Share) SetProperties(options *FileRequestOptions) error { 181 extraheaders := map[string]string{} 182 if s.Properties.Quota > 0 { 183 if s.Properties.Quota > 5120 { 184 return fmt.Errorf("invalid value %v for quota, valid values are [1, 5120]", s.Properties.Quota) 185 } 186 extraheaders["x-ms-share-quota"] = strconv.Itoa(s.Properties.Quota) 187 } 188 189 headers, err := s.fsc.setResourceHeaders(s.buildPath(), compProperties, resourceShare, extraheaders, options) 190 if err != nil { 191 return err 192 } 193 194 s.updateEtagAndLastModified(headers) 195 return nil 196} 197 198// updates Etag and last modified date 199func (s *Share) updateEtagAndLastModified(headers http.Header) { 200 s.Properties.Etag = headers.Get("Etag") 201 s.Properties.LastModified = headers.Get("Last-Modified") 202} 203 204// updates quota value 205func (s *Share) updateQuota(headers http.Header) { 206 quota, err := strconv.Atoi(headers.Get("x-ms-share-quota")) 207 if err == nil { 208 s.Properties.Quota = quota 209 } 210} 211 212// URL gets the canonical URL to this share. This method does not create a publicly accessible 213// URL if the share is private and this method does not check if the share exists. 214func (s *Share) URL() string { 215 return s.fsc.client.getEndpoint(fileServiceName, s.buildPath(), url.Values{}) 216} 217