Skip to content
Open
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
8 changes: 8 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,12 @@ resources:
kind: DHCPRelay
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: networking.metal.ironcore.dev
kind: ConfigBackup
path: github.com/ironcore-dev/network-operator/api/core/v1alpha1
version: v1alpha1
version: "3"
211 changes: 211 additions & 0 deletions api/core/v1alpha1/configbackup_types.go
Comment thread
elinalin marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
// SPDX-FileCopyrightText: 2026 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: Apache-2.0

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// ConfigBackupType defines how the device should persist a configuration backup.
// +kubebuilder:validation:Enum=Local;Startup
type ConfigBackupType string

const (
// ConfigBackupTypeLocal stores the running configuration in a device-local file path.
ConfigBackupTypeLocal ConfigBackupType = "Local"
// ConfigBackupTypeStartup stores the running configuration as the device startup configuration.
ConfigBackupTypeStartup ConfigBackupType = "Startup"
)

// ConfigBackupSpec defines the desired state of ConfigBackup.
// +kubebuilder:validation:XValidation:rule="self.type != 'Startup' || (!has(self.path) || size(self.path) == 0)",message="path must be omitted for Startup backups"
// +kubebuilder:validation:XValidation:rule="self.type != 'Local' || (has(self.path) && size(self.path) > 0)",message="path must be set for Local backups"
// +kubebuilder:validation:XValidation:rule="self.type == 'Local' || !has(self.retention)",message="retention must only be specified for Local backups"
// +kubebuilder:validation:XValidation:rule="self.type == 'Local' || !has(self.storageThreshold)",message="storageThreshold must only be specified for Local backups"
type ConfigBackupSpec struct {
// DeviceRef is a reference to the Device this object belongs to. The Device object must exist in the same namespace.
// Immutable.
// +required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="DeviceRef is immutable"
DeviceRef LocalObjectReference `json:"deviceRef"`

// Schedule is an optional cron expression. If omitted, the controller performs a one-shot backup per generation.
// +optional
Schedule string `json:"schedule,omitempty"`

// Type determines whether the backup is saved as a local file or as startup-config.
// +required
Type ConfigBackupType `json:"type"`

// Path is the device-local destination path for Local backups.
// Different providers may accept different path formats, such as "bootflash:///backups/".
// +optional
Path string `json:"path,omitempty"`

// Retention configures automatic cleanup of older backups for Local backups.
// +optional
Retention *ConfigBackupRetention `json:"retention,omitempty"`

// StorageThreshold defines the minimum free space that must remain before creating a new Local backup.
// +optional
StorageThreshold *ConfigBackupStorageThreshold `json:"storageThreshold,omitempty"`
Comment thread
elinalin marked this conversation as resolved.
}

// ConfigBackupRetention defines how many historical backups are kept on the device.
type ConfigBackupRetention struct {
// KeepLast is the number of most recent backups to keep for Local backups.
// Startup backups always keep a single copy.
// +optional
// +kubebuilder:default=1
// +kubebuilder:validation:Minimum=1
KeepLast int32 `json:"keepLast,omitempty"`
}

// ConfigBackupStorageThreshold defines when the controller must stop writing additional backups.
// +kubebuilder:validation:XValidation:rule="has(self.minFreeBytes) || has(self.minFreePercent)",message="at least one threshold must be specified"
type ConfigBackupStorageThreshold struct {
// MinFreeBytes is the minimum number of free bytes required before a new backup can be written.
// +optional
// +kubebuilder:validation:Minimum=0
MinFreeBytes *int64 `json:"minFreeBytes,omitempty"`

// MinFreePercent is the minimum percentage of free storage required before a new backup can be written.
// +optional
// +kubebuilder:validation:Minimum=0
// +kubebuilder:validation:Maximum=100
MinFreePercent *int32 `json:"minFreePercent,omitempty"`
}

