Skip to content

Commit 402dae3

Browse files
committed
[Feature] Imported ArangoBackup Cleanup
1 parent 3803488 commit 402dae3

File tree

9 files changed

+239
-66
lines changed

9 files changed

+239
-66
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- (Feature) (ML) Unify Integration Sidecar
1818
- (Feature) (Analytics) Metadata
1919
- (Feature) (Analytics) StatefulSet
20+
- (Feature) Imported ArangoBackup Cleanup
2021

2122
## [1.2.40](https://github.com/arangodb/kube-arangodb/tree/1.2.40) (2024-04-10)
2223
- (Feature) Add Core fields to the Scheduler Container Spec

README.md

+36-33
Large diffs are not rendered by default.

cmd/cmd.go

+4
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ func init() {
262262
}
263263
}
264264

265+
func Command() *cobra.Command {
266+
return &cmdMain
267+
}
268+
265269
func Execute() int {
266270
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)
267271

docs/features/README.md

+30-29
Large diffs are not rendered by default.

internal/features.yaml

+10-1
Original file line numberDiff line numberDiff line change
@@ -235,12 +235,21 @@ features:
235235
- operatorVersion: 1.2.34
236236
state: Production
237237
- name: Create backups asynchronously
238-
enabled: false
238+
enabled: true
239239
remarks: Create backups asynchronously to avoid blocking the operator and reaching the timeout
240240
flag: --deployment.feature.async-backup-creation
241241
releases:
242+
- operatorVersion: 1.2.41
243+
state: Production
242244
- operatorVersion: 1.2.35
243245
state: Production
246+
- name: Cleanup Imported Backups
247+
enabled: false
248+
remarks: Cleanup backups created outside of the Operator and imported into Kubernetes ArangoBackup
249+
flag: --deployment.feature.backup-cleanup
250+
releases:
251+
- operatorVersion: 1.2.41
252+
state: Production
244253
- name: ArangoML integration
245254
operatorEditions: Enterprise
246255
arangoDBEditions: Enterprise

internal/readme.go

+37
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,20 @@
2121
package internal
2222

2323
import (
24+
"bytes"
2425
"fmt"
2526
"os"
2627
"path"
2728
"path/filepath"
2829
"sort"
2930
"strings"
3031

32+
"github.com/spf13/cobra"
3133
"gopkg.in/yaml.v3"
3234

3335
"github.com/arangodb/go-driver"
3436

37+
"github.com/arangodb/kube-arangodb/cmd"
3538
"github.com/arangodb/kube-arangodb/internal/md"
3639
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
3740
"github.com/arangodb/kube-arangodb/pkg/util"
@@ -128,13 +131,47 @@ func GenerateReadme(root string) error {
128131
readmeSections["limits"] = section
129132
}
130133

134+
if section, err := GenerateHelp(cmd.Command()); err != nil {
135+
return err
136+
} else {
137+
readmeSections["operatorArguments"] = section
138+
}
139+
131140
if err := md.ReplaceSectionsInFile(path.Join(root, "README.md"), readmeSections); err != nil {
132141
return err
133142
}
134143

135144
return nil
136145
}
137146

147+
func GenerateHelp(cmd *cobra.Command) (string, error) {
148+
var lines []string
149+
150+
lines = append(lines, "```", "Flags:")
151+
152+
buff := bytes.NewBuffer(nil)
153+
154+
cmd.SetOut(buff)
155+
156+
cmd.SetArgs([]string{"--help"})
157+
158+
if err := cmd.Execute(); err != nil {
159+
return "", err
160+
}
161+
162+
help := buff.String()
163+
164+
for _, line := range strings.Split(help, "\n") {
165+
if strings.HasPrefix(line, " --") {
166+
lines = append(lines, line)
167+
}
168+
}
169+
170+
lines = append(lines, "```")
171+
172+
return strings.Join(lines, "\n"), nil
173+
}
174+
138175
func GenerateReadmeFeatures(root, basePath string, eeOnly bool) (string, error) {
139176
feature := md.NewColumn("Feature", md.ColumnLeftAlign)
140177
introduced := md.NewColumn("Introduced", md.ColumnLeftAlign)

pkg/deployment/features/backup_async.go renamed to pkg/deployment/features/backup.go

+13
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,29 @@ package features
2222

2323
func init() {
2424
registerFeature(asyncBackupCreation)
25+
registerFeature(backupCleanup)
2526
}
2627

2728
var asyncBackupCreation = &feature{
2829
name: "async-backup-creation",
2930
description: "Create backups asynchronously to avoid blocking the operator and reaching the timeout",
3031
enterpriseRequired: false,
32+
enabledByDefault: true,
33+
}
34+
35+
var backupCleanup = &feature{
36+
name: "backup-cleanup",
37+
description: "Cleanup imported backups if required",
38+
enterpriseRequired: false,
3139
enabledByDefault: false,
3240
}
3341

3442
// AsyncBackupCreation returns mode for backup creation (sync/async).
3543
func AsyncBackupCreation() Feature {
3644
return asyncBackupCreation
3745
}
46+
47+
// BackupCleanup returns mode for Imported backups cleanup.
48+
func BackupCleanup() Feature {
49+
return backupCleanup
50+
}

pkg/handlers/backup/handler.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import (
3737
"github.com/arangodb/kube-arangodb/pkg/apis/backup"
3838
backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
3939
database "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
40+
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
4041
arangoClientSet "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
4142
"github.com/arangodb/kube-arangodb/pkg/logging"
4243
operator "github.com/arangodb/kube-arangodb/pkg/operatorV2"
@@ -133,6 +134,35 @@ func (h *handler) refreshDeployment(deployment *database.ArangoDeployment) error
133134
}
134135
}
135136

