mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-06 22:57:02 +02:00
113 lines
3.9 KiB
Go
113 lines
3.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package rotation
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/robfig/cron/v3"
|
|
)
|
|
|
|
const (
|
|
// Minimum allowed value for rotation_window
|
|
minRotationWindowSeconds = 3600
|
|
parseOptions = cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow
|
|
)
|
|
|
|
// RotationSchedule holds the parsed and unparsed versions of the schedule, along with the projected next rotation time.
|
|
type RotationSchedule struct {
|
|
Schedule *cron.SpecSchedule `json:"schedule"`
|
|
RotationWindow time.Duration `json:"rotation_window"` // seconds of window
|
|
RotationSchedule string `json:"rotation_schedule"`
|
|
RotationPeriod time.Duration `json:"rotation_period"`
|
|
NextVaultRotation time.Time `json:"next_vault_rotation"`
|
|
LastVaultRotation time.Time `json:"last_vault_rotation"`
|
|
}
|
|
|
|
type Scheduler interface {
|
|
Parse(rotationSchedule string) (*cron.SpecSchedule, error)
|
|
ValidateRotationWindow(s int) error
|
|
NextRotationTimeFromInput(rs *RotationSchedule, input time.Time) time.Time
|
|
IsInsideRotationWindow(rs *RotationSchedule, t time.Time) bool
|
|
ShouldRotate(rs *RotationSchedule, priority int64, t time.Time) bool
|
|
NextRotationTime(rs *RotationSchedule) time.Time
|
|
SetNextVaultRotation(rs *RotationSchedule, t time.Time)
|
|
UsesTTL(rs *RotationSchedule) bool
|
|
UsesRotationSchedule(rs *RotationSchedule) bool
|
|
}
|
|
|
|
var DefaultScheduler Scheduler = &DefaultSchedule{}
|
|
|
|
type DefaultSchedule struct{}
|
|
|
|
func (d *DefaultSchedule) Parse(rotationSchedule string) (*cron.SpecSchedule, error) {
|
|
parser := cron.NewParser(parseOptions)
|
|
schedule, err := parser.Parse(rotationSchedule)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sched, ok := schedule.(*cron.SpecSchedule)
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid rotation schedule")
|
|
}
|
|
return sched, nil
|
|
}
|
|
|
|
func (d *DefaultSchedule) ValidateRotationWindow(s int) error {
|
|
if s < minRotationWindowSeconds {
|
|
return fmt.Errorf("rotation_window must be %d seconds or more", minRotationWindowSeconds)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (d *DefaultSchedule) UsesRotationSchedule(rs *RotationSchedule) bool {
|
|
return rs.RotationSchedule != "" && rs.RotationPeriod == 0
|
|
}
|
|
|
|
func (d *DefaultSchedule) UsesTTL(rs *RotationSchedule) bool {
|
|
return rs.RotationPeriod != 0 && rs.RotationSchedule == ""
|
|
}
|
|
|
|
// NextRotationTime calculates the next scheduled rotation
|
|
func (d *DefaultSchedule) NextRotationTime(rs *RotationSchedule) time.Time {
|
|
if d.UsesTTL(rs) {
|
|
return rs.LastVaultRotation.Add(rs.RotationPeriod)
|
|
}
|
|
return rs.Schedule.Next(time.Now())
|
|
}
|
|
|
|
// NextRotationTimeFromInput calculates and returns the next rotation time based on the provided schedule and input time
|
|
func (d *DefaultSchedule) NextRotationTimeFromInput(rs *RotationSchedule, input time.Time) time.Time {
|
|
if d.UsesTTL(rs) {
|
|
return input.Add(rs.RotationPeriod)
|
|
}
|
|
return rs.Schedule.Next(input)
|
|
}
|
|
|
|
// IsInsideRotationWindow checks if the current time is before the calculated end of the rotation window,
|
|
// to make sure that t time is within the specified rotation window
|
|
// It returns true if rotation window is not specified
|
|
func (d *DefaultSchedule) IsInsideRotationWindow(rs *RotationSchedule, t time.Time) bool {
|
|
if rs.RotationWindow != 0 {
|
|
return t.Before(rs.NextVaultRotation.Add(rs.RotationWindow))
|
|
}
|
|
return true
|
|
}
|
|
|
|
// ShouldRotate checks if the rotation should occur based on priority, current time, and rotation window
|
|
// It returns true if the priority is less than or equal to the current time and the current time is within the rotation window
|
|
func (d *DefaultSchedule) ShouldRotate(rs *RotationSchedule, priority int64, t time.Time) bool {
|
|
return priority <= t.Unix() && d.IsInsideRotationWindow(rs, t)
|
|
}
|
|
|
|
// SetNextVaultRotation calculates the next rotation time of a given schedule based on the time.
|
|
func (d *DefaultSchedule) SetNextVaultRotation(rs *RotationSchedule, t time.Time) {
|
|
if d.UsesTTL(rs) {
|
|
rs.NextVaultRotation = t.Add(rs.RotationPeriod)
|
|
} else {
|
|
rs.NextVaultRotation = rs.Schedule.Next(t)
|
|
}
|
|
}
|