Skip to content
This repository is currently being migrated. It's locked while the migration is in progress.

Commit

Permalink
Handle partial results returned from a GetVolumes API call (#229)
Browse files Browse the repository at this point in the history
Handle the context deadline exceeded error from the backend (identified
by the http status code PartialContent returned) and print the data we
managed to get back

This data set will most likely fail to include some of the data about
volume replicas so for those volumes we may see 0/2 replicas healthy
even thought they may exist and be healthy

To make this clear for the user I have added a message that prints just
before the table output that reads:
`context deadline exceeded, these are partial results and may be missing
replica states for some volumes`

We expect this to be a rare case to hit
  • Loading branch information
Ricardo-Osorio authored Feb 27, 2023
1 parent 4e189d7 commit 302eefc
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 211 deletions.
2 changes: 1 addition & 1 deletion apiclient/apiclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ type Transport interface {
GetPolicyGroup(ctx context.Context, uid id.PolicyGroup) (*model.PolicyGroup, error)
// ListNodes returns all the node resources in the cluster.
ListNodes(ctx context.Context) ([]*model.Node, error)
// ListVolumes returns all the user resources in the cluster.
// ListVolumes returns all the volume resources in a specific namespace.
ListVolumes(ctx context.Context, namespaceID id.Namespace) ([]*model.Volume, error)
// ListNamespaces returns all the namespace resources in the cluster.
ListNamespaces(ctx context.Context) ([]*model.Namespace, error)
Expand Down
19 changes: 8 additions & 11 deletions apiclient/openapi/map_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,17 @@ func newOpenAPIError(err openapi.Error) openAPIError {
// HTTP error to an application level error.
//
// err is returned as is when any of the following are true:
//
// → resp is nil
// → err is not a GenericOpenAPIError or the unexported openAPIError
// - resp is nil
// - err is not a GenericOpenAPIError or the unexported openAPIError
//
// Some response codes must be mapped by the caller in order to provide useful
// application level errors:
//
// → http.StatusBadRequest returns a badRequestError, which must have a 1-to-1
// mapping to a context specific application error
// → http.StatusNotFound returns a notFoundError, which must have a 1-to-1
// mapping to a context specific application error
// → http.StatusConflict returns a conflictError which must have a 1-to-1
// mapping to a context specific application error
//
// - http.StatusBadRequest returns a badRequestError, which must have a 1-to-1
// mapping to a context specific application error
// - http.StatusNotFound returns a notFoundError, which must have a 1-to-1
// mapping to a context specific application error
// - http.StatusConflict returns a conflictError which must have a 1-to-1
// mapping to a context specific application error
func mapOpenAPIError(err error, resp *http.Response) error {
if resp == nil {
return err
Expand Down
96 changes: 49 additions & 47 deletions apiclient/openapi/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package openapi

import (
"context"
"net/http"

"github.com/antihax/optional"

Expand All @@ -18,13 +19,12 @@ import (
//
// The behaviour of the operation is dictated by params:
//
//
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the create
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the create
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
func (o *OpenAPI) CreateVolume(ctx context.Context, namespace id.Namespace, name, description string, fs model.FsType, sizeBytes uint64, labels labels.Set, params *apiclient.CreateVolumeRequestParams) (*model.Volume, error) {
o.mu.RLock()
defer o.mu.RUnlock()
Expand Down Expand Up @@ -118,32 +118,35 @@ func (o *OpenAPI) ListVolumes(ctx context.Context, namespaceID id.Namespace) ([]
volumes[i] = v
}

// the below targets a very specific scenario of the response of the ListVolumes
if resp.StatusCode == http.StatusPartialContent {
return volumes, context.DeadlineExceeded
}
return volumes, nil
}

// DeleteVolume makes a delete request for volumeID in namespaceID.
//
// The behaviour of the operation is dictated by params:
//
// Version constraints:
// - If params is nil or params.CASVersion is empty then the delete request is
// unconditional
// - If params.CASVersion is set, the request is conditional upon it matching
// the volume entity's version as seen by the server.
//
// Version constraints:
// - If params is nil or params.CASVersion is empty then the delete request is
// unconditional
// - If params.CASVersion is set, the request is conditional upon it matching
// the volume entity's version as seen by the server.
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the delete
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
//
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the delete
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
//
// Offline deletion behaviour:
// - If params is nil then offline deletion behaviour is not requested,
// otherwise the value of params.OfflineDelete determines is used. The default
// value of false reflects normal deletion behaviour, so does not need setting
// unless offline deletion behaviour is desired.
// Offline deletion behaviour:
// - If params is nil then offline deletion behaviour is not requested,
// otherwise the value of params.OfflineDelete determines is used. The default
// value of false reflects normal deletion behaviour, so does not need setting
// unless offline deletion behaviour is desired.
func (o *OpenAPI) DeleteVolume(ctx context.Context, namespaceID id.Namespace, volumeID id.Volume, params *apiclient.DeleteVolumeRequestParams) error {
o.mu.RLock()
defer o.mu.RUnlock()
Expand Down Expand Up @@ -435,19 +438,18 @@ func (o *OpenAPI) setFailureMode(ctx context.Context, namespaceID id.Namespace,
//
// The behaviour of the operation is dictated by params:
//
// Version constraints:
// - If params is nil or params.CASVersion is empty then the detach request is
// unconditional
// - If params.CASVersion is set, the request is conditional upon it matching
// the volume entity's version as seen by the server.
//
// Version constraints:
// - If params is nil or params.CASVersion is empty then the detach request is
// unconditional
// - If params.CASVersion is set, the request is conditional upon it matching
// the volume entity's version as seen by the server.
//
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the delete
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the delete
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
func (o *OpenAPI) DetachVolume(ctx context.Context, namespaceID id.Namespace, volumeID id.Volume, params *apiclient.DetachVolumeRequestParams) error {
o.mu.RLock()
defer o.mu.RUnlock()
Expand Down Expand Up @@ -523,18 +525,18 @@ func (o *OpenAPI) SetReplicas(ctx context.Context, nsID id.Namespace, volID id.V

// UpdateVolume changes the description of a specified volume.
//
// Version constraints:
// - If params is nil or params.CASVersion is empty then the detach request is
// unconditional
// - If params.CASVersion is set, the request is conditional upon it matching
// the volume entity's version as seen by the server.
// Version constraints:
// - If params is nil or params.CASVersion is empty then the detach request is
// unconditional
// - If params.CASVersion is set, the request is conditional upon it matching
// the volume entity's version as seen by the server.
//
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the delete
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
// Asynchrony:
// - If params is nil or params.AsyncMax is empty/zero valued then the delete
// request is performed synchronously.
// - If params.AsyncMax is set, the request is performed asynchronously using
// the duration given as the maximum amount of time allowed for the request
// before it times out.
func (o *OpenAPI) UpdateVolume(
ctx context.Context,
nsID id.Namespace,
Expand Down
Loading

0 comments on commit 302eefc

Please sign in to comment.