90 lines
2.9 KiB
Go
90 lines
2.9 KiB
Go
|
|
package k8s
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"sort"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||
|
|
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||
|
|
)
|
||
|
|
|
||
|
|
const longhornSystemNamespace = "longhorn-system"
|
||
|
|
|
||
|
|
var longhornBackupGVR = schema.GroupVersionResource{
|
||
|
|
Group: "longhorn.io",
|
||
|
|
Version: "v1beta2",
|
||
|
|
Resource: "backups",
|
||
|
|
}
|
||
|
|
|
||
|
|
// LonghornBackupSummary is the subset of the Longhorn Backup CRD that Soteria
|
||
|
|
// needs while waiting for an async backup to reach a terminal state.
|
||
|
|
type LonghornBackupSummary struct {
|
||
|
|
Name string
|
||
|
|
SnapshotName string
|
||
|
|
State string
|
||
|
|
Error string
|
||
|
|
Progress int64
|
||
|
|
CreatedAt string
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetLonghornBackupBySnapshot returns the newest Longhorn Backup CRD for a snapshot.
|
||
|
|
func (c *Client) GetLonghornBackupBySnapshot(ctx context.Context, snapshotName string) (LonghornBackupSummary, bool, error) {
|
||
|
|
snapshotName = strings.TrimSpace(snapshotName)
|
||
|
|
if snapshotName == "" {
|
||
|
|
return LonghornBackupSummary{}, false, fmt.Errorf("snapshot name is required")
|
||
|
|
}
|
||
|
|
if c.Dynamic == nil {
|
||
|
|
return LonghornBackupSummary{}, false, fmt.Errorf("dynamic Kubernetes client is unavailable")
|
||
|
|
}
|
||
|
|
|
||
|
|
list, err := c.Dynamic.Resource(longhornBackupGVR).Namespace(longhornSystemNamespace).List(ctx, metav1.ListOptions{})
|
||
|
|
if err != nil {
|
||
|
|
return LonghornBackupSummary{}, false, fmt.Errorf("list Longhorn backups: %w", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
matches := make([]LonghornBackupSummary, 0, len(list.Items))
|
||
|
|
for _, item := range list.Items {
|
||
|
|
summary := summarizeLonghornBackup(item)
|
||
|
|
if summary.SnapshotName == snapshotName {
|
||
|
|
matches = append(matches, summary)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if len(matches) == 0 {
|
||
|
|
return LonghornBackupSummary{}, false, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
sort.Slice(matches, func(i, j int) bool {
|
||
|
|
if matches[i].CreatedAt == matches[j].CreatedAt {
|
||
|
|
return matches[i].Name > matches[j].Name
|
||
|
|
}
|
||
|
|
return matches[i].CreatedAt > matches[j].CreatedAt
|
||
|
|
})
|
||
|
|
return matches[0], true, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func summarizeLonghornBackup(item unstructured.Unstructured) LonghornBackupSummary {
|
||
|
|
snapshotName, _, _ := unstructured.NestedString(item.Object, "spec", "snapshotName")
|
||
|
|
state, _, _ := unstructured.NestedString(item.Object, "status", "state")
|
||
|
|
errorMessage, _, _ := unstructured.NestedString(item.Object, "status", "error")
|
||
|
|
progress, _, _ := unstructured.NestedInt64(item.Object, "status", "progress")
|
||
|
|
createdAt, _, _ := unstructured.NestedString(item.Object, "status", "backupCreatedAt")
|
||
|
|
if strings.TrimSpace(createdAt) == "" {
|
||
|
|
createdAt, _, _ = unstructured.NestedString(item.Object, "status", "snapshotCreatedAt")
|
||
|
|
}
|
||
|
|
if strings.TrimSpace(createdAt) == "" {
|
||
|
|
createdAt = item.GetCreationTimestamp().UTC().Format("2006-01-02T15:04:05Z")
|
||
|
|
}
|
||
|
|
|
||
|
|
return LonghornBackupSummary{
|
||
|
|
Name: item.GetName(),
|
||
|
|
SnapshotName: strings.TrimSpace(snapshotName),
|
||
|
|
State: strings.TrimSpace(state),
|
||
|
|
Error: strings.TrimSpace(errorMessage),
|
||
|
|
Progress: progress,
|
||
|
|
CreatedAt: strings.TrimSpace(createdAt),
|
||
|
|
}
|
||
|
|
}
|