Skip to content

[Feature] Imported ArangoBackup Cleanup #1667

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- (Feature) (ML) Unify Integration Sidecar
- (Feature) (Analytics) Metadata
- (Feature) (Analytics) StatefulSet
- (Feature) Imported ArangoBackup Cleanup

## [1.2.40](https://github.com/arangodb/kube-arangodb/tree/1.2.40) (2024-04-10)
- (Feature) Add Core fields to the Scheduler Container Spec
Expand Down
73 changes: 40 additions & 33 deletions README.md

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,10 @@ func init() {
}
}

func Command() *cobra.Command {
return &cmdMain
}

func Execute() int {
flag.CommandLine.AddGoFlagSet(goflag.CommandLine)

Expand Down
59 changes: 30 additions & 29 deletions docs/features/README.md

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion internal/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,21 @@ features:
- operatorVersion: 1.2.34
state: Production
- name: Create backups asynchronously
enabled: false
enabled: true
remarks: Create backups asynchronously to avoid blocking the operator and reaching the timeout
flag: --deployment.feature.async-backup-creation
releases:
- operatorVersion: 1.2.41
state: Production
- operatorVersion: 1.2.35
state: Production
- name: Cleanup Imported Backups
enabled: false
remarks: Cleanup backups created outside of the Operator and imported into Kubernetes ArangoBackup
flag: --deployment.feature.backup-cleanup
releases:
- operatorVersion: 1.2.41
state: Production
- name: ArangoML integration
operatorEditions: Enterprise
arangoDBEditions: Enterprise
Expand Down
37 changes: 37 additions & 0 deletions internal/readme.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,20 @@
package internal

import (
"bytes"
"fmt"
"os"
"path"
"path/filepath"
"sort"
"strings"

"github.com/spf13/cobra"
"gopkg.in/yaml.v3"

"github.com/arangodb/go-driver"

"github.com/arangodb/kube-arangodb/cmd"
"github.com/arangodb/kube-arangodb/internal/md"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/util"
Expand Down Expand Up @@ -128,13 +131,47 @@ func GenerateReadme(root string) error {
readmeSections["limits"] = section
}

if section, err := GenerateHelp(cmd.Command()); err != nil {
return err
} else {
readmeSections["operatorArguments"] = section
}

if err := md.ReplaceSectionsInFile(path.Join(root, "README.md"), readmeSections); err != nil {
return err
}

return nil
}

func GenerateHelp(cmd *cobra.Command) (string, error) {
var lines []string

lines = append(lines, "```", "Flags:")

buff := bytes.NewBuffer(nil)

cmd.SetOut(buff)

cmd.SetArgs([]string{"--help"})

if err := cmd.Execute(); err != nil {
return "", err
}

help := buff.String()

for _, line := range strings.Split(help, "\n") {
if strings.HasPrefix(line, " --") {
lines = append(lines, line)
}
}

lines = append(lines, "```")

return md.WrapWithNewLines(md.WrapWithNewLines(strings.Join(lines, "\n"))), nil
}

func GenerateReadmeFeatures(root, basePath string, eeOnly bool) (string, error) {
feature := md.NewColumn("Feature", md.ColumnLeftAlign)
introduced := md.NewColumn("Introduced", md.ColumnLeftAlign)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,29 @@ package features

func init() {
registerFeature(asyncBackupCreation)
registerFeature(backupCleanup)
}

var asyncBackupCreation = &feature{
name: "async-backup-creation",
description: "Create backups asynchronously to avoid blocking the operator and reaching the timeout",
enterpriseRequired: false,
enabledByDefault: true,
}

var backupCleanup = &feature{
name: "backup-cleanup",
description: "Cleanup imported backups if required",
enterpriseRequired: false,
enabledByDefault: false,
}

// AsyncBackupCreation returns mode for backup creation (sync/async).
func AsyncBackupCreation() Feature {
return asyncBackupCreation
}

// BackupCleanup returns mode for Imported backups cleanup.
func BackupCleanup() Feature {
return backupCleanup
}
38 changes: 36 additions & 2 deletions pkg/handlers/backup/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/arangodb/kube-arangodb/pkg/apis/backup"
backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
database "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
arangoClientSet "github.com/arangodb/kube-arangodb/pkg/generated/clientset/versioned"
"github.com/arangodb/kube-arangodb/pkg/logging"
operator "github.com/arangodb/kube-arangodb/pkg/operatorV2"
Expand Down Expand Up @@ -133,6 +134,35 @@ func (h *handler) refreshDeployment(deployment *database.ArangoDeployment) error
}
}

if err = h.cleanupImportedBackups(backups.Items); err != nil {
return err
}

