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

Modify DeactivationService to set scheduled deactivation time for active UserSignups #988

Merged
merged 57 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
b744180
Allow registration service to get/list/watch UserTier resources
sbryzak Mar 11, 2024
73d39ca
regenerated from API
sbryzak Mar 12, 2024
cd8819a
regenerated
sbryzak Mar 15, 2024
7d35f22
updated deactivation controller
sbryzak Mar 15, 2024
0ece3f2
regenerated api
sbryzak Mar 15, 2024
fa5a1b8
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Mar 15, 2024
30ae9a9
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Mar 16, 2024
3d864f6
updated api
sbryzak Mar 16, 2024
27eab89
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Mar 20, 2024
76b3d25
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Mar 25, 2024
7376ab0
refactor
sbryzak Mar 26, 2024
8ac7c1c
restore missing code
sbryzak Mar 26, 2024
141eda3
revert change to deactivation controller, documented reasons why
sbryzak Mar 26, 2024
0018b07
added test
sbryzak Mar 27, 2024
96bd1d0
Merge branch 'master' into SANDBOX-554
mfrancisc Apr 4, 2024
fdc0529
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Apr 8, 2024
5fde800
moved deactivation schedule logic to deactivation controller
sbryzak Apr 8, 2024
bc400eb
Merge branch 'SANDBOX-554' of https://github.com/sbryzak/host-operato…
sbryzak Apr 8, 2024
ee76ba0
moved testing
sbryzak Apr 9, 2024
51582aa
updated
sbryzak Apr 11, 2024
cd5409c
added sufficient test coverage for all places where scheduled deactiv…
sbryzak Apr 11, 2024
d7f3ae4
Merge remote-tracking branch 'origin' into SANDBOX-554
sbryzak Apr 11, 2024
fa1967d
fix setting scheduled deactivation time to nil only when usersignup.S…
sbryzak Apr 12, 2024
7b5087b
disable gocyclo linter for reconcile function, breaking this function…
sbryzak Apr 12, 2024
91f822e
deactivation controller should reconcile after MUR is deleted in orde…
sbryzak Apr 14, 2024
50aeeb2
a smarter way to handle resetting the scheduled deactivation time to …
sbryzak Apr 15, 2024
645191e
fix test
sbryzak Apr 15, 2024
e5f0ed6
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Apr 17, 2024
5334e28
fixed broken test, improve coverage
sbryzak Apr 18, 2024
3496763
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Apr 18, 2024
9fcee7c
set scheduled deactivation time to nil when user in domain exclusion …
sbryzak Apr 20, 2024
74f9329
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Apr 21, 2024
bdf613b
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Apr 24, 2024
85b95f1
oops
sbryzak Apr 24, 2024
8333dea
set temporary deactivation time
sbryzak Apr 29, 2024
67ec7fd
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak Apr 29, 2024
67082ca
improve coverage
sbryzak May 1, 2024
e729c5a
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 1, 2024
193ac5b
even more coverage
sbryzak May 1, 2024
cdd1bcd
fixed lint
sbryzak May 1, 2024
cde7536
coverage
sbryzak May 1, 2024
c7df220
fixed linter
sbryzak May 2, 2024
087cdcb
fix potential infinite loop
sbryzak May 2, 2024
2652fa2
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 2, 2024
e2dfbcb
fix
sbryzak May 5, 2024
5fcb467
removed commented code
sbryzak May 5, 2024
d9c3511
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 9, 2024
5879cab
updated comments
sbryzak May 9, 2024
5a14763
removed predicate, set scheduled deactivation time to nil when in dea…
sbryzak May 10, 2024
a8bdb69
review comments
sbryzak May 10, 2024
10bbfb6
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 12, 2024
473a24a
slightly improve logic
sbryzak May 14, 2024
3fcd207
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 14, 2024
8486dc6
review comment
sbryzak May 14, 2024
9f420e9
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 14, 2024
4f742c6
Merge remote-tracking branch 'origin/master' into SANDBOX-554
sbryzak May 14, 2024
1cbcfa9
coverage
sbryzak May 15, 2024
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
87 changes: 84 additions & 3 deletions controllers/deactivation/deactivation_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package deactivation
import (
"context"
"fmt"
"github.com/go-logr/logr"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"strings"
"time"

Expand Down Expand Up @@ -34,7 +36,8 @@ func (r *Reconciler) SetupWithManager(mgr manager.Manager) error {
Named("deactivation").
For(&toolchainv1alpha1.MasterUserRecord{}, builder.WithPredicates(CreateAndUpdateOnlyPredicate{})).
Watches(&source.Kind{Type: &toolchainv1alpha1.UserSignup{}},
handler.EnqueueRequestsFromMapFunc(MapUserSignupToMasterUserRecord())).
handler.EnqueueRequestsFromMapFunc(MapUserSignupToMasterUserRecord()),
builder.WithPredicates(GenerationOrConditionsChangedPredicate{})).
Complete(r)
}

