Skip to content

Commit 728b79f

Browse files
authored
Add cached requirement feature and fix typo (#18)
* feat: add cached requirement feature to integration tests * fix: correct typo in TestNamespace constant * fix: correct namespace references in integration tests
1 parent df3d5c0 commit 728b79f

File tree

3 files changed

+196
-12
lines changed

3 files changed

+196
-12
lines changed

test/integration/integration_test.go

Lines changed: 192 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/stretchr/testify/assert"
9+
apierr "k8s.io/apimachinery/pkg/api/errors"
910
"k8s.io/apimachinery/pkg/util/wait"
1011
"sigs.k8s.io/e2e-framework/pkg/envconf"
1112
"sigs.k8s.io/e2e-framework/pkg/features"
@@ -18,30 +19,31 @@ import (
1819
type requirementKey struct{}
1920

2021
const (
21-
testRequirementName = "test-requirement"
22+
testRequirementName = "test-requirement"
23+
cachedRequirementName = "cached-requirement"
2224
)
2325

24-
var CacheFeature = features.New("Simple Requirements").
26+
var SimpleRequirementFeature = features.New("Simple Requirements").
2527
Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
2628
// start a deployment
27-
requiremnt := utils.NewRequirement(testRequirementName, utils.TestNamespcae)
28-
requiremnt.Namespace = utils.TestNamespcae
29-
if err := c.Client().Resources().Create(ctx, requiremnt); err != nil {
29+
testRequirement := utils.NewRequirement(testRequirementName, utils.TestNamespace)
30+
testRequirement.Namespace = utils.TestNamespace
31+
if err := c.Client().Resources().Create(ctx, testRequirement); err != nil {
3032
t.Fatal(err)
3133
}
3234
time.Sleep(2 * time.Second)
3335

3436
return ctx
3537
}).
36-
Assess("create requirement", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
38+
Assess("requirement created successfully", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
3739
var requirement v1.Requirement
38-
if err := cfg.Client().Resources().Get(ctx, testRequirementName, utils.TestNamespcae, &requirement); err != nil {
40+
if err := cfg.Client().Resources().Get(ctx, testRequirementName, utils.TestNamespace, &requirement); err != nil {
3941
t.Fatal(err)
4042
}
4143
assert.Equal(t, testRequirementName, requirement.Name)
4244
if err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) {
4345
requirement := &v1.Requirement{}
44-
if err := cfg.Client().Resources().Get(ctx, testRequirementName, utils.TestNamespcae, requirement); err != nil {
46+
if err := cfg.Client().Resources().Get(ctx, testRequirementName, utils.TestNamespace, requirement); err != nil {
4547
return false, err
4648
}
4749
if requirement.Status.Phase != rqutils.PhaseReady {
@@ -60,3 +62,185 @@ var CacheFeature = features.New("Simple Requirements").
6062
}
6163
return ctx
6264
}).Feature()
65+
66+
func newRequirementWithCache(name string) *v1.Requirement {
67+
requirement := utils.NewRequirement(name, utils.TestNamespace)
68+
requirement.Spec.EnableCache = true
69+
return requirement
70+
}
71+
72+
var CachedRequirementFeature = features.New("Cached Requirements").
73+
Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context {
74+
// Create a requirement with cache enabled
75+
if err := c.Client().Resources().Create(ctx, newRequirementWithCache(cachedRequirementName)); err != nil {
76+
t.Fatal(err)
77+
}
78+
time.Sleep(2 * time.Second)
79+
return ctx
80+
}).
81+
Assess("cache requirement created and synced", func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
82+
var requirement v1.Requirement
83+
if err := cfg.Client().Resources().Get(ctx, cachedRequirementName, utils.TestNamespace, &requirement); err != nil {
84+
t.Fatal(err)
85+
}
86+
assert.Equal(t, cachedRequirementName, requirement.Name)
87+
88+
// Wait for requirement to be ready
89+
if err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) {
90+
requirement := &v1.Requirement{}
91+
if err := cfg.Client().Resources().Get(ctx, cachedRequirementName, utils.TestNamespace, requirement); err != nil {
92+
return false, err
93+
}
94+
if requirement.Status.Phase != rqutils.PhaseReady {
95+
return false, nil
96+
}
97+
return true, nil
98+
}); err != nil {
99+
t.Fatal(err, "requirement not ready")
100+
}
101+
cacheKey := requirement.Status.CacheKey
102+
// Get the associated Cache resource
103+
cache := &v1.Cache{}
104+
cacheName := "cache-" + cacheKey
105+
if err := cfg.Client().Resources().Get(ctx, cacheName, utils.TestNamespace, cache); err != nil {
106+
t.Fatal(err, "cache not found")
107+
}
108+
// Verify the cache key matches
109+
assert.Equal(t, cacheKey, cache.Status.CacheKey)
110+
111+
// Get all Operations
112+
var operations v1.OperationList
113+
if err := cfg.Client().Resources().List(ctx, &operations); err != nil {
114+
t.Fatal(err, "failed to list operations")
115+
}
116+
117+
// Verify one operation is owned by our requirement by checking owner references
118+
var (
119+
ownedByRequirement []v1.Operation
120+
ownedByCache []v1.Operation
121+
)
122+
for _, op := range operations.Items {
123+
for _, ownerRef := range op.OwnerReferences {
124+
if ownerRef.APIVersion == v1.GroupVersion.String() &&
125+
ownerRef.Kind == "Requirement" &&
126+
ownerRef.Name == requirement.Name &&
127+
ownerRef.UID == requirement.UID {
128+
ownedByRequirement = append(ownedByRequirement, op)
129+
}
130+
if ownerRef.APIVersion == v1.GroupVersion.String() &&
131+
ownerRef.Kind == "Cache" &&
132+
ownerRef.Name == cache.Name &&
133+
ownerRef.UID == cache.UID {
134+
ownedByCache = append(ownedByCache, op)
135+
}
136+
}
137+
}
138+
// Verify one operation is owned by our requirement
139+
assert.Equal(t, 1, len(ownedByRequirement), "expected one operation owned by requirement")
140+
// Verify number of cache operations matches keepAlive count
141+
assert.Equal(t, int(cache.Status.KeepAliveCount), len(ownedByCache))
142+
143+
// delete the requirement
144+
if err := cfg.Client().Resources().Delete(ctx, &requirement); err != nil {
145+
t.Fatal(err)
146+
}
147+
// wait for the requirement to be deleted
148+
if err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) {
149+
requirement := &v1.Requirement{}
150+
err := cfg.Client().Resources().Get(ctx, cachedRequirementName, utils.TestNamespace, requirement)
151+
// err is not found, so requirement is deleted
152+
if err != nil {
153+
if apierr.IsNotFound(err) {
154+
return true, nil
155+
}
156+
return false, err
157+
}
158+
return false, nil
159+
}); err != nil {
160+
t.Fatal(err, "requirement not deleted")
161+
}
162+
163+
newCachedRequirementName := cachedRequirementName + "-new"
164+
// create a new requirement with the same name
165+
if err := cfg.Client().Resources().Create(ctx, newRequirementWithCache(newCachedRequirementName)); err != nil {
166+
t.Fatal(err)
167+
}
168+
169+
newRequirement := &v1.Requirement{}
170+
// wait for the new requirement to be ready
171+
if err := wait.PollUntilContextTimeout(ctx, 10*time.Second, 60*time.Second, true, func(ctx context.Context) (bool, error) {
172+
if err := cfg.Client().Resources().Get(ctx, newCachedRequirementName, utils.TestNamespace, newRequirement); err != nil {
173+
return false, err
174+
}
175+
if newRequirement.Status.Phase != rqutils.PhaseReady {
176+
return false, nil
177+
}
178+
return true, nil
179+
}); err != nil {
180+
t.Fatal(err, "new requirement not ready")
181+
}
182+
183+
// cache should be hit
184+
cacheHit := false
185+
for _, condition := range newRequirement.Status.Conditions {
186+
if condition.Type == rqutils.ConditionOperationReady {
187+
cacheHit = true
188+
break
189+
}
190+
}
191+
assert.True(t, cacheHit, "expected cache hit condition")
192+
193+
// list operations and verify the new requirement has operation with the same name from original cache
194+
var newOperations v1.OperationList
195+
if err := cfg.Client().Resources().List(ctx, &newOperations); err != nil {
196+
t.Fatal(err, "failed to list operations")
197+
}
198+
var (
199+
ownedByNewRequirement []v1.Operation
200+
ownedByCurrentCache []v1.Operation
201+
)
202+
203+
for _, op := range newOperations.Items {
204+
for _, ownerRef := range op.OwnerReferences {
205+
if ownerRef.APIVersion == v1.GroupVersion.String() &&
206+
ownerRef.Kind == "Requirement" &&
207+
ownerRef.Name == newCachedRequirementName &&
208+
ownerRef.UID == newRequirement.UID {
209+
ownedByNewRequirement = append(ownedByNewRequirement, op)
210+
}
211+
if ownerRef.APIVersion == v1.GroupVersion.String() &&
212+
ownerRef.Kind == "Cache" &&
213+
ownerRef.Name == cacheName &&
214+
ownerRef.UID == cache.UID {
215+
ownedByCurrentCache = append(ownedByCurrentCache, op)
216+
}
217+
}
218+
}
219+
// Verify one operation is owned by our requirement
220+
assert.Equal(t, 1, len(ownedByNewRequirement), "expected one operation owned by requirement")
221+
// Verify number of cache operations matches keepAlive count
222+
assert.Equal(t, int(cache.Status.KeepAliveCount), len(ownedByCurrentCache))
223+
224+
// Verify the operation come from the original cache
225+
found := false
226+
for _, op := range ownedByCache {
227+
if op.Name == ownedByNewRequirement[0].Name {
228+
found = true
229+
break
230+
}
231+
}
232+
assert.True(t, found, "expected operation to be from original cache")
233+
234+
// the operation should not be included in the new cache
235+
for _, op := range ownedByCurrentCache {
236+
assert.NotEqual(t, op.Name, ownedByNewRequirement[0].Name, "operation should not be included in the new cache")
237+
}
238+
return context.WithValue(ctx, requirementKey{}, newRequirement)
239+
}).
240+
Teardown(func(ctx context.Context, t *testing.T, cfg *envconf.Config) context.Context {
241+
requirement := ctx.Value(requirementKey{}).(*v1.Requirement)
242+
if err := cfg.Client().Resources().Delete(ctx, requirement); err != nil {
243+
t.Fatal(err)
244+
}
245+
return ctx
246+
}).Feature()

