Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PTEUDO-1507: Implement the controller to sync DBInstance Sync condition with DBClaim. #388

Open
wants to merge 31 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
565462b
fix: handle GCP DBCluster Labels for DatabaseClaim tags
leandrorichardtoledo Dec 6, 2024
2a3573d
implementing tests
leandrorichardtoledo Dec 6, 2024
c3d84a4
PTEUDO-1422: refactor claim status to add conditions
bfabricio Dec 10, 2024
46e1e53
PTEUDO-1422: add wip
bfabricio Dec 10, 2024
9b85e57
PTEUDO-1422: refactor claim status and conditions
bfabricio Dec 11, 2024
4c65394
fix post migration error handling
bfabricio Dec 11, 2024
05b89d9
fix panic log condition
bfabricio Dec 12, 2024
ceed454
PTEUDO-1422: rename condition type
bfabricio Dec 12, 2024
9e54aba
Implement DBInstance status reconciliation and DatabaseClaim update l…
leandrorichardtoledo Dec 12, 2024
44f0b27
working in progress
leandrorichardtoledo Dec 13, 2024
941aa0b
PTEUDO-1422: rename func
bfabricio Dec 13, 2024
b0ab674
PTEUDO-1422: keep only two condition status
bfabricio Dec 16, 2024
76cf740
PTEUDO-1422: fix new db status
bfabricio Dec 16, 2024
f5eef11
PTEUDO-1507: working in progress
leandrorichardtoledo Dec 16, 2024
0fdc109
Merge remote-tracking branch 'origin/main' into PTEUDO-1507
leandrorichardtoledo Dec 16, 2024
b315c47
PTEUDO-1507: rollback go mod changes
leandrorichardtoledo Dec 16, 2024
40ac6a1
PTEUDO-1507: working in progress
leandrorichardtoledo Dec 16, 2024
e4da309
PTEUDO-1507: working in progress
leandrorichardtoledo Dec 17, 2024
f433df9
working progress
leandrorichardtoledo Dec 17, 2024
42bd6b3
Merge branch 'PTEUDO-1422' of https://github.com/infobloxopen/db-cont…
leandrorichardtoledo Dec 17, 2024
ed25826
working in progress
leandrorichardtoledo Dec 19, 2024
483cab5
PTEUDO-1507: working in progress, fixed broken tests
leandrorichardtoledo Dec 19, 2024
5290d1c
working in progres, implementing tests
leandrorichardtoledo Dec 20, 2024
4032e22
PTEUDO-1507: working in progress
leandrorichardtoledo Jan 6, 2025
2b7ed94
Merge remote-tracking branch 'origin/main' into PTEUDO-1507
leandrorichardtoledo Jan 6, 2025
ebaaeae
PTEUDO-1507: working in progress
leandrorichardtoledo Jan 6, 2025
1948bd6
PTEUDO-1507: rollback testdb change
leandrorichardtoledo Jan 6, 2025
97d88cb
PTEUDO-1507: remove not used file
leandrorichardtoledo Jan 6, 2025
574ebfe
PTEUDO-1507: rollback a change
leandrorichardtoledo Jan 6, 2025
f07dc30
PTEUDO-1507: removed not needed constants
leandrorichardtoledo Jan 6, 2025
0bf5f6d
Merge remote-tracking branch 'origin/main' into PTEUDO-1507
leandrorichardtoledo Jan 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions api/v1/condition.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package v1

import (
"fmt"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

Expand Down Expand Up @@ -100,6 +101,15 @@ func ReconcileErrorCondition(err error) metav1.Condition {
)
}

func ReconcileSyncErrorCondition(err error) metav1.Condition {
return CreateCondition(
ConditionSync,
metav1.ConditionFalse,
ReasonUnavailable,
fmt.Sprintf("Reconciliation encountered an issue: %v", err),
)
}