Expand All @@ -48,6 +51,7 @@ type Reconciler struct {
// Note:
// The Controller will requeue the Request to be processed again if the returned error is non-nil or
// Result.Requeue is true, otherwise upon completion it will remove the work from the queue.
// nolint: gocyclo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the linter believes that the reconcile function is too complex, so we disabled that check

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried to re-enable it now? The code is less complex now then the initial one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the linter still doesn't like it.

Copy link
Contributor

@MatousJobanek MatousJobanek May 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

have you considered refactoring the code and splitting the method into multiple ones? TBH, the linter is correct that the method is really complex and should be simplified/refactored.

func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.Result, error) {
logger := log.FromContext(ctx)
logger.Info("Reconciling Deactivation")
Expand Down Expand Up @@ -103,6 +107,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
for _, domain := range config.Deactivation().DeactivationDomainsExcluded() {
if strings.HasSuffix(usersignup.Spec.IdentityClaims.Email, domain) {
logger.Info("user cannot be automatically deactivated because they belong to the exclusion list", "domain", domain)

// Also set the Scheduled deactivation time to nil if it's not already
if usersignup.Status.ScheduledDeactivationTimestamp != nil {
usersignup.Status.ScheduledDeactivationTimestamp = nil
if err := r.Client.Status().Update(ctx, usersignup); err != nil {
sbryzak marked this conversation as resolved.
Show resolved Hide resolved
logger.Error(err, "failed to update usersignup status")
return reconcile.Result{}, err
}
}

return reconcile.Result{}, nil
}
}
Expand All @@ -128,6 +142,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
if err := r.resetDeactivatingState(ctx, usersignup); err != nil {
return reconcile.Result{}, err
}
// Set the scheduled deactivation time to nil
alexeykazakov marked this conversation as resolved.
Show resolved Hide resolved
if usersignup.Status.ScheduledDeactivationTimestamp != nil {
sbryzak marked this conversation as resolved.
Show resolved Hide resolved
usersignup.Status.ScheduledDeactivationTimestamp = nil
if err := r.Client.Status().Update(ctx, usersignup); err != nil {
logger.Error(err, "failed to update usersignup status")
return reconcile.Result{}, err
}
}

logger.Info("User belongs to a tier that does not have a deactivation timeout. The user will not be automatically deactivated")
// Users belonging to this tier will not be auto deactivated, no need to requeue.
return reconcile.Result{}, nil
Expand All @@ -153,6 +176,12 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
return reconcile.Result{}, err
}

// Reset the scheduled deactivation time if required
err = setDeactivationTimestamp(ctx, r, mur, usersignup, deactivationTimeout, logger)
if err != nil {
return reconcile.Result{}, err
}

