Skip to content

Commit ab25b63

Browse files
committed
refactor komega package
rename Matcher to komega since it's not really a matcher komega.With... methods now return copies allow to specify a Gomega instance to use
1 parent c472f24 commit ab25b63

File tree

6 files changed

+408
-268
lines changed

6 files changed

+408
-268
lines changed

pkg/envtest/komega/default.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package komega
2+
3+
import (
4+
"sigs.k8s.io/controller-runtime/pkg/client"
5+
)
6+
7+
// defaultK is the Komega used by the package global functions.
8+
var defaultK = &komega{}
9+
10+
// SetDefaultClient sets the client used by the package global functions.
11+
func SetDefaultClient(c client.Client) {
12+
defaultK = &komega{client: c}
13+
}
14+
15+
func checkDefaultClient() {
16+
if defaultK.client == nil {
17+
panic("Default Komega's client is not set. Use WithDefaultClient to set it.")
18+
}
19+
}
20+
21+
// Get returns a function that fetches a resource and returns the occurring error.
22+
// It can be used with gomega.Eventually() like this
23+
// deployment := appsv1.Deployment{ ... }
24+
// gomega.Eventually(komega.Get(&deployment)).To(gomega.Succeed())
25+
// By calling the returned function directly it can also be used with gomega.Expect(komega.Get(...)()).To(...)
26+
func Get(obj client.Object) func() error {
27+
checkDefaultClient()
28+
return defaultK.Get(obj)
29+
}
30+
31+
// List returns a function that lists resources and returns the occurring error.
32+
// It can be used with gomega.Eventually() like this
33+
// deployments := v1.DeploymentList{ ... }
34+
// gomega.Eventually(k.List(&deployments)).To(gomega.Succeed())
35+
// By calling the returned function directly it can also be used as gomega.Expect(k.List(...)()).To(...)
36+
func List(obj client.ObjectList, opts ...client.ListOption) func() error {
37+
checkDefaultClient()
38+
return defaultK.List(obj, opts...)
39+
}
40+
41+
// Update returns a function that fetches a resource, applies the provided update function and then updates the resource.
42+
// It can be used with gomega.Eventually() like this:
43+
// deployment := appsv1.Deployment{ ... }
44+
// gomega.Eventually(k.Update(&deployment, func (o client.Object) {
45+
// deployment.Spec.Replicas = 3
46+
// return &deployment
47+
// })).To(gomega.Scucceed())
48+
// By calling the returned function directly it can also be used as gomega.Expect(k.Update(...)()).To(...)
49+
func Update(obj client.Object, f UpdateFunc, opts ...client.UpdateOption) func() error {
50+
checkDefaultClient()
51+
return defaultK.Update(obj, f, opts...)
52+
}
53+
54+
// UpdateStatus returns a function that fetches a resource, applies the provided update function and then updates the resource's status.
55+
// It can be used with gomega.Eventually() like this:
56+
// deployment := appsv1.Deployment{ ... }
57+
// gomega.Eventually(k.Update(&deployment, func (o client.Object) {
58+
// deployment.Status.AvailableReplicas = 1
59+
// return &deployment
60+
// })).To(gomega.Scucceed())
61+
// By calling the returned function directly it can also be used as gomega.Expect(k.UpdateStatus(...)()).To(...)
62+
func UpdateStatus(obj client.Object, f UpdateFunc, opts ...client.UpdateOption) func() error {
63+
checkDefaultClient()
64+
return defaultK.UpdateStatus(obj, f, opts...)
65+
}
66+
67+
// Object returns a function that fetches a resource and returns the object.
68+
// It can be used with gomega.Eventually() like this:
69+
// deployment := appsv1.Deployment{ ... }
70+
// gomega.Eventually(k.Object(&deployment)).To(HaveField("Spec.Replicas", gomega.Equal(pointer.Int32(3))))
71+
// By calling the returned function directly it can also be used as gomega.Expect(k.Object(...)()).To(...)
72+
func Object(obj client.Object) func() (client.Object, error) {
73+
checkDefaultClient()
74+
return defaultK.Object(obj)
75+
}
76+
77+
// ObjectList returns a function that fetches a resource and returns the object.
78+
// It can be used with gomega.Eventually() like this:
79+
// deployments := appsv1.DeploymentList{ ... }
80+
// gomega.Eventually(k.ObjectList(&deployments)).To(HaveField("Items", HaveLen(1)))
81+
// By calling the returned function directly it can also be used as gomega.Expect(k.ObjectList(...)()).To(...)
82+
func ObjectList(obj client.ObjectList, opts ...client.ListOption) func() (client.ObjectList, error) {
83+
checkDefaultClient()
84+
return defaultK.ObjectList(obj, opts...)
85+
}

