mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-05 05:20:46 +08:00

When the initial DeleteVolume times out (as it does on slow clusters due to the low 10 second limit), the external-provisioner calls it again. The CSI standard requires the second call to succeed if the volume has been deleted in the meantime. This didn't work because DeleteVolume returned an error when failing to find the volume info file: rbdplugin: E1008 08:05:35.631783 1 utils.go:100] GRPC error: rbd: open err /var/lib/kubelet/plugins/csi-rbdplugin/controller/csi-rbd-622a252c-cad0-11e8-9112-deadbeef0101.json/open /var/lib/kubelet/plugins/csi-rbdplugin/controller/csi-rbd-622a252c-cad0-11e8-9112-deadbeef0101.json: no such file or directory The fix is to treat a missing volume info file as "volume already deleted" and return success. To detect this, the original os error must be wrapped, otherwise the caller of loadVolInfo cannot determine the root cause. Note that further work may be needed to make the driver really resilient, for example there are probably concurrency issues. But for now this fixes: #82
227 lines
4.7 KiB
Go
227 lines
4.7 KiB
Go
package errors
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestNew(t *testing.T) {
|
|
tests := []struct {
|
|
err string
|
|
want error
|
|
}{
|
|
{"", fmt.Errorf("")},
|
|
{"foo", fmt.Errorf("foo")},
|
|
{"foo", New("foo")},
|
|
{"string with format specifiers: %v", errors.New("string with format specifiers: %v")},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := New(tt.err)
|
|
if got.Error() != tt.want.Error() {
|
|
t.Errorf("New.Error(): got: %q, want %q", got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWrapNil(t *testing.T) {
|
|
got := Wrap(nil, "no error")
|
|
if got != nil {
|
|
t.Errorf("Wrap(nil, \"no error\"): got %#v, expected nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWrap(t *testing.T) {
|
|
tests := []struct {
|
|
err error
|
|
message string
|
|
want string
|
|
}{
|
|
{io.EOF, "read error", "read error: EOF"},
|
|
{Wrap(io.EOF, "read error"), "client error", "client error: read error: EOF"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := Wrap(tt.err, tt.message).Error()
|
|
if got != tt.want {
|
|
t.Errorf("Wrap(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
type nilError struct{}
|
|
|
|
func (nilError) Error() string { return "nil error" }
|
|
|
|
func TestCause(t *testing.T) {
|
|
x := New("error")
|
|
tests := []struct {
|
|
err error
|
|
want error
|
|
}{{
|
|
// nil error is nil
|
|
err: nil,
|
|
want: nil,
|
|
}, {
|
|
// explicit nil error is nil
|
|
err: (error)(nil),
|
|
want: nil,
|
|
}, {
|
|
// typed nil is nil
|
|
err: (*nilError)(nil),
|
|
want: (*nilError)(nil),
|
|
}, {
|
|
// uncaused error is unaffected
|
|
err: io.EOF,
|
|
want: io.EOF,
|
|
}, {
|
|
// caused error returns cause
|
|
err: Wrap(io.EOF, "ignored"),
|
|
want: io.EOF,
|
|
}, {
|
|
err: x, // return from errors.New
|
|
want: x,
|
|
}, {
|
|
WithMessage(nil, "whoops"),
|
|
nil,
|
|
}, {
|
|
WithMessage(io.EOF, "whoops"),
|
|
io.EOF,
|
|
}, {
|
|
WithStack(nil),
|
|
nil,
|
|
}, {
|
|
WithStack(io.EOF),
|
|
io.EOF,
|
|
}}
|
|
|
|
for i, tt := range tests {
|
|
got := Cause(tt.err)
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("test %d: got %#v, want %#v", i+1, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWrapfNil(t *testing.T) {
|
|
got := Wrapf(nil, "no error")
|
|
if got != nil {
|
|
t.Errorf("Wrapf(nil, \"no error\"): got %#v, expected nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWrapf(t *testing.T) {
|
|
tests := []struct {
|
|
err error
|
|
message string
|
|
want string
|
|
}{
|
|
{io.EOF, "read error", "read error: EOF"},
|
|
{Wrapf(io.EOF, "read error without format specifiers"), "client error", "client error: read error without format specifiers: EOF"},
|
|
{Wrapf(io.EOF, "read error with %d format specifier", 1), "client error", "client error: read error with 1 format specifier: EOF"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := Wrapf(tt.err, tt.message).Error()
|
|
if got != tt.want {
|
|
t.Errorf("Wrapf(%v, %q): got: %v, want %v", tt.err, tt.message, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestErrorf(t *testing.T) {
|
|
tests := []struct {
|
|
err error
|
|
want string
|
|
}{
|
|
{Errorf("read error without format specifiers"), "read error without format specifiers"},
|
|
{Errorf("read error with %d format specifier", 1), "read error with 1 format specifier"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := tt.err.Error()
|
|
if got != tt.want {
|
|
t.Errorf("Errorf(%v): got: %q, want %q", tt.err, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWithStackNil(t *testing.T) {
|
|
got := WithStack(nil)
|
|
if got != nil {
|
|
t.Errorf("WithStack(nil): got %#v, expected nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWithStack(t *testing.T) {
|
|
tests := []struct {
|
|
err error
|
|
want string
|
|
}{
|
|
{io.EOF, "EOF"},
|
|
{WithStack(io.EOF), "EOF"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := WithStack(tt.err).Error()
|
|
if got != tt.want {
|
|
t.Errorf("WithStack(%v): got: %v, want %v", tt.err, got, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestWithMessageNil(t *testing.T) {
|
|
got := WithMessage(nil, "no error")
|
|
if got != nil {
|
|
t.Errorf("WithMessage(nil, \"no error\"): got %#v, expected nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWithMessage(t *testing.T) {
|
|
tests := []struct {
|
|
err error
|
|
message string
|
|
want string
|
|
}{
|
|
{io.EOF, "read error", "read error: EOF"},
|
|
{WithMessage(io.EOF, "read error"), "client error", "client error: read error: EOF"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got := WithMessage(tt.err, tt.message).Error()
|
|
if got != tt.want {
|
|
t.Errorf("WithMessage(%v, %q): got: %q, want %q", tt.err, tt.message, got, tt.want)
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// errors.New, etc values are not expected to be compared by value
|
|
// but the change in errors#27 made them incomparable. Assert that
|
|
// various kinds of errors have a functional equality operator, even
|
|
// if the result of that equality is always false.
|
|
func TestErrorEquality(t *testing.T) {
|
|
vals := []error{
|
|
nil,
|
|
io.EOF,
|
|
errors.New("EOF"),
|
|
New("EOF"),
|
|
Errorf("EOF"),
|
|
Wrap(io.EOF, "EOF"),
|
|
Wrapf(io.EOF, "EOF%d", 2),
|
|
WithMessage(nil, "whoops"),
|
|
WithMessage(io.EOF, "whoops"),
|
|
WithStack(io.EOF),
|
|
WithStack(nil),
|
|
}
|
|
|
|
for i := range vals {
|
|
for j := range vals {
|
|
_ = vals[i] == vals[j] // mustn't panic
|
|
}
|
|
}
|
|
}
|