// ConfigBackupStatus defines the observed state of ConfigBackup.
type ConfigBackupStatus struct {
// Conditions represent the current state of the ConfigBackup resource.
// +listType=map
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`

// LastBackup contains details about the most recent successful backup operation.
// +optional
LastBackup *ConfigBackupRunStatus `json:"lastBackup,omitempty"`

// LastAttemptTime is the timestamp of the most recent backup attempt, regardless of outcome.
// +optional
LastAttemptTime *metav1.Time `json:"lastAttemptTime,omitempty"`

// NextScheduledBackup is the next time at which the controller intends to trigger a backup.
// +optional
NextScheduledBackup *metav1.Time `json:"nextScheduledBackup,omitempty"`

// TotalBackups is the number of backups currently discovered on the device for this ConfigBackup policy.
// +optional
TotalBackups int32 `json:"totalBackups,omitempty"`

// TotalSizeBytes is the total size in bytes of the discovered backups, if known.
// +optional
TotalSizeBytes *int64 `json:"totalSizeBytes,omitempty"`

// OldestBackupTimestamp is the timestamp of the oldest discovered backup, if known.
// +optional
OldestBackupTimestamp *metav1.Time `json:"oldestBackupTimestamp,omitempty"`

// Storage contains device-local storage statistics for the configured backup target, if known.
// +optional
Storage *ConfigBackupStorageStatus `json:"storage,omitempty"`
}

// ConfigBackupRunStatus contains the result of a single successful backup run.
type ConfigBackupRunStatus struct {
// Timestamp is the time at which the backup was created on the device.
// +required
Timestamp metav1.Time `json:"timestamp"`

// Duration is the duration of the backup operation.
// +required
Duration metav1.Duration `json:"duration"`

// SizeBytes is the size in bytes of the backup artifact, if known.
// +optional
SizeBytes *int64 `json:"sizeBytes,omitempty"`

// Checksum is the integrity checksum of the backup artifact, if known.
// +optional
Checksum string `json:"checksum,omitempty"`

// Location is the device-local backup location or the logical target name.
// +optional
Location string `json:"location,omitempty"`

// Generation is the metadata generation that produced this backup.
// +optional
Generation int64 `json:"generation,omitempty"`
}

// ConfigBackupStorageStatus contains storage utilization for the configured backup target.
type ConfigBackupStorageStatus struct {
// TotalBytes is the total storage capacity in bytes, if known.
// +optional
TotalBytes *int64 `json:"totalBytes,omitempty"`

// UsedBytes is the used storage in bytes, if known.
// +optional
UsedBytes *int64 `json:"usedBytes,omitempty"`

// FreeBytes is the free storage in bytes, if known.
// +optional
FreeBytes *int64 `json:"freeBytes,omitempty"`

// FreePercent is the free storage percentage, if known.
// +optional
FreePercent *int32 `json:"freePercent,omitempty"`

// ThresholdBreached indicates whether the configured threshold currently blocks new backups.
// +optional
ThresholdBreached bool `json:"thresholdBreached,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:path=configbackups
// +kubebuilder:resource:singular=configbackup
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Last Backup",type=date,JSONPath=`.status.lastBackup.timestamp`,priority=1
// +kubebuilder:printcolumn:name="Next Backup",type=date,JSONPath=`.status.nextScheduledBackup`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// ConfigBackup is the Schema for the configbackups API.
type ConfigBackup struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitzero"`

// +required
Spec ConfigBackupSpec `json:"spec"`

// +optional
Status ConfigBackupStatus `json:"status,omitzero"`
}

// GetConditions implements conditions.Getter.
func (c *ConfigBackup) GetConditions() []metav1.Condition {
return c.Status.Conditions
}

// SetConditions implements conditions.Setter.
func (c *ConfigBackup) SetConditions(conditions []metav1.Condition) {
c.Status.Conditions = conditions
}

// +kubebuilder:object:root=true

// ConfigBackupList contains a list of ConfigBackup.
type ConfigBackupList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []ConfigBackup `json:"items"`
}

func init() {
SchemeBuilder.Register(&ConfigBackup{}, &ConfigBackupList{})
}
18 changes: 18 additions & 0 deletions api/core/v1alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,24 @@ const (
DuplicateResourceOnDevice = "DuplicateResourceOnDevice"
)

// Reasons that are specific to [ConfigBackup] objects.
const (
// BackupSuccessfulReason indicates that the latest backup operation completed successfully.
BackupSuccessfulReason = "BackupSuccessful"

// BackupPendingReason indicates that the controller is waiting for the next scheduled backup time.
BackupPendingReason = "BackupPending"

// BackupFailedReason indicates that a backup operation failed.
BackupFailedReason = "BackupFailed"

// ScheduleInvalidReason indicates that the configured cron schedule is invalid.
ScheduleInvalidReason = "ScheduleInvalid"

// StorageThresholdExceededReason indicates that a configured storage safety threshold was exceeded.
StorageThresholdExceededReason = "StorageThresholdExceeded"
Comment thread
elinalin marked this conversation as resolved.
)

// Reasons that are specific to [Interface] objects.
const (
// InterfaceNotFoundReason indicates that a referenced interface was not found.
Expand Down
Loading
Loading