docs(soteria): replace doc waivers with inline comments
This commit is contained in:
parent
f7a08eb420
commit
2caabd28ea
@ -1,5 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
|
// BackupRequest is the request payload for a namespace or PVC backup.
|
||||||
type BackupRequest struct {
|
type BackupRequest struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc"`
|
PVC string `json:"pvc"`
|
||||||
@ -10,6 +11,7 @@ type BackupRequest struct {
|
|||||||
KeepLast *int `json:"keep_last,omitempty"`
|
KeepLast *int `json:"keep_last,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupResponse is the response payload returned after a backup request.
|
||||||
type BackupResponse struct {
|
type BackupResponse struct {
|
||||||
Driver string `json:"driver,omitempty"`
|
Driver string `json:"driver,omitempty"`
|
||||||
Volume string `json:"volume,omitempty"`
|
Volume string `json:"volume,omitempty"`
|
||||||
@ -23,6 +25,7 @@ type BackupResponse struct {
|
|||||||
KeepLast int `json:"keep_last"`
|
KeepLast int `json:"keep_last"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestoreTestRequest is the request payload for a restore test.
|
||||||
type RestoreTestRequest struct {
|
type RestoreTestRequest struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc,omitempty"`
|
PVC string `json:"pvc,omitempty"`
|
||||||
@ -34,6 +37,7 @@ type RestoreTestRequest struct {
|
|||||||
DryRun bool `json:"dry_run"`
|
DryRun bool `json:"dry_run"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RestoreTestResponse is the response payload returned after a restore test.
|
||||||
type RestoreTestResponse struct {
|
type RestoreTestResponse struct {
|
||||||
Driver string `json:"driver,omitempty"`
|
Driver string `json:"driver,omitempty"`
|
||||||
Volume string `json:"volume,omitempty"`
|
Volume string `json:"volume,omitempty"`
|
||||||
@ -47,16 +51,19 @@ type RestoreTestResponse struct {
|
|||||||
DryRun bool `json:"dry_run"`
|
DryRun bool `json:"dry_run"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InventoryResponse wraps the full backup inventory view returned to the UI.
|
||||||
type InventoryResponse struct {
|
type InventoryResponse struct {
|
||||||
GeneratedAt string `json:"generated_at"`
|
GeneratedAt string `json:"generated_at"`
|
||||||
Namespaces []NamespaceInventory `json:"namespaces"`
|
Namespaces []NamespaceInventory `json:"namespaces"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceInventory groups PVC inventory for a single namespace.
|
||||||
type NamespaceInventory struct {
|
type NamespaceInventory struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
PVCs []PVCInventory `json:"pvcs"`
|
PVCs []PVCInventory `json:"pvcs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PVCInventory captures the backup health snapshot for one PVC.
|
||||||
type PVCInventory struct {
|
type PVCInventory struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc"`
|
PVC string `json:"pvc"`
|
||||||
@ -82,6 +89,7 @@ type PVCInventory struct {
|
|||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupListResponse returns the backup history for a PVC.
|
||||||
type BackupListResponse struct {
|
type BackupListResponse struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc"`
|
PVC string `json:"pvc"`
|
||||||
@ -89,6 +97,7 @@ type BackupListResponse struct {
|
|||||||
Backups []BackupRecord `json:"backups"`
|
Backups []BackupRecord `json:"backups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupRecord summarizes one backup entry in a history list.
|
||||||
type BackupRecord struct {
|
type BackupRecord struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
SnapshotName string `json:"snapshot_name,omitempty"`
|
SnapshotName string `json:"snapshot_name,omitempty"`
|
||||||
@ -99,6 +108,7 @@ type BackupRecord struct {
|
|||||||
Latest bool `json:"latest,omitempty"`
|
Latest bool `json:"latest,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthInfoResponse reports the authenticated user and the allowed groups.
|
||||||
type AuthInfoResponse struct {
|
type AuthInfoResponse struct {
|
||||||
Authenticated bool `json:"authenticated"`
|
Authenticated bool `json:"authenticated"`
|
||||||
User string `json:"user,omitempty"`
|
User string `json:"user,omitempty"`
|
||||||
@ -107,6 +117,7 @@ type AuthInfoResponse struct {
|
|||||||
AllowedGroups []string `json:"allowed_groups,omitempty"`
|
AllowedGroups []string `json:"allowed_groups,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupPolicy stores the scheduling policy for a namespace or PVC.
|
||||||
type BackupPolicy struct {
|
type BackupPolicy struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
@ -119,6 +130,7 @@ type BackupPolicy struct {
|
|||||||
UpdatedAt string `json:"updated_at,omitempty"`
|
UpdatedAt string `json:"updated_at,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupPolicyUpsertRequest updates the stored backup policy for a scope.
|
||||||
type BackupPolicyUpsertRequest struct {
|
type BackupPolicyUpsertRequest struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc,omitempty"`
|
PVC string `json:"pvc,omitempty"`
|
||||||
@ -128,10 +140,12 @@ type BackupPolicyUpsertRequest struct {
|
|||||||
KeepLast *int `json:"keep_last,omitempty"`
|
KeepLast *int `json:"keep_last,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupPolicyListResponse returns the configured backup policies.
|
||||||
type BackupPolicyListResponse struct {
|
type BackupPolicyListResponse struct {
|
||||||
Policies []BackupPolicy `json:"policies"`
|
Policies []BackupPolicy `json:"policies"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceBackupRequest requests a backup sweep across a namespace.
|
||||||
type NamespaceBackupRequest struct {
|
type NamespaceBackupRequest struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
DryRun bool `json:"dry_run"`
|
DryRun bool `json:"dry_run"`
|
||||||
@ -139,6 +153,7 @@ type NamespaceBackupRequest struct {
|
|||||||
KeepLast *int `json:"keep_last,omitempty"`
|
KeepLast *int `json:"keep_last,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceBackupResult reports the outcome for a single PVC backup.
|
||||||
type NamespaceBackupResult struct {
|
type NamespaceBackupResult struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc"`
|
PVC string `json:"pvc"`
|
||||||
@ -148,6 +163,7 @@ type NamespaceBackupResult struct {
|
|||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceBackupResponse summarizes the namespace backup sweep results.
|
||||||
type NamespaceBackupResponse struct {
|
type NamespaceBackupResponse struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
RequestedBy string `json:"requested_by,omitempty"`
|
RequestedBy string `json:"requested_by,omitempty"`
|
||||||
@ -161,6 +177,7 @@ type NamespaceBackupResponse struct {
|
|||||||
Results []NamespaceBackupResult `json:"results"`
|
Results []NamespaceBackupResult `json:"results"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceRestoreRequest requests a restore sweep across a namespace.
|
||||||
type NamespaceRestoreRequest struct {
|
type NamespaceRestoreRequest struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
TargetNamespace string `json:"target_namespace,omitempty"`
|
TargetNamespace string `json:"target_namespace,omitempty"`
|
||||||
@ -169,6 +186,7 @@ type NamespaceRestoreRequest struct {
|
|||||||
DryRun bool `json:"dry_run"`
|
DryRun bool `json:"dry_run"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceRestoreResult reports the outcome for a single PVC restore.
|
||||||
type NamespaceRestoreResult struct {
|
type NamespaceRestoreResult struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
PVC string `json:"pvc"`
|
PVC string `json:"pvc"`
|
||||||
@ -180,6 +198,7 @@ type NamespaceRestoreResult struct {
|
|||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NamespaceRestoreResponse summarizes the namespace restore sweep results.
|
||||||
type NamespaceRestoreResponse struct {
|
type NamespaceRestoreResponse struct {
|
||||||
Namespace string `json:"namespace"`
|
Namespace string `json:"namespace"`
|
||||||
TargetNamespace string `json:"target_namespace"`
|
TargetNamespace string `json:"target_namespace"`
|
||||||
@ -192,6 +211,7 @@ type NamespaceRestoreResponse struct {
|
|||||||
Results []NamespaceRestoreResult `json:"results"`
|
Results []NamespaceRestoreResult `json:"results"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// B2UsageResponse reports account and per-bucket Backblaze B2 usage.
|
||||||
type B2UsageResponse struct {
|
type B2UsageResponse struct {
|
||||||
Enabled bool `json:"enabled"`
|
Enabled bool `json:"enabled"`
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
@ -207,6 +227,7 @@ type B2UsageResponse struct {
|
|||||||
Error string `json:"error,omitempty"`
|
Error string `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// B2BucketUsage captures the usage metrics for one Backblaze B2 bucket.
|
||||||
type B2BucketUsage struct {
|
type B2BucketUsage struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
ObjectCount int64 `json:"object_count"`
|
ObjectCount int64 `json:"object_count"`
|
||||||
|
|||||||
@ -27,6 +27,7 @@ const (
|
|||||||
serviceNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
serviceNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Config holds the runtime settings used to build and serve Soteria.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
SecretNamespace string
|
SecretNamespace string
|
||||||
@ -67,6 +68,7 @@ type Config struct {
|
|||||||
B2ScanTimeout time.Duration
|
B2ScanTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load builds the runtime configuration from environment variables and cluster state.
|
||||||
func Load() (*Config, error) {
|
func Load() (*Config, error) {
|
||||||
cfg := &Config{}
|
cfg := &Config{}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"k8s.io/client-go/tools/clientcmd"
|
"k8s.io/client-go/tools/clientcmd"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Client wraps the Kubernetes interface used by Soteria.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
Clientset kubernetes.Interface
|
Clientset kubernetes.Interface
|
||||||
}
|
}
|
||||||
@ -21,6 +22,7 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// New returns a Kubernetes client, preferring in-cluster config and falling back to KUBECONFIG.
|
||||||
func New() (*Client, error) {
|
func New() (*Client, error) {
|
||||||
cfg, err := inClusterConfigFn()
|
cfg, err := inClusterConfigFn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -26,6 +26,7 @@ const (
|
|||||||
annotationKeepLast = "soteria.bstein.dev/keep-last"
|
annotationKeepLast = "soteria.bstein.dev/keep-last"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// BackupJobSummary captures the backup job details shown in the UI and metrics.
|
||||||
type BackupJobSummary struct {
|
type BackupJobSummary struct {
|
||||||
Name string
|
Name string
|
||||||
Namespace string
|
Namespace string
|
||||||
@ -38,6 +39,7 @@ type BackupJobSummary struct {
|
|||||||
State string
|
State string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListBackupJobs returns backup jobs in a namespace, newest first.
|
||||||
func (c *Client) ListBackupJobs(ctx context.Context, namespace string) ([]BackupJobSummary, error) {
|
func (c *Client) ListBackupJobs(ctx context.Context, namespace string) ([]BackupJobSummary, error) {
|
||||||
selector := fmt.Sprintf("%s=soteria,%s=backup,%s=backup", labelAppName, labelComponent, labelAction)
|
selector := fmt.Sprintf("%s=soteria,%s=backup,%s=backup", labelAppName, labelComponent, labelAction)
|
||||||
jobs, err := c.Clientset.BatchV1().Jobs(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
jobs, err := c.Clientset.BatchV1().Jobs(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
||||||
@ -58,6 +60,7 @@ func (c *Client) ListBackupJobs(ctx context.Context, namespace string) ([]Backup
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadBackupJobLog fetches the log stream for the most recent pod of a backup job.
|
||||||
func (c *Client) ReadBackupJobLog(ctx context.Context, namespace, jobName string) (string, error) {
|
func (c *Client) ReadBackupJobLog(ctx context.Context, namespace, jobName string) (string, error) {
|
||||||
selector := fmt.Sprintf("job-name=%s", jobName)
|
selector := fmt.Sprintf("job-name=%s", jobName)
|
||||||
pods, err := c.Clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
pods, err := c.Clientset.CoreV1().Pods(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
||||||
@ -97,6 +100,7 @@ func latestBackupJobPodName(pods []corev1.Pod) string {
|
|||||||
return pods[0].Name
|
return pods[0].Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListBackupJobsForPVC returns backup jobs for a single PVC in newest-first order.
|
||||||
func (c *Client) ListBackupJobsForPVC(ctx context.Context, namespace, pvc string) ([]BackupJobSummary, error) {
|
func (c *Client) ListBackupJobsForPVC(ctx context.Context, namespace, pvc string) ([]BackupJobSummary, error) {
|
||||||
selector := fmt.Sprintf("%s=soteria,%s=backup,%s=backup,%s=%s", labelAppName, labelComponent, labelAction, labelPVC, pvc)
|
selector := fmt.Sprintf("%s=soteria,%s=backup,%s=backup,%s=%s", labelAppName, labelComponent, labelAction, labelPVC, pvc)
|
||||||
jobs, err := c.Clientset.BatchV1().Jobs(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
jobs, err := c.Clientset.BatchV1().Jobs(namespace).List(ctx, metav1.ListOptions{LabelSelector: selector})
|
||||||
@ -160,6 +164,7 @@ func sortBackupJobSummaries(items []BackupJobSummary) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateBackupJob provisions the Kubernetes job and companion secret for a backup request.
|
||||||
func (c *Client) CreateBackupJob(ctx context.Context, cfg *config.Config, req api.BackupRequest) (string, string, error) {
|
func (c *Client) CreateBackupJob(ctx context.Context, cfg *config.Config, req api.BackupRequest) (string, string, error) {
|
||||||
if req.Namespace == "" {
|
if req.Namespace == "" {
|
||||||
return "", "", errors.New("namespace is required")
|
return "", "", errors.New("namespace is required")
|
||||||
@ -235,6 +240,7 @@ func (c *Client) resolvePVCMountedNode(ctx context.Context, namespace, pvc strin
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateRestoreJob provisions the Kubernetes job and companion secret for a restore request.
|
||||||
func (c *Client) CreateRestoreJob(ctx context.Context, cfg *config.Config, req api.RestoreTestRequest) (string, string, error) {
|
func (c *Client) CreateRestoreJob(ctx context.Context, cfg *config.Config, req api.RestoreTestRequest) (string, string, error) {
|
||||||
if req.Namespace == "" {
|
if req.Namespace == "" {
|
||||||
return "", "", errors.New("namespace is required")
|
return "", "", errors.New("namespace is required")
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadSecretData returns a copy of the requested secret value when it exists.
|
||||||
func (c *Client) LoadSecretData(ctx context.Context, namespace, secretName, key string) ([]byte, error) {
|
func (c *Client) LoadSecretData(ctx context.Context, namespace, secretName, key string) ([]byte, error) {
|
||||||
secret, err := c.Clientset.CoreV1().Secrets(namespace).Get(ctx, secretName, metav1.GetOptions{})
|
secret, err := c.Clientset.CoreV1().Secrets(namespace).Get(ctx, secretName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -29,6 +30,7 @@ func (c *Client) LoadSecretData(ctx context.Context, namespace, secretName, key
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveSecretData creates or updates the target secret while preserving labels.
|
||||||
func (c *Client) SaveSecretData(ctx context.Context, namespace, secretName, key string, value []byte, labels map[string]string) error {
|
func (c *Client) SaveSecretData(ctx context.Context, namespace, secretName, key string, value []byte, labels map[string]string) error {
|
||||||
secretClient := c.Clientset.CoreV1().Secrets(namespace)
|
secretClient := c.Clientset.CoreV1().Secrets(namespace)
|
||||||
secret, err := secretClient.Get(ctx, secretName, metav1.GetOptions{})
|
secret, err := secretClient.Get(ctx, secretName, metav1.GetOptions{})
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PVCSummary describes a bound PVC and the volume metadata Soteria needs.
|
||||||
type PVCSummary struct {
|
type PVCSummary struct {
|
||||||
Namespace string
|
Namespace string
|
||||||
Name string
|
Name string
|
||||||
@ -20,6 +21,7 @@ type PVCSummary struct {
|
|||||||
AccessModes []string
|
AccessModes []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolvePVCVolume loads a PVC and its bound PV so backup handlers can act on the real volume.
|
||||||
func (c *Client) ResolvePVCVolume(ctx context.Context, namespace, pvcName string) (string, *corev1.PersistentVolumeClaim, *corev1.PersistentVolume, error) {
|
func (c *Client) ResolvePVCVolume(ctx context.Context, namespace, pvcName string) (string, *corev1.PersistentVolumeClaim, *corev1.PersistentVolume, error) {
|
||||||
pvc, err := c.Clientset.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
|
pvc, err := c.Clientset.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -37,6 +39,7 @@ func (c *Client) ResolvePVCVolume(ctx context.Context, namespace, pvcName string
|
|||||||
return pvc.Spec.VolumeName, pvc, pv, nil
|
return pvc.Spec.VolumeName, pvc, pv, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListBoundPVCs returns all bound PVCs in a stable namespace/name order.
|
||||||
func (c *Client) ListBoundPVCs(ctx context.Context) ([]PVCSummary, error) {
|
func (c *Client) ListBoundPVCs(ctx context.Context) ([]PVCSummary, error) {
|
||||||
list, err := c.Clientset.CoreV1().PersistentVolumeClaims("").List(ctx, metav1.ListOptions{})
|
list, err := c.Clientset.CoreV1().PersistentVolumeClaims("").List(ctx, metav1.ListOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -87,6 +90,7 @@ func (c *Client) ListBoundPVCs(ctx context.Context) ([]PVCSummary, error) {
|
|||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PersistentVolumeClaimExists reports whether a PVC can be read successfully.
|
||||||
func (c *Client) PersistentVolumeClaimExists(ctx context.Context, namespace, pvcName string) (bool, error) {
|
func (c *Client) PersistentVolumeClaimExists(ctx context.Context, namespace, pvcName string) (bool, error) {
|
||||||
_, err := c.Clientset.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
|
_, err := c.Clientset.CoreV1().PersistentVolumeClaims(namespace).Get(ctx, pvcName, metav1.GetOptions{})
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@ -14,11 +14,13 @@ import (
|
|||||||
|
|
||||||
const defaultTimeout = 30 * time.Second
|
const defaultTimeout = 30 * time.Second
|
||||||
|
|
||||||
|
// Client wraps the Longhorn API base URL and HTTP client.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
baseURL string
|
baseURL string
|
||||||
http *http.Client
|
http *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New returns a Longhorn API client for the supplied base URL.
|
||||||
func New(baseURL string) *Client {
|
func New(baseURL string) *Client {
|
||||||
baseURL = strings.TrimSuffix(baseURL, "/")
|
baseURL = strings.TrimSuffix(baseURL, "/")
|
||||||
return &Client{
|
return &Client{
|
||||||
@ -29,15 +31,18 @@ func New(baseURL string) *Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// APIError reports a non-2xx Longhorn API response.
|
||||||
type APIError struct {
|
type APIError struct {
|
||||||
Status int
|
Status int
|
||||||
Message string
|
Message string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Error renders the Longhorn API failure in a human-friendly form.
|
||||||
func (e *APIError) Error() string {
|
func (e *APIError) Error() string {
|
||||||
return fmt.Sprintf("longhorn api error: status=%d message=%s", e.Status, e.Message)
|
return fmt.Sprintf("longhorn api error: status=%d message=%s", e.Status, e.Message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Volume describes the Longhorn volume payload Soteria consumes.
|
||||||
type Volume struct {
|
type Volume struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Size string `json:"size"`
|
Size string `json:"size"`
|
||||||
@ -48,6 +53,7 @@ type Volume struct {
|
|||||||
Actions map[string]any `json:"actions"`
|
Actions map[string]any `json:"actions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupStatus describes the backup progress for a Longhorn snapshot.
|
||||||
type BackupStatus struct {
|
type BackupStatus struct {
|
||||||
Snapshot string `json:"snapshot"`
|
Snapshot string `json:"snapshot"`
|
||||||
BackupURL string `json:"backupURL"`
|
BackupURL string `json:"backupURL"`
|
||||||
@ -56,11 +62,13 @@ type BackupStatus struct {
|
|||||||
Progress int `json:"progress"`
|
Progress int `json:"progress"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BackupVolume describes a Longhorn backup volume and its action URLs.
|
||||||
type BackupVolume struct {
|
type BackupVolume struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Actions map[string]string `json:"actions"`
|
Actions map[string]string `json:"actions"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backup describes a Longhorn backup record.
|
||||||
type Backup struct {
|
type Backup struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
SnapshotName string `json:"snapshotName"`
|
SnapshotName string `json:"snapshotName"`
|
||||||
@ -95,6 +103,7 @@ type pvcCreateInput struct {
|
|||||||
PVCName string `json:"pvcName"`
|
PVCName string `json:"pvcName"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateSnapshot requests a snapshot on the target Longhorn volume.
|
||||||
func (c *Client) CreateSnapshot(ctx context.Context, volume, name string, labels map[string]string) error {
|
func (c *Client) CreateSnapshot(ctx context.Context, volume, name string, labels map[string]string) error {
|
||||||
path := fmt.Sprintf("%s/v1/volumes/%s?action=snapshotCreate", c.baseURL, url.PathEscape(volume))
|
path := fmt.Sprintf("%s/v1/volumes/%s?action=snapshotCreate", c.baseURL, url.PathEscape(volume))
|
||||||
input := snapshotCreateInput{
|
input := snapshotCreateInput{
|
||||||
@ -104,6 +113,7 @@ func (c *Client) CreateSnapshot(ctx context.Context, volume, name string, labels
|
|||||||
return c.doJSON(ctx, http.MethodPost, path, input, nil)
|
return c.doJSON(ctx, http.MethodPost, path, input, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SnapshotBackup requests a backup snapshot and returns the updated volume payload.
|
||||||
func (c *Client) SnapshotBackup(ctx context.Context, volume, name string, labels map[string]string, backupMode string) (*Volume, error) {
|
func (c *Client) SnapshotBackup(ctx context.Context, volume, name string, labels map[string]string, backupMode string) (*Volume, error) {
|
||||||
path := fmt.Sprintf("%s/v1/volumes/%s?action=snapshotBackup", c.baseURL, url.PathEscape(volume))
|
path := fmt.Sprintf("%s/v1/volumes/%s?action=snapshotBackup", c.baseURL, url.PathEscape(volume))
|
||||||
input := snapshotInput{
|
input := snapshotInput{
|
||||||
@ -120,6 +130,7 @@ func (c *Client) SnapshotBackup(ctx context.Context, volume, name string, labels
|
|||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetVolume fetches the named Longhorn volume.
|
||||||
func (c *Client) GetVolume(ctx context.Context, volume string) (*Volume, error) {
|
func (c *Client) GetVolume(ctx context.Context, volume string) (*Volume, error) {
|
||||||
path := fmt.Sprintf("%s/v1/volumes/%s", c.baseURL, url.PathEscape(volume))
|
path := fmt.Sprintf("%s/v1/volumes/%s", c.baseURL, url.PathEscape(volume))
|
||||||
var out Volume
|
var out Volume
|
||||||
@ -129,6 +140,7 @@ func (c *Client) GetVolume(ctx context.Context, volume string) (*Volume, error)
|
|||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateVolumeFromBackup restores a volume from a Longhorn backup URL.
|
||||||
func (c *Client) CreateVolumeFromBackup(ctx context.Context, name, size string, replicas int, backupURL string) (*Volume, error) {
|
func (c *Client) CreateVolumeFromBackup(ctx context.Context, name, size string, replicas int, backupURL string) (*Volume, error) {
|
||||||
path := fmt.Sprintf("%s/v1/volumes", c.baseURL)
|
path := fmt.Sprintf("%s/v1/volumes", c.baseURL)
|
||||||
payload := map[string]any{
|
payload := map[string]any{
|
||||||
@ -147,6 +159,7 @@ func (c *Client) CreateVolumeFromBackup(ctx context.Context, name, size string,
|
|||||||
return &out, nil
|
return &out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreatePVC asks Longhorn to create a PVC that binds to the supplied volume.
|
||||||
func (c *Client) CreatePVC(ctx context.Context, volumeName, namespace, pvcName string) error {
|
func (c *Client) CreatePVC(ctx context.Context, volumeName, namespace, pvcName string) error {
|
||||||
path := fmt.Sprintf("%s/v1/volumes/%s?action=pvcCreate", c.baseURL, url.PathEscape(volumeName))
|
path := fmt.Sprintf("%s/v1/volumes/%s?action=pvcCreate", c.baseURL, url.PathEscape(volumeName))
|
||||||
input := pvcCreateInput{
|
input := pvcCreateInput{
|
||||||
@ -156,11 +169,13 @@ func (c *Client) CreatePVC(ctx context.Context, volumeName, namespace, pvcName s
|
|||||||
return c.doJSON(ctx, http.MethodPost, path, input, nil)
|
return c.doJSON(ctx, http.MethodPost, path, input, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteVolume removes the named Longhorn volume.
|
||||||
func (c *Client) DeleteVolume(ctx context.Context, volumeName string) error {
|
func (c *Client) DeleteVolume(ctx context.Context, volumeName string) error {
|
||||||
path := fmt.Sprintf("%s/v1/volumes/%s", c.baseURL, url.PathEscape(volumeName))
|
path := fmt.Sprintf("%s/v1/volumes/%s", c.baseURL, url.PathEscape(volumeName))
|
||||||
return c.doJSON(ctx, http.MethodDelete, path, nil, nil)
|
return c.doJSON(ctx, http.MethodDelete, path, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBackupVolume fetches the backup volume metadata for the named volume.
|
||||||
func (c *Client) GetBackupVolume(ctx context.Context, volumeName string) (*BackupVolume, error) {
|
func (c *Client) GetBackupVolume(ctx context.Context, volumeName string) (*BackupVolume, error) {
|
||||||
path := fmt.Sprintf("%s/v1/backupvolumes/%s", c.baseURL, url.PathEscape(volumeName))
|
path := fmt.Sprintf("%s/v1/backupvolumes/%s", c.baseURL, url.PathEscape(volumeName))
|
||||||
var out BackupVolume
|
var out BackupVolume
|
||||||
@ -183,6 +198,7 @@ func (c *Client) GetBackupVolume(ctx context.Context, volumeName string) (*Backu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListBackups returns the backups available for a Longhorn volume.
|
||||||
func (c *Client) ListBackups(ctx context.Context, volumeName string) ([]Backup, error) {
|
func (c *Client) ListBackups(ctx context.Context, volumeName string) ([]Backup, error) {
|
||||||
backupVolume, err := c.GetBackupVolume(ctx, volumeName)
|
backupVolume, err := c.GetBackupVolume(ctx, volumeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -199,6 +215,7 @@ func (c *Client) ListBackups(ctx context.Context, volumeName string) ([]Backup,
|
|||||||
return out.Data, nil
|
return out.Data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindBackup locates either a specific backup or the most recent completed one.
|
||||||
func (c *Client) FindBackup(ctx context.Context, volumeName, snapshot string) (*Backup, error) {
|
func (c *Client) FindBackup(ctx context.Context, volumeName, snapshot string) (*Backup, error) {
|
||||||
backups, err := c.ListBackups(ctx, volumeName)
|
backups, err := c.ListBackups(ctx, volumeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -72,6 +72,7 @@ func newTelemetry() *telemetry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler exposes the telemetry renderer as an HTTP endpoint.
|
||||||
func (t *telemetry) Handler() http.Handler {
|
func (t *telemetry) Handler() http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
|
w.Header().Set("Content-Type", "text/plain; version=0.0.4; charset=utf-8")
|
||||||
@ -79,48 +80,56 @@ func (t *telemetry) Handler() http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordBackupRequest counts a backup request outcome by driver.
|
||||||
func (t *telemetry) RecordBackupRequest(driver, result string) {
|
func (t *telemetry) RecordBackupRequest(driver, result string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
incMetric(t.backupRequests, map[string]string{"driver": driver, "result": result})
|
incMetric(t.backupRequests, map[string]string{"driver": driver, "result": result})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordRestoreRequest counts a restore request outcome by driver.
|
||||||
func (t *telemetry) RecordRestoreRequest(driver, result string) {
|
func (t *telemetry) RecordRestoreRequest(driver, result string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
incMetric(t.restoreRequests, map[string]string{"driver": driver, "result": result})
|
incMetric(t.restoreRequests, map[string]string{"driver": driver, "result": result})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordPolicyBackup counts a policy-cycle backup outcome.
|
||||||
func (t *telemetry) RecordPolicyBackup(result string) {
|
func (t *telemetry) RecordPolicyBackup(result string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
incMetric(t.policyBackups, map[string]string{"result": result})
|
incMetric(t.policyBackups, map[string]string{"result": result})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordNamespaceBackupRequest counts a namespace backup outcome by driver.
|
||||||
func (t *telemetry) RecordNamespaceBackupRequest(driver, result string) {
|
func (t *telemetry) RecordNamespaceBackupRequest(driver, result string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
incMetric(t.namespaceBackupRequests, map[string]string{"driver": driver, "result": result})
|
incMetric(t.namespaceBackupRequests, map[string]string{"driver": driver, "result": result})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordNamespaceRestoreRequest counts a namespace restore outcome by driver.
|
||||||
func (t *telemetry) RecordNamespaceRestoreRequest(driver, result string) {
|
func (t *telemetry) RecordNamespaceRestoreRequest(driver, result string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
incMetric(t.namespaceRestoreReqs, map[string]string{"driver": driver, "result": result})
|
incMetric(t.namespaceRestoreReqs, map[string]string{"driver": driver, "result": result})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordAuthzDenied counts authorization denials by reason.
|
||||||
func (t *telemetry) RecordAuthzDenied(reason string) {
|
func (t *telemetry) RecordAuthzDenied(reason string) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
incMetric(t.authzDenials, map[string]string{"reason": reason})
|
incMetric(t.authzDenials, map[string]string{"reason": reason})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordInventoryFailure increments the inventory refresh failure counter.
|
||||||
func (t *telemetry) RecordInventoryFailure() {
|
func (t *telemetry) RecordInventoryFailure() {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
t.inventoryRefreshFailure++
|
t.inventoryRefreshFailure++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordInventory updates PVC backup metrics from the latest inventory snapshot.
|
||||||
func (t *telemetry) RecordInventory(inv api.InventoryResponse) {
|
func (t *telemetry) RecordInventory(inv api.InventoryResponse) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
@ -171,6 +180,7 @@ func (t *telemetry) RecordInventory(inv api.InventoryResponse) {
|
|||||||
t.inventoryRefreshTime = float64(time.Now().Unix())
|
t.inventoryRefreshTime = float64(time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordB2Usage updates the B2 telemetry gauges from the latest scan result.
|
||||||
func (t *telemetry) RecordB2Usage(usage api.B2UsageResponse) {
|
func (t *telemetry) RecordB2Usage(usage api.B2UsageResponse) {
|
||||||
t.mu.Lock()
|
t.mu.Lock()
|
||||||
defer t.mu.Unlock()
|
defer t.mu.Unlock()
|
||||||
|
|||||||
@ -40,6 +40,7 @@ type longhornClient interface {
|
|||||||
ListBackups(ctx context.Context, volumeName string) ([]longhorn.Backup, error)
|
ListBackups(ctx context.Context, volumeName string) ([]longhorn.Backup, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Server owns HTTP routing, policy state, telemetry, and the UI renderer.
|
||||||
type Server struct {
|
type Server struct {
|
||||||
cfg *config.Config
|
cfg *config.Config
|
||||||
client kubeClient
|
client kubeClient
|
||||||
@ -99,6 +100,7 @@ type resticPersistedUsageDocument struct {
|
|||||||
} `json:"jobs"`
|
} `json:"jobs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New constructs a server with fresh telemetry and in-memory policy state.
|
||||||
func New(cfg *config.Config, client *k8s.Client, lh *longhorn.Client) *Server {
|
func New(cfg *config.Config, client *k8s.Client, lh *longhorn.Client) *Server {
|
||||||
s := &Server{
|
s := &Server{
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
@ -114,6 +116,7 @@ func New(cfg *config.Config, client *k8s.Client, lh *longhorn.Client) *Server {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start launches telemetry and policy refresh loops for the active server.
|
||||||
func (s *Server) Start(ctx context.Context) {
|
func (s *Server) Start(ctx context.Context) {
|
||||||
if err := s.loadPolicies(ctx); err != nil {
|
if err := s.loadPolicies(ctx); err != nil {
|
||||||
log.Printf("policy load failed: %v", err)
|
log.Printf("policy load failed: %v", err)
|
||||||
@ -155,6 +158,7 @@ func (s *Server) Start(ctx context.Context) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handler returns the HTTP handler used by the embedded server.
|
||||||
func (s *Server) Handler() http.Handler {
|
func (s *Server) Handler() http.Handler {
|
||||||
return s.handler
|
return s.handler
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,10 +16,12 @@ type uiRenderer struct {
|
|||||||
fsys fs.FS
|
fsys fs.FS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newUIRenderer loads the embedded UI bundle using the default dist directory.
|
||||||
func newUIRenderer() *uiRenderer {
|
func newUIRenderer() *uiRenderer {
|
||||||
return newUIRendererFromFS(uiDist, "ui-dist")
|
return newUIRendererFromFS(uiDist, "ui-dist")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newUIRendererFromFS builds a renderer from an arbitrary filesystem root.
|
||||||
func newUIRendererFromFS(root fs.FS, dir string) *uiRenderer {
|
func newUIRendererFromFS(root fs.FS, dir string) *uiRenderer {
|
||||||
sub, err := fs.Sub(root, dir)
|
sub, err := fs.Sub(root, dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -28,6 +30,7 @@ func newUIRendererFromFS(root fs.FS, dir string) *uiRenderer {
|
|||||||
return &uiRenderer{fsys: sub}
|
return &uiRenderer{fsys: sub}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeIndex writes the SPA entrypoint so deep links can hydrate client-side routes.
|
||||||
func (u *uiRenderer) ServeIndex(w http.ResponseWriter, _ *http.Request) error {
|
func (u *uiRenderer) ServeIndex(w http.ResponseWriter, _ *http.Request) error {
|
||||||
if u.fsys == nil {
|
if u.fsys == nil {
|
||||||
return fmt.Errorf("UI assets are not available")
|
return fmt.Errorf("UI assets are not available")
|
||||||
@ -42,6 +45,7 @@ func (u *uiRenderer) ServeIndex(w http.ResponseWriter, _ *http.Request) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServeAsset serves hashed static assets when the request path matches a built file.
|
||||||
func (u *uiRenderer) ServeAsset(w http.ResponseWriter, r *http.Request) bool {
|
func (u *uiRenderer) ServeAsset(w http.ResponseWriter, r *http.Request) bool {
|
||||||
if u.fsys == nil {
|
if u.fsys == nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@ -1,71 +1 @@
|
|||||||
# relative_path kind name reason
|
# relative_path kind name reason
|
||||||
internal/api/types.go type BackupRequest legacy-no-doc
|
|
||||||
internal/api/types.go type BackupResponse legacy-no-doc
|
|
||||||
internal/api/types.go type RestoreTestRequest legacy-no-doc
|
|
||||||
internal/api/types.go type RestoreTestResponse legacy-no-doc
|
|
||||||
internal/api/types.go type InventoryResponse legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceInventory legacy-no-doc
|
|
||||||
internal/api/types.go type PVCInventory legacy-no-doc
|
|
||||||
internal/api/types.go type BackupListResponse legacy-no-doc
|
|
||||||
internal/api/types.go type BackupRecord legacy-no-doc
|
|
||||||
internal/api/types.go type AuthInfoResponse legacy-no-doc
|
|
||||||
internal/api/types.go type BackupPolicy legacy-no-doc
|
|
||||||
internal/api/types.go type BackupPolicyUpsertRequest legacy-no-doc
|
|
||||||
internal/api/types.go type BackupPolicyListResponse legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceBackupRequest legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceBackupResult legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceBackupResponse legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceRestoreRequest legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceRestoreResult legacy-no-doc
|
|
||||||
internal/api/types.go type NamespaceRestoreResponse legacy-no-doc
|
|
||||||
internal/api/types.go type B2UsageResponse legacy-no-doc
|
|
||||||
internal/api/types.go type B2BucketUsage legacy-no-doc
|
|
||||||
internal/config/config.go type Config legacy-no-doc
|
|
||||||
internal/config/config.go func Load legacy-no-doc
|
|
||||||
internal/k8s/client.go type Client legacy-no-doc
|
|
||||||
internal/k8s/client.go func New legacy-no-doc
|
|
||||||
internal/k8s/jobs.go type BackupJobSummary legacy-no-doc
|
|
||||||
internal/k8s/jobs.go func ListBackupJobs legacy-no-doc
|
|
||||||
internal/k8s/jobs.go func ReadBackupJobLog legacy-no-doc
|
|
||||||
internal/k8s/jobs.go func ListBackupJobsForPVC legacy-no-doc
|
|
||||||
internal/k8s/jobs.go func CreateBackupJob legacy-no-doc
|
|
||||||
internal/k8s/jobs.go func CreateRestoreJob legacy-no-doc
|
|
||||||
internal/k8s/state.go func LoadSecretData legacy-no-doc
|
|
||||||
internal/k8s/state.go func SaveSecretData legacy-no-doc
|
|
||||||
internal/k8s/volumes.go type PVCSummary legacy-no-doc
|
|
||||||
internal/k8s/volumes.go func ResolvePVCVolume legacy-no-doc
|
|
||||||
internal/k8s/volumes.go func ListBoundPVCs legacy-no-doc
|
|
||||||
internal/k8s/volumes.go func PersistentVolumeClaimExists legacy-no-doc
|
|
||||||
internal/longhorn/client.go type Client legacy-no-doc
|
|
||||||
internal/longhorn/client.go func New legacy-no-doc
|
|
||||||
internal/longhorn/client.go type APIError legacy-no-doc
|
|
||||||
internal/longhorn/client.go func Error legacy-no-doc
|
|
||||||
internal/longhorn/client.go type Volume legacy-no-doc
|
|
||||||
internal/longhorn/client.go type BackupStatus legacy-no-doc
|
|
||||||
internal/longhorn/client.go type BackupVolume legacy-no-doc
|
|
||||||
internal/longhorn/client.go type Backup legacy-no-doc
|
|
||||||
internal/longhorn/client.go func CreateSnapshot legacy-no-doc
|
|
||||||
internal/longhorn/client.go func SnapshotBackup legacy-no-doc
|
|
||||||
internal/longhorn/client.go func GetVolume legacy-no-doc
|
|
||||||
internal/longhorn/client.go func CreateVolumeFromBackup legacy-no-doc
|
|
||||||
internal/longhorn/client.go func CreatePVC legacy-no-doc
|
|
||||||
internal/longhorn/client.go func DeleteVolume legacy-no-doc
|
|
||||||
internal/longhorn/client.go func GetBackupVolume legacy-no-doc
|
|
||||||
internal/longhorn/client.go func ListBackups legacy-no-doc
|
|
||||||
internal/longhorn/client.go func FindBackup legacy-no-doc
|
|
||||||
internal/server/metrics.go func Handler legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordBackupRequest legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordRestoreRequest legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordPolicyBackup legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordNamespaceBackupRequest legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordNamespaceRestoreRequest legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordAuthzDenied legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordInventoryFailure legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordInventory legacy-no-doc
|
|
||||||
internal/server/metrics.go func RecordB2Usage legacy-no-doc
|
|
||||||
internal/server/server.go type Server legacy-no-doc
|
|
||||||
internal/server/server.go func New legacy-no-doc
|
|
||||||
internal/server/server.go func Start legacy-no-doc
|
|
||||||
internal/server/server.go func Handler legacy-no-doc
|
|
||||||
internal/server/ui_renderer.go func ServeIndex legacy-no-doc
|
|
||||||
internal/server/ui_renderer.go func ServeAsset legacy-no-doc
|
|
||||||
|
|||||||
|
Loading…
x
Reference in New Issue
Block a user