return nil
}

func (h *handler) cleanupImportedBackups(backups []backupApi.ArangoBackup) error {
if !features.BackupCleanup().Enabled() {
return nil
}
for _, backup := range backups {
if backup.GetDeletionTimestamp() != nil {
continue
}

if b := backup.Status.Backup; b == nil || !util.TypeOrDefault(b.Imported, false) {
continue
}

logger.Str("name", backup.GetName()).Str("namespace", backup.GetNamespace()).Info("Removing Imported ArangoBackup")

err := h.client.BackupV1().ArangoBackups(backup.GetNamespace()).Delete(context.Background(), backup.GetName(), meta.DeleteOptions{})
if err != nil {
return err
}

}

return nil
}

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

name := fmt.Sprintf("backup-%s", uuid.NewUUID())

logger.Str("id", string(backupMeta.ID)).Strs("namespace", deployment.GetNamespace()).Str("namespace", name).Info("Importing ArangoBackup from API")

// New backup found, need to recreate
backup := &backupApi.ArangoBackup{
ObjectMeta: meta.ObjectMeta{
Name: fmt.Sprintf("backup-%s", uuid.NewUUID()),
Namespace: deployment.Namespace,
Name: name,
Namespace: deployment.GetNamespace(),
},
Spec: backupApi.ArangoBackupSpec{
Deployment: backupApi.ArangoBackupSpecDeployment{
Expand Down
73 changes: 72 additions & 1 deletion pkg/handlers/backup/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//
// DISCLAIMER
//
// Copyright 2016-2023 ArangoDB GmbH, Cologne, Germany
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -23,11 +23,18 @@ package backup
import (
"context"
"testing"
"time"

"github.com/stretchr/testify/require"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/uuid"

"github.com/arangodb/go-driver"

backupApi "github.com/arangodb/kube-arangodb/pkg/apis/backup/v1"
database "github.com/arangodb/kube-arangodb/pkg/apis/deployment/v1"
"github.com/arangodb/kube-arangodb/pkg/deployment/features"
"github.com/arangodb/kube-arangodb/pkg/operatorV2/operation"
"github.com/arangodb/kube-arangodb/pkg/util/tests"
)
Expand Down Expand Up @@ -59,3 +66,67 @@ func Test_ObjectNotFound(t *testing.T) {
})
}
}

func resetFeature(f features.Feature) func() {
enabled := f.Enabled()

return func() {
*f.EnabledPointer() = enabled
}
}

func Test_Refresh_Cleanup(t *testing.T) {
defer resetFeature(features.BackupCleanup())()

// Arrange
handler, client := newErrorsFakeHandler(mockErrorsArangoClientBackup{})

id := driver.BackupID(uuid.NewUUID())
client.state.backups = map[driver.BackupID]driver.BackupMeta{
id: {
ID: id,
Version: "3.12.0",
DateTime: time.Now().Add(-time.Hour),
NumberOfFiles: 123,
NumberOfDBServers: 3,
SizeInBytes: 123,
PotentiallyInconsistent: false,
Available: true,
NumberOfPiecesPresent: 123,
},
}

arangoDeployment := tests.NewMetaObject[*database.ArangoDeployment](t, tests.FakeNamespace, "deployment")

t.Run("Discover", func(t *testing.T) {
require.NoError(t, handler.refreshDeployment(arangoDeployment))

backups, err := handler.client.BackupV1().ArangoBackups(tests.FakeNamespace).List(context.Background(), meta.ListOptions{})
require.NoError(t, err)
require.Len(t, backups.Items, 1)
require.NotNil(t, backups.Items[0].Status.Backup)
require.EqualValues(t, id, backups.Items[0].Status.Backup.ID)
})

t.Run("Without Cleanup Feature", func(t *testing.T) {
*features.BackupCleanup().EnabledPointer() = false

require.NoError(t, handler.refreshDeployment(arangoDeployment))

backups, err := handler.client.BackupV1().ArangoBackups(tests.FakeNamespace).List(context.Background(), meta.ListOptions{})
require.NoError(t, err)
require.Len(t, backups.Items, 1)
require.NotNil(t, backups.Items[0].Status.Backup)
require.EqualValues(t, id, backups.Items[0].Status.Backup.ID)
})

t.Run("With Cleanup Feature", func(t *testing.T) {
*features.BackupCleanup().EnabledPointer() = true

require.NoError(t, handler.refreshDeployment(arangoDeployment))

backups, err := handler.client.BackupV1().ArangoBackups(tests.FakeNamespace).List(context.Background(), meta.ListOptions{})
require.NoError(t, err)
require.Len(t, backups.Items, 0)
})
}