refactor(storage): refactoring storage (#1459)

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
LaurentiuNiculae
2023-05-26 21:08:19 +03:00
committed by GitHub
parent 9acd19f7ea
commit a3f355c278
45 changed files with 850 additions and 751 deletions
+106
View File
@@ -0,0 +1,106 @@
//go:build dev
// +build dev
// This file should be linked only in **development** mode.
package inject
import (
"net/http"
"sync"
zerr "zotregistry.io/zot/errors"
"zotregistry.io/zot/pkg/log"
)
func Ok(ok bool) bool {
if !ok {
return ok
}
if injectedFailure() {
return false
}
return true
}
func Error(err error) error {
if err != nil {
return err
}
if injectedFailure() {
return zerr.ErrInjected
}
return nil
}
// Used to inject error status codes for coverage purposes.
// -1 will be returned in case of successful failure injection.
func ErrStatusCode(status int) int {
if !injectedFailure() {
if status == http.StatusAccepted || status == http.StatusCreated {
return status
}
return 0
}
return -1
}
/**
*
* Failure injection infrastructure to cover hard-to-reach code paths.
*
**/
type inject struct {
skip int
}
//nolint:gochecknoglobals // only used by test code
var injMap sync.Map
func InjectFailure(skip int) bool {
gid := log.GoroutineID()
if gid < 0 {
panic("invalid goroutine id")
}
if _, ok := injMap.Load(gid); ok {
panic("prior incomplete fault injection")
}
injst := inject{skip: skip}
injMap.Store(gid, injst)
return true
}
func injectedFailure() bool {
gid := log.GoroutineID()
val, ok := injMap.Load(gid)
if !ok {
return false
}
injst, ok := val.(inject)
if !ok {
panic("invalid type")
}
if injst.skip == 0 {
injMap.Delete(gid)
return true
}
injst.skip--
injMap.Store(gid, injst)
return false
}
+154
View File
@@ -0,0 +1,154 @@
//go:build dev
// +build dev
// This file should be linked only in **development** mode.
package inject_test
import (
"errors"
"testing"
. "github.com/smartystreets/goconvey/convey"
"zotregistry.io/zot/pkg/test/inject"
)
var (
errKey1 = errors.New("key1 not found")
errKey2 = errors.New("key2 not found")
errNotZero = errors.New("not zero")
errCall1 = errors.New("call1 error")
errCall2 = errors.New("call2 error")
)
func foo() error {
fmap := map[string]string{"key1": "val1", "key2": "val2"}
_, ok := fmap["key1"] // should never fail
if !inject.Ok(ok) {
return errKey1
}
_, ok = fmap["key2"] // should never fail
if !inject.Ok(ok) {
return errKey2
}
return nil
}
func errgen(i int) error {
if i != 0 {
return errNotZero
}
return nil
}
func bar() error {
err := errgen(0) // should never fail
if inject.Error(err) != nil {
return errCall1
}
err = errgen(0) // should never fail
if inject.Error(err) != nil {
return errCall2
}
return nil
}
func baz() error {
if inject.ErrStatusCode(0) != 0 {
return errCall1
}
if inject.ErrStatusCode(0) != 0 {
return errCall2
}
return nil
}
func alwaysErr() error {
return errNotZero
}
func alwaysNotOk() bool {
return false
}
func TestInject(t *testing.T) {
Convey("Injected failure", t, func(c C) {
// should be success without injection
err := foo()
So(err, ShouldBeNil)
Convey("Check Ok", func() {
Convey("Without skipping", func() {
inject.InjectFailure(0) // inject a failure
err := foo() // should be a failure
So(err, ShouldNotBeNil) // should be a failure
So(errors.Is(err, errKey1), ShouldBeTrue)
})
Convey("With skipping", func() {
inject.InjectFailure(1) // inject a failure but skip first one
err := foo() // should be a failure
So(errors.Is(err, errKey1), ShouldBeFalse)
So(errors.Is(err, errKey2), ShouldBeTrue)
})
})
// should be success without injection
err = bar()
So(err, ShouldBeNil)
Convey("Check Err", func() {
Convey("Without skipping", func() {
inject.InjectFailure(0) // inject a failure
err := bar() // should be a failure
So(err, ShouldNotBeNil) // should be a failure
So(errors.Is(err, errCall1), ShouldBeTrue)
})
Convey("With skipping", func() {
inject.InjectFailure(1) // inject a failure but skip first one
err := bar() // should be a failure
So(errors.Is(err, errCall1), ShouldBeFalse)
So(errors.Is(err, errCall2), ShouldBeTrue)
})
})
Convey("Check ErrStatusCode", func() {
Convey("Without skipping", func() {
inject.InjectFailure(0) // inject a failure
err := baz() // should be a failure
So(err, ShouldNotBeNil) // should be a failure
So(errors.Is(err, errCall1), ShouldBeTrue)
})
Convey("With skipping", func() {
inject.InjectFailure(1) // inject a failure but skip first one
err := baz() // should be a failure
So(errors.Is(err, errCall1), ShouldBeFalse)
So(errors.Is(err, errCall2), ShouldBeTrue)
})
})
})
Convey("Without injected failure", t, func(c C) {
err := alwaysErr()
So(inject.Error(err), ShouldNotBeNil)
ok := alwaysNotOk()
So(inject.Ok(ok), ShouldBeFalse)
})
Convey("Incomplete injected failure", t, func(c C) {
inject.InjectFailure(0) // inject a failure
So(func() { inject.InjectFailure(0) }, ShouldPanic)
})
}
+24
View File
@@ -0,0 +1,24 @@
//go:build !dev
// +build !dev
package inject
func Error(err error) error {
return err
}
func Ok(ok bool) bool {
return ok
}
func ErrStatusCode(statusCode int) int {
return statusCode
}
/**
*
* Failure injection infrastructure to cover hard-to-reach code paths (nop in production).
*
**/
func InjectFailure(skip int) bool { return false }