1package azblob 2 3import ( 4 "context" 5 "net/url" 6 "strings" 7 8 "github.com/Azure/azure-pipeline-go/pipeline" 9) 10 11const ( 12 // ContainerNameRoot is the special Azure Storage name used to identify a storage account's root container. 13 ContainerNameRoot = "$root" 14 15 // ContainerNameLogs is the special Azure Storage name used to identify a storage account's logs container. 16 ContainerNameLogs = "$logs" 17) 18 19// A ServiceURL represents a URL to the Azure Storage Blob service allowing you to manipulate blob containers. 20type ServiceURL struct { 21 client serviceClient 22} 23 24// NewServiceURL creates a ServiceURL object using the specified URL and request policy pipeline. 25func NewServiceURL(primaryURL url.URL, p pipeline.Pipeline) ServiceURL { 26 client := newServiceClient(primaryURL, p) 27 return ServiceURL{client: client} 28} 29 30//GetUserDelegationCredential obtains a UserDelegationKey object using the base ServiceURL object. 31//OAuth is required for this call, as well as any role that can delegate access to the storage account. 32func (s ServiceURL) GetUserDelegationCredential(ctx context.Context, info KeyInfo, timeout *int32, requestID *string) (UserDelegationCredential, error) { 33 sc := newServiceClient(s.client.url, s.client.p) 34 udk, err := sc.GetUserDelegationKey(ctx, info, timeout, requestID) 35 if err != nil { 36 return UserDelegationCredential{}, err 37 } 38 return NewUserDelegationCredential(strings.Split(s.client.url.Host, ".")[0], *udk), nil 39} 40 41// URL returns the URL endpoint used by the ServiceURL object. 42func (s ServiceURL) URL() url.URL { 43 return s.client.URL() 44} 45 46// String returns the URL as a string. 47func (s ServiceURL) String() string { 48 u := s.URL() 49 return u.String() 50} 51 52// WithPipeline creates a new ServiceURL object identical to the source but with the specified request policy pipeline. 53func (s ServiceURL) WithPipeline(p pipeline.Pipeline) ServiceURL { 54 return NewServiceURL(s.URL(), p) 55} 56 57// NewContainerURL creates a new ContainerURL object by concatenating containerName to the end of 58// ServiceURL's URL. The new ContainerURL uses the same request policy pipeline as the ServiceURL. 59// To change the pipeline, create the ContainerURL and then call its WithPipeline method passing in the 60// desired pipeline object. Or, call this package's NewContainerURL instead of calling this object's 61// NewContainerURL method. 62func (s ServiceURL) NewContainerURL(containerName string) ContainerURL { 63 containerURL := appendToURLPath(s.URL(), containerName) 64 return NewContainerURL(containerURL, s.client.Pipeline()) 65} 66 67// appendToURLPath appends a string to the end of a URL's path (prefixing the string with a '/' if required) 68func appendToURLPath(u url.URL, name string) url.URL { 69 // e.g. "https://ms.com/a/b/?k1=v1&k2=v2#f" 70 // When you call url.Parse() this is what you'll get: 71 // Scheme: "https" 72 // Opaque: "" 73 // User: nil 74 // Host: "ms.com" 75 // Path: "/a/b/" This should start with a / and it might or might not have a trailing slash 76 // RawPath: "" 77 // ForceQuery: false 78 // RawQuery: "k1=v1&k2=v2" 79 // Fragment: "f" 80 if len(u.Path) == 0 || u.Path[len(u.Path)-1] != '/' { 81 u.Path += "/" // Append "/" to end before appending name 82 } 83 u.Path += name 84 return u 85} 86 87// ListContainersFlatSegment returns a single segment of containers starting from the specified Marker. Use an empty 88// Marker to start enumeration from the beginning. Container names are returned in lexicographic order. 89// After getting a segment, process it, and then call ListContainersFlatSegment again (passing the the 90// previously-returned Marker) to get the next segment. For more information, see 91// https://docs.microsoft.com/rest/api/storageservices/list-containers2. 92func (s ServiceURL) ListContainersSegment(ctx context.Context, marker Marker, o ListContainersSegmentOptions) (*ListContainersSegmentResponse, error) { 93 prefix, include, maxResults := o.pointers() 94 return s.client.ListContainersSegment(ctx, prefix, marker.Val, maxResults, include, nil, nil) 95} 96 97// ListContainersOptions defines options available when calling ListContainers. 98type ListContainersSegmentOptions struct { 99 Detail ListContainersDetail // No IncludeType header is produced if "" 100 Prefix string // No Prefix header is produced if "" 101 MaxResults int32 // 0 means unspecified 102 // TODO: update swagger to generate this type? 103} 104 105func (o *ListContainersSegmentOptions) pointers() (prefix *string, include ListContainersIncludeType, maxResults *int32) { 106 if o.Prefix != "" { 107 prefix = &o.Prefix 108 } 109 if o.MaxResults != 0 { 110 maxResults = &o.MaxResults 111 } 112 include = ListContainersIncludeType(o.Detail.string()) 113 return 114} 115 116// ListContainersFlatDetail indicates what additional information the service should return with each container. 117type ListContainersDetail struct { 118 // Tells the service whether to return metadata for each container. 119 Metadata bool 120} 121 122// string produces the Include query parameter's value. 123func (d *ListContainersDetail) string() string { 124 items := make([]string, 0, 1) 125 // NOTE: Multiple strings MUST be appended in alphabetic order or signing the string for authentication fails! 126 if d.Metadata { 127 items = append(items, string(ListContainersIncludeMetadata)) 128 } 129 if len(items) > 0 { 130 return strings.Join(items, ",") 131 } 132 return string(ListContainersIncludeNone) 133} 134 135func (bsu ServiceURL) GetProperties(ctx context.Context) (*StorageServiceProperties, error) { 136 return bsu.client.GetProperties(ctx, nil, nil) 137} 138 139func (bsu ServiceURL) SetProperties(ctx context.Context, properties StorageServiceProperties) (*ServiceSetPropertiesResponse, error) { 140 return bsu.client.SetProperties(ctx, properties, nil, nil) 141} 142 143func (bsu ServiceURL) GetStatistics(ctx context.Context) (*StorageServiceStats, error) { 144 return bsu.client.GetStatistics(ctx, nil, nil) 145} 146