Skip to content

Commit db767c6

Browse files
Ensure list is retried when object resolution fails
In order for the error producer and the error consumer can work together, we extract the error type and the error predicate in a dedicated package and define a constructor for the error The error producer must use the constructor (as the error type is package visible), and the consumer must use the predefined predicate (as it cannot create a pointer to the type). This way we guarantee that no type mismatch will happen Co-authored-by: Danail Branekov <[email protected]>
1 parent 48157c8 commit db767c6

File tree

7 files changed

+100
-26
lines changed

7 files changed

+100
-26
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package errors
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
7+
"k8s.io/apimachinery/pkg/runtime/schema"
8+
)
9+
10+
type objectResolutionError struct {
11+
guid string
12+
gvk schema.GroupVersionKind
13+
}
14+
15+
func (e objectResolutionError) Error() string {
16+
return fmt.Sprintf("item not found: guid %q, gvk %q", e.guid, e.gvk)
17+
}
18+
19+
func NewObjectResolutionError(guid string, gvk schema.GroupVersionKind) error {
20+
return objectResolutionError{
21+
guid: guid,
22+
gvk: gvk,
23+
}
24+
}
25+
26+
func IsObjectResolutionError(err error) bool {
27+
return errors.As(err, new(objectResolutionError))
28+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package errors_test
2+
3+
import (
4+
"errors"
5+
6+
desc_errs "code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors/errors"
7+
. "github.com/onsi/ginkgo/v2"
8+
. "github.com/onsi/gomega"
9+
"k8s.io/apimachinery/pkg/runtime/schema"
10+
)
11+
12+
var _ = Describe("IsObjectResolutionError", func() {
13+
var (
14+
isObjectResolutionErr bool
15+
err error
16+
)
17+
18+
BeforeEach(func() {
19+
err = nil
20+
})
21+
22+
JustBeforeEach(func() {
23+
isObjectResolutionErr = desc_errs.IsObjectResolutionError(err)
24+
})
25+
26+
It("returns false", func() {
27+
Expect(isObjectResolutionErr).To(BeFalse())
28+
})
29+
30+
When("the error is a ObjectResulutionError", func() {
31+
BeforeEach(func() {
32+
err = desc_errs.NewObjectResolutionError("1234", schema.GroupVersionKind{})
33+
})
34+
35+
It("returns true", func() {
36+
Expect(isObjectResolutionErr).To(BeTrue())
37+
})
38+
})
39+
40+
When("the error is a random error", func() {
41+
BeforeEach(func() {
42+
err = errors.New("foo")
43+
})
44+
45+
It("returns false", func() {
46+
Expect(isObjectResolutionErr).To(BeFalse())
47+
})
48+
})
49+
})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package errors_test
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestErrors(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "Errors Suite")
13+
}

api/repositories/k8sklient/descriptors/mapper.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"reflect"
77

88
"code.cloudfoundry.org/korifi/api/authorization"
9+
"code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors/errors"
910
"k8s.io/apimachinery/pkg/labels"
1011
"k8s.io/apimachinery/pkg/runtime"
1112
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -60,15 +61,6 @@ func (m *ObjectListMapper) GUIDsToObjectList(ctx context.Context, listObjectGVK
6061
return order(orderedGUIDs, listObjectGVK, list)
6162
}
6263