pkg/envtest/komega/interfaces.go

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,41 +18,65 @@ package komega
1818

1919
import (
2020
"context"
21-
"time"
2221

23-
"github.com/onsi/gomega"
24-
"k8s.io/apimachinery/pkg/runtime"
2522
"sigs.k8s.io/controller-runtime/pkg/client"
2623
)
2724

2825
// Komega is the root interface that the Matcher implements.
2926
type Komega interface {
30-
KomegaAsync
31-
KomegaSync
32-
WithContext(context.Context) Komega
33-
}
27+
// Get returns a function that fetches a resource and returns the occurring error.
28+
// It can be used with gomega.Eventually() like this
29+
// deployment := appsv1.Deployment{ ... }
30+
// gomega.Eventually(k.Get(&deployment)).To(gomega.Succeed())
31+
// By calling the returned function directly it can also be used with gomega.Expect(k.Get(...)()).To(...)
32+
Get(client.Object) func() error
3433

35-
// KomegaSync is the interface for any sync assertions that
36-
// the matcher implements.
37-
type KomegaSync interface {
38-
Create(client.Object, ...client.CreateOption) gomega.GomegaAssertion
39-
Delete(client.Object, ...client.DeleteOption) gomega.GomegaAssertion
40-
WithExtras(...interface{}) KomegaSync
41-
}
34+
// List returns a function that lists resources and returns the occurring error.
35+
// It can be used with gomega.Eventually() like this
36+
// deployments := v1.DeploymentList{ ... }
37+
// gomega.Eventually(k.List(&deployments)).To(gomega.Succeed())
38+
// By calling the returned function directly it can also be used as gomega.Expect(k.List(...)()).To(...)
39+
List(client.ObjectList, ...client.ListOption) func() error
40+
41+
// Update returns a function that fetches a resource, applies the provided update function and then updates the resource.
42+
// It can be used with gomega.Eventually() like this:
43+
// deployment := appsv1.Deployment{ ... }
44+
// gomega.Eventually(k.Update(&deployment, func (o client.Object) {
45+
// deployment.Spec.Replicas = 3
46+
// return &deployment
47+
// })).To(gomega.Scucceed())
48+
// By calling the returned function directly it can also be used as gomega.Expect(k.Update(...)()).To(...)
49+
Update(client.Object, UpdateFunc, ...client.UpdateOption) func() error
4250

43-
// KomegaAsync is the interface for any async assertions that
44-
// the matcher implements.
45-
type KomegaAsync interface {
46-
Consistently(runtime.Object, ...client.ListOption) gomega.AsyncAssertion
47-
Eventually(runtime.Object, ...client.ListOption) gomega.AsyncAssertion
48-
Get(client.Object) gomega.AsyncAssertion
49-
List(client.ObjectList, ...client.ListOption) gomega.AsyncAssertion
50-
Update(client.Object, UpdateFunc, ...client.UpdateOption) gomega.AsyncAssertion
51-
UpdateStatus(client.Object, UpdateFunc, ...client.UpdateOption) gomega.AsyncAssertion
52-
WithTimeout(time.Duration) KomegaAsync
53-
WithPollInterval(time.Duration) KomegaAsync
51+
// UpdateStatus returns a function that fetches a resource, applies the provided update function and then updates the resource's status.
52+
// It can be used with gomega.Eventually() like this:
53+
// deployment := appsv1.Deployment{ ... }
54+
// gomega.Eventually(k.Update(&deployment, func (o client.Object) {
55+
// deployment.Status.AvailableReplicas = 1
56+
// return &deployment
57+
// })).To(gomega.Scucceed())
58+
// By calling the returned function directly it can also be used as gomega.Expect(k.UpdateStatus(...)()).To(...)
59+
UpdateStatus(client.Object, UpdateFunc, ...client.UpdateOption) func() error
60+
61+
// Object returns a function that fetches a resource and returns the object.
62+
// It can be used with gomega.Eventually() like this:
63+
// deployment := appsv1.Deployment{ ... }
64+
// gomega.Eventually(k.Object(&deployment)).To(HaveField("Spec.Replicas", gomega.Equal(pointer.Int32(3))))
65+
// By calling the returned function directly it can also be used as gomega.Expect(k.Object(...)()).To(...)
66+
Object(client.Object) func() (client.Object, error)
67+
68+
// ObjectList returns a function that fetches a resource and returns the object.
69+
// It can be used with gomega.Eventually() like this:
70+
// deployments := appsv1.DeploymentList{ ... }
71+
// gomega.Eventually(k.ObjectList(&deployments)).To(HaveField("Items", HaveLen(1)))
72+
// By calling the returned function directly it can also be used as gomega.Expect(k.ObjectList(...)()).To(...)
73+
ObjectList(client.ObjectList, ...client.ListOption) func() (client.ObjectList, error)
74+
75+
// WithClient returns a copy that uses the given client.
76+
WithClient(client.Client) Komega
77+
// WithContext returns a copy that uses the given context.
78+
WithContext(context.Context) Komega
5479
}
5580

