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  }