339 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			339 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
| package storage
 | |
| 
 | |
| // Copyright 2017 Microsoft Corporation
 | |
| //
 | |
| //  Licensed under the Apache License, Version 2.0 (the "License");
 | |
| //  you may not use this file except in compliance with the License.
 | |
| //  You may obtain a copy of the License at
 | |
| //
 | |
| //      http://www.apache.org/licenses/LICENSE-2.0
 | |
| //
 | |
| //  Unless required by applicable law or agreed to in writing, software
 | |
| //  distributed under the License is distributed on an "AS IS" BASIS,
 | |
| //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
| //  See the License for the specific language governing permissions and
 | |
| //  limitations under the License.
 | |
| 
 | |
| import (
 | |
| 	"encoding/xml"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"net/url"
 | |
| 	"strconv"
 | |
| )
 | |
| 
 | |
| // FileServiceClient contains operations for Microsoft Azure File Service.
 | |
| type FileServiceClient struct {
 | |
| 	client Client
 | |
| 	auth   authentication
 | |
| }
 | |
| 
 | |
| // ListSharesParameters defines the set of customizable parameters to make a
 | |
| // List Shares call.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Shares
 | |
| type ListSharesParameters struct {
 | |
| 	Prefix     string
 | |
| 	Marker     string
 | |
| 	Include    string
 | |
| 	MaxResults uint
 | |
| 	Timeout    uint
 | |
| }
 | |
| 
 | |
| // ShareListResponse contains the response fields from
 | |
| // ListShares call.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/List-Shares
 | |
| type ShareListResponse struct {
 | |
| 	XMLName    xml.Name `xml:"EnumerationResults"`
 | |
| 	Xmlns      string   `xml:"xmlns,attr"`
 | |
| 	Prefix     string   `xml:"Prefix"`
 | |
| 	Marker     string   `xml:"Marker"`
 | |
| 	NextMarker string   `xml:"NextMarker"`
 | |
| 	MaxResults int64    `xml:"MaxResults"`
 | |
| 	Shares     []Share  `xml:"Shares>Share"`
 | |
| }
 | |
| 
 | |
| type compType string
 | |
| 
 | |
| const (
 | |
| 	compNone       compType = ""
 | |
| 	compList       compType = "list"
 | |
| 	compMetadata   compType = "metadata"
 | |
| 	compProperties compType = "properties"
 | |
| 	compRangeList  compType = "rangelist"
 | |
| )
 | |
| 
 | |
| func (ct compType) String() string {
 | |
| 	return string(ct)
 | |
| }
 | |
| 
 | |
| type resourceType string
 | |
| 
 | |
| const (
 | |
| 	resourceDirectory resourceType = "directory"
 | |
| 	resourceFile      resourceType = ""
 | |
| 	resourceShare     resourceType = "share"
 | |
| )
 | |
| 
 | |
| func (rt resourceType) String() string {
 | |
| 	return string(rt)
 | |
| }
 | |
| 
 | |
| func (p ListSharesParameters) getParameters() url.Values {
 | |
| 	out := url.Values{}
 | |
| 
 | |
| 	if p.Prefix != "" {
 | |
| 		out.Set("prefix", p.Prefix)
 | |
| 	}
 | |
| 	if p.Marker != "" {
 | |
| 		out.Set("marker", p.Marker)
 | |
| 	}
 | |
| 	if p.Include != "" {
 | |
| 		out.Set("include", p.Include)
 | |
| 	}
 | |
| 	if p.MaxResults != 0 {
 | |
| 		out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
 | |
| 	}
 | |
| 	if p.Timeout != 0 {
 | |
| 		out.Set("timeout", strconv.FormatUint(uint64(p.Timeout), 10))
 | |
| 	}
 | |
| 
 | |
| 	return out
 | |
| }
 | |
| 
 | |
| func (p ListDirsAndFilesParameters) getParameters() url.Values {
 | |
| 	out := url.Values{}
 | |
| 
 | |
| 	if p.Prefix != "" {
 | |
| 		out.Set("prefix", p.Prefix)
 | |
| 	}
 | |
| 	if p.Marker != "" {
 | |
| 		out.Set("marker", p.Marker)
 | |
| 	}
 | |
| 	if p.MaxResults != 0 {
 | |
| 		out.Set("maxresults", strconv.FormatUint(uint64(p.MaxResults), 10))
 | |
| 	}
 | |
| 	out = addTimeout(out, p.Timeout)
 | |
| 
 | |
| 	return out
 | |
| }
 | |
| 
 | |
| // returns url.Values for the specified types
 | |