// requeue until it will be time to send it
requeueAfterTimeToNotify := deactivatingNotificationTimeout - timeSinceProvisioned
logger.Info("requeueing request", "RequeueAfter", requeueAfterTimeToNotify,
Expand All @@ -164,6 +193,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
if !states.Deactivating(usersignup) {
states.SetDeactivating(usersignup, true)

// Before we update the UserSignup in order to set the deactivating state, we should reset the scheduled
// deactivation time if required just in case the current value is nil or has somehow changed. Since the UserSignup
// controller is going to be reconciling immediately after setting the deactivating state then it will be
// creating a deactivating notification, meaning that the scheduled deactivation time that we set here is going
// to be extremely temporary (as it will be recalculated after the next reconcile), however it is done for correctness
err = setDeactivationTimestamp(ctx, r, mur, usersignup, deactivationTimeout, logger)
if err != nil {
return reconcile.Result{}, err
}

logger.Info("setting usersignup state to deactivating")
if err := r.Client.Update(ctx, usersignup); err != nil {
logger.Error(err, "failed to update usersignup")
Expand All @@ -188,14 +227,41 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
if !found || deactivatingCondition.Status != corev1.ConditionTrue ||
deactivatingCondition.Reason != toolchainv1alpha1.UserSignupDeactivatingNotificationCRCreatedReason {
// If the UserSignup has been marked as deactivating, however the deactivating notification hasn't been
// created yet, then wait - the notification should be created shortly by the UserSignup controller
// once the "deactivating" state has been set which should cause a new reconciliation to be triggered here
// created yet, then set the deactivation timestamp to a temporary value, calculated as being the current
// timestamp plus the number of pre-deactivation days configured in the settings, BUT only if it is currently nil
// OR the calculated value is more than 24 hours later than the current value
deactivationDueTime := time.Now().Add(time.Duration(deactivatingNotificationDays) * 24 * time.Hour)
if usersignup.Status.ScheduledDeactivationTimestamp == nil ||
usersignup.Status.ScheduledDeactivationTimestamp.Time.Before(deactivationDueTime.Add(-24*time.Hour)) {
sbryzak marked this conversation as resolved.
Show resolved Hide resolved
ts := v1.NewTime(deactivationDueTime)
usersignup.Status.ScheduledDeactivationTimestamp = &ts
if err := r.Client.Status().Update(ctx, usersignup); err != nil {
logger.Error(err, "failed to update usersignup status")
return reconcile.Result{}, err
}
}

return reconcile.Result{}, nil
}

// We calculate the actual deactivation due time based on when the deactivating condition was set. This may end up
// being significantly later than the scheduled deactivation time set in UserSignup.Status.ScheduledDeactivationTime, as
// in some rare circumstances the deactivating notification/status may fail to be set due to cluster downtime or
// other reasons. Because of this, the scheduled deactivation time that is set in the UserSignup.Status should be
// treated as informational only.
deactivationDueTime := deactivatingCondition.LastTransitionTime.Time.Add(time.Duration(deactivatingNotificationDays*24) * time.Hour)
alexeykazakov marked this conversation as resolved.
Show resolved Hide resolved

if time.Now().Before(deactivationDueTime) {
// Update the ScheduledDeactivationTimestamp to the recalculated time base on when the deactivating notification was sent
if usersignup.Status.ScheduledDeactivationTimestamp == nil || !usersignup.Status.ScheduledDeactivationTimestamp.Time.Equal(deactivationDueTime) {
sbryzak marked this conversation as resolved.
Show resolved Hide resolved
ts := v1.NewTime(deactivationDueTime)
usersignup.Status.ScheduledDeactivationTimestamp = &ts
if err := r.Client.Status().Update(ctx, usersignup); err != nil {
alexeykazakov marked this conversation as resolved.
Show resolved Hide resolved
logger.Error(err, "failed to update usersignup status")
return reconcile.Result{}, err
}
}

// It is not yet time to deactivate so requeue when it will be
requeueAfterExpired := time.Until(deactivationDueTime)

Expand All @@ -221,6 +287,21 @@ func (r *Reconciler) Reconcile(ctx context.Context, request ctrl.Request) (ctrl.
return reconcile.Result{}, nil
}

func setDeactivationTimestamp(ctx context.Context, r *Reconciler, mur *toolchainv1alpha1.MasterUserRecord,
userSignup *toolchainv1alpha1.UserSignup, deactivationTimeout time.Duration, logger logr.Logger) error {
scheduledDeactivationTime := (*mur.Status.ProvisionedTime).Add(deactivationTimeout)
if userSignup.Status.ScheduledDeactivationTimestamp == nil || !userSignup.Status.ScheduledDeactivationTimestamp.Time.Equal(scheduledDeactivationTime) {
ts := v1.NewTime(scheduledDeactivationTime)
userSignup.Status.ScheduledDeactivationTimestamp = &ts
if err := r.Client.Status().Update(ctx, userSignup); err != nil {
sbryzak marked this conversation as resolved.
Show resolved Hide resolved
logger.Error(err, "failed to update usersignup status")
return err
}
}

return nil
}

func (r *Reconciler) resetDeactivatingState(ctx context.Context, usersignup *toolchainv1alpha1.UserSignup) error {
if states.Deactivating(usersignup) {
states.SetDeactivating(usersignup, false)
Expand Down
Loading
Loading