Skip to content

Commit

Permalink
Expose Medusa backup size
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvoncek committed Nov 30, 2023
1 parent be45e84 commit 8aec328
Show file tree
Hide file tree
Showing 12 changed files with 478 additions and 249 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG/CHANGELOG-1.10.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ When cutting a new release, update the `unreleased` heading to the tag being gen

## unreleased

* [ENHANCEMENT] [#1122](https://github.com/k8ssandra/k8ssandra-operator/issues/1122) Expose backup size in MedusaBackup CRD

## v1.10.3 - 2023-11-15

* [BUGFIX] [#1110](https://github.com/k8ssandra/k8ssandra-operator/issues/1110) Fix cluster name being set to "Test Cluster" when running Cassandra 4.1+
Expand Down
4 changes: 4 additions & 0 deletions apis/medusa/v1alpha1/medusabackup_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type MedusaBackupStatus struct {
TotalNodes int32 `json:"totalNodes,omitempty"`
FinishedNodes int32 `json:"finishedNodes,omitempty"`
Nodes []*MedusaBackupNode `json:"nodes,omitempty"`
TotalFiles int64 `json:"totalFiles,omitempty"`
TotalSize string `json:"totalSize,omitempty"`
Status string `json:"status,omitempty"`
}

Expand All @@ -57,6 +59,8 @@ type MedusaBackupNode struct {
//+kubebuilder:printcolumn:name="Started",type=date,JSONPath=".status.startTime",description="Backup start time"
//+kubebuilder:printcolumn:name="Finished",type=date,JSONPath=".status.finishTime",description="Backup finish time"
//+kubebuilder:printcolumn:name="Nodes",type=string,JSONPath=".status.totalNodes",description="Total number of nodes at the time of the backup"
//+kubebuilder:printcolumn:name="Files",type=integer,JSONPath=".status.totalFiles",description="Total number of files in the backup"
//+kubebuilder:printcolumn:name="Size",type=string,JSONPath=".status.totalSize",description="Human-readable total size of the backup"
//+kubebuilder:printcolumn:name="Completed",type=string,JSONPath=".status.finishedNodes",description="Number of nodes that completed this backup"
//+kubebuilder:printcolumn:name="Status",type=string,JSONPath=".status.status",description="Backup completion status"

Expand Down
13 changes: 13 additions & 0 deletions config/crd/bases/medusa.k8ssandra.io_medusabackups.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ spec:
jsonPath: .status.totalNodes
name: Nodes
type: string
- description: Total number of files in the backup
jsonPath: .status.totalFiles
name: Files
type: integer
- description: Human-readable total size of the backup
jsonPath: .status.totalSize
name: Size
type: string
- description: Number of nodes that completed this backup
jsonPath: .status.finishedNodes
name: Completed
Expand Down Expand Up @@ -98,9 +106,14 @@ spec:
type: string
status:
type: string
totalFiles:
format: int64
type: integer
totalNodes:
format: int32
type: integer
totalSize:
type: string
type: object
type: object
served: true
Expand Down
12 changes: 12 additions & 0 deletions controllers/medusa/medusabackupjob_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,8 @@ func (r *MedusaBackupJobReconciler) createMedusaBackup(ctx context.Context, back
backupResource.Status.FinishTime = finishTime
backupResource.Status.TotalNodes = backupSummary.TotalNodes
backupResource.Status.FinishedNodes = backupSummary.FinishedNodes
backupResource.Status.TotalFiles = backupSummary.TotalObjects
backupResource.Status.TotalSize = humanize(backupSummary.TotalSize)
backupResource.Status.Nodes = make([]*medusav1alpha1.MedusaBackupNode, len(backupSummary.Nodes))
for i, node := range backupSummary.Nodes {
backupResource.Status.Nodes[i] = &medusav1alpha1.MedusaBackupNode{
Expand Down Expand Up @@ -336,3 +338,13 @@ func (r *MedusaBackupJobReconciler) SetupWithManager(mgr ctrl.Manager) error {
For(&medusav1alpha1.MedusaBackupJob{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Complete(r)
}

func humanize(bytes int64) string {
units := []string{"B", "KB", "MB", "GB", "TB", "PB"}
size := float64(bytes)
i := 0
for ; size >= 1024 && i < len(units)-1; i++ {
size /= 1024
}
return fmt.Sprintf("%.2f %s", size, units[i])
}
26 changes: 26 additions & 0 deletions controllers/medusa/medusabackupjob_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ const (
defaultBackupName = "backup1"
dc1PodPrefix = "192.168.1."
dc2PodPrefix = "192.168.2."
fakeBackupFileCount = int64(13)
fakeBackupByteSize = int64(42)
fakeBackupHumanSize = "42.00 B"
)

func testMedusaBackupDatacenter(t *testing.T, ctx context.Context, f *framework.Framework, namespace string) {
Expand Down Expand Up @@ -243,6 +246,8 @@ func createAndVerifyMedusaBackup(dcKey framework.ClusterKey, dc *cassdcapi.Cassa
require.Equal(medusaBackup.Status.TotalNodes, dc.Spec.Size, "backup total nodes doesn't match dc nodes")
require.Equal(medusaBackup.Status.FinishedNodes, dc.Spec.Size, "backup finished nodes doesn't match dc nodes")
require.Equal(len(medusaBackup.Status.Nodes), int(dc.Spec.Size), "backup topology doesn't match dc topology")
require.Equal(medusaBackup.Status.TotalFiles, fakeBackupFileCount, "backup total files doesn't match")
require.Equal(medusaBackup.Status.TotalSize, fakeBackupHumanSize, "backup total size doesn't match")
require.Equal(medusa.StatusType_SUCCESS.String(), medusaBackup.Status.Status, "backup status is not success")

require.Equal(int(dc.Spec.Size), len(medusaClientFactory.GetRequestedBackups(dc.DatacenterName())))
Expand Down Expand Up @@ -354,6 +359,8 @@ func (c *fakeMedusaClient) GetBackups(ctx context.Context) ([]*medusa.BackupSumm
FinishTime: 10,
TotalNodes: 3,
FinishedNodes: 3,
TotalObjects: fakeBackupFileCount,
TotalSize: fakeBackupByteSize,
Status: *medusa.StatusType_SUCCESS.Enum(),
Nodes: []*medusa.BackupNode{
{
Expand Down Expand Up @@ -502,3 +509,22 @@ func reconcileMedusaStandaloneDeployment(ctx context.Context, t *testing.T, f *f

require.NoError(t, err, "Failed to update Medusa Deployment status")
}

func TestHumanize(t *testing.T) {
t.Run("humanizeTrivialSizes", humanizeTrivialSizes)
t.Run("humanizeArbitrarySizes", humanizeArbitrarySizes)
}

func humanizeTrivialSizes(t *testing.T) {
assert.Equal(t, "1.00 B", humanize(1))
assert.Equal(t, "1.00 KB", humanize(1024))
assert.Equal(t, "1.00 MB", humanize(1024*1024))
assert.Equal(t, "1.00 GB", humanize(1024*1024*1024))
}

func humanizeArbitrarySizes(t *testing.T) {
assert.Equal(t, "127.50 KB", humanize(130557))
assert.Equal(t, "4.03 GB", humanize(4325130557))
assert.Equal(t, "7.67 TB", humanize(8434729356343))
assert.Equal(t, "1096.52 PB", humanize(1234567890123456790))
}
12 changes: 7 additions & 5 deletions docs/content/en/tasks/backup-restore/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ K8ssandra Operator will detect the `MedusaBackupJob` object creation and trigger
To monitor the backup completion, check if the `finishTime` is set in the `MedusaBackupJob` object status. Example:

```sh
% kubectl get medusabackupjob/medusa-backup1 -o yaml
% kubectl get medusabackupjob/backup1 -o yaml
kind: MedusaBackupJob
metadata:
Expand Down Expand Up @@ -159,7 +159,7 @@ medusa-backup1 19m 19m

All pods having completed the backup will be in the `finished` list.
At the end of the backup operation, a `MedusaBackup` custom resource will be created with the same name as the `MedusaBackupJob` object. It materializes the backup locally on the Kubernetes cluster.
The MedusaBackup object status contains the total number of node in the cluster at the time of the backup, the number of nodes that successfully achieved the backup, and the topology of the DC at the time of the backup:
The MedusaBackup object status contains the total number of node in the cluster at the time of the backup, the number of nodes that successfully achieved the backup, the topology of the DC at the time of the backup, the number of files backed up and their total size:

```yaml
apiVersion: medusa.k8ssandra.io/v1alpha1
Expand Down Expand Up @@ -189,6 +189,8 @@ status:
- -1058110708807841300
- -107256661843445790
status: SUCCESS
totalFiles: 120
totalSize: 127.67 KB
spec:
backupType: differential
cassandraDatacenter: dc1
Expand All @@ -199,9 +201,9 @@ The `kubectl get`` output for MedusaBackup objects will show a subset of this in

```sh
kubectl get MedusaBackup -A
NAME STARTED FINISHED NODES COMPLETED STATUS
backup1 29m 28m 2 2 SUCCESS
medusa-backup1 23m 23m 2 2 SUCCESS
NAME STARTED FINISHED NODES FILES SIZE COMPLETED STATUS
backup1 29m 28m 2 120 127.67 KB 2 SUCCESS
medusa-backup1 23m 23m 2 137 241.61 KB 2 SUCCESS
```

For a restore to be possible, a `MedusaBackup` object must exist.
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ require (
github.com/stretchr/testify v1.8.2
go.uber.org/zap v1.24.0
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.30.0
google.golang.org/protobuf v1.31.0
gopkg.in/resty.v1 v1.12.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/api v0.26.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1598,8 +1598,8 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/asn1-ber.v1 v1.0.0-20170511165959-379148ca0225/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw=
Expand Down
Loading

0 comments on commit 8aec328

Please sign in to comment.