56-
// UpdateFunc modifies the object fetched from the API server before sending
57-
// the update
81+
// UpdateFunc receives an object and expects a modified version of it to be returned.
5882
type UpdateFunc func(client.Object) client.Object

pkg/envtest/komega/komega.go

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
Copyright 2021 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package komega
18+
19+
import (
20+
"context"
21+
22+
"k8s.io/apimachinery/pkg/types"
23+
"sigs.k8s.io/controller-runtime/pkg/client"
24+
)
25+
26+
// komega is a collection of utilites for writing tests involving a mocked
27+
// Kubernetes API.
28+
type komega struct {
29+
ctx context.Context
30+
client client.Client
31+
}
32+
33+
var _ Komega = &komega{}
34+
35+
// New creates a new Komega instance with the given client.
36+
func New(c client.Client) Komega {
37+
return &komega{
38+
client: c,
39+
}
40+
}
41+
42+
// WithContext returns a copy that uses the given context.
43+
func (k komega) WithContext(ctx context.Context) Komega {
44+
k.ctx = ctx
45+
return &k
46+
}
47+
48+
// WithClient returns a copy that uses the given client.
49+
func (k komega) WithClient(c client.Client) Komega {
50+
k.client = c
51+
return &k
52+
}
53+
54+
// context returns the matcher context if one has been set or context.Background() otherwise.
55+
func (k *komega) context() context.Context {
56+
if k.ctx == nil {
57+
return context.Background()
58+
}
59+
return k.ctx
60+
}
61+
62+
// Get returns a function that fetches a resource and returns the occurring error.
63+
func (k *komega) Get(obj client.Object) func() error {
64+
key := types.NamespacedName{
65+
Name: obj.GetName(),
66+
Namespace: obj.GetNamespace(),
67+
}
68+
return func() error {
69+
return k.client.Get(k.context(), key, obj)
70+
}
71+
}
72+
73+
// List returns a function that lists resources and returns the occurring error.
74+
func (k *komega) List(obj client.ObjectList, opts ...client.ListOption) func() error {
75+
return func() error {
76+
return k.client.List(k.context(), obj, opts...)
77+
}
78+
}
79+
80+
// Update returns a function that fetches a resource, applies the provided update function and then updates the resource.
81+
func (k *komega) Update(obj client.Object, updateFunc UpdateFunc, opts ...client.UpdateOption) func() error {
82+
key := types.NamespacedName{
83+
Name: obj.GetName(),
84+
Namespace: obj.GetNamespace(),
85+
}
86+
return func() error {
87+
err := k.client.Get(k.context(), key, obj)
88+
if err != nil {
89+
return err
90+
}
91+
return k.client.Update(k.context(), updateFunc(obj), opts...)
92+
}
93+
}
94+
95+
// UpdateStatus returns a function that fetches a resource, applies the provided update function and then updates the resource's status.
96+
func (k *komega) UpdateStatus(obj client.Object, updateFunc UpdateFunc, opts ...client.UpdateOption) func() error {
97+
key := types.NamespacedName{
98+
Name: obj.GetName(),
99+
Namespace: obj.GetNamespace(),
100+
}
101+
return func() error {
102+
err := k.client.Get(k.context(), key, obj)
103+
if err != nil {
104+
return err
105+
}
106+
return k.client.Status().Update(k.context(), updateFunc(obj), opts...)
107+
}
108+
}
109+
110+
// Object returns a function that fetches a resource and returns the object.
111+
func (k *komega) Object(obj client.Object) func() (client.Object, error) {
112+
key := types.NamespacedName{
113+
Name: obj.GetName(),
114+
Namespace: obj.GetNamespace(),
115+
}
116+
return func() (client.Object, error) {
117+
err := k.client.Get(k.context(), key, obj)
118+
return obj, err
119+
}
120+
}
121+
122+
// ObjectList returns a function that fetches a resource and returns the object.
123+
func (k *komega) ObjectList(obj client.ObjectList, opts ...client.ListOption) func() (client.ObjectList, error) {
124+
return func() (client.ObjectList, error) {
125+
err := k.client.List(k.context(), obj, opts...)
126+
return obj, err
127+
}
128+
}

0 commit comments

Comments
 (0)