Skip to content

Commit

Permalink
🐛 Check if the token has expired (open-cluster-management-io#116) (op…
Browse files Browse the repository at this point in the history
…en-cluster-management-io#135)

* Check if the token has expired periodically



* Requeue before the token is about to expire



* Add unit tests



---------

Signed-off-by: zhujian <[email protected]>
  • Loading branch information
zhujian7 authored Sep 12, 2024
1 parent 8be6318 commit 97ceb83
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 4 deletions.
28 changes: 24 additions & 4 deletions pkg/addon/agent/controller/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package controller
import (
"context"
"os"
"reflect"
"time"

"github.com/pkg/errors"
authv1 "k8s.io/api/authentication/v1"
Expand Down Expand Up @@ -137,12 +139,30 @@ func (r *TokenReconciler) Reconcile(ctx context.Context, request reconcile.Reque
LastRefreshTimestamp: tokenRefreshTime,
}

if err := r.HubClient.Status().Update(context.TODO(), msaCopy); err != nil {
return reconcile.Result{}, errors.Wrapf(err, "failed to update status")
if !reflect.DeepEqual(msa.Status, msaCopy.Status) {
if err := r.HubClient.Status().Update(context.TODO(), msaCopy); err != nil {
return reconcile.Result{}, errors.Wrapf(err, "failed to update status")
}
logger.Info("Token refreshed")
return reconcile.Result{}, nil
}

logger.Info("Refreshed token")
return reconcile.Result{}, nil
return reconcile.Result{
// Requeue even if the token is not refreshed, otherwise if the agent restarts
// at the time that the token is not expried, no chance to trigger the expiration
// check again
RequeueAfter: checkTokenRefreshAfter(now, expiring, msa.Spec.Rotation.Validity.Duration),
}, nil
}

func checkTokenRefreshAfter(now metav1.Time, expiring *metav1.Time, validityDuration time.Duration) time.Duration {
refreshThreshold := validityDuration / 5 * 1
lifetime := expiring.Sub(now.Time)
if (lifetime - refreshThreshold) > 0 {
return lifetime - refreshThreshold + time.Duration(5*time.Second)
} else {
return time.Duration(5 * time.Second)
}
}

// sync is the main logic of token rotation, it returns the expiration time of the token if the token is created/updated
Expand Down
33 changes: 33 additions & 0 deletions pkg/addon/agent/controller/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,3 +649,36 @@ func TestReconcileCreateTokenByDefaultSecret(t *testing.T) {
})
}
}

func TestCheckTokenRefreshAfter(t *testing.T) {
now := metav1.Time{Time: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)}
cases := []struct {
name string
expiring *metav1.Time
validityDuration time.Duration
expectedRequeueAfter time.Duration
}{
{
name: "expired",
expiring: &metav1.Time{Time: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)},
validityDuration: 10 * time.Hour,
expectedRequeueAfter: 5 * time.Second,
},
{
name: "not expired",
expiring: &metav1.Time{Time: time.Date(2024, 1, 1, 9, 0, 0, 0, time.UTC)},
validityDuration: 10 * time.Hour,
expectedRequeueAfter: 7*time.Hour + 5*time.Second,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {

ra := checkTokenRefreshAfter(now, c.expiring, c.validityDuration)
if ra != c.expectedRequeueAfter {
t.Errorf("expected %v but got %v", c.expectedRequeueAfter, ra)
}

})
}
}

0 comments on commit 97ceb83

Please sign in to comment.