-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
NonAdminBackup reconcile loop major rework
Rework of the reconcile loop to include batch reconcile. Move assignment of the Log or Context outisde of the reconcile loop. Signed-off-by: Michal Pryc <[email protected]>
- Loading branch information
Showing
6 changed files
with
502 additions
and
155 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
Copyright 2024. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package controller | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-logr/logr" | ||
nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1" | ||
"github.com/migtools/oadp-non-admin/internal/common/constant" | ||
"github.com/migtools/oadp-non-admin/internal/common/function" | ||
) | ||
|
||
// InitNonAdminBackup sets the New Phase on a NonAdminBackup object if it is not already set. | ||
// | ||
// Parameters: | ||
// | ||
// log: Logger instance for logging messages. | ||
// ctx: Context for the request. | ||
// Name: Name of the NonAdminBackup object. | ||
// NameSpace: Namespace of the NonAdminBackup object. | ||
// nab: Pointer to the NonAdminBackup object. | ||
// | ||
// The function checks if the Phase of the NonAdminBackup object is empty. | ||
// If it is empty, it sets the Phase to "New". | ||
// It then returns boolean values indicating whether the reconciliation loop should requeue | ||
// and whether the status was updated. | ||
func (r *NonAdminBackupReconciler) InitNonAdminBackup(log logr.Logger, ctx context.Context, Name string, NameSpace string, nab *nacv1alpha1.NonAdminBackup) (bool, bool, error) { | ||
logger := log.WithValues("NonAdminBackup", NameSpace) | ||
// Set initial Phase | ||
if nab.Status.Phase == constant.EmptyString { | ||
// Phase: New | ||
updatedStatus, errUpdate := function.UpdateNonAdminPhase(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminBackupPhaseNew) | ||
if updatedStatus { | ||
logger.V(1).Info("NonAdminBackup CR - Rqueue after Phase Update") | ||
return false, true, nil | ||
} | ||
if errUpdate != nil { | ||
logger.Error(errUpdate, "Unable to set NonAdminBackup Phase: New", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdate | ||
} | ||
} | ||
|
||
return false, false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
/* | ||
Copyright 2024. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package controller | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/go-logr/logr" | ||
nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1" | ||
|
||
"github.com/migtools/oadp-non-admin/internal/common/constant" | ||
"github.com/migtools/oadp-non-admin/internal/common/function" | ||
|
||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// ValidateVeleroBackupSpec validates the VeleroBackup Spec from the NonAdminBackup. | ||
// | ||
// Parameters: | ||
// | ||
// log: Logger instance for logging messages. | ||
// ctx: Context for the request. | ||
// Name: Name of the NonAdminBackup object. | ||
// NameSpace: Namespace of the NonAdminBackup object. | ||
// nab: Pointer to the NonAdminBackup object. | ||
// | ||
// The function attempts to get the BackupSpec from the NonAdminBackup object. | ||
// If an error occurs during this process, the function sets the NonAdminBackup status to "BackingOff" | ||
// and updates the corresponding condition accordingly. | ||
// If the BackupSpec is invalid, the function sets the NonAdminBackup condition to "InvalidBackupSpec". | ||
// If the BackupSpec is valid, the function sets the NonAdminBackup condition to "BackupAccepted". | ||
func (r *NonAdminBackupReconciler) ValidateVeleroBackupSpec(log logr.Logger, ctx context.Context, Name string, NameSpace string, nab *nacv1alpha1.NonAdminBackup) (bool, bool, error) { | ||
logger := log.WithValues("NonAdminBackup", NameSpace) | ||
|
||
// Main Validation point for the VeleroBackup included in NonAdminBackup spec | ||
_, err := function.GetBackupSpecFromNonAdminBackup(nab) | ||
|
||
if err != nil { | ||
errMsg := "NonAdminBackup CR does not contain valid BackupSpec" | ||
logger.Error(err, errMsg) | ||
|
||
updatedStatus, errUpdateStatus := function.UpdateNonAdminPhase(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminBackupPhaseBackingOff) | ||
if errUpdateStatus != nil { | ||
logger.Error(errUpdateStatus, "Unable to set NonAdminBackup Phase: BackingOff", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdateStatus | ||
} else if updatedStatus { | ||
// We do not requeue - the State was set to BackingOff | ||
return true, false, nil | ||
} | ||
|
||
// Continue. VeleroBackup looks fine, setting Accepted condition | ||
updatedCondition, errUpdateCondition := function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionAccepted, metav1.ConditionFalse, "InvalidBackupSpec", errMsg) | ||
if updatedCondition { | ||
// We do not requeue - this was only Condition update | ||
return true, false, nil | ||
} | ||
|
||
if errUpdateCondition != nil { | ||
logger.Error(errUpdateCondition, "Unable to set BackupAccepted Condition: False", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdateCondition | ||
} | ||
// We do not requeue - this was error from getting Spec from NAB | ||
return true, false, err | ||
} | ||
|
||
updatedStatus, errUpdateStatus := function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionAccepted, metav1.ConditionTrue, "BackupAccepted", "backup accepted") | ||
if updatedStatus { | ||
// We do requeue - The VeleroBackup got accepted and next reconcile loop will continue | ||
// with further work on the VeleroBackup such as creating it | ||
return false, true, nil | ||
} | ||
|
||
if errUpdateStatus != nil { | ||
logger.Error(errUpdateStatus, "Unable to set BackupAccepted Condition: True", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdateStatus | ||
} | ||
|
||
return false, false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* | ||
Copyright 2024. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package controller | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
|
||
"github.com/go-logr/logr" | ||
nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1" | ||
"github.com/migtools/oadp-non-admin/internal/common/constant" | ||
"github.com/migtools/oadp-non-admin/internal/common/function" | ||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" | ||
apierrors "k8s.io/apimachinery/pkg/api/errors" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" | ||
) | ||
|
||
// CreateVeleroBackupSpec creates or updates a Velero Backup object based on the provided NonAdminBackup object. | ||
// | ||
// Parameters: | ||
// | ||
// log: Logger instance for logging messages. | ||
// ctx: Context for the request. | ||
// Name: Name of the NonAdminBackup object. | ||
// NameSpace: Namespace of the NonAdminBackup object. | ||
// nab: Pointer to the NonAdminBackup object. | ||
// | ||
// The function generates a name for the Velero Backup object based on the provided namespace and name. | ||
// It then checks if a Velero Backup object with that name already exists. If it does not exist, it creates a new one. | ||
// The function returns boolean values indicating whether the reconciliation loop should exit or requeue | ||
func (r *NonAdminBackupReconciler) CreateVeleroBackupSpec(log logr.Logger, ctx context.Context, Name string, NameSpace string, nab *nacv1alpha1.NonAdminBackup) (bool, bool, error) { | ||
logger := log.WithValues("NonAdminBackup", NameSpace) | ||
|
||
veleroBackupName := function.GenerateVeleroBackupName(NameSpace, Name) | ||
|
||
if veleroBackupName == constant.EmptyString { | ||
return true, false, errors.New("unable to generate Velero Backup name") | ||
} | ||
|
||
veleroBackup := velerov1api.Backup{} | ||
err := r.Get(ctx, client.ObjectKey{Namespace: constant.OadpNamespace, Name: veleroBackupName}, &veleroBackup) | ||
|
||
if err != nil && apierrors.IsNotFound(err) { | ||
// Create VeleroBackup | ||
// Don't update phase nor conditions yet. | ||
// Those will be updated when then Reconcile loop is triggered by the VeleroBackup object | ||
logger.Info("No backup found", nameField, veleroBackupName) | ||
|
||
// We don't validate error here. | ||
// This was already validated in the ValidateVeleroBackupSpec | ||
backupSpec, _ := function.GetBackupSpecFromNonAdminBackup(nab) | ||
|
||
veleroBackup = velerov1api.Backup{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
Name: veleroBackupName, | ||
Namespace: constant.OadpNamespace, | ||
}, | ||
Spec: *backupSpec, | ||
} | ||
} else if err != nil && !apierrors.IsNotFound(err) { | ||
logger.Error(err, "Unable to fetch VeleroBackup") | ||
return true, false, err | ||
} else { | ||
// We should not update already created VeleroBackup object. | ||
// The VeleroBackup within NonAdminBackup will | ||
// be reverted back to the previous state - the state which created VeleroBackup | ||
// in a first place, so they will be in sync. | ||
logger.Info("Backup already exists, updating NonAdminBackup status", nameField, veleroBackupName) | ||
updatedNab, errBackupUpdate := function.UpdateNonAdminBackupFromVeleroBackup(ctx, r.Client, logger, nab, &veleroBackup) | ||
// Regardless if the status was updated or not, we should not | ||
// requeue here as it was only status update. | ||
if errBackupUpdate != nil { | ||
return true, false, errBackupUpdate | ||
} else if updatedNab { | ||
logger.V(1).Info("NonAdminBackup CR - Rqueue after Status Update") | ||
return false, true, nil | ||
} | ||
return true, false, nil | ||
} | ||
|
||
// Ensure labels are set for the Backup object | ||
existingLabels := veleroBackup.Labels | ||
naManagedLabels := function.AddNonAdminLabels(existingLabels) | ||
veleroBackup.Labels = naManagedLabels | ||
|
||
// Ensure annotations are set for the Backup object | ||
existingAnnotations := veleroBackup.Annotations | ||
ownerUUID := string(nab.ObjectMeta.UID) | ||
nabManagedAnnotations := function.AddNonAdminBackupAnnotations(nab.Namespace, nab.Name, ownerUUID, existingAnnotations) | ||
veleroBackup.Annotations = nabManagedAnnotations | ||
|
||
_, err = controllerutil.CreateOrPatch(ctx, r.Client, &veleroBackup, nil) | ||
if err != nil { | ||
logger.Error(err, "Failed to create backup", nameField, veleroBackupName) | ||
return true, false, err | ||
} | ||
logger.Info("VeleroBackup successfully created", nameField, veleroBackupName) | ||
|
||
_, errUpdate := function.UpdateNonAdminPhase(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminBackupPhaseCreated) | ||
if errUpdate != nil { | ||
logger.Error(errUpdate, "Unable to set NonAdminBackup Phase: Created", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdate | ||
} | ||
_, errUpdate = function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionAccepted, metav1.ConditionTrue, "Validated", "Valid Backup config") | ||
if errUpdate != nil { | ||
logger.Error(errUpdate, "Unable to set BackupAccepted Condition: True", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdate | ||
} | ||
_, errUpdate = function.UpdateNonAdminBackupCondition(ctx, r.Client, logger, nab, nacv1alpha1.NonAdminConditionQueued, metav1.ConditionTrue, "BackupScheduled", "Created Velero Backup object") | ||
if errUpdate != nil { | ||
logger.Error(errUpdate, "Unable to set BackupQueued Condition: True", nameField, Name, constant.NameSpaceString, NameSpace) | ||
return true, false, errUpdate | ||
} | ||
|
||
return false, false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
/* | ||
Copyright 2024. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
http://www.apache.org/licenses/LICENSE-2.0 | ||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
package controller | ||
|
||
import "errors" | ||
|
||
// ReconcileFunc represents a function that performs a reconciliation operation. | ||
// Parameters: | ||
// - ...interface{}: A variadic number of arguments of type interface{}, | ||
// allowing for flexibility in passing parameters to the reconciliation function. | ||
// | ||
// Returns: | ||
// - bool: Indicates whether the reconciliation process should exit. | ||
// - bool: Indicates whether the reconciliation process should requeue. | ||
// - error: An error encountered during reconciliation, if any. | ||
type ReconcileFunc func(...interface{}) (bool, bool, error) | ||
|
||
// ReconcileBatch executes a batch of reconcile functions sequentially, | ||
// allowing for complex reconciliation processes in a controlled manner. | ||
// It iterates through the provided reconcile functions until one of the | ||
// following conditions is met: | ||
// - A function signals the need to exit the reconciliation process. | ||
// - A function signals the need to exit and requeue the reconciliation process. | ||
// - An error occurs during reconciliation. | ||
// | ||
// If any reconcile function indicates a need to exit or requeue, | ||
// the function immediately returns with the respective exit and requeue | ||
// flags set. If an error occurs during any reconciliation function call, | ||
// it is propagated up and returned. If none of the reconcile functions | ||
// indicate a need to exit, requeue, or result in an error, the function | ||
// returns false for both exit and requeue flags, indicating successful reconciliation. | ||
// | ||
// If a reconcile function signals both the need to exit and requeue, indicating | ||
// conflicting signals, the function returns an error with the exit flag set to true | ||
// and the requeue flag set to false, so no . | ||
// | ||
// Parameters: | ||
// - reconcileFuncs: A list of ReconcileFunc functions representing | ||
// the reconciliation steps to be executed sequentially. | ||
// | ||
// Returns: | ||
// - bool: Indicates whether the reconciliation process should exit. | ||
// - bool: Indicates whether the reconciliation process should requeue. | ||
// - error: An error encountered during reconciliation, if any. | ||
func ReconcileBatch(reconcileFuncs ...ReconcileFunc) (bool, bool, error) { | ||
var exit, requeue bool | ||
var err error | ||
var conflictError = errors.New("conflicting exit and requeue signals - can not be both true") | ||
|
||
for _, f := range reconcileFuncs { | ||
exit, requeue, err = f() | ||
|
||
// Check if both exit and requeue are true | ||
// If this happens do not requeue, but exit with error | ||
if exit && requeue { | ||
return true, false, conflictError | ||
} | ||
|
||
// Return if there is a need to exit, requeue, or an error occurred | ||
if exit || requeue || err != nil { | ||
return exit, requeue, err | ||
} | ||
} | ||
|
||
// Do not requeue or exit reconcile. Also no error occured. | ||
return false, false, nil | ||
} |
Oops, something went wrong.