func ReconcileSuccessCondition() metav1.Condition {
return CreateCondition(
ConditionReady,
Expand Down
9 changes: 9 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ func init() {
utilruntime.Must(crossplanerdsv1alpha1.SchemeBuilder.AddToScheme(scheme))

utilruntime.Must(crossplanegcpv1beta2.SchemeBuilder.AddToScheme(scheme))

}

func main() {
Expand Down Expand Up @@ -248,6 +249,14 @@ func main() {
os.Exit(1)
}

if err := (&controller.DBInstanceStatusReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "DBInstanceStatus")
os.Exit(1)
}

// +kubebuilder:scaffold:builder

if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
Expand Down
8 changes: 8 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ rules:
verbs:
- create
- patch
- apiGroups:
- database.aws.crossplane.io
resources:
- dbinstances
verbs:
- get
- list
- watch
- apiGroups:
- persistance.atlas.infoblox.com
resources:
Expand Down
162 changes: 162 additions & 0 deletions internal/controller/dbinstance_status_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package controller

import (
"context"
"errors"
"fmt"

crossplaneaws "github.com/crossplane-contrib/provider-aws/apis/rds/v1alpha1"
xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
"github.com/go-logr/logr"
v1 "github.com/infobloxopen/db-controller/api/v1"
statusmanager "github.com/infobloxopen/db-controller/pkg/databaseclaim"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/log"
)

// DBInstanceStatusReconciler reconciles the status of DBInstance resources with DatabaseClaims.
type DBInstanceStatusReconciler struct {
client.Client
Scheme *runtime.Scheme
StatusManager *statusmanager.StatusManager
}

// RBAC markers
// +kubebuilder:rbac:groups=database.aws.crossplane.io,resources=dbinstances,verbs=get;list;watch
// +kubebuilder:rbac:groups=persistance.atlas.infoblox.com,resources=databaseclaims,verbs=get;list;watch
// +kubebuilder:rbac:groups=persistance.atlas.infoblox.com,resources=databaseclaims/status,verbs=get;update;patch

// Reconcile reconciles DBInstance with its corresponding DatabaseClaim.
func (r *DBInstanceStatusReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
logger.Info("Starting DBInstance Status reconciliation", "DBInstance", req.NamespacedName)

dbInstance, err := r.getDBInstance(ctx, req)
if err != nil {
return ctrl.Result{}, err
}

dbClaimRef, err := r.getDBClaimRefFromDBInstance(dbInstance, logger)
if err != nil {
return ctrl.Result{}, err
}

dbClaim, err := r.getDatabaseClaim(ctx, dbClaimRef)
if err != nil {
return ctrl.Result{}, err
}

if err := r.updateDatabaseClaimStatus(ctx, dbInstance, dbClaim, logger); err != nil {
return ctrl.Result{}, err
}

logger.Info("Reconciliation complete", "DBInstance", req.NamespacedName)
return ctrl.Result{}, nil
}

// getDBInstance retrieves the DBInstance resource.
func (r *DBInstanceStatusReconciler) getDBInstance(ctx context.Context, req ctrl.Request) (*crossplaneaws.DBInstance, error) {
var dbInstance crossplaneaws.DBInstance
if err := r.Get(ctx, req.NamespacedName, &dbInstance); err != nil {
return nil, fmt.Errorf("failed to get DBInstance: %w", err)
}
return &dbInstance, nil
}

// getDBClaimRefLabelsFromDBInstance extracts the DBClaim labels from the DBInstance.
func (r *DBInstanceStatusReconciler) getDBClaimRefFromDBInstance(dbInstance *crossplaneaws.DBInstance, logger logr.Logger) (*types.NamespacedName, error) {
labels := dbInstance.GetLabels()
if labels == nil {
logger.Error(errors.New("missing labels"), "DBInstance has no labels", "DBInstance", dbInstance.Name)
return nil, fmt.Errorf("DBInstance %s has no labels", dbInstance.Name)
}

instanceLabel, exists := labels["app.kubernetes.io/instance"]
if !exists {
err := errors.New("DBInstance is missing app.kubernetes.io/instance required label")
logger.Error(err, err.Error(), "DBInstance", dbInstance.Name)
return nil, err
}

componentLabel, exists := labels["app.kubernetes.io/component"]
if !exists {
err := errors.New("DBInstance is missing app.kubernetes.io/component required label")
logger.Error(err, err.Error(), "DBInstance", dbInstance.Name)
return nil, err
}

dbClaimNSName := types.NamespacedName{
Name: fmt.Sprintf("%s-%s", instanceLabel, componentLabel),
Namespace: instanceLabel,
}

return &dbClaimNSName, nil
}

// fetchDatabaseClaim retrieves the DatabaseClaim resource.
func (r *DBInstanceStatusReconciler) getDatabaseClaim(ctx context.Context, dbClaimRef *types.NamespacedName) (*v1.DatabaseClaim, error) {
var dbClaim v1.DatabaseClaim
if err := r.Get(ctx, *dbClaimRef, &dbClaim); err != nil {
return nil, fmt.Errorf("failed to get DatabaseClaim: %w", err)
}

return &dbClaim, nil
}

// updateDatabaseClaimStatus updates the status of the DatabaseClaim based on the DBInstance status.
func (r *DBInstanceStatusReconciler) updateDatabaseClaimStatus(ctx context.Context, dbInstance *crossplaneaws.DBInstance, dbClaim *v1.DatabaseClaim, logger logr.Logger) error {
if dbInstance.Status.Conditions == nil || len(dbInstance.Status.Conditions) == 0 {
logger.Info("DBInstance has no conditions", "DBInstance", dbInstance.Name)
return nil
}

var conditionSyncedAtProvider, conditionReadyAtProvider metav1.Condition

// Retrieve the conditions from the DBInstance status.
for _, condition := range dbInstance.Status.Conditions {
switch condition.Type {
case xpv1.TypeSynced:
conditionSyncedAtProvider = v1.CreateCondition(
v1.ConditionSync,
metav1.ConditionStatus(condition.Status),
string(condition.Reason),
condition.Message,
)
conditionSyncedAtProvider.LastTransitionTime = condition.LastTransitionTime
case xpv1.TypeReady:
conditionReadyAtProvider = v1.CreateCondition(
v1.ConditionReady,
metav1.ConditionStatus(condition.Status),
string(condition.Reason),
condition.Message,
)
conditionReadyAtProvider.LastTransitionTime = condition.LastTransitionTime
}
}

if conditionReadyAtProvider.Status == metav1.ConditionTrue && conditionSyncedAtProvider.Status == metav1.ConditionTrue {
if err := r.StatusManager.SetConditionAndUpdateStatus(ctx, dbClaim, v1.DatabaseReadyCondition()); err != nil {
logger.Error(err, "failed to set success condition in DatabaseClaim", "DatabaseClaim", dbClaim.Name)
return err
}
return nil
}

errorCondition := v1.ReconcileSyncErrorCondition(fmt.Errorf("%s: %s", conditionSyncedAtProvider.Reason, conditionSyncedAtProvider.Message))
if err := r.StatusManager.SetConditionAndUpdateStatus(ctx, dbClaim, errorCondition); err != nil {
logger.Error(err, "failed to set error condition in DatabaseClaim", "DatabaseClaim", dbClaim.Name)
return err
}
return nil
}

// SetupWithManager configures the controller with the Manager.
func (r *DBInstanceStatusReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&crossplaneaws.DBInstance{}).
Complete(r)
}
Loading
Loading