gopkg.in/goose.v2@v2.0.1/cinder/client.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the LGPLv3, see LICENCE file for details. 3 4 package cinder 5 6 import ( 7 "crypto/tls" 8 "fmt" 9 "net/http" 10 "net/url" 11 "time" 12 13 "gopkg.in/goose.v2/errors" 14 goosehttp "gopkg.in/goose.v2/http" 15 ) 16 17 // Basic returns a basic Cinder client which will handle authorization 18 // of requests, and routing to the correct endpoint. 19 func Basic(endpoint *url.URL, tenantId string, token TokenFn) *Client { 20 return NewClient(tenantId, endpoint, 21 SetAuthHeaderFn(token, http.DefaultClient.Do), 22 ) 23 } 24 25 // BasicTLSConfig returns a basic Cinder client which will handle authorization 26 // of requests where CACerts are required, and routing to the correct endpoint. 27 func BasicTLSConfig(endpoint *url.URL, tenantId string, token TokenFn, tlsConfig *tls.Config) *Client { 28 return NewClient(tenantId, endpoint, 29 AuthHeaderTSLConfigDoRequestFn(token, tlsConfig), 30 ) 31 } 32 33 // TokenFn represents a function signature which returns the user's 34 // authorization token when called. 35 type TokenFn func() string 36 37 // SetAuthHeaderFn returns a RequestHandlerFn which sets the 38 // authentication headers for a given request. 39 func SetAuthHeaderFn(token TokenFn, wrappedHandler RequestHandlerFn) RequestHandlerFn { 40 return func(req *http.Request) (*http.Response, error) { 41 req.Header.Set("X-Auth-Token", token()) 42 return wrappedHandler(req) 43 } 44 } 45 46 // AuthHeaderTSLConfigDoRequestFn returns a RequestHandlerFn which sets the 47 // authentication headers for a given request and provides the 48 // client with a tls config. 49 func AuthHeaderTSLConfigDoRequestFn(token TokenFn, tlsConfig *tls.Config) RequestHandlerFn { 50 return func(req *http.Request) (*http.Response, error) { 51 req.Header.Set("X-Auth-Token", token()) 52 defaultClient := *http.DefaultClient 53 defaultClient.Transport = &http.Transport{ 54 TLSClientConfig: tlsConfig, 55 } 56 return defaultClient.Do(req) 57 } 58 } 59 60 // NewClient is the most flexible way to instantiate a Cinder 61 // Client. The handleRequest function will be responsible for 62 // modifying requests and dispatching them as needed. For an example 63 // of how to utilize this method, see the Basic function. 64 func NewClient(tenantId string, endpoint *url.URL, handleRequest RequestHandlerFn) *Client { 65 if endpoint == nil { 66 return nil 67 } 68 // Ensure the cinder endpoint has a trailing slash on the path. 69 path := endpoint.Path 70 if len(path) != 0 && path[len(path)-1:] != "/" { 71 changedEndpoint := *endpoint 72 changedEndpoint.Path += "/" 73 endpoint = &changedEndpoint 74 } 75 httpClient := goosehttp.New() 76 httpClient.Client = http.Client{Transport: handleRequest} 77 return &Client{tenantId, endpoint, httpClient} 78 } 79 80 // Client is a Cinder client. 81 type Client struct { 82 tenantId string 83 endpoint *url.URL 84 client *goosehttp.Client 85 } 86 87 // TODO(axw) update all callers of handleRequest 88 // to use c.client.JsonRequest instead, so we can 89 // benefit from the common goose error handling. 90 func (c *Client) handleRequest(req *http.Request) (*http.Response, error) { 91 return c.client.Do(req) 92 } 93 94 // GetSnapshot shows information for a specified snapshot. 95 func (c *Client) GetSnapshot(snapshotId string) (*GetSnapshotResults, error) { 96 return getSnapshot( 97 c, 98 GetSnapshotParams{TenantId: c.tenantId, SnapshotId: snapshotId}, 99 ) 100 } 101 102 // UpdateSnapshot updates a specified snapshot. 103 func (c *Client) UpdateSnapshot(snapshotId string, args UpdateSnapshotSnapshotParams) (*UpdateSnapshotResults, error) { 104 return updateSnapshot(c, UpdateSnapshotParams{ 105 TenantId: c.tenantId, 106 SnapshotId: snapshotId, 107 Snapshot: args, 108 }) 109 } 110 111 // DeleteSnapshot deletes a specified snapshot. 112 func (c *Client) DeleteSnapshot(snapshotId string) error { 113 _, err := deleteSnapshot( 114 c, 115 DeleteSnapshotParams{TenantId: c.tenantId, SnapshotId: snapshotId}, 116 ) 117 return err 118 } 119 120 // VersionDetails shows details for Block Storage API v2. 121 func (c *Client) VersionDetails() (*VersionDetailsResults, error) { 122 return versionDetails(c, VersionDetailsParams{}) 123 } 124 125 // ListExtensionsCinderV2 lists Block Storage API extensions. 126 func (c *Client) ListExtensionsCinderV2() (*ListExtensionsCinderV2Results, error) { 127 return listExtensionsCinderV2(c, ListExtensionsCinderV2Params{}) 128 } 129 130 // GetVolumesSimple lists summary information for all Block Storage 131 // volumes that the tenant who submits the request can access. 132 func (c *Client) GetVolumesSimple() (*GetVolumesSimpleResults, error) { 133 return getVolumesSimple(c, GetVolumesSimpleParams{TenantId: c.tenantId}) 134 } 135 136 // UpdateVolumeType updates a volume type. 137 func (c *Client) UpdateVolumeType(volumeTypeId, volumeType string) (*UpdateVolumeTypeResults, error) { 138 return updateVolumeType(c, UpdateVolumeTypeParams{ 139 TenantId: c.tenantId, 140 VolumeTypeId: volumeTypeId, 141 VolumeType: volumeType, 142 }) 143 } 144 145 // DeleteVolumeType deletes a specified volume type. 146 func (c *Client) DeleteVolumeType(volumeTypeId string) error { 147 _, err := deleteVolumeType( 148 c, 149 DeleteVolumeTypeParams{TenantId: c.tenantId, VolumeTypeId: volumeTypeId}, 150 ) 151 return err 152 } 153 154 // GetVolumesDetail lists detailed information for all Block Storage 155 // volumes that the tenant who submits the request can access. 156 func (c *Client) GetVolumesDetail() (*GetVolumesDetailResults, error) { 157 return getVolumesDetail(c, GetVolumesDetailParams{TenantId: c.tenantId}) 158 } 159 160 // GetVolume lists information about the volume with the given 161 // volumeId. 162 func (c *Client) GetVolume(volumeId string) (*GetVolumeResults, error) { 163 return getVolume(c, GetVolumeParams{TenantId: c.tenantId, VolumeId: volumeId}) 164 } 165 166 // CreateVolumeType creates a volume type. 167 func (c *Client) CreateVolumeType(args CreateVolumeTypeVolumeTypeParams) (*CreateVolumeTypeResults, error) { 168 return createVolumeType( 169 c, 170 CreateVolumeTypeParams{TenantId: c.tenantId, VolumeType: args}, 171 ) 172 } 173 174 // GetVolumeType shows information about a specified volume type. 175 func (c *Client) GetVolumeType(volumeTypeId string) (*GetVolumeTypeResults, error) { 176 return getVolumeType( 177 c, 178 GetVolumeTypeParams{TenantId: c.tenantId, VolumeTypeId: volumeTypeId}, 179 ) 180 } 181 182 // ListVersion lists information about all Block Storage API versions. 183 func (c *Client) ListVersions() (*ListVersionsResults, error) { 184 return listVersions(c, ListVersionsParams{}) 185 } 186 187 // UpdateVolumeTypeExtraSpecs updates the extra specifications 188 // assigned to a volume type. 189 func (c *Client) UpdateVolumeTypeExtraSpecs(volumeTypeId, volumeType, extraSpecs string) (*UpdateVolumeTypeExtraSpecsResults, error) { 190 return updateVolumeTypeExtraSpecs(c, UpdateVolumeTypeExtraSpecsParams{ 191 TenantId: c.tenantId, 192 VolumeTypeId: volumeTypeId, 193 VolumeType: volumeType, 194 ExtraSpecs: extraSpecs, 195 }) 196 } 197 198 // GetSnapshotsSimple lists summary information for all Block Storage 199 // snapshots that the tenant who submits the request can access. 200 func (c *Client) GetSnapshotsSimple() (*GetSnapshotsSimpleResults, error) { 201 return getSnapshotsSimple(c, GetSnapshotsSimpleParams{TenantId: c.tenantId}) 202 } 203 204 // ShowSnapshotMetadata shows the metadata for a specified snapshot. 205 func (c *Client) ShowSnapshotMetadata(snapshotId string) (*ShowSnapshotMetadataResults, error) { 206 return showSnapshotMetadata( 207 c, 208 ShowSnapshotMetadataParams{TenantId: c.tenantId, SnapshotId: snapshotId}, 209 ) 210 } 211 212 // CreateSnapshot creates a snapshot, which is a point-in-time 213 // complete copy of a volume. You can create a volume from the 214 // snapshot. 215 func (c *Client) CreateSnapshot(args CreateSnapshotSnapshotParams) (*CreateSnapshotResults, error) { 216 return createSnapshot(c, CreateSnapshotParams{TenantId: c.tenantId, Snapshot: args}) 217 } 218 219 // GetSnapshotsDetail lists detailed information for all Block Storage 220 // snapshots that the tenant who submits the request can access. 221 func (c *Client) GetSnapshotsDetail() (*GetSnapshotsDetailResults, error) { 222 return getSnapshotsDetail(c, GetSnapshotsDetailParams{TenantId: c.tenantId}) 223 } 224 225 // UpdateSnapshotMetadata updates the metadata for a specified 226 // snapshot. 227 func (c *Client) UpdateSnapshotMetadata(snapshotId, key string) (*UpdateSnapshotMetadataResults, error) { 228 return updateSnapshotMetadata(c, UpdateSnapshotMetadataParams{ 229 TenantId: c.tenantId, 230 SnapshotId: snapshotId, 231 Metadata: UpdateSnapshotMetadataMetadataParams{ 232 Key: key, 233 }, 234 }) 235 } 236 237 // CreateVolume creates a volume. To create a bootable volume, include 238 // the image ID and set the bootable flag to true in the request body. 239 // 240 // Preconditions: 241 // 242 // - The user must have enough volume storage quota remaining to create 243 // a volume of size requested. 244 // 245 // Asynchronous Postconditions: 246 // 247 // - With correct permissions, you can see the volume status as 248 // available through API calls. 249 // - With correct access, you can see the created volume in the 250 // storage system that OpenStack Block Storage manages. 251 // 252 // Troubleshooting: 253 // 254 // - If volume status remains creating or shows another error status, 255 // the request failed. Ensure you meet the preconditions then 256 // investigate the storage backend. 257 // - Volume is not created in the storage system which OpenStack Block Storage manages. 258 // - The storage node needs enough free storage space to match the 259 // specified size of the volume creation request. 260 func (c *Client) CreateVolume(args CreateVolumeVolumeParams) (*CreateVolumeResults, error) { 261 return createVolume(c, CreateVolumeParams{TenantId: c.tenantId, Volume: args}) 262 } 263 264 // UpdateVolume updates a volume. 265 func (c *Client) UpdateVolume(volumeId string, args UpdateVolumeVolumeParams) (*UpdateVolumeResults, error) { 266 return updateVolume(c, UpdateVolumeParams{TenantId: c.tenantId, VolumeId: volumeId, Volume: args}) 267 } 268 269 // DeleteVolume flags a volume for deletion. The volume managed by 270 // OpenStack Block Storage is not deleted from the storage system. 271 func (c *Client) DeleteVolume(volumeId string) error { 272 _, err := deleteVolume( 273 c, 274 DeleteVolumeParams{TenantId: c.tenantId, VolumeId: volumeId}, 275 ) 276 return err 277 } 278 279 // GetVolumeTypes lists volume types. 280 func (c *Client) GetVolumeTypes() (*GetVolumeTypesResults, error) { 281 return getVolumeTypes(c, GetVolumeTypesParams{TenantId: c.tenantId}) 282 } 283 284 type predicateFn func() (bool, error) 285 286 func notifier(predicate predicateFn, numAttempts int, waitDur time.Duration) <-chan error { 287 notifierChan := make(chan error) 288 go func() { 289 defer close(notifierChan) 290 for attemptNum := 0; attemptNum < numAttempts; attemptNum++ { 291 if ok, err := predicate(); err != nil { 292 notifierChan <- err 293 return 294 } else if ok { 295 return 296 } 297 298 time.Sleep(waitDur) 299 } 300 notifierChan <- fmt.Errorf("too many attempts") 301 }() 302 return notifierChan 303 } 304 305 // VolumeStatusNotifier will check a volume's status to determine 306 // whether it matches the given status. After a check, it waits for 307 // "waitDur" before attempting again. If the status does not match 308 // after "numAttempts", an error is returned. 309 func (c *Client) VolumeStatusNotifier(volId, status string, numAttempts int, waitDur time.Duration) <-chan error { 310 statusMatches := func() (bool, error) { 311 volInfo, err := c.GetVolume(volId) 312 if err != nil { 313 return false, err 314 } 315 return volInfo.Volume.Status == status, nil 316 } 317 return notifier(statusMatches, numAttempts, waitDur) 318 } 319 320 // SnapshotStatusNotifier will check a volume's status to determine 321 // whether it matches the given status. After a check, it waits for 322 // "waitDur" before attempting again. If the status does not match 323 // after "numAttempts", an error is returned. 324 func (c *Client) SnapshotStatusNotifier(snapId, status string, numAttempts int, waitDur time.Duration) <-chan error { 325 statusMatches := func() (bool, error) { 326 snapInfo, err := c.GetSnapshot(snapId) 327 if err != nil { 328 return false, err 329 } 330 return snapInfo.Snapshot.Status == status, nil 331 } 332 return notifier(statusMatches, numAttempts, waitDur) 333 } 334 335 // SetVolumeMetadata sets metadata on a server. Replaces metadata 336 // items that match keys - doesn't modify items that aren't in the 337 // request. Returns the complete, updated metadata for the volume. 338 func (c *Client) SetVolumeMetadata(volumeId string, metadata map[string]string) (map[string]string, error) { 339 response, err := updateVolumeMetadata(c, volumeId, &UpdateVolumeMetadataParams{metadata}) 340 if err != nil { 341 return nil, err 342 } 343 return response.Metadata, nil 344 } 345 346 // ListVolumeAvailabilityZones lists any volume availability zones. 347 func (c *Client) ListVolumeAvailabilityZones() ([]AvailabilityZone, error) { 348 resp, err := listAvailabilityZones(c) 349 if err != nil { 350 return nil, errors.Newf(err, "failed to get list of availability zones") 351 } 352 return resp.AvailabilityZoneInfo, nil 353 }