| func getURLInitValues(comp compType, res resourceType) url.Values {
 | |
| 	values := url.Values{}
 | |
| 	if comp != compNone {
 | |
| 		values.Set("comp", comp.String())
 | |
| 	}
 | |
| 	if res != resourceFile {
 | |
| 		values.Set("restype", res.String())
 | |
| 	}
 | |
| 	return values
 | |
| }
 | |
| 
 | |
| // GetShareReference returns a Share object for the specified share name.
 | |
| func (f *FileServiceClient) GetShareReference(name string) *Share {
 | |
| 	return &Share{
 | |
| 		fsc:  f,
 | |
| 		Name: name,
 | |
| 		Properties: ShareProperties{
 | |
| 			Quota: -1,
 | |
| 		},
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ListShares returns the list of shares in a storage account along with
 | |
| // pagination token and other response details.
 | |
| //
 | |
| // See https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/list-shares
 | |
| func (f FileServiceClient) ListShares(params ListSharesParameters) (*ShareListResponse, error) {
 | |
| 	q := mergeParams(params.getParameters(), url.Values{"comp": {"list"}})
 | |
| 
 | |
| 	var out ShareListResponse
 | |
| 	resp, err := f.listContent("", q, nil)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer resp.Body.Close()
 | |
| 	err = xmlUnmarshal(resp.Body, &out)
 | |
| 
 | |
| 	// assign our client to the newly created Share objects
 | |
| 	for i := range out.Shares {
 | |
| 		out.Shares[i].fsc = &f
 | |
| 	}
 | |
| 	return &out, err
 | |
| }
 | |
| 
 | |
| // GetServiceProperties gets the properties of your storage account's file service.
 | |
| // File service does not support logging
 | |
| // See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/get-file-service-properties
 | |
| func (f *FileServiceClient) GetServiceProperties() (*ServiceProperties, error) {
 | |
| 	return f.client.getServiceProperties(fileServiceName, f.auth)
 | |
| }
 | |
| 
 | |
| // SetServiceProperties sets the properties of your storage account's file service.
 | |
| // File service does not support logging
 | |
| // See: https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/set-file-service-properties
 | |
| func (f *FileServiceClient) SetServiceProperties(props ServiceProperties) error {
 | |
| 	return f.client.setServiceProperties(props, fileServiceName, f.auth)
 | |
| }
 | |
| 
 | |
| // retrieves directory or share content
 | |
| func (f FileServiceClient) listContent(path string, params url.Values, extraHeaders map[string]string) (*http.Response, error) {
 | |
| 	if err := f.checkForStorageEmulator(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	uri := f.client.getEndpoint(fileServiceName, path, params)
 | |
| 	extraHeaders = f.client.protectUserAgent(extraHeaders)
 | |
| 	headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
 | |
| 
 | |
| 	resp, err := f.client.exec(http.MethodGet, uri, headers, nil, f.auth)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
 | |
| 		drainRespBody(resp)
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return resp, nil
 | |
| }
 | |
| 
 | |
| // returns true if the specified resource exists
 | |
| func (f FileServiceClient) resourceExists(path string, res resourceType) (bool, http.Header, error) {
 | |
| 	if err := f.checkForStorageEmulator(); err != nil {
 | |
| 		return false, nil, err
 | |
| 	}
 | |
| 
 | |
| 	uri := f.client.getEndpoint(fileServiceName, path, getURLInitValues(compNone, res))
 | |
| 	headers := f.client.getStandardHeaders()
 | |
| 
 | |
| 	resp, err := f.client.exec(http.MethodHead, uri, headers, nil, f.auth)
 | |
| 	if resp != nil {
 | |
| 		defer drainRespBody(resp)
 | |
| 		if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusNotFound {
 | |
| 			return resp.StatusCode == http.StatusOK, resp.Header, nil
 | |
| 		}
 | |
| 	}
 | |
| 	return false, nil, err
 | |
| }
 | |
| 
 | |
| // creates a resource depending on the specified resource type
 | |
| func (f FileServiceClient) createResource(path string, res resourceType, urlParams url.Values, extraHeaders map[string]string, expectedResponseCodes []int) (http.Header, error) {
 | |
| 	resp, err := f.createResourceNoClose(path, res, urlParams, extraHeaders)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer drainRespBody(resp)
 | |
| 	return resp.Header, checkRespCode(resp, expectedResponseCodes)
 | |
| }
 | |
| 
 | |
| // creates a resource depending on the specified resource type, doesn't close the response body
 | |
| func (f FileServiceClient) createResourceNoClose(path string, res resourceType, urlParams url.Values, extraHeaders map[string]string) (*http.Response, error) {
 | |
| 	if err := f.checkForStorageEmulator(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	values := getURLInitValues(compNone, res)
 | |
| 	combinedParams := mergeParams(values, urlParams)
 | |
| 	uri := f.client.getEndpoint(fileServiceName, path, combinedParams)
 | |
| 	extraHeaders = f.client.protectUserAgent(extraHeaders)
 | |
| 	headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
 | |
| 
 | |
| 	return f.client.exec(http.MethodPut, uri, headers, nil, f.auth)
 | |
| }
 | |
| 
 | |
| // returns HTTP header data for the specified directory or share
 | |
| func (f FileServiceClient) getResourceHeaders(path string, comp compType, res resourceType, params url.Values, verb string) (http.Header, error) {
 | |
| 	resp, err := f.getResourceNoClose(path, comp, res, params, verb, nil)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer drainRespBody(resp)
 | |
| 
 | |
| 	if err = checkRespCode(resp, []int{http.StatusOK}); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	return resp.Header, nil
 | |
| }
 | |
| 
 | |
| // gets the specified resource, doesn't close the response body
 | |
| func (f FileServiceClient) getResourceNoClose(path string, comp compType, res resourceType, params url.Values, verb string, extraHeaders map[string]string) (*http.Response, error) {
 | |
| 	if err := f.checkForStorageEmulator(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	params = mergeParams(params, getURLInitValues(comp, res))
 | |
| 	uri := f.client.getEndpoint(fileServiceName, path, params)
 | |
| 	headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
 | |
| 
 | |
| 	return f.client.exec(verb, uri, headers, nil, f.auth)
 | |
| }
 | |
| 
 | |
| // deletes the resource and returns the response
 | |
| func (f FileServiceClient) deleteResource(path string, res resourceType, options *FileRequestOptions) error {
 | |
| 	resp, err := f.deleteResourceNoClose(path, res, options)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	defer drainRespBody(resp)
 | |
| 	return checkRespCode(resp, []int{http.StatusAccepted})
 | |
| }
 | |
| 
 | |
| // deletes the resource and returns the response, doesn't close the response body
 | |
| func (f FileServiceClient) deleteResourceNoClose(path string, res resourceType, options *FileRequestOptions) (*http.Response, error) {
 | |
| 	if err := f.checkForStorageEmulator(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	values := mergeParams(getURLInitValues(compNone, res), prepareOptions(options))
 | |
| 	uri := f.client.getEndpoint(fileServiceName, path, values)
 | |
| 	return f.client.exec(http.MethodDelete, uri, f.client.getStandardHeaders(), nil, f.auth)
 | |
| }
 | |
| 
 | |
| // merges metadata into extraHeaders and returns extraHeaders
 | |
| func mergeMDIntoExtraHeaders(metadata, extraHeaders map[string]string) map[string]string {
 | |
| 	if metadata == nil && extraHeaders == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if extraHeaders == nil {
 | |
| 		extraHeaders = make(map[string]string)
 | |
| 	}
 | |
| 	for k, v := range metadata {
 | |
| 		extraHeaders[userDefinedMetadataHeaderPrefix+k] = v
 | |
| 	}
 | |
| 	return extraHeaders
 | |
| }
 | |
| 
 | |
| // sets extra header data for the specified resource
 | |
| func (f FileServiceClient) setResourceHeaders(path string, comp compType, res resourceType, extraHeaders map[string]string, options *FileRequestOptions) (http.Header, error) {
 | |
| 	if err := f.checkForStorageEmulator(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	params := mergeParams(getURLInitValues(comp, res), prepareOptions(options))
 | |
| 	uri := f.client.getEndpoint(fileServiceName, path, params)
 | |
| 	extraHeaders = f.client.protectUserAgent(extraHeaders)
 | |
| 	headers := mergeHeaders(f.client.getStandardHeaders(), extraHeaders)
 | |
| 
 | |
| 	resp, err := f.client.exec(http.MethodPut, uri, headers, nil, f.auth)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer drainRespBody(resp)
 | |
| 
 | |
| 	return resp.Header, checkRespCode(resp, []int{http.StatusOK})
 | |
| }
 | |
| 
 | |
| //checkForStorageEmulator determines if the client is setup for use with
 | |
| //Azure Storage Emulator, and returns a relevant error
 | |
| func (f FileServiceClient) checkForStorageEmulator() error {
 | |
| 	if f.client.accountName == StorageEmulatorAccountName {
 | |
| 		return fmt.Errorf("Error: File service is not currently supported by Azure Storage Emulator")
 | |
| 	}
 | |
| 	return nil
 | |
| }
 |