2021-06-09 04:54:52 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
2022-03-30 08:46:01 +00:00
|
|
|
"bytes"
|
2021-06-09 04:54:52 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
// ErrStatusNotEmpty may be returned if a call should not have a status
|
|
|
|
// string set but one is.
|
|
|
|
ErrStatusNotEmpty = errors.New("response status not empty")
|
|
|
|
// ErrBodyNotEmpty may be returned if a call should have an empty body but
|
|
|
|
// a body value is present.
|
|
|
|
ErrBodyNotEmpty = errors.New("response body not empty")
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
deprecatedSuffix = "call is deprecated and will be removed in a future release"
|
|
|
|
missingPrefix = "No handler found"
|
|
|
|
einval = -22
|
|
|
|
)
|
|
|
|
|
|
|
|
type cephError interface {
|
|
|
|
ErrorCode() int
|
|
|
|
}
|
|
|
|
|
|
|
|
// NotImplementedError error values will be returned in the case that an API
|
|
|
|
// call is not available in the version of Ceph that is running in the target
|
|
|
|
// cluster.
|
|
|
|
type NotImplementedError struct {
|
|
|
|
Response
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error implements the error interface.
|
|
|
|
func (e NotImplementedError) Error() string {
|
|
|
|
return fmt.Sprintf("API call not implemented server-side: %s", e.status)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Response encapsulates the data returned by ceph and supports easy processing
|
|
|
|
// pipelines.
|
|
|
|
type Response struct {
|
|
|
|
body []byte
|
|
|
|
status string
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ok returns true if the response contains no error.
|
|
|
|
func (r Response) Ok() bool {
|
|
|
|
return r.err == nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error implements the error interface.
|
|
|
|
func (r Response) Error() string {
|
|
|
|
if r.status == "" {
|
|
|
|
return r.err.Error()
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s: %q", r.err, r.status)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unwrap returns the error this response contains.
|
|
|
|
func (r Response) Unwrap() error {
|
|
|
|
return r.err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Status returns the status string value.
|
|
|
|
func (r Response) Status() string {
|
|
|
|
return r.status
|
|
|
|
}
|
|
|
|
|
|
|
|
// Body returns the response body as a raw byte-slice.
|
|
|
|
func (r Response) Body() []byte {
|
|
|
|
return r.body
|
|
|
|
}
|
|
|
|
|
|
|
|
// End returns an error if the response contains an error or nil, indicating
|
|
|
|
// that response is no longer needed for processing.
|
|
|
|
func (r Response) End() error {
|
|
|
|
if !r.Ok() {
|
|
|
|
if ce, ok := r.err.(cephError); ok {
|
|
|
|
if ce.ErrorCode() == einval && strings.HasPrefix(r.status, missingPrefix) {
|
|
|
|
return NotImplementedError{Response: r}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// NoStatus asserts that the input response has no status value.
|
|
|
|
func (r Response) NoStatus() Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if r.status != "" {
|
|
|
|
return Response{r.body, r.status, ErrStatusNotEmpty}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// NoBody asserts that the input response has no body value.
|
|
|
|
func (r Response) NoBody() Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if len(r.body) != 0 {
|
|
|
|
return Response{r.body, r.status, ErrBodyNotEmpty}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2021-08-12 10:02:52 +00:00
|
|
|
// EmptyBody is similar to NoBody but also accepts an empty JSON object.
|
|
|
|
func (r Response) EmptyBody() Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if len(r.body) != 0 {
|
|
|
|
d := map[string]interface{}{}
|
|
|
|
if err := json.Unmarshal(r.body, &d); err != nil {
|
|
|
|
return Response{r.body, r.status, err}
|
|
|
|
}
|
|
|
|
if len(d) != 0 {
|
|
|
|
return Response{r.body, r.status, ErrBodyNotEmpty}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2021-06-09 04:54:52 +00:00
|
|
|
// NoData asserts that the input response has no status or body values.
|
|
|
|
func (r Response) NoData() Response {
|
|
|
|
return r.NoStatus().NoBody()
|
|
|
|
}
|
|
|
|
|
|
|
|
// FilterPrefix sets the status value to an empty string if the status
|
|
|
|
// value contains the given prefix string.
|
|
|
|
func (r Response) FilterPrefix(p string) Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if strings.HasPrefix(r.status, p) {
|
|
|
|
return Response{r.body, "", r.err}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// FilterSuffix sets the status value to an empty string if the status
|
|
|
|
// value contains the given suffix string.
|
|
|
|
func (r Response) FilterSuffix(s string) Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if strings.HasSuffix(r.status, s) {
|
|
|
|
return Response{r.body, "", r.err}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2022-03-30 08:46:01 +00:00
|
|
|
// FilterBodyPrefix sets the body value equivalent to an empty string if the
|
|
|
|
// body value contains the given prefix string.
|
|
|
|
func (r Response) FilterBodyPrefix(p string) Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if bytes.HasPrefix(r.body, []byte(p)) {
|
|
|
|
return Response{[]byte(""), r.status, r.err}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
2021-06-09 04:54:52 +00:00
|
|
|
// FilterDeprecated removes deprecation warnings from the response status.
|
|
|
|
// Use it when checking the response from calls that may be deprecated in ceph
|
|
|
|
// if you want those calls to continue working if the warning is present.
|
|
|
|
func (r Response) FilterDeprecated() Response {
|
|
|
|
return r.FilterSuffix(deprecatedSuffix)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unmarshal data from the response body into v.
|
|
|
|
func (r Response) Unmarshal(v interface{}) Response {
|
|
|
|
if !r.Ok() {
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
if err := json.Unmarshal(r.body, v); err != nil {
|
|
|
|
return Response{body: r.body, err: err}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewResponse returns a response.
|
|
|
|
func NewResponse(b []byte, s string, e error) Response {
|
|
|
|
return Response{b, s, e}
|
|
|
|
}
|