Skip to content

Commit

Permalink
[Feature] Imported ArangoBackup Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
ajanikow committed May 20, 2024
1 parent 3803488 commit c267944
Show file tree
Hide file tree
Showing 9 changed files with 243 additions and 66 deletions.
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)
})
}

0 comments on commit c267944

Please sign in to comment.