Skip to content

Commit ec51900

Browse files
authored
Refactor operation and requirement handling (#23)
* Refactor operation and requirement handling - Removed the `ClearConditions` function and its associated test from the `operation` package. - Introduced `OperationHelper` struct with methods for operation management, including condition clearing and ID generation. - Added tests for `OperationHelper` methods, ensuring functionality for diffing app deployments and comparing job specifications. - Deleted unused constants and introduced `RequirementHelper` for managing requirement conditions. - Implemented tests for `RequirementHelper`, covering condition updates and cache miss checks. - Added utility functions for generating random strings. - Updated integration tests to remove dependencies on removed constants and ensure proper phase checks. - Generated mock implementations for client interfaces to facilitate testing. * Add tests for application teardown and provision cache fields - Implemented `TestAppCacheFieldFromApplicationTeardown` to validate the extraction of cache fields from application teardown specifications. - Implemented `TestAppCacheFieldFromApplicationProvision` to validate the extraction of cache fields from application provision specifications. - Enhanced `CompareProvisionJobs` to utilize a helper function for comparing dependencies, ensuring consistent behavior. - Added comprehensive test cases for comparing provision jobs, including scenarios with identical jobs, different dependencies, and empty dependencies. - Introduced utility functions for comparing string slices and environment variables to streamline comparison logic. - Updated existing tests to reflect changes in the comparison logic and ensure thorough coverage of edge cases.
1 parent 747e08c commit ec51900

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+2672
-946
lines changed

api/v1alpha1/appdeployment_types.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ import (
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2222
)
2323

24+
const (
25+
AppDeploymentOwnerKey = ".appDeployment.metadata.controller"
26+
27+
AppDeploymentFinalizerName = "finalizer.appdeployment.devinfra.goms.io"
28+
29+
// phase types
30+
AppDeploymentPhaseEmpty = ""
31+
AppDeploymentPhasePending = "Pending"
32+
AppDeploymentPhaseDeploying = "Deploying"
33+
AppDeploymentPhaseReady = "Ready"
34+
AppDeploymentPhaseDeleting = "Deleting"
35+
AppDeploymentPhaseDeleted = "Deleted"
36+
)
37+
2438
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
2539
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
2640

api/v1alpha1/cache_types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import (
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2121
)
2222

23+
const (
24+
CacheOwnerKey = ".metadata.controller.cache"
25+
)
26+
2327
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
2428
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
2529

api/v1alpha1/operation_types.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ import (
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2222
)
2323

24+
const (
25+
OperationOwnerKey = ".operation.metadata.controller"
26+
27+
OperationFinalizerName = "finalizer.operation.controller.azure.com"
28+
OperationAcquiredAnnotationKey = "operation.controller.azure.com/acquired"
29+
30+
OperationPhaseEmpty = ""
31+
OperationPhaseReconciling = "Reconciling"
32+
OperationPhaseReconciled = "Reconciled"
33+
OperationPhaseDeleting = "Deleting"
34+
OperationPhaseDeleted = "Deleted"
35+
)
36+
2437
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
2538
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
2639

api/v1alpha1/requirement_types.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ import (
2020
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2121
)
2222

23+
const (
24+
RequirementOwnerKey = ".requirement.metadata.controller"
25+
26+
RequirementFinalizerName = "finalizer.requirement.devinfra.goms.io"
27+
28+
RequirementConditionRequirementInitialized = "RequirementInitialized"
29+
RequirementConditionCacheResourceFound = "CacheCRFound"
30+
RequirementConditionCachedOperationAcquired = "CachedOpAcquired"
31+
RequirementConditionOperationReady = "OperationReady"
32+
33+
RequirementConditionReasonNoOperationAvailable = "NoOperationAvailable"
34+
RequirementConditionReasonCacheCRNotFound = "CacheCRNotFound"
35+
RequirementConditionReasonCacheCRFound = "CacheCRFound"
36+
RequirementConditionReasonCacheHit = "CacheHit"
37+
RequirementConditionReasonCacheMiss = "CacheMiss"
38+
39+
RequirementPhaseEmpty = ""
40+
RequirementPhaseCacheChecking = "CacheChecking"
41+
RequirementPhaseOperating = "Operating"
42+
RequirementPhaseReady = "Ready"
43+
RequirementPhaseDeleted = "Deleted"
44+
RequirementPhaseDeleting = "Deleting"
45+
)
46+
2347
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
2448
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
2549

internal/controller/appdeployment_controller.go

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@ import (
2626
ctrl "sigs.k8s.io/controller-runtime"
2727
"sigs.k8s.io/controller-runtime/pkg/client"
2828
"sigs.k8s.io/controller-runtime/pkg/controller"
29-
"sigs.k8s.io/controller-runtime/pkg/log"
29+
klog "sigs.k8s.io/controller-runtime/pkg/log"
3030

3131
"github.com/Azure/operation-cache-controller/api/v1alpha1"
32-
apdutil "github.com/Azure/operation-cache-controller/internal/utils/controller/appdeployment"
32+
"github.com/Azure/operation-cache-controller/internal/handler"
33+
"github.com/Azure/operation-cache-controller/internal/log"
3334
"github.com/Azure/operation-cache-controller/internal/utils/reconciler"
3435
)
3536

@@ -57,23 +58,22 @@ type AppDeploymentReconciler struct {
5758
// For more details, check Reconcile and its Result here:
5859
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
5960
func (r *AppDeploymentReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
60-
logger := log.FromContext(ctx).WithValues(apdutil.LogKeyAppDeploymentName, req.NamespacedName)
61+
logger := klog.FromContext(ctx).WithValues(log.AppDeploymentJobName, req.NamespacedName)
6162
appdeployment := &v1alpha1.AppDeployment{}
6263
if err := r.Get(ctx, req.NamespacedName, appdeployment); err != nil {
6364
return ctrl.Result{}, client.IgnoreNotFound(err)
6465
}
6566

66-
adapter := NewAppDeploymentAdapter(ctx, appdeployment, logger, r.Client, r.recorder)
67-
return r.ReconcileHandler(ctx, adapter)
67+
return r.ReconcileHandler(ctx, handler.NewAppDeploymentHandler(ctx, appdeployment, logger, r.Client, r.recorder))
6868
}
69-
func (r *AppDeploymentReconciler) ReconcileHandler(ctx context.Context, adapter AppDeploymentAdapterInterface) (ctrl.Result, error) {
69+
func (r *AppDeploymentReconciler) ReconcileHandler(ctx context.Context, h handler.AppDeploymentHandlerInterface) (ctrl.Result, error) {
7070
operations := []reconciler.ReconcileOperation{
71-
adapter.EnsureApplicationValid,
72-
adapter.EnsureFinalizer,
73-
adapter.EnsureFinalizerDeleted,
74-
adapter.EnsureDependenciesReady,
75-
adapter.EnsureDeployingFinished,
76-
adapter.EnsureTeardownFinished,
71+
h.EnsureApplicationValid,
72+
h.EnsureFinalizer,
73+
h.EnsureFinalizerDeleted,
74+
h.EnsureDependenciesReady,
75+
h.EnsureDeployingFinished,
76+
h.EnsureTeardownFinished,
7777
}
7878

7979
for _, operation := range operations {
@@ -88,22 +88,21 @@ func (r *AppDeploymentReconciler) ReconcileHandler(ctx context.Context, adapter
8888
return ctrl.Result{}, nil
8989
}
9090

91-
var appDeploymentOwnerKey = ".appDeployment.metadata.controller"
91+
func appDeploymentIndexerFunc(rawObj client.Object) []string {
92+
job := rawObj.(*batchv1.Job)
93+
owner := metav1.GetControllerOf(job)
94+
if owner == nil {
95+
return nil
96+
}
97+
if owner.APIVersion != v1alpha1.GroupVersion.String() || owner.Kind != "AppDeployment" {
98+
return nil
99+
}
100+
return []string{owner.Name}
101+
}
92102

93103
// SetupWithManager sets up the controller with the Manager.
94104
func (r *AppDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error {
95-
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &batchv1.Job{}, appDeploymentOwnerKey,
96-
func(rawObj client.Object) []string {
97-
job := rawObj.(*batchv1.Job)
98-
owner := metav1.GetControllerOf(job)
99-
if owner == nil {
100-
return nil
101-
}
102-
if owner.APIVersion != v1alpha1.GroupVersion.String() || owner.Kind != "AppDeployment" {
103-
return nil
104-
}
105-
return []string{owner.Name}
106-
}); err != nil {
105+
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &batchv1.Job{}, v1alpha1.AppDeploymentOwnerKey, appDeploymentIndexerFunc); err != nil {
107106
return err
108107
}
109108

internal/controller/appdeployment_controller_test.go

Lines changed: 88 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ import (
3333
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3434

3535
"github.com/Azure/operation-cache-controller/api/v1alpha1"
36-
ctrlmocks "github.com/Azure/operation-cache-controller/internal/controller/mocks"
37-
mockpkg "github.com/Azure/operation-cache-controller/internal/mocks"
36+
"github.com/Azure/operation-cache-controller/internal/handler"
37+
hmocks "github.com/Azure/operation-cache-controller/internal/handler/mocks"
38+
utilsmock "github.com/Azure/operation-cache-controller/internal/utils/mocks"
3839
"github.com/Azure/operation-cache-controller/internal/utils/reconciler"
3940
)
4041

@@ -87,9 +88,9 @@ var _ = Describe("AppDeployment Controller", func() {
8788
const resourceName = "test-resource"
8889
var (
8990
mockRecorderCtrl *gomock.Controller
90-
mockRecorder *mockpkg.MockEventRecorder
91+
mockRecorder *utilsmock.MockEventRecorder
9192
mockAdapterCtrl *gomock.Controller
92-
mockAdapter *ctrlmocks.MockAppDeploymentAdapterInterface
93+
mockAdapter *hmocks.MockAppDeploymentHandlerInterface
9394
)
9495
ctx := context.Background()
9596

@@ -121,9 +122,9 @@ var _ = Describe("AppDeployment Controller", func() {
121122
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
122123
}
123124
mockRecorderCtrl = gomock.NewController(GinkgoT())
124-
mockRecorder = mockpkg.NewMockEventRecorder(mockRecorderCtrl)
125+
mockRecorder = utilsmock.NewMockEventRecorder(mockRecorderCtrl)
125126
mockAdapterCtrl = gomock.NewController(GinkgoT())
126-
mockAdapter = ctrlmocks.NewMockAppDeploymentAdapterInterface(mockAdapterCtrl)
127+
mockAdapter = hmocks.NewMockAppDeploymentHandlerInterface(mockAdapterCtrl)
127128
})
128129

129130
AfterEach(func() {
@@ -142,7 +143,7 @@ var _ = Describe("AppDeployment Controller", func() {
142143
Scheme: k8sClient.Scheme(),
143144
recorder: mockRecorder,
144145
}
145-
ctx = context.WithValue(ctx, appdeploymentAdapterContextKey{}, mockAdapter)
146+
ctx = context.WithValue(ctx, handler.AppdeploymentHandlerContextKey{}, mockAdapter)
146147

147148
mockAdapter.EXPECT().EnsureApplicationValid(gomock.Any()).Return(reconciler.OperationResult{}, nil)
148149
mockAdapter.EXPECT().EnsureFinalizer(gomock.Any()).Return(reconciler.OperationResult{}, nil)
@@ -165,7 +166,7 @@ var _ = Describe("AppDeployment Controller", func() {
165166
Scheme: k8sClient.Scheme(),
166167
recorder: mockRecorder,
167168
}
168-
ctx = context.WithValue(ctx, appdeploymentAdapterContextKey{}, mockAdapter)
169+
ctx = context.WithValue(ctx, handler.AppdeploymentHandlerContextKey{}, mockAdapter)
169170

170171
mockAdapter.EXPECT().EnsureApplicationValid(gomock.Any()).Return(reconciler.OperationResult{
171172
CancelRequest: true,
@@ -184,7 +185,7 @@ var _ = Describe("AppDeployment Controller", func() {
184185
Scheme: k8sClient.Scheme(),
185186
recorder: mockRecorder,
186187
}
187-
ctx = context.WithValue(ctx, appdeploymentAdapterContextKey{}, mockAdapter)
188+
ctx = context.WithValue(ctx, handler.AppdeploymentHandlerContextKey{}, mockAdapter)
188189

189190
mockAdapter.EXPECT().EnsureApplicationValid(gomock.Any()).Return(reconciler.OperationResult{}, errors.NewServiceUnavailable("test error"))
190191

@@ -194,4 +195,82 @@ var _ = Describe("AppDeployment Controller", func() {
194195
Expect(errors.IsServiceUnavailable(err)).To(BeTrue(), "expected error is ServiceUnavailable")
195196
})
196197
})
198+
199+
Context("appDeploymentIndexerFunc tests", func() {
200+
It("should return nil for Job without owner", func() {
201+
job := &batchv1.Job{
202+
ObjectMeta: metav1.ObjectMeta{
203+
Name: "job-without-owner",
204+
Namespace: "default",
205+
},
206+
}
207+
208+
result := appDeploymentIndexerFunc(job)
209+
Expect(result).To(BeNil())
210+
})
211+
212+
It("should return nil for Job with non-AppDeployment owner", func() {
213+
job := &batchv1.Job{
214+
ObjectMeta: metav1.ObjectMeta{
215+
Name: "job-with-wrong-owner",
216+
Namespace: "default",
217+
OwnerReferences: []metav1.OwnerReference{
218+
{
219+
APIVersion: "v1",
220+
Kind: "Pod",
221+
Name: "owner-pod",
222+
UID: "12345",
223+
Controller: &[]bool{true}[0],
224+
},
225+
},
226+
},
227+
}
228+
229+
result := appDeploymentIndexerFunc(job)
230+
Expect(result).To(BeNil())
231+
})
232+
233+
It("should return owner name for Job with AppDeployment owner", func() {
234+
ownerName := "test-appdeployment"
235+
job := &batchv1.Job{
236+
ObjectMeta: metav1.ObjectMeta{
237+
Name: "job-with-appdeployment-owner",
238+
Namespace: "default",
239+
OwnerReferences: []metav1.OwnerReference{
240+
{
241+
APIVersion: v1alpha1.GroupVersion.String(),
242+
Kind: "AppDeployment",
243+
Name: ownerName,
244+
UID: "67890",
245+
Controller: &[]bool{true}[0],
246+
},
247+
},
248+
},
249+
}
250+
251+
result := appDeploymentIndexerFunc(job)
252+
Expect(result).To(Equal([]string{ownerName}))
253+
})
254+
255+
It("should return nil for Job with AppDeployment owner reference that is not controller", func() {
256+
job := &batchv1.Job{
257+
ObjectMeta: metav1.ObjectMeta{
258+
Name: "job-with-non-controller-appdeployment",
259+
Namespace: "default",
260+
OwnerReferences: []metav1.OwnerReference{
261+
{
262+
APIVersion: v1alpha1.GroupVersion.String(),
263+
Kind: "AppDeployment",
264+
Name: "test-appdeployment",
265+
UID: "13579",
266+
Controller: nil, // Not a controller reference
267+
},
268+
},
269+
},
270+
}
271+
272+
result := appDeploymentIndexerFunc(job)
273+
Expect(result).To(BeNil())
274+
})
275+
})
197276
})

internal/controller/cache_controller.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"sigs.k8s.io/controller-runtime/pkg/log"
3030

3131
"github.com/Azure/operation-cache-controller/api/v1alpha1"
32+
"github.com/Azure/operation-cache-controller/internal/handler"
3233
"github.com/Azure/operation-cache-controller/internal/utils/reconciler"
3334
)
3435

@@ -65,18 +66,17 @@ func (r *CacheReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
6566
return ctrl.Result{}, client.IgnoreNotFound(err)
6667
}
6768

68-
adapter := NewCacheAdapter(ctx, cache, logger, r.Client, r.Scheme, r.recorder, ctrl.SetControllerReference)
69-
return r.reconcileHandler(ctx, adapter)
69+
return r.reconcileHandler(ctx, handler.NewCacheHandler(ctx, cache, logger, r.Client, r.Scheme, r.recorder, ctrl.SetControllerReference))
7070
}
7171

72-
func (r *CacheReconciler) reconcileHandler(ctx context.Context, adapter CacheAdapterInterface) (ctrl.Result, error) {
72+
func (r *CacheReconciler) reconcileHandler(ctx context.Context, h handler.CacheHandlerInterface) (ctrl.Result, error) {
7373
logger := log.FromContext(ctx)
7474

7575
operations := []reconciler.ReconcileOperation{
76-
adapter.CheckCacheExpiry,
77-
adapter.EnsureCacheInitialized,
78-
adapter.CalculateKeepAliveCount,
79-
adapter.AdjustCache,
76+
h.CheckCacheExpiry,
77+
h.EnsureCacheInitialized,
78+
h.CalculateKeepAliveCount,
79+
h.AdjustCache,
8080
}
8181

8282
for _, operation := range operations {
@@ -94,8 +94,6 @@ func (r *CacheReconciler) reconcileHandler(ctx context.Context, adapter CacheAda
9494
return ctrl.Result{RequeueAfter: defaultCacheCheckInterval}, nil
9595
}
9696

97-
var cacheOwnerKey = ".metadata.controller.cache"
98-
9997
func cacheOperationIndexerFunc(obj client.Object) []string {
10098
// grab the operation object, extract the owner...
10199
operation := obj.(*v1alpha1.Operation)
@@ -113,7 +111,7 @@ func cacheOperationIndexerFunc(obj client.Object) []string {
113111

114112
// SetupWithManager sets up the controller with the Manager.
115113
func (r *CacheReconciler) SetupWithManager(mgr ctrl.Manager) error { // +gocover:ignore:block init controller
116-
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &v1alpha1.Operation{}, cacheOwnerKey, cacheOperationIndexerFunc); err != nil { // +gocover:ignore:block init controller
114+
if err := mgr.GetFieldIndexer().IndexField(context.Background(), &v1alpha1.Operation{}, v1alpha1.CacheOwnerKey, cacheOperationIndexerFunc); err != nil { // +gocover:ignore:block init controller
117115
return err
118116
}
119117
// +gocover:ignore:block init controller

internal/controller/cache_controller_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
"sigs.k8s.io/controller-runtime/pkg/client/fake"
3232

3333
"github.com/Azure/operation-cache-controller/api/v1alpha1"
34-
"github.com/Azure/operation-cache-controller/internal/controller/mocks"
34+
"github.com/Azure/operation-cache-controller/internal/handler/mocks"
3535
"github.com/Azure/operation-cache-controller/internal/utils/reconciler"
3636
)
3737

@@ -62,7 +62,7 @@ func TestReconcileHandler(t *testing.T) {
6262
Scheme: scheme,
6363
}
6464
mockCacheAdapterCtrl := gomock.NewController(t)
65-
cacheAdapter := mocks.NewMockCacheAdapterInterface(mockCacheAdapterCtrl)
65+
cacheAdapter := mocks.NewMockCacheHandlerInterface(mockCacheAdapterCtrl)
6666
cacheAdapter.EXPECT().CheckCacheExpiry(ctx).Return(reconciler.OperationResult{}, nil)
6767
cacheAdapter.EXPECT().EnsureCacheInitialized(ctx).Return(reconciler.OperationResult{}, nil)
6868
cacheAdapter.EXPECT().CalculateKeepAliveCount(ctx).Return(reconciler.OperationResult{}, nil)
@@ -83,7 +83,7 @@ func TestReconcileHandler(t *testing.T) {
8383
Scheme: scheme,
8484
}
8585
mockCacheAdapterCtrl := gomock.NewController(t)
86-
cacheAdapter := mocks.NewMockCacheAdapterInterface(mockCacheAdapterCtrl)
86+
cacheAdapter := mocks.NewMockCacheHandlerInterface(mockCacheAdapterCtrl)
8787
cacheAdapter.EXPECT().CheckCacheExpiry(ctx).Return(reconciler.OperationResult{CancelRequest: true}, nil)
8888
res, err := cacheReconciler.reconcileHandler(ctx, cacheAdapter)
8989
assert.NoError(t, err)
@@ -101,7 +101,7 @@ func TestReconcileHandler(t *testing.T) {
101101
Scheme: scheme,
102102
}
103103
mockCacheAdapterCtrl := gomock.NewController(t)
104-
cacheAdapter := mocks.NewMockCacheAdapterInterface(mockCacheAdapterCtrl)
104+
cacheAdapter := mocks.NewMockCacheHandlerInterface(mockCacheAdapterCtrl)
105105
cacheAdapter.EXPECT().CheckCacheExpiry(ctx).Return(reconciler.OperationResult{}, assert.AnError)
106106
_, err := cacheReconciler.reconcileHandler(ctx, cacheAdapter)
107107
assert.NotNil(t, err)

0 commit comments

Comments
 (0)