fileserviceclient.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. package storage
  2. // Copyright 2017 Microsoft Corporation
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. import (
  16. "encoding/xml"
  17. "fmt"
  18. "net/http"
  19. "net/url"
  20. "strconv"
  21. )
  22. // FileServiceClient contains operations for Microsoft Azure File Service.
  23. type FileServiceClient struct {
  24. client Client
  25. auth authentication
  26. }
  27. // ListSharesParameters defines the set of customizable parameters to make a
  28. // List Shares call.
  29. //
  30. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Shares
  31. type ListSharesParameters struct {
  32. Prefix string
  33. Marker string
  34. Include string
  35. MaxResults uint
  36. Timeout uint
  37. }
  38. // ShareListResponse contains the response fields from
  39. // ListShares call.
  40. //
  41. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Shares
  42. type ShareListResponse struct {
  43. XMLName xml.Name `xml:"EnumerationResults"`
  44. Xmlns string `xml:"xmlns,attr"`
  45. Prefix string `xml:"Prefix"`
  46. Marker string `xml:"Marker"`
  47. NextMarker string `xml:"NextMarker"`
  48. MaxResults int64 `xml:"MaxResults"`
  49. Shares []Share `xml:"Shares>Share"`
  50. }
  51. type compType string
  52. const (
  53. compNone compType = ""
  54. compList compType = "list"
  55. compMetadata compType = "metadata"
  56. compProperties compType = "properties"
  57. compRangeList compType = "rangelist"
  58. )
  59. func (ct compType) String() string {
  60. return string(ct)
  61. }
  62. type resourceType string
  63. const (
  64. resourceDirectory resourceType = "directory"
  65. resourceFile resourceType = ""
  66. resourceShare resourceType = "share"
  67. )
  68. func (rt resourceType) String() string {
  69. return string(rt)
  70. }
  71. func (p ListSharesParameters) getParameters() url.Values {
  72. out := url.Values{}
  73. if p.Prefix != "" {
  74. out.Set("prefix", p.Prefix)
  75. }
  76. if p.Marker != "" {
  77. out.Set("marker", p.Marker)
  78. }
  79. if p.Include != "" {
  80. out.Set("include", p.Include)
  81. }
  82. if p.MaxResults != 0 {
  83. out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
  84. }
  85. if p.Timeout != 0 {
  86. out.Set("timeout", strconv.FormatUint(uint64(p.Timeout), 10))
  87. }
  88. return out
  89. }
  90. func (p ListDirsAndFilesParameters) getParameters() url.Values {
  91. out := url.Values{}
  92. if p.Prefix != "" {
  93. out.Set("prefix", p.Prefix)
  94. }
  95. if p.Marker != "" {
  96. out.Set("marker", p.Marker)
  97. }
  98. if p.MaxResults != 0 {
  99. out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
  100. }
  101. out = addTimeout(out, p.Timeout)
  102. return out
  103. }
  104. // returns url.Values for the specified types
  105. func getURLInitValues(comp compType, res resourceType) url.Values {
  106. values := url.Values{}
  107. if comp != compNone {
  108. values.Set("comp", comp.String())
  109. }
  110. if res != resourceFile {
  111. values.Set("restype", res.String())
  112. }
  113. return values
  114. }
  115. // GetShareReference returns a Share object for the specified share name.
  116. func (f *FileServiceClient) GetShareReference(name string) *Share {
  117. return &Share{
  118. fsc: f,
  119. Name: name,
  120. Properties: ShareProperties{
  121. Quota: -1,
  122. },
  123. }
  124. }
  125. // ListShares returns the list of shares in a storage account along with
  126. // pagination token and other response details.
  127. //
  128. // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-shares
  129. func (f FileServiceClient) ListShares(params ListSharesParameters) (*ShareListResponse, error) {
  130. q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}})
  131. var out ShareListResponse
  132. resp, err := f.listContent("", q, nil)
  133. if err != nil {
  134. return nil, err
  135. }
  136. defer resp.Body.Close()
  137. err = xmlUnmarshal(resp.Body, &out)
  138. // assign our client to the newly created Share objects
  139. for i := range out.Shares {
  140. out.Shares[i].fsc = &f
  141. }
  142. return &out, err
  143. }
  144. // GetServiceProperties gets the properties of your storage account's file service.
  145. // File service does not support logging
  146. // See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file-service-properties
  147. func (f *FileServiceClient) GetServiceProperties() (*ServiceProperties, error) {
  148. return f.client.getServiceProperties(fileServiceName, f.auth)
  149. }
  150. // SetServiceProperties sets the properties of your storage account's file service.
  151. // File service does not support logging
  152. // See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-file-service-properties
  153. func (f *FileServiceClient) SetServiceProperties(props ServiceProperties) error {
  154. return f.client.setServiceProperties(props, fileServiceName, f.auth)
  155. }
  156. // retrieves directory or share content
  157. func (f FileServiceClient) listContent(path string, params url.Values, extraHeaders map[string]string) (*http.Response, error) {
  158. if err := f.checkForStorageEmulator(); err != nil {
  159. return nil, err
  160. }
  161. uri := f.client.getEndpoint(fileServiceName, path, params)
  162. extraHeaders = f.client.protectUserAgent(extraHeaders)
  163. headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
  164. resp, err := f.client.exec(http.MethodGet, uri, headers, nil, f.auth)
  165. if err != nil {
  166. return nil, err
  167. }
  168. if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
  169. drainRespBody(resp)
  170. return nil, err
  171. }
  172. return resp, nil
  173. }
  174. // returns true if the specified resource exists
  175. func (f FileServiceClient) resourceExists(path string, res resourceType) (bool, http.Header, error) {
  176. if err := f.checkForStorageEmulator(); err != nil {
  177. return false, nil, err
  178. }
  179. uri := f.client.getEndpoint(fileServiceName, path, getURLInitValues(compNone, res))
  180. headers := f.client.getStandardHeaders()
  181. resp, err := f.client.exec(http.MethodHead, uri, headers, nil, f.auth)
  182. if resp != nil {
  183. defer drainRespBody(resp)
  184. if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound {
  185. return resp.StatusCode == http.StatusOK, resp.Header, nil
  186. }
  187. }
  188. return false, nil, err
  189. }
  190. // creates a resource depending on the specified resource type
  191. func (f FileServiceClient) createResource(path string, res resourceType, urlParams url.Values, extraHeaders map[string]string, expectedResponseCodes []int) (http.Header, error) {
  192. resp, err := f.createResourceNoClose(path, res, urlParams, extraHeaders)
  193. if err != nil {
  194. return nil, err
  195. }
  196. defer drainRespBody(resp)
  197. return resp.Header, checkRespCode(resp, expectedResponseCodes)
  198. }
  199. // creates a resource depending on the specified resource type, doesn't close the response body
  200. func (f FileServiceClient) createResourceNoClose(path string, res resourceType, urlParams url.Values, extraHeaders map[string]string) (*http.Response, error) {
  201. if err := f.checkForStorageEmulator(); err != nil {
  202. return nil, err
  203. }
  204. values := getURLInitValues(compNone, res)
  205. combinedParams := mergeParams(values, urlParams)
  206. uri := f.client.getEndpoint(fileServiceName, path, combinedParams)
  207. extraHeaders = f.client.protectUserAgent(extraHeaders)
  208. headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
  209. return f.client.exec(http.MethodPut, uri, headers, nil, f.auth)
  210. }
  211. // returns HTTP header data for the specified directory or share
  212. func (f FileServiceClient) getResourceHeaders(path string, comp compType, res resourceType, params url.Values, verb string) (http.Header, error) {
  213. resp, err := f.getResourceNoClose(path, comp, res, params, verb, nil)
  214. if err != nil {
  215. return nil, err
  216. }
  217. defer drainRespBody(resp)
  218. if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
  219. return nil, err
  220. }
  221. return resp.Header, nil
  222. }
  223. // gets the specified resource, doesn't close the response body
  224. func (f FileServiceClient) getResourceNoClose(path string, comp compType, res resourceType, params url.Values, verb string, extraHeaders map[string]string) (*http.Response, error) {
  225. if err := f.checkForStorageEmulator(); err != nil {
  226. return nil, err
  227. }
  228. params = mergeParams(params, getURLInitValues(comp, res))
  229. uri := f.client.getEndpoint(fileServiceName, path, params)
  230. headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
  231. return f.client.exec(verb, uri, headers, nil, f.auth)
  232. }
  233. // deletes the resource and returns the response
  234. func (f FileServiceClient) deleteResource(path string, res resourceType, options *FileRequestOptions) error {
  235. resp, err := f.deleteResourceNoClose(path, res, options)
  236. if err != nil {
  237. return err
  238. }
  239. defer drainRespBody(resp)
  240. return checkRespCode(resp, []int{http.StatusAccepted})
  241. }
  242. // deletes the resource and returns the response, doesn't close the response body
  243. func (f FileServiceClient) deleteResourceNoClose(path string, res resourceType, options *FileRequestOptions) (*http.Response, error) {
  244. if err := f.checkForStorageEmulator(); err != nil {
  245. return nil, err
  246. }
  247. values := mergeParams(getURLInitValues(compNone, res), prepareOptions(options))
  248. uri := f.client.getEndpoint(fileServiceName, path, values)
  249. return f.client.exec(http.MethodDelete, uri, f.client.getStandardHeaders(), nil, f.auth)
  250. }
  251. // merges metadata into extraHeaders and returns extraHeaders
  252. func mergeMDIntoExtraHeaders(metadata, extraHeaders map[string]string) map[string]string {
  253. if metadata == nil && extraHeaders == nil {
  254. return nil
  255. }
  256. if extraHeaders == nil {
  257. extraHeaders = make(map[string]string)
  258. }
  259. for k, v := range metadata {
  260. extraHeaders[userDefinedMetadataHeaderPrefix+k] = v
  261. }
  262. return extraHeaders
  263. }
  264. // sets extra header data for the specified resource
  265. func (f FileServiceClient) setResourceHeaders(path string, comp compType, res resourceType, extraHeaders map[string]string, options *FileRequestOptions) (http.Header, error) {
  266. if err := f.checkForStorageEmulator(); err != nil {
  267. return nil, err
  268. }
  269. params := mergeParams(getURLInitValues(comp, res), prepareOptions(options))
  270. uri := f.client.getEndpoint(fileServiceName, path, params)
  271. extraHeaders = f.client.protectUserAgent(extraHeaders)
  272. headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
  273. resp, err := f.client.exec(http.MethodPut, uri, headers, nil, f.auth)
  274. if err != nil {
  275. return nil, err
  276. }
  277. defer drainRespBody(resp)
  278. return resp.Header, checkRespCode(resp, []int{http.StatusOK})
  279. }
  280. //checkForStorageEmulator determines if the client is setup for use with
  281. //Azure Storage Emulator, and returns a relevant error
  282. func (f FileServiceClient) checkForStorageEmulator() error {
  283. if f.client.accountName == StorageEmulatorAccountName {
  284. return fmt.Errorf("Error: File service is not currently supported by Azure Storage Emulator")
  285. }
  286. return nil
  287. }