v2/internal/reconcilers/mysql/mysql_user_reconciler.go (145 lines of code) (raw):
/*
Copyright (c) Microsoft Corporation.
Licensed under the MIT license.
*/
package mysql
import (
"context"
. "github.com/Azure/azure-service-operator/v2/internal/logging"
"github.com/go-logr/logr"
_ "github.com/go-sql-driver/mysql" // mysql driver
"github.com/rotisserie/eris"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
asomysql "github.com/Azure/azure-service-operator/v2/api/dbformysql/v1"
"github.com/Azure/azure-service-operator/v2/internal/config"
"github.com/Azure/azure-service-operator/v2/internal/identity"
"github.com/Azure/azure-service-operator/v2/internal/reconcilers"
"github.com/Azure/azure-service-operator/v2/internal/resolver"
"github.com/Azure/azure-service-operator/v2/internal/util/kubeclient"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/conditions"
"github.com/Azure/azure-service-operator/v2/pkg/genruntime/core"
)
var _ genruntime.Reconciler = &MySQLUserReconciler{}
type MySQLUserReconciler struct {
reconcilers.ARMOwnedResourceReconcilerCommon
ResourceResolver *resolver.Resolver
CredentialProvider identity.CredentialProvider
Config config.Values
}
func NewMySQLUserReconciler(
kubeClient kubeclient.Client,
resourceResolver *resolver.Resolver,
positiveConditions *conditions.PositiveConditionBuilder,
credentialProvider identity.CredentialProvider,
cfg config.Values,
) *MySQLUserReconciler {
return &MySQLUserReconciler{
ResourceResolver: resourceResolver,
CredentialProvider: credentialProvider,
Config: cfg,
ARMOwnedResourceReconcilerCommon: reconcilers.ARMOwnedResourceReconcilerCommon{
ResourceResolver: resourceResolver,
ReconcilerCommon: reconcilers.ReconcilerCommon{
KubeClient: kubeClient,
PositiveConditions: positiveConditions,
},
},
}
}
func (r *MySQLUserReconciler) asUser(obj genruntime.MetaObject) (*asomysql.User, error) {
typedObj, ok := obj.(*asomysql.User)
if !ok {
return nil, eris.Errorf("cannot modify resource that is not of type *asomysql.User. Type is %T", obj)
}
return typedObj, nil
}
func (r *MySQLUserReconciler) CreateOrUpdate(ctx context.Context, log logr.Logger, eventRecorder record.EventRecorder, obj genruntime.MetaObject) (ctrl.Result, error) {
user, err := r.asUser(obj)
if err != nil {
return ctrl.Result{}, err
}
// Augment Log
log = log.WithValues("azureName", user.AzureName())
connector, err := r.newDBConnector(log, user)
if err != nil {
return ctrl.Result{}, err
}
err = connector.CreateOrUpdate(ctx)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *MySQLUserReconciler) Delete(ctx context.Context, log logr.Logger, eventRecorder record.EventRecorder, obj genruntime.MetaObject) (ctrl.Result, error) {
user, err := r.asUser(obj)
if err != nil {
return ctrl.Result{}, err
}
// Augment Log
log = log.WithValues("azureName", user.AzureName())
log.V(Status).Info("Starting delete of resource")
// Check that this objects owner still exists
// This is an optimization to avoid excess requests to Azure.
_, err = r.ResourceResolver.ResolveOwner(ctx, user)
if err != nil {
var typedErr *core.ReferenceNotFound
if eris.As(err, &typedErr) {
return ctrl.Result{}, nil
}
return ctrl.Result{}, err
}
connector, err := r.newDBConnector(log, user)
if err != nil {
return ctrl.Result{}, err
}
err = connector.Delete(ctx)
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *MySQLUserReconciler) Claim(ctx context.Context, log logr.Logger, eventRecorder record.EventRecorder, obj genruntime.MetaObject) error {
user, err := r.asUser(obj)
if err != nil {
return err
}
err = r.ClaimResource(ctx, log, user)
if err != nil {
return err
}
return nil
}
func (r *MySQLUserReconciler) UpdateStatus(ctx context.Context, log logr.Logger, eventRecorder record.EventRecorder, obj genruntime.MetaObject) error {
user, err := r.asUser(obj)
if err != nil {
return err
}
connector, err := r.newDBConnector(log, user)
if err != nil {
return err
}
exists, err := connector.Exists(ctx)
if err != nil {
return err
}
if !exists {
err = eris.Errorf("user %s does not exist", user.Spec.AzureName)
err = conditions.NewReadyConditionImpactingError(err, conditions.ConditionSeverityWarning, conditions.ReasonAzureResourceNotFound)
return err
}
return nil
}
func (r *MySQLUserReconciler) newDBConnector(log logr.Logger, user *asomysql.User) (Connector, error) {
if user.Spec.LocalUser != nil {
return &localUser{
user: user,
resourceResolver: r.ResourceResolver,
credentialProvider: r.CredentialProvider,
log: log,
}, nil
}
if user.Spec.AADUser != nil {
return &aadUser{
user: user,
resourceResolver: r.ResourceResolver,
credentialProvider: r.CredentialProvider,
log: log,
}, nil
}
// This is also enforced with a webhook
err := eris.Errorf("unknown user type, user must be LocalUser or AADUser")
return nil, conditions.NewReadyConditionImpactingError(err, conditions.ConditionSeverityError, conditions.ReasonFailed)
}