mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00: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)
 | |
| 	}
 | |
| }
 | 
