Add errdefs package
Providers should use this package so the virtual kubelet core controllers can understand the errors produced from the provider code.
This commit is contained in:
65
errdefs/invalid.go
Normal file
65
errdefs/invalid.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package errdefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InvalidInput is an error interface which denotes whether the opration failed due
|
||||||
|
// to a the resource not being found.
|
||||||
|
type ErrInvalidInput interface {
|
||||||
|
InvalidInput() bool
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
type invalidInputError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *invalidInputError) InvalidInput() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *invalidInputError) Cause() error {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsInvalidInput wraps the passed in error to make it of type ErrInvalidInput
|
||||||
|
//
|
||||||
|
// Callers should make sure the passed in error has exactly the error message
|
||||||
|
// it wants as this function does not decorate the message.
|
||||||
|
func AsInvalidInput(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &invalidInputError{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidInput makes an ErrInvalidInput from the provided error message
|
||||||
|
func InvalidInput(msg string) error {
|
||||||
|
return &invalidInputError{errors.New(msg)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InvalidInputf makes an ErrInvalidInput from the provided error format and args
|
||||||
|
func InvalidInputf(format string, args ...interface{}) error {
|
||||||
|
return &invalidInputError{fmt.Errorf(format, args...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidInput determines if the passed in error is of type ErrInvalidInput
|
||||||
|
//
|
||||||
|
// This will traverse the causal chain (`Cause() error`), until it finds an error
|
||||||
|
// which implements the `InvalidInput` interface.
|
||||||
|
func IsInvalidInput(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e, ok := err.(ErrInvalidInput); ok {
|
||||||
|
return e.InvalidInput()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := err.(causal); ok {
|
||||||
|
return IsInvalidInput(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
80
errdefs/invalid_test.go
Normal file
80
errdefs/invalid_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package errdefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
|
"gotest.tools/assert/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testingInvalidInputError bool
|
||||||
|
|
||||||
|
func (e testingInvalidInputError) Error() string {
|
||||||
|
return fmt.Sprintf("%v", bool(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e testingInvalidInputError) InvalidInput() bool {
|
||||||
|
return bool(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsInvalidInput(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
xMsg string
|
||||||
|
xInvalidInput bool
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range []testCase{
|
||||||
|
{
|
||||||
|
name: "InvalidInputf",
|
||||||
|
err: InvalidInputf("%s not found", "foo"),
|
||||||
|
xMsg: "foo not found",
|
||||||
|
xInvalidInput: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AsInvalidInput",
|
||||||
|
err: AsInvalidInput(errors.New("this is a test")),
|
||||||
|
xMsg: "this is a test",
|
||||||
|
xInvalidInput: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AsInvalidInputWithNil",
|
||||||
|
err: AsInvalidInput(nil),
|
||||||
|
xMsg: "",
|
||||||
|
xInvalidInput: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nilError",
|
||||||
|
err: nil,
|
||||||
|
xMsg: "",
|
||||||
|
xInvalidInput: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customInvalidInputFalse",
|
||||||
|
err: testingInvalidInputError(false),
|
||||||
|
xMsg: "false",
|
||||||
|
xInvalidInput: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customInvalidInputTrue",
|
||||||
|
err: testingInvalidInputError(true),
|
||||||
|
xMsg: "true",
|
||||||
|
xInvalidInput: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
assert.Check(t, cmp.Equal(IsInvalidInput(c.err), c.xInvalidInput))
|
||||||
|
if c.err != nil {
|
||||||
|
assert.Check(t, cmp.Equal(c.err.Error(), c.xMsg))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInvalidInputCause(t *testing.T) {
|
||||||
|
err := errors.New("test")
|
||||||
|
assert.Equal(t, (&invalidInputError{err}).Cause(), err)
|
||||||
|
}
|
||||||
65
errdefs/notfound.go
Normal file
65
errdefs/notfound.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package errdefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NotFound is an error interface which denotes whether the opration failed due
|
||||||
|
// to a the resource not being found.
|
||||||
|
type ErrNotFound interface {
|
||||||
|
NotFound() bool
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
type notFoundError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *notFoundError) NotFound() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *notFoundError) Cause() error {
|
||||||
|
return e.error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsNotFound wraps the passed in error to make it of type ErrNotFound
|
||||||
|
//
|
||||||
|
// Callers should make sure the passed in error has exactly the error message
|
||||||
|
// it wants as this function does not decorate the message.
|
||||||
|
func AsNotFound(err error) error {
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ¬FoundError{err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFound makes an ErrNotFound from the provided error message
|
||||||
|
func NotFound(msg string) error {
|
||||||
|
return ¬FoundError{errors.New(msg)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NotFoundf makes an ErrNotFound from the provided error format and args
|
||||||
|
func NotFoundf(format string, args ...interface{}) error {
|
||||||
|
return ¬FoundError{fmt.Errorf(format, args...)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotFound determines if the passed in error is of type ErrNotFound
|
||||||
|
//
|
||||||
|
// This will traverse the causal chain (`Cause() error`), until it finds an error
|
||||||
|
// which implements the `NotFound` interface.
|
||||||
|
func IsNotFound(err error) bool {
|
||||||
|
if err == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if e, ok := err.(ErrNotFound); ok {
|
||||||
|
return e.NotFound()
|
||||||
|
}
|
||||||
|
|
||||||
|
if e, ok := err.(causal); ok {
|
||||||
|
return IsNotFound(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
80
errdefs/notfound_test.go
Normal file
80
errdefs/notfound_test.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package errdefs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gotest.tools/assert"
|
||||||
|
"gotest.tools/assert/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testingNotFoundError bool
|
||||||
|
|
||||||
|
func (e testingNotFoundError) Error() string {
|
||||||
|
return fmt.Sprintf("%v", bool(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e testingNotFoundError) NotFound() bool {
|
||||||
|
return bool(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsNotFound(t *testing.T) {
|
||||||
|
type testCase struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
xMsg string
|
||||||
|
xNotFound bool
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range []testCase{
|
||||||
|
{
|
||||||
|
name: "NotFoundf",
|
||||||
|
err: NotFoundf("%s not found", "foo"),
|
||||||
|
xMsg: "foo not found",
|
||||||
|
xNotFound: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AsNotFound",
|
||||||
|
err: AsNotFound(errors.New("this is a test")),
|
||||||
|
xMsg: "this is a test",
|
||||||
|
xNotFound: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "AsNotFoundWithNil",
|
||||||
|
err: AsNotFound(nil),
|
||||||
|
xMsg: "",
|
||||||
|
xNotFound: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nilError",
|
||||||
|
err: nil,
|
||||||
|
xMsg: "",
|
||||||
|
xNotFound: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customNotFoundFalse",
|
||||||
|
err: testingNotFoundError(false),
|
||||||
|
xMsg: "false",
|
||||||
|
xNotFound: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "customNotFoundTrue",
|
||||||
|
err: testingNotFoundError(true),
|
||||||
|
xMsg: "true",
|
||||||
|
xNotFound: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
assert.Check(t, cmp.Equal(IsNotFound(c.err), c.xNotFound))
|
||||||
|
if c.err != nil {
|
||||||
|
assert.Check(t, cmp.Equal(c.err.Error(), c.xMsg))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNotFoundCause(t *testing.T) {
|
||||||
|
err := errors.New("test")
|
||||||
|
assert.Equal(t, (¬FoundError{err}).Cause(), err)
|
||||||
|
}
|
||||||
10
errdefs/wrapped.go
Normal file
10
errdefs/wrapped.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package errdefs
|
||||||
|
|
||||||
|
// Causal is an error interface for errors which have wrapped another error
|
||||||
|
// in a non-opaque way.
|
||||||
|
//
|
||||||
|
// This pattern is used by github.com/pkg/errors
|
||||||
|
type causal interface {
|
||||||
|
Cause() error
|
||||||
|
error
|
||||||
|
}
|
||||||
@@ -11,6 +11,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Provider contains the methods required to implement a virtual-kubelet provider.
|
// Provider contains the methods required to implement a virtual-kubelet provider.
|
||||||
|
//
|
||||||
|
// Errors produced by these methods should implement an interface from
|
||||||
|
// github.com/virtual-kubelet/virtual-kubelet/errdefs package in order for the
|
||||||
|
// core logic to be able to understand the type of failure.
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
vkubelet.PodLifecycleHandler
|
vkubelet.PodLifecycleHandler
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,10 @@ import (
|
|||||||
|
|
||||||
// PodLifecycleHandler defines the interface used by the PodController to react
|
// PodLifecycleHandler defines the interface used by the PodController to react
|
||||||
// to new and changed pods scheduled to the node that is being managed.
|
// to new and changed pods scheduled to the node that is being managed.
|
||||||
|
//
|
||||||
|
// Errors produced by these methods should implement an interface from
|
||||||
|
// github.com/virtual-kubelet/virtual-kubelet/errdefs package in order for the
|
||||||
|
// core logic to be able to understand the type of failure.
|
||||||
type PodLifecycleHandler interface {
|
type PodLifecycleHandler interface {
|
||||||
// CreatePod takes a Kubernetes Pod and deploys it within the provider.
|
// CreatePod takes a Kubernetes Pod and deploys it within the provider.
|
||||||
CreatePod(ctx context.Context, pod *corev1.Pod) error
|
CreatePod(ctx context.Context, pod *corev1.Pod) error
|
||||||
|
|||||||
Reference in New Issue
Block a user