Skip to content
Merged
Changes from all commits
Commits
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
132 changes: 66 additions & 66 deletions pkg/controller/atlasdeployment/atlasdeployment_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (r *AtlasDeploymentReconciler) Reconcile(context context.Context, req ctrl.
}
ctx.Client = atlasClient

// Allow users to specify M0/M2/M5 clusters without providing TENANT for Normal and Serverless clusters
// Allow users to specify M0/M2/M5 deployments without providing TENANT for Normal and Serverless deployments
r.verifyNonTenantCase(deployment)

handleDeployment := r.selectDeploymentHandler(deployment)
Expand All @@ -150,64 +150,64 @@ func (r *AtlasDeploymentReconciler) Reconcile(context context.Context, req ctrl.
return workflow.OK().ReconcileResult(), nil
}

func (r *AtlasDeploymentReconciler) verifyNonTenantCase(cluster *mdbv1.AtlasDeployment) {
func (r *AtlasDeploymentReconciler) verifyNonTenantCase(deployment *mdbv1.AtlasDeployment) {
var pSettings *mdbv1.ProviderSettingsSpec
var clusterType string
if cluster.Spec.DeploymentSpec != nil {
if cluster.Spec.DeploymentSpec.ProviderSettings == nil {
var deploymentType string
if deployment.Spec.DeploymentSpec != nil {
if deployment.Spec.DeploymentSpec.ProviderSettings == nil {
return
}
pSettings = cluster.Spec.DeploymentSpec.ProviderSettings
clusterType = "TENANT"
pSettings = deployment.Spec.DeploymentSpec.ProviderSettings
deploymentType = "TENANT"
}

if cluster.Spec.ServerlessSpec != nil {
if cluster.Spec.ServerlessSpec.ProviderSettings == nil {
if deployment.Spec.ServerlessSpec != nil {
if deployment.Spec.ServerlessSpec.ProviderSettings == nil {
return
}
pSettings = cluster.Spec.ServerlessSpec.ProviderSettings
clusterType = "SERVERLESS"
pSettings = deployment.Spec.ServerlessSpec.ProviderSettings
deploymentType = "SERVERLESS"
}

modifyProviderSettings(pSettings, clusterType)
modifyProviderSettings(pSettings, deploymentType)
}

func modifyProviderSettings(pSettings *mdbv1.ProviderSettingsSpec, clusterType string) {
if pSettings == nil || string(pSettings.ProviderName) == clusterType {
func modifyProviderSettings(pSettings *mdbv1.ProviderSettingsSpec, deploymentType string) {
if pSettings == nil || string(pSettings.ProviderName) == deploymentType {
return
}

switch strings.ToUpper(clusterType) {
switch strings.ToUpper(deploymentType) {
case "TENANT":
switch pSettings.InstanceSizeName {
case "M0", "M2", "M5":
pSettings.BackingProviderName = string(pSettings.ProviderName)
pSettings.ProviderName = provider.ProviderName(clusterType)
pSettings.ProviderName = provider.ProviderName(deploymentType)
}
case "SERVERLESS":
pSettings.BackingProviderName = string(pSettings.ProviderName)
pSettings.ProviderName = provider.ProviderName(clusterType)
pSettings.ProviderName = provider.ProviderName(deploymentType)
}
}

func (r *AtlasDeploymentReconciler) selectDeploymentHandler(cluster *mdbv1.AtlasDeployment) clusterHandlerFunc {
if cluster.IsAdvancedDeployment() {
func (r *AtlasDeploymentReconciler) selectDeploymentHandler(deployment *mdbv1.AtlasDeployment) deploymentHandlerFunc {
if deployment.IsAdvancedDeployment() {
return r.handleAdvancedDeployment
}
if cluster.IsServerless() {
if deployment.IsServerless() {
return r.handleServerlessInstance
}
return r.handleRegularDeployment
}

func (r *AtlasDeploymentReconciler) handleDeploymentBackupSchedule(ctx *workflow.Context, deployment *mdbv1.AtlasDeployment, projectID, cName string, backupEnabled bool, req ctrl.Request) error {
if deployment.Spec.BackupScheduleRef.Name == "" && deployment.Spec.BackupScheduleRef.Namespace == "" {
r.Log.Debug("no backup schedule configured for the cluster")
r.Log.Debug("no backup schedule configured for the deployment")
return nil
}

if !backupEnabled {
return fmt.Errorf("can not proceed with backup schedule. Backups are not enabled for cluster %v", deployment.ClusterName)
return fmt.Errorf("can not proceed with backup schedule. Backups are not enabled for deployment %v", deployment.ClusterName)
}

resourcesToWatch := []watch.WatchedObject{}
Expand All @@ -231,7 +231,7 @@ func (r *AtlasDeploymentReconciler) handleDeploymentBackupSchedule(ctx *workflow
resourcesToWatch = append(resourcesToWatch, watch.WatchedObject{ResourceKind: bPolicy.Kind, Resource: pKey})

// Create new backup schedule
r.Log.Infof("updating backupschedule for the atlas cluster: %v", cName)
r.Log.Infof("updating backupschedule for the atlas deployment: %v", cName)
apiScheduleRes := &mongodbatlas.CloudProviderSnapshotBackupPolicy{
ClusterName: cName,
ReferenceHourOfDay: &bSchedule.Spec.ReferenceHourOfDay,
Expand Down Expand Up @@ -273,13 +273,13 @@ func (r *AtlasDeploymentReconciler) handleDeploymentBackupSchedule(ctx *workflow
if _, _, err := ctx.Client.CloudProviderSnapshotBackupPolicies.Update(context.Background(), projectID, cName, apiScheduleRes); err != nil {
return fmt.Errorf("unable to create backupschedule %v. e: %w", bKey, err)
}
r.Log.Infof("successfully updated backupschedule for cluster %v", cName)
r.Log.Infof("successfully updated backupschedule for deployment %v", cName)
return nil
}

// handleAdvancedDeployment ensures the state of the cluster using the Advanced Deployment API
func (r *AtlasDeploymentReconciler) handleAdvancedDeployment(ctx *workflow.Context, project *mdbv1.AtlasProject, cluster *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error) {
c, result := r.ensureAdvancedDeploymentState(ctx, project, cluster)
// handleAdvancedDeployment ensures the state of the deployment using the Advanced Deployment API
func (r *AtlasDeploymentReconciler) handleAdvancedDeployment(ctx *workflow.Context, project *mdbv1.AtlasProject, deployment *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error) {
c, result := r.ensureAdvancedDeploymentState(ctx, project, deployment)
if c != nil && c.StateName != "" {
ctx.EnsureStatusOption(status.AtlasDeploymentStateNameOption(c.StateName))
}
Expand All @@ -288,13 +288,13 @@ func (r *AtlasDeploymentReconciler) handleAdvancedDeployment(ctx *workflow.Conte
return result, nil
}

if err := r.handleDeploymentBackupSchedule(ctx, cluster, project.ID(), c.Name, *c.BackupEnabled, req); err != nil {
if err := r.handleDeploymentBackupSchedule(ctx, deployment, project.ID(), c.Name, *c.BackupEnabled, req); err != nil {
result := workflow.Terminate(workflow.Internal, err.Error())
ctx.SetConditionFromResult(status.DeploymentReadyType, result)
return result, nil
}

if csResult := r.ensureConnectionSecrets(ctx, project, c.Name, c.ConnectionStrings, cluster); !csResult.IsOk() {
if csResult := r.ensureConnectionSecrets(ctx, project, c.Name, c.ConnectionStrings, deployment); !csResult.IsOk() {
return csResult, nil
}

Expand All @@ -308,50 +308,50 @@ func (r *AtlasDeploymentReconciler) handleAdvancedDeployment(ctx *workflow.Conte
}

// handleServerlessInstance ensures the state of the serverless instance using the serverless API
func (r *AtlasDeploymentReconciler) handleServerlessInstance(ctx *workflow.Context, project *mdbv1.AtlasProject, cluster *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error) {
c, result := ensureServerlessInstanceState(ctx, project, cluster.Spec.ServerlessSpec)
return r.ensureConnectionSecretsAndSetStatusOptions(ctx, project, cluster, result, c)
func (r *AtlasDeploymentReconciler) handleServerlessInstance(ctx *workflow.Context, project *mdbv1.AtlasProject, deployment *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error) {
c, result := ensureServerlessInstanceState(ctx, project, deployment.Spec.ServerlessSpec)
return r.ensureConnectionSecretsAndSetStatusOptions(ctx, project, deployment, result, c)
}

// handleRegularDeployment ensures the state of the cluster using the Regular Deployment API
func (r *AtlasDeploymentReconciler) handleRegularDeployment(ctx *workflow.Context, project *mdbv1.AtlasProject, cluster *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error) {
c, result := ensureDeploymentState(ctx, project, cluster)
if c != nil && c.StateName != "" {
ctx.EnsureStatusOption(status.AtlasDeploymentStateNameOption(c.StateName))
// handleRegularDeployment ensures the state of the deployment using the Regular Deployment API
func (r *AtlasDeploymentReconciler) handleRegularDeployment(ctx *workflow.Context, project *mdbv1.AtlasProject, deployment *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error) {
atlasDeployment, result := ensureDeploymentState(ctx, project, deployment)
if atlasDeployment != nil && atlasDeployment.StateName != "" {
ctx.EnsureStatusOption(status.AtlasDeploymentStateNameOption(atlasDeployment.StateName))
}

if !result.IsOk() {
return result, nil
}

if err := r.handleDeploymentBackupSchedule(ctx, cluster, project.ID(), c.Name, *c.ProviderBackupEnabled || *c.BackupEnabled, req); err != nil {
if err := r.handleDeploymentBackupSchedule(ctx, deployment, project.ID(), atlasDeployment.Name, *atlasDeployment.ProviderBackupEnabled || *atlasDeployment.BackupEnabled, req); err != nil {
result := workflow.Terminate(workflow.Internal, err.Error())
ctx.SetConditionFromResult(status.DeploymentReadyType, result)
return result, nil
}
return r.ensureConnectionSecretsAndSetStatusOptions(ctx, project, cluster, result, c)
return r.ensureConnectionSecretsAndSetStatusOptions(ctx, project, deployment, result, atlasDeployment)
}

// ensureConnectionSecretsAndSetStatusOptions creates the relevant connection secrets and sets
// status options to the given context. This function can be used for regular clusters and serverless instances
func (r *AtlasDeploymentReconciler) ensureConnectionSecretsAndSetStatusOptions(ctx *workflow.Context, project *mdbv1.AtlasProject, cluster *mdbv1.AtlasDeployment, result workflow.Result, deployment *mongodbatlas.Cluster) (workflow.Result, error) {
if deployment != nil && deployment.StateName != "" {
ctx.EnsureStatusOption(status.AtlasDeploymentStateNameOption(deployment.StateName))
// status options to the given context. This function can be used for regular deployments and serverless instances
func (r *AtlasDeploymentReconciler) ensureConnectionSecretsAndSetStatusOptions(ctx *workflow.Context, project *mdbv1.AtlasProject, deployment *mdbv1.AtlasDeployment, result workflow.Result, d *mongodbatlas.Cluster) (workflow.Result, error) {
if d != nil && d.StateName != "" {
ctx.EnsureStatusOption(status.AtlasDeploymentStateNameOption(d.StateName))
}

if !result.IsOk() {
return result, nil
}

if csResult := r.ensureConnectionSecrets(ctx, project, deployment.Name, deployment.ConnectionStrings, cluster); !csResult.IsOk() {
if csResult := r.ensureConnectionSecrets(ctx, project, d.Name, d.ConnectionStrings, deployment); !csResult.IsOk() {
return csResult, nil
}

ctx.
SetConditionTrue(status.DeploymentReadyType).
EnsureStatusOption(status.AtlasDeploymentMongoDBVersionOption(deployment.MongoDBVersion)).
EnsureStatusOption(status.AtlasDeploymentConnectionStringsOption(deployment.ConnectionStrings)).
EnsureStatusOption(status.AtlasDeploymentMongoURIUpdatedOption(deployment.MongoURIUpdated))
EnsureStatusOption(status.AtlasDeploymentMongoDBVersionOption(d.MongoDBVersion)).
EnsureStatusOption(status.AtlasDeploymentConnectionStringsOption(d.ConnectionStrings)).
EnsureStatusOption(status.AtlasDeploymentMongoURIUpdatedOption(d.MongoURIUpdated))

ctx.SetConditionTrue(status.ReadyType)
return result, nil
Expand Down Expand Up @@ -382,8 +382,8 @@ func (r *AtlasDeploymentReconciler) handleAdvancedOptions(ctx *workflow.Context,
return workflow.OK()
}

func (r *AtlasDeploymentReconciler) readProjectResource(cluster *mdbv1.AtlasDeployment, project *mdbv1.AtlasProject) workflow.Result {
if err := r.Client.Get(context.Background(), cluster.AtlasProjectObjectKey(), project); err != nil {
func (r *AtlasDeploymentReconciler) readProjectResource(deployment *mdbv1.AtlasDeployment, project *mdbv1.AtlasProject) workflow.Result {
if err := r.Client.Get(context.Background(), deployment.AtlasProjectObjectKey(), project); err != nil {
return workflow.Terminate(workflow.Internal, err.Error())
}
return workflow.OK()
Expand Down Expand Up @@ -419,31 +419,31 @@ func (r *AtlasDeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error {
// Delete implements a handler for the Delete event.
func (r *AtlasDeploymentReconciler) Delete(e event.DeleteEvent) error {
// TODO: Add deletion for AtlasBackupSchedule and AtlasBackupPolicy
cluster, ok := e.Object.(*mdbv1.AtlasDeployment)
deployment, ok := e.Object.(*mdbv1.AtlasDeployment)
if !ok {
r.Log.Errorf("Ignoring malformed Delete() call (expected type %T, got %T)", &mdbv1.AtlasDeployment{}, e.Object)
return nil
}

log := r.Log.With("atlasdeployment", kube.ObjectKeyFromObject(cluster))
log := r.Log.With("atlasdeployment", kube.ObjectKeyFromObject(deployment))

log.Infow("-> Starting AtlasDeployment deletion", "spec", cluster.Spec)
log.Infow("-> Starting AtlasDeployment deletion", "spec", deployment.Spec)

project := &mdbv1.AtlasProject{}
if result := r.readProjectResource(cluster, project); !result.IsOk() {
if result := r.readProjectResource(deployment, project); !result.IsOk() {
return errors.New("cannot read project resource")
}

log = log.With("projectID", project.Status.ID, "clusterName", cluster.GetDeploymentName())
log = log.With("projectID", project.Status.ID, "deploymentName", deployment.GetDeploymentName())

if customresource.ResourceShouldBeLeftInAtlas(cluster) {
if customresource.ResourceShouldBeLeftInAtlas(deployment) {
log.Infof("Not removing Atlas Deployment from Atlas as the '%s' annotation is set", customresource.ResourcePolicyAnnotation)
} else if err := r.deleteDeploymentFromAtlas(cluster, project, log); err != nil {
log.Error("Failed to remove cluster from Atlas: %s", err)
} else if err := r.deleteDeploymentFromAtlas(deployment, project, log); err != nil {
log.Error("Failed to remove deployment from Atlas: %s", err)
}

// We always remove the connection secrets even if the cluster is not removed from Atlas
secrets, err := connectionsecret.ListByDeploymentName(r.Client, cluster.Namespace, project.ID(), cluster.GetDeploymentName())
// We always remove the connection secrets even if the deployment is not removed from Atlas
secrets, err := connectionsecret.ListByDeploymentName(r.Client, deployment.Namespace, project.ID(), deployment.GetDeploymentName())
if err != nil {
return fmt.Errorf("failed to find connection secrets for the user: %w", err)
}
Expand All @@ -460,7 +460,7 @@ func (r *AtlasDeploymentReconciler) Delete(e event.DeleteEvent) error {
return nil
}

func (r *AtlasDeploymentReconciler) deleteDeploymentFromAtlas(cluster *mdbv1.AtlasDeployment, project *mdbv1.AtlasProject, log *zap.SugaredLogger) error {
func (r *AtlasDeploymentReconciler) deleteDeploymentFromAtlas(deployment *mdbv1.AtlasDeployment, project *mdbv1.AtlasProject, log *zap.SugaredLogger) error {
connection, err := atlas.ReadConnection(log, r.Client, r.GlobalAPISecret, project.ConnectionSecretObjectKey())
if err != nil {
return err
Expand All @@ -476,14 +476,14 @@ func (r *AtlasDeploymentReconciler) deleteDeploymentFromAtlas(cluster *mdbv1.Atl

for time.Now().Before(timeout) {
deleteDeploymentFunc := atlasClient.Clusters.Delete
if cluster.Spec.AdvancedDeploymentSpec != nil {
if deployment.Spec.AdvancedDeploymentSpec != nil {
deleteDeploymentFunc = atlasClient.AdvancedClusters.Delete
}
if cluster.IsServerless() {
if deployment.IsServerless() {
deleteDeploymentFunc = atlasClient.ServerlessInstances.Delete
}

_, err = deleteDeploymentFunc(context.Background(), project.Status.ID, cluster.GetDeploymentName())
_, err = deleteDeploymentFunc(context.Background(), project.Status.ID, deployment.GetDeploymentName())

var apiError *mongodbatlas.ErrorResponse
if errors.As(err, &apiError) && apiError.ErrorCode == atlas.ClusterNotFound {
Expand All @@ -497,13 +497,13 @@ func (r *AtlasDeploymentReconciler) deleteDeploymentFromAtlas(cluster *mdbv1.Atl
continue
}

log.Info("Started Atlas cluster deletion process")
log.Info("Started Atlas deployment deletion process")
return
}

log.Error("Failed to delete Atlas cluster in time")
log.Error("Failed to delete Atlas deployment in time")
}()
return nil
}

type clusterHandlerFunc func(ctx *workflow.Context, project *mdbv1.AtlasProject, cluster *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error)
type deploymentHandlerFunc func(ctx *workflow.Context, project *mdbv1.AtlasProject, deployment *mdbv1.AtlasDeployment, req reconcile.Request) (workflow.Result, error)