63-
type ObjectResolutionError struct {
64-
guid string
65-
gvk schema.GroupVersionKind
66-
}
67-
68-
func (e ObjectResolutionError) Error() string {
69-
return fmt.Sprintf("item not found: guid %q, gvk %q", e.guid, e.gvk)
70-
}
71-
7264
func order(orderedGUIDs []string, listObjectGVK schema.GroupVersionKind, list client.ObjectList) (client.ObjectList, error) {
7365
resultList, err := scheme.Scheme.New(listObjectGVK)
7466
if err != nil {
@@ -84,10 +76,7 @@ func order(orderedGUIDs []string, listObjectGVK schema.GroupVersionKind, list cl
8476
for _, guid := range orderedGUIDs {
8577
item, ok := itemsIndex[guid]
8678
if !ok {
87-
return nil, &ObjectResolutionError{
88-
guid: guid,
89-
gvk: listObjectGVK,
90-
}
79+
return nil, errors.NewObjectResolutionError(guid, listObjectGVK)
9180
}
9281
orderedObjects = append(orderedObjects, item)
9382
}

api/repositories/k8sklient/k8sklient_suite_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@ package k8sklient_test
22

33
import (
44
"context"
5+
"log"
56
"testing"
67

78
"code.cloudfoundry.org/korifi/api/authorization"
89
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
10+
"github.com/go-logr/logr"
11+
"github.com/go-logr/stdr"
912
. "github.com/onsi/ginkgo/v2"
1013
. "github.com/onsi/gomega"
1114
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -27,5 +30,6 @@ var _ = BeforeEach(func() {
2730
Token: "i-am-user",
2831
}
2932
ctx = authorization.NewContext(context.Background(), &authInfo)
33+
ctx = logr.NewContext(ctx, stdr.New(log.New(GinkgoWriter, ">>>", log.LstdFlags)))
3034
utilruntime.Must(korifiv1alpha1.AddToScheme(scheme.Scheme))
3135
})

api/repositories/k8sklient/lister.go

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ package k8sklient
22

33
import (
44
"context"
5-
"errors"
65
"fmt"
76

87
"code.cloudfoundry.org/korifi/api/repositories"
98
"code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors"
9+
"code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors/errors"
1010
"code.cloudfoundry.org/korifi/tools/k8s"
1111
"github.com/go-logr/logr"
1212
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -57,7 +57,7 @@ func (k *DescriptorsBasedLister) retryList(ctx context.Context, listObjectGVK sc
5757
err error
5858
)
5959

60-
err = retry.OnError(k8s.NewDefaultBackoff(), isObjectResolutionError, func() error {
60+
err = retry.OnError(k8s.NewDefaultBackoff(), errors.IsObjectResolutionError, func() error {
6161
listResult, pageInfo, err = k.descriptorsBasedList(ctx, listObjectGVK, listOpts)
6262
if err != nil {
6363
logger.Info("failed to resolve objects, retrying", "error", err)
@@ -68,16 +68,6 @@ func (k *DescriptorsBasedLister) retryList(ctx context.Context, listObjectGVK sc
6868
return listResult, pageInfo, err
6969
}
7070

71-
func isObjectResolutionError(err error) bool {
72-
if err == nil {
73-
return false
74-
}
75-
if errors.As(err, new(descriptors.ObjectResolutionError)) {
76-
return true
77-
}
78-
return false
79-
}
80-
8171
func (k *DescriptorsBasedLister) descriptorsBasedList(ctx context.Context, listObjectGVK schema.GroupVersionKind, listOpts repositories.ListOptions) (client.ObjectList, descriptors.PageInfo, error) {
8272
objectGUIDs, err := k.fetchObjectGUIDs(ctx, listObjectGVK, listOpts)
8373
if err != nil {

api/repositories/k8sklient/lister_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"code.cloudfoundry.org/korifi/api/repositories"
1212
"code.cloudfoundry.org/korifi/api/repositories/k8sklient"
1313
"code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors"
14+
desc_errs "code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors/errors"
1415
descfake "code.cloudfoundry.org/korifi/api/repositories/k8sklient/descriptors/fake"
1516
"code.cloudfoundry.org/korifi/api/repositories/k8sklient/fake"
1617
korifiv1alpha1 "code.cloudfoundry.org/korifi/controllers/api/v1alpha1"
@@ -163,7 +164,7 @@ var _ = Describe("Lister", func() {
163164
},
164165
}
165166

166-
objectListMapper.GUIDsToObjectListReturnsOnCall(0, nil, descriptors.ObjectResolutionError{})
167+
objectListMapper.GUIDsToObjectListReturnsOnCall(0, nil, desc_errs.NewObjectResolutionError("1234", schema.GroupVersionKind{}))
167168
objectListMapper.GUIDsToObjectListReturns(appList, nil)
168169
})
169170

0 commit comments

Comments
 (0)