test/integration/main_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ func TestMain(m *testing.M) {
4141
BuildImage,
4242
envfuncs.CreateCluster(kind.NewProvider(), kindClusterName),
4343
envfuncs.LoadDockerImageToCluster(kindClusterName, projectImage),
44-
envfuncs.CreateNamespace(utils.TestNamespcae),
44+
envfuncs.CreateNamespace(utils.TestNamespace),
4545
InstallCRD,
4646
DeployControllerManager,
4747
)
4848

4949
// Teardown the test environment
5050
testenv = testenv.Finish(
51-
envfuncs.DeleteNamespace(utils.TestNamespcae),
51+
envfuncs.DeleteNamespace(utils.TestNamespace),
5252
envfuncs.DestroyCluster(kindClusterName),
5353
)
5454

@@ -86,5 +86,5 @@ func UninstallCRD(ctx context.Context, cfg *envconf.Config) (context.Context, er
8686
func TestRealCluster(t *testing.T) {
8787
// Create a new test environment configuration
8888
// Run the integration tests against the Kind cluster
89-
testenv.Test(t, CacheFeature)
89+
testenv.Test(t, SimpleRequirementFeature, CachedRequirementFeature)
9090
}

test/utils/resources.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
)
99

1010
const (
11-
TestNamespcae = "operation-cache-controller-system"
11+
TestNamespace = "operation-cache-controller-system"
1212
)
1313

1414
func NewTestJobSpec(name string) batchv1.JobSpec {

0 commit comments

Comments
 (0)