137+
if err = h.cleanupImportedBackups(backups.Items); err != nil {
138+
return err
139+
}
140+
141+
return nil
142+
}
143+
144+
func (h *handler) cleanupImportedBackups(backups []backupApi.ArangoBackup) error {
145+
if !features.BackupCleanup().Enabled() {
146+
return nil
147+
}
148+
for _, backup := range backups {
149+
if backup.GetDeletionTimestamp() != nil {
150+
continue
151+
}
152+
153+
if b := backup.Status.Backup; b == nil || !util.TypeOrDefault(b.Imported, false) {
154+
continue
155+
}
156+
157+
logger.Str("name", backup.GetName()).Str("namespace", backup.GetNamespace()).Info("Removing Imported ArangoBackup")
158+
159+
err := h.client.BackupV1().ArangoBackups(backup.GetNamespace()).Delete(context.Background(), backup.GetName(), meta.DeleteOptions{})
160+
if err != nil {
161+
return err
162+
}
163+
164+
}
165+
136166
return nil
137167
}
138168

@@ -153,11 +183,15 @@ func (h *handler) refreshDeploymentBackup(deployment *database.ArangoDeployment,
153183
}
154184
}
155185

186+
name := fmt.Sprintf("backup-%s", uuid.NewUUID())
187+
188+
logger.Str("id", string(backupMeta.ID)).Strs("namespace", deployment.GetNamespace()).Str("namespace", name).Info("Importing ArangoBackup from API")
189+
156190
// New backup found, need to recreate
157191
backup := &backupApi.ArangoBackup{
158192
ObjectMeta: meta.ObjectMeta{
159-
Name: fmt.Sprintf("backup-%s", uuid.NewUUID()),
160-
Namespace: deployment.Namespace,
193+
Name: name,
194+
Namespace: deployment.GetNamespace(),
161195
},
162196
Spec: backupApi.ArangoBackupSpec{
163197
Deployment: backupApi.ArangoBackupSpecDeployment{

pkg/handlers/backup/handler_test.go

+72-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// DISCLAIMER
33
//
4-
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
4+
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
55
//
66
// Licensed under the Apache License, Version 2.0 (the "License");
77
// you may not use this file except in compliance with the License.
@@ -23,11 +23,18 @@ package backup
2323
import (
2424
"context"
2525
"testing"
26+
"time"
2627

2728
"github.com/stretchr/testify/require"
2829
apiErrors "k8s.io/apimachinery/pkg/api/errors"
30+
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
"k8s.io/apimachinery/pkg/util/uuid"
32+
33+
"github.com/arangodb/go-driver"
2934

3035
backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
36+
database "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
37+
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
3138
"github.com/arangodb/kube-arangodb/pkg/operatorV2/operation"
3239
"github.com/arangodb/kube-arangodb/pkg/util/tests"
3340
)
@@ -59,3 +66,67 @@ func Test_ObjectNotFound(t *testing.T) {
5966
})
6067
}
6168
}
69+
70+
func resetFeature(f features.Feature) func() {
71+
enabled := f.Enabled()
72+
73+
return func() {
74+
*f.EnabledPointer() = enabled
75+
}
76+
}
77+
78+
func Test_Refresh_Cleanup(t *testing.T) {
79+
defer resetFeature(features.BackupCleanup())()
80+
81+
// Arrange
82+
handler, client := newErrorsFakeHandler(mockErrorsArangoClientBackup{})
83+
84+
id := driver.BackupID(uuid.NewUUID())
85+
client.state.backups = map[driver.BackupID]driver.BackupMeta{
86+
id: {
87+
ID: id,
88+
Version: "3.12.0",
89+
DateTime: time.Now().Add(-time.Hour),
90+
NumberOfFiles: 123,
91+
NumberOfDBServers: 3,
92+
SizeInBytes: 123,
93+
PotentiallyInconsistent: false,
94+
Available: true,
95+
NumberOfPiecesPresent: 123,
96+
},
97+
}
98+
99+
arangoDeployment := tests.NewMetaObject[*database.ArangoDeployment](t, tests.FakeNamespace, "deployment")
100+
101+
t.Run("Discover", func(t *testing.T) {
102+
require.NoError(t, handler.refreshDeployment(arangoDeployment))
103+
104+
backups, err := handler.client.BackupV1().ArangoBackups(tests.FakeNamespace).List(context.Background(), meta.ListOptions{})
105+
require.NoError(t, err)
106+
require.Len(t, backups.Items, 1)
107+
require.NotNil(t, backups.Items[0].Status.Backup)
108+
require.EqualValues(t, id, backups.Items[0].Status.Backup.ID)
109+
})
110+
111+
t.Run("Without Cleanup Feature", func(t *testing.T) {
112+
*features.BackupCleanup().EnabledPointer() = false
113+
114+
require.NoError(t, handler.refreshDeployment(arangoDeployment))
115+
116+
backups, err := handler.client.BackupV1().ArangoBackups(tests.FakeNamespace).List(context.Background(), meta.ListOptions{})
117+
require.NoError(t, err)
118+
require.Len(t, backups.Items, 1)
119+
require.NotNil(t, backups.Items[0].Status.Backup)
120+
require.EqualValues(t, id, backups.Items[0].Status.Backup.ID)
121+
})
122+
123+
t.Run("With Cleanup Feature", func(t *testing.T) {
124+
*features.BackupCleanup().EnabledPointer() = true
125+
126+
require.NoError(t, handler.refreshDeployment(arangoDeployment))
127+
128+
backups, err := handler.client.BackupV1().ArangoBackups(tests.FakeNamespace).List(context.Background(), meta.ListOptions{})
129+
require.NoError(t, err)
130+
require.Len(t, backups.Items, 0)
131+
})
132+
}

0 commit comments

Comments
 (0)