Skip to content

Commit cca9af6

Browse files
Add PreReconcileOwnerCheck and implement for Kusto Databases (#4976)
* Move PreReconcileCheckResult to separate file for sharing * Create PreReconcileOwnerChecker * Tidy up PrereconcileChecker * Update reconciler * Modify Kusto database extension * Add comment
1 parent f3d3dfe commit cca9af6

File tree

5 files changed

+231
-91
lines changed

5 files changed

+231
-91
lines changed

v2/api/kusto/customizations/database_extensions.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,23 @@ import (
1919
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/extensions"
2020
)
2121

22-
var _ extensions.PreReconciliationChecker = &ClusterExtension{}
22+
var _ extensions.PreReconciliationOwnerChecker = &DatabaseExtension{}
2323

2424
// Ensure we're dealing with the hub version of Cluster
2525
// If this no longer compiles (due to a newer version being imported), change the import above to that version.
2626
var _ conversion.Hub = &kusto.Cluster{}
2727

28-
// PreReconcileCheck is called before the reconciliation of the resource to see if the cluster
28+
// PreReconcileOwnerCheck is called before the reconciliation of the resource to see if the cluster
2929
// is in a state that will allow reconciliation to proceed.
3030
// We can't try to create/update a Database unless the cluster is in a state that allows it.
31-
func (ext *DatabaseExtension) PreReconcileCheck(
31+
// We use PreReconcileOwnerCheck instead of PreReconcileCheck because you can't even GET a database if the cluster is powered down.
32+
func (ext *DatabaseExtension) PreReconcileOwnerCheck(
3233
ctx context.Context,
33-
obj genruntime.MetaObject,
3434
owner genruntime.MetaObject,
3535
resourceResolver *resolver.Resolver,
3636
armClient_ *genericarmclient.GenericClient,
3737
log logr.Logger,
38-
next extensions.PreReconcileCheckFunc,
38+
next extensions.PreReconcileOwnerCheckFunc,
3939
) (extensions.PreReconcileCheckResult, error) {
4040
// Check to see if the owning cluster is in a state that will block us from reconciling
4141
// Owner nil can happen if the owner of the database is referenced by armID
@@ -58,5 +58,5 @@ func (ext *DatabaseExtension) PreReconcileCheck(
5858
}
5959
}
6060

61-
return next(ctx, obj, owner, resourceResolver, armClient_, log)
61+
return next(ctx, owner, resourceResolver, armClient_, log)
6262
}

v2/internal/reconcilers/arm/azure_generic_arm_reconciler_instance.go

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -296,29 +296,49 @@ func (r *azureDeploymentReconcilerInstance) BeginCreateOrUpdateResource(
296296
return ctrl.Result{Requeue: true}, nil
297297
}
298298

299-
func (r *azureDeploymentReconcilerInstance) preReconciliationCheck(ctx context.Context) (extensions.PreReconcileCheckResult, error) {
300-
// Create a checker for access to the extension point, if required
299+
func (r *azureDeploymentReconcilerInstance) preReconciliationCheck(
300+
ctx context.Context,
301+
) (extensions.PreReconcileCheckResult, error) {
302+
// Check to see which extensions are available
301303
checker, extensionFound := extensions.CreatePreReconciliationChecker(r.Extension)
302-
if !extensionFound {
303-
// No extension found, nothing to do
304+
ownerChecker, ownerExtensionFound := extensions.CreatePreReconciliationOwnerChecker(r.Extension)
305+
306+
if !extensionFound && !ownerExtensionFound {
307+
// No extensions found, nothing to do
304308
return extensions.ProceedWithReconcile(), nil
305309
}
306310

307-
// Having a checker requires our resource to have an up-to-date status
308-
r.Log.V(Verbose).Info("Refreshing Status of resource")
311+
// Load owner details so it has an an up-to-date status
312+
ownerDetails, ownerErr := r.ResourceResolver.ResolveOwner(ctx, r.Obj)
313+
if ownerErr != nil {
314+
// We can't obtain the owner, so we can't run either extension
315+
return extensions.PreReconcileCheckResult{}, ownerErr
316+
}
317+
318+
// Run the PreReconciliationOwnerChecker if we have one
319+
if ownerExtensionFound {
320+
check, checkErr := ownerChecker(ctx, ownerDetails.Owner, r.ResourceResolver, r.ARMConnection.Client(), r.Log)
321+
if checkErr != nil {
322+
// Something went wrong running the check.
323+
return extensions.PreReconcileCheckResult{}, checkErr
324+
}
325+
326+
// If the check says we're postponing reconcile, we're done for now as there's nothing to do.
327+
if check.PostponeReconciliation() {
328+
return extensions.PreReconcileCheckResult{}, nil
329+
}
330+
}
331+
332+
// Load resource details so it also has an up-to-date status
333+
// We defer this until after we've done the owner check as if that says postpone
334+
// we don't need to do this work.
335+
// Plus, this avoids errors if the owner is in a state where going a GET on the resource will fail.
309336
statusErr := r.updateStatus(ctx)
310337
if statusErr != nil && !genericarmclient.IsNotFoundError(statusErr) {
311338
// We have an error, and it's not because the resource doesn't exist yet
312339
return extensions.PreReconcileCheckResult{}, statusErr
313340
}
314341

315-
// We also need to have our owner, it too with an up-to-date status
316-
ownerDetails, ownerErr := r.ResourceResolver.ResolveOwner(ctx, r.Obj)
317-
if ownerErr != nil {
318-
// We can't obtain the owner, so we can't run the extension
319-
return extensions.PreReconcileCheckResult{}, ownerErr
320-
}
321-
322342
// Run our pre-reconciliation checker
323343
check, checkErr := checker(ctx, r.Obj, ownerDetails.Owner, r.ResourceResolver, r.ARMConnection.Client(), r.Log)
324344
if checkErr != nil {
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation.
3+
* Licensed under the MIT license.
4+
*/
5+
6+
package extensions
7+
8+
import (
9+
"github.com/rotisserie/eris"
10+
11+
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/conditions"
12+
)
13+
14+
type PreReconcileCheckResult struct {
15+
action preReconcileCheckResultType
16+
severity conditions.ConditionSeverity
17+
reason conditions.Reason
18+
message string
19+
}
20+
21+
// ProceedWithReconcile indicates that a resource is ready for reconciliation by returning a PreReconcileCheckResult
22+
// with action `Proceed`.
23+
func ProceedWithReconcile() PreReconcileCheckResult {
24+
return PreReconcileCheckResult{
25+
action: preReconcileCheckResultTypeProceed,
26+
}
27+
}
28+
29+
// BlockReconcile indicates reconciliation of a resource is currently blocked by returning a PreReconcileCheckResult
30+
// with action `Block`. The reconciliation will automatically be retried after a short delay.
31+
// reason is an explanatory reason to show to the user via a warning condition on the resource.
32+
func BlockReconcile(reason string) PreReconcileCheckResult {
33+
return PreReconcileCheckResult{
34+
action: preReconcileCheckResultTypeBlock,
35+
severity: conditions.ConditionSeverityWarning,
36+
reason: conditions.ReasonReconcileBlocked,
37+
message: reason,
38+
}
39+
}
40+
41+
// PostponeReconcile indicates reconciliation of a resource is not currently required by returning a
42+
// PreReconcileCheckResult with action `Postpone`. Reconciliation will not be retried until the usual scheduled check.
43+
func PostponeReconcile() PreReconcileCheckResult {
44+
return PreReconcileCheckResult{
45+
action: preReconcileCheckResultTypePostpone,
46+
severity: conditions.ConditionSeverityInfo,
47+
reason: conditions.ReasonReconcilePostponed,
48+
}
49+
}
50+
51+
func (r PreReconcileCheckResult) PostponeReconciliation() bool {
52+
return r.action == preReconcileCheckResultTypePostpone
53+
}
54+
55+
func (r PreReconcileCheckResult) BlockReconciliation() bool {
56+
return r.action == preReconcileCheckResultTypeBlock
57+
}
58+
59+
func (r PreReconcileCheckResult) Message() string {
60+
return r.message
61+
}
62+
63+
// CreateConditionError returns an error that can be used to set a condition on the resource.
64+
func (r PreReconcileCheckResult) CreateConditionError() error {
65+
return conditions.NewReadyConditionImpactingError(
66+
eris.New(r.message),
67+
r.severity,
68+
r.reason)
69+
}
70+
71+
// PreReconcileCheckResultType is the type of result returned by PreReconcileCheck.
72+
type preReconcileCheckResultType string
73+
74+
const (
75+
preReconcileCheckResultTypeBlock preReconcileCheckResultType = "Block"
76+
preReconcileCheckResultTypeProceed preReconcileCheckResultType = "Proceed"
77+
preReconcileCheckResultTypePostpone preReconcileCheckResultType = "Postpone"
78+
)

v2/pkg/genruntime/extensions/prereconciliation_checker.go

Lines changed: 6 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,10 @@ import (
1111
. "github.com/Azure/azure-service-operator/v2/internal/logging"
1212

1313
"github.com/go-logr/logr"
14-
"github.com/rotisserie/eris"
1514

1615
"github.com/Azure/azure-service-operator/v2/internal/genericarmclient"
1716
"github.com/Azure/azure-service-operator/v2/internal/resolver"
1817
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
19-
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/conditions"
2018
)
2119

2220
// PreReconciliationChecker is implemented by resources that want to do extra checks before proceeding with
@@ -28,6 +26,8 @@ type PreReconciliationChecker interface {
2826
// Returns BlockReconcile and a human-readable reason if the reconciliation should be skipped.
2927
// ctx is the current operation context.
3028
// obj is the resource about to be reconciled. The resource's State will be freshly updated.
29+
// owner is the owner of the resource about to be reconciled. The owner's State will be freshly updated. May be nil
30+
// if the resource has no owner, or if it has been referenced via ARMID directly.
3131
// kubeClient allows access to the cluster for any required queries.
3232
// armClient allows access to ARM for any required queries.
3333
// log is the logger for the current operation.
@@ -52,72 +52,6 @@ type PreReconcileCheckFunc func(
5252
log logr.Logger,
5353
) (PreReconcileCheckResult, error)
5454

55-
type PreReconcileCheckResult struct {
56-
action preReconcileCheckResultType
57-
severity conditions.ConditionSeverity
58-
reason conditions.Reason
59-
message string
60-
}
61-
62-
// ProceedWithReconcile indicates that a resource is ready for reconciliation by returning a PreReconcileCheckResult
63-
// with action `Proceed`.
64-
func ProceedWithReconcile() PreReconcileCheckResult {
65-
return PreReconcileCheckResult{
66-
action: preReconcileCheckResultTypeProceed,
67-
}
68-
}
69-
70-
// BlockReconcile indicates reconciliation of a resource is currently blocked by returning a PreReconcileCheckResult
71-
// with action `Block`. The reconciliation will automatically be retried after a short delay.
72-
// reason is an explanatory reason to show to the user via a warning condition on the resource.
73-
func BlockReconcile(reason string) PreReconcileCheckResult {
74-
return PreReconcileCheckResult{
75-
action: preReconcileCheckResultTypeBlock,
76-
severity: conditions.ConditionSeverityWarning,
77-
reason: conditions.ReasonReconcileBlocked,
78-
message: reason,
79-
}
80-
}
81-
82-
// PostponeReconcile indicates reconciliation of a resource is not currently required by returning a
83-
// PreReconcileCheckResult with action `Postpone`. Reconciliation will not be retried until the usual scheduled check.
84-
func PostponeReconcile() PreReconcileCheckResult {
85-
return PreReconcileCheckResult{
86-
action: preReconcileCheckResultTypePostpone,
87-
severity: conditions.ConditionSeverityInfo,
88-
reason: conditions.ReasonReconcilePostponed,
89-
}
90-
}
91-
92-
func (r PreReconcileCheckResult) PostponeReconciliation() bool {
93-
return r.action == preReconcileCheckResultTypePostpone
94-
}
95-
96-
func (r PreReconcileCheckResult) BlockReconciliation() bool {
97-
return r.action == preReconcileCheckResultTypeBlock
98-
}
99-
100-
func (r PreReconcileCheckResult) Message() string {
101-
return r.message
102-
}
103-
104-
// CreateConditionError returns an error that can be used to set a condition on the resource.
105-
func (r PreReconcileCheckResult) CreateConditionError() error {
106-
return conditions.NewReadyConditionImpactingError(
107-
eris.New(r.message),
108-
r.severity,
109-
r.reason)
110-
}
111-
112-
// PreReconcileCheckResultType is the type of result returned by PreReconcileCheck.
113-
type preReconcileCheckResultType string
114-
115-
const (
116-
preReconcileCheckResultTypeBlock preReconcileCheckResultType = "Block"
117-
preReconcileCheckResultTypeProceed preReconcileCheckResultType = "Proceed"
118-
preReconcileCheckResultTypePostpone preReconcileCheckResultType = "Postpone"
119-
)
120-
12155
// CreatePreReconciliationChecker creates a checker that can be used to check if a resource is ready for reconciliation.
12256
// If the resource in question has not implemented the PreReconciliationChecker interface, the provided default checker
12357
// is returned directly.
@@ -128,7 +62,7 @@ func CreatePreReconciliationChecker(
12862
) (PreReconcileCheckFunc, bool) {
12963
impl, ok := host.(PreReconciliationChecker)
13064
if !ok {
131-
return alwaysReconcile, false
65+
return preReconciliationCheckerAlways, false
13266
}
13367

13468
return func(
@@ -141,7 +75,7 @@ func CreatePreReconciliationChecker(
14175
) (PreReconcileCheckResult, error) {
14276
log.V(Status).Info("Extension pre-reconcile check running")
14377

144-
result, err := impl.PreReconcileCheck(ctx, obj, owner, resourceResolver, armClient, log, alwaysReconcile)
78+
result, err := impl.PreReconcileCheck(ctx, obj, owner, resourceResolver, armClient, log, preReconciliationCheckerAlways)
14579
if err != nil {
14680
log.V(Status).Info(
14781
"Extension pre-reconcile check failed",
@@ -160,10 +94,10 @@ func CreatePreReconciliationChecker(
16094
}, true
16195
}
16296

163-
// alwaysReconcile is a PreReconciliationChecker that always indicates a reconciliation is required.
97+
// preReconciliationCheckerAlways is a PreReconciliationChecker that always indicates a reconciliation is required.
16498
// We have this here so we can set up a chain, even if it's only one link long.
16599
// When we start doing proper comparisons between Spec and Status, we'll have an actual chain of checkers.
166-
func alwaysReconcile(
100+
func preReconciliationCheckerAlways(
167101
_ context.Context,
168102
_ genruntime.MetaObject,
169103
_ genruntime.MetaObject,

0 commit comments

Comments
 (0)