scheduler: bound policy backup starts
This commit is contained in:
parent
fe2dd6a695
commit
ad0109305a
@ -50,6 +50,7 @@ type Config struct {
|
|||||||
AuthBearerTokens []string
|
AuthBearerTokens []string
|
||||||
MetricsRefreshInterval time.Duration
|
MetricsRefreshInterval time.Duration
|
||||||
PolicyEvalInterval time.Duration
|
PolicyEvalInterval time.Duration
|
||||||
|
PolicyBackupsPerCycle int
|
||||||
PolicySecretName string
|
PolicySecretName string
|
||||||
UsageSecretName string
|
UsageSecretName string
|
||||||
BackupMaxAge time.Duration
|
BackupMaxAge time.Duration
|
||||||
@ -133,6 +134,9 @@ func Load() (*Config, error) {
|
|||||||
if seconds, ok := getenvInt("SOTERIA_POLICY_EVAL_SECONDS"); ok {
|
if seconds, ok := getenvInt("SOTERIA_POLICY_EVAL_SECONDS"); ok {
|
||||||
cfg.PolicyEvalInterval = time.Duration(seconds) * time.Second
|
cfg.PolicyEvalInterval = time.Duration(seconds) * time.Second
|
||||||
}
|
}
|
||||||
|
if limit, ok := getenvInt("SOTERIA_POLICY_BACKUPS_PER_CYCLE"); ok {
|
||||||
|
cfg.PolicyBackupsPerCycle = limit
|
||||||
|
}
|
||||||
if hours, ok := getenvFloat("SOTERIA_BACKUP_MAX_AGE_HOURS"); ok {
|
if hours, ok := getenvFloat("SOTERIA_BACKUP_MAX_AGE_HOURS"); ok {
|
||||||
cfg.BackupMaxAge = time.Duration(hours * float64(time.Hour))
|
cfg.BackupMaxAge = time.Duration(hours * float64(time.Hour))
|
||||||
}
|
}
|
||||||
@ -187,6 +191,9 @@ func Load() (*Config, error) {
|
|||||||
if cfg.PolicyEvalInterval <= 0 {
|
if cfg.PolicyEvalInterval <= 0 {
|
||||||
return nil, errors.New("SOTERIA_POLICY_EVAL_SECONDS must be greater than zero")
|
return nil, errors.New("SOTERIA_POLICY_EVAL_SECONDS must be greater than zero")
|
||||||
}
|
}
|
||||||
|
if cfg.PolicyBackupsPerCycle < 0 {
|
||||||
|
return nil, errors.New("SOTERIA_POLICY_BACKUPS_PER_CYCLE must be greater than or equal to zero")
|
||||||
|
}
|
||||||
if strings.TrimSpace(cfg.PolicySecretName) == "" {
|
if strings.TrimSpace(cfg.PolicySecretName) == "" {
|
||||||
return nil, errors.New("SOTERIA_POLICY_SECRET_NAME must not be empty")
|
return nil, errors.New("SOTERIA_POLICY_SECRET_NAME must not be empty")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,7 @@ func clearConfigEnv(t *testing.T) {
|
|||||||
"SOTERIA_AUTH_BEARER_TOKENS",
|
"SOTERIA_AUTH_BEARER_TOKENS",
|
||||||
"SOTERIA_METRICS_REFRESH_SECONDS",
|
"SOTERIA_METRICS_REFRESH_SECONDS",
|
||||||
"SOTERIA_POLICY_EVAL_SECONDS",
|
"SOTERIA_POLICY_EVAL_SECONDS",
|
||||||
|
"SOTERIA_POLICY_BACKUPS_PER_CYCLE",
|
||||||
"SOTERIA_POLICY_SECRET_NAME",
|
"SOTERIA_POLICY_SECRET_NAME",
|
||||||
"SOTERIA_USAGE_SECRET_NAME",
|
"SOTERIA_USAGE_SECRET_NAME",
|
||||||
"SOTERIA_BACKUP_MAX_AGE_HOURS",
|
"SOTERIA_BACKUP_MAX_AGE_HOURS",
|
||||||
@ -117,6 +118,7 @@ func TestLoadSupportsResticAndB2Overrides(t *testing.T) {
|
|||||||
withEnv(t, "SOTERIA_AUTH_BEARER_TOKENS", "alpha,beta")
|
withEnv(t, "SOTERIA_AUTH_BEARER_TOKENS", "alpha,beta")
|
||||||
withEnv(t, "SOTERIA_METRICS_REFRESH_SECONDS", "60")
|
withEnv(t, "SOTERIA_METRICS_REFRESH_SECONDS", "60")
|
||||||
withEnv(t, "SOTERIA_POLICY_EVAL_SECONDS", "120")
|
withEnv(t, "SOTERIA_POLICY_EVAL_SECONDS", "120")
|
||||||
|
withEnv(t, "SOTERIA_POLICY_BACKUPS_PER_CYCLE", "3")
|
||||||
withEnv(t, "SOTERIA_POLICY_SECRET_NAME", "policy-doc")
|
withEnv(t, "SOTERIA_POLICY_SECRET_NAME", "policy-doc")
|
||||||
withEnv(t, "SOTERIA_USAGE_SECRET_NAME", "usage-doc")
|
withEnv(t, "SOTERIA_USAGE_SECRET_NAME", "usage-doc")
|
||||||
withEnv(t, "SOTERIA_BACKUP_MAX_AGE_HOURS", "12.5")
|
withEnv(t, "SOTERIA_BACKUP_MAX_AGE_HOURS", "12.5")
|
||||||
@ -162,6 +164,9 @@ func TestLoadSupportsResticAndB2Overrides(t *testing.T) {
|
|||||||
if cfg.MetricsRefreshInterval != 60*time.Second || cfg.PolicyEvalInterval != 120*time.Second {
|
if cfg.MetricsRefreshInterval != 60*time.Second || cfg.PolicyEvalInterval != 120*time.Second {
|
||||||
t.Fatalf("unexpected intervals: %#v", cfg)
|
t.Fatalf("unexpected intervals: %#v", cfg)
|
||||||
}
|
}
|
||||||
|
if cfg.PolicyBackupsPerCycle != 3 {
|
||||||
|
t.Fatalf("unexpected policy backup limit: %#v", cfg.PolicyBackupsPerCycle)
|
||||||
|
}
|
||||||
if cfg.BackupMaxAge != time.Duration(12.5*float64(time.Hour)) {
|
if cfg.BackupMaxAge != time.Duration(12.5*float64(time.Hour)) {
|
||||||
t.Fatalf("unexpected backup max age: %#v", cfg.BackupMaxAge)
|
t.Fatalf("unexpected backup max age: %#v", cfg.BackupMaxAge)
|
||||||
}
|
}
|
||||||
@ -285,6 +290,13 @@ func TestLoadRejectsInvalidConfigurations(t *testing.T) {
|
|||||||
},
|
},
|
||||||
substr: "SOTERIA_POLICY_EVAL_SECONDS must be greater than zero",
|
substr: "SOTERIA_POLICY_EVAL_SECONDS must be greater than zero",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "invalid policy backup limit",
|
||||||
|
env: map[string]string{
|
||||||
|
"SOTERIA_POLICY_BACKUPS_PER_CYCLE": "-1",
|
||||||
|
},
|
||||||
|
substr: "SOTERIA_POLICY_BACKUPS_PER_CYCLE must be greater than or equal to zero",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|||||||
@ -154,3 +154,42 @@ func TestRunPolicyCycleCoversEffectivePoliciesAndResultTracking(t *testing.T) {
|
|||||||
t.Fatalf("expected one failed executed backup request, got %f", got)
|
t.Fatalf("expected one failed executed backup request, got %f", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunPolicyCycleHonorsBackupLimit(t *testing.T) {
|
||||||
|
client := &policyCycleTestKubeClient{
|
||||||
|
inventoryTestKubeClient: &inventoryTestKubeClient{
|
||||||
|
fakeKubeClient: &fakeKubeClient{
|
||||||
|
pvcs: []k8s.PVCSummary{
|
||||||
|
{Namespace: "apps", Name: "alpha", VolumeName: "vol-alpha", Phase: "Bound"},
|
||||||
|
{Namespace: "apps", Name: "beta", VolumeName: "vol-beta", Phase: "Bound"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
srv := &Server{
|
||||||
|
cfg: &config.Config{
|
||||||
|
BackupDriver: "restic",
|
||||||
|
BackupMaxAge: 24 * time.Hour,
|
||||||
|
ResticRepository: "s3:https://repo/root",
|
||||||
|
PolicyBackupsPerCycle: 1,
|
||||||
|
},
|
||||||
|
client: client,
|
||||||
|
longhorn: &fakeLonghornClient{},
|
||||||
|
metrics: newTelemetry(),
|
||||||
|
policies: map[string]api.BackupPolicy{
|
||||||
|
"apps__all": {ID: "apps__all", Namespace: "apps", IntervalHours: 1, Enabled: true, Dedupe: true},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
srv.runPolicyCycle(context.Background())
|
||||||
|
|
||||||
|
if len(client.backupRequests) != 1 {
|
||||||
|
t.Fatalf("expected one policy backup due to cycle limit, got %#v", client.backupRequests)
|
||||||
|
}
|
||||||
|
if client.backupRequests[0].PVC != "alpha" {
|
||||||
|
t.Fatalf("expected deterministic first backup for alpha, got %#v", client.backupRequests[0])
|
||||||
|
}
|
||||||
|
if got := metricCount(srv.metrics.policyBackups, map[string]string{"result": "cycle_limit"}); got != 1 {
|
||||||
|
t.Fatalf("expected cycle_limit metric, got %f", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -72,7 +72,15 @@ func (s *Server) runPolicyCycle(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, effective := range effectivePolicies {
|
keys := make([]string, 0, len(effectivePolicies))
|
||||||
|
for key := range effectivePolicies {
|
||||||
|
keys = append(keys, key)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
|
||||||
|
started := 0
|
||||||
|
for _, key := range keys {
|
||||||
|
effective := effectivePolicies[key]
|
||||||
pvc, ok := pvcMap[key]
|
pvc, ok := pvcMap[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
@ -95,6 +103,10 @@ func (s *Server) runPolicyCycle(ctx context.Context) {
|
|||||||
s.metrics.RecordPolicyBackup("not_due")
|
s.metrics.RecordPolicyBackup("not_due")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if s.cfg.PolicyBackupsPerCycle > 0 && started >= s.cfg.PolicyBackupsPerCycle {
|
||||||
|
s.metrics.RecordPolicyBackup("cycle_limit")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
_, result, err := s.executeBackup(runCtx, api.BackupRequest{
|
_, result, err := s.executeBackup(runCtx, api.BackupRequest{
|
||||||
Namespace: pvc.Namespace,
|
Namespace: pvc.Namespace,
|
||||||
@ -103,6 +115,7 @@ func (s *Server) runPolicyCycle(ctx context.Context) {
|
|||||||
Dedupe: boolPtr(effective.Dedupe),
|
Dedupe: boolPtr(effective.Dedupe),
|
||||||
KeepLast: intPtr(effective.KeepLast),
|
KeepLast: intPtr(effective.KeepLast),
|
||||||
}, "policy-scheduler")
|
}, "policy-scheduler")
|
||||||
|
started++
|
||||||
s.metrics.RecordBackupRequest(s.cfg.BackupDriver, result)
|
s.metrics.RecordBackupRequest(s.cfg.BackupDriver, result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.metrics.RecordPolicyBackup(result)
|
s.metrics.RecordPolicyBackup(result)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user