diff --git a/api/backups/strategy/v1alpha1/velero_types.go b/api/backups/strategy/v1alpha1/velero_types.go new file mode 100644 index 00000000..ea61d421 --- /dev/null +++ b/api/backups/strategy/v1alpha1/velero_types.go @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: Apache-2.0 +// Package v1alpha1 defines strategy.backups.cozystack.io API types. +// +// Group: strategy.backups.cozystack.io +// Version: v1alpha1 +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func init() { + SchemeBuilder.Register(func(s *runtime.Scheme) error { + s.AddKnownTypes(GroupVersion, + &Velero{}, + &VeleroList{}, + ) + return nil + }) +} + +const ( + VeleroStrategyKind = "Velero" +) + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster + +// Velero defines a backup strategy using Velero as the driver. +type Velero struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec VeleroSpec `json:"spec,omitempty"` + Status VeleroStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// VeleroList contains a list of Velero backup strategies. +type VeleroList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Velero `json:"items"` +} + +// VeleroSpec specifies the desired strategy for backing up with Velero. +type VeleroSpec struct{} + +type VeleroStatus struct { + Conditions []metav1.Condition `json:"conditions,omitempty"` +} diff --git a/api/backups/strategy/v1alpha1/zz_generated.deepcopy.go b/api/backups/strategy/v1alpha1/zz_generated.deepcopy.go index 5c300bfe..50ee19a4 100644 --- a/api/backups/strategy/v1alpha1/zz_generated.deepcopy.go +++ b/api/backups/strategy/v1alpha1/zz_generated.deepcopy.go @@ -121,3 +121,99 @@ func (in *JobStatus) DeepCopy() *JobStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Velero) DeepCopyInto(out *Velero) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Velero. +func (in *Velero) DeepCopy() *Velero { + if in == nil { + return nil + } + out := new(Velero) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Velero) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VeleroList) DeepCopyInto(out *VeleroList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Velero, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroList. +func (in *VeleroList) DeepCopy() *VeleroList { + if in == nil { + return nil + } + out := new(VeleroList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *VeleroList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VeleroSpec) DeepCopyInto(out *VeleroSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroSpec. +func (in *VeleroSpec) DeepCopy() *VeleroSpec { + if in == nil { + return nil + } + out := new(VeleroSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VeleroStatus) DeepCopyInto(out *VeleroStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VeleroStatus. +func (in *VeleroStatus) DeepCopy() *VeleroStatus { + if in == nil { + return nil + } + out := new(VeleroStatus) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/backupstrategy-controller/main.go b/cmd/backupstrategy-controller/main.go index c44f0dc8..61edc40a 100644 --- a/cmd/backupstrategy-controller/main.go +++ b/cmd/backupstrategy-controller/main.go @@ -147,7 +147,7 @@ func main() { os.Exit(1) } - if err = (&backupcontroller.BackupJobStrategyReconciler{ + if err = (&backupcontroller.BackupJobReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { diff --git a/internal/backupcontroller/backupjob_controller.go b/internal/backupcontroller/backupjob_controller.go new file mode 100644 index 00000000..c16a0ec1 --- /dev/null +++ b/internal/backupcontroller/backupjob_controller.go @@ -0,0 +1,53 @@ +package backupcontroller + +import ( + "context" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" +) + +// BackupVeleroStrategyReconciler reconciles BackupJob with a strategy referencing +// Velero.strategy.backups.cozystack.io objects. +type BackupJobReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *BackupJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + j := &backupsv1alpha1.BackupJob{} + err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, j) + if err != nil { + if apierrors.IsNotFound(err) { + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + if j.Spec.StrategyRef.APIGroup == nil || *j.Spec.StrategyRef.APIGroup != strategyv1alpha1.GroupVersion.Group { + return ctrl.Result{}, nil + } + switch j.Spec.StrategyRef.Kind { + case strategyv1alpha1.JobStrategyKind: + return r.reconcileJob(ctx, j) + case strategyv1alpha1.VeleroStrategyKind: + return r.reconcileVelero(ctx, j) + default: + return ctrl.Result{}, nil + } + +} + +// SetupWithManager registers our controller with the Manager and sets up watches. +func (r *BackupJobReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&backupsv1alpha1.BackupJob{}). + Complete(r) +} diff --git a/internal/backupcontroller/jobstrategy_controller.go b/internal/backupcontroller/jobstrategy_controller.go index 4da02e0b..e52e38f2 100644 --- a/internal/backupcontroller/jobstrategy_controller.go +++ b/internal/backupcontroller/jobstrategy_controller.go @@ -3,29 +3,13 @@ package backupcontroller import ( "context" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" ) -// BackupJobStrategyReconciler reconciles BackupJob with a strategy referencing -// Job.strategy.backups.cozystack.io objects. -type BackupJobStrategyReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -func (r *BackupJobStrategyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +func (r *BackupJobReconciler) reconcileJob(ctx context.Context, j *backupsv1alpha1.BackupJob) (ctrl.Result, error) { _ = log.FromContext(ctx) return ctrl.Result{}, nil } - -// SetupWithManager registers our controller with the Manager and sets up watches. -func (r *BackupJobStrategyReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&backupsv1alpha1.BackupJob{}). - Complete(r) -} diff --git a/internal/backupcontroller/velerostrategy_controller.go b/internal/backupcontroller/velerostrategy_controller.go new file mode 100644 index 00000000..85049b7b --- /dev/null +++ b/internal/backupcontroller/velerostrategy_controller.go @@ -0,0 +1,15 @@ +package backupcontroller + +import ( + "context" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log" + + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" +) + +func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1alpha1.BackupJob) (ctrl.Result, error) { + _ = log.FromContext(ctx) + return ctrl.Result{}, nil +} diff --git a/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml b/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml new file mode 100644 index 00000000..4433c7cb --- /dev/null +++ b/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml @@ -0,0 +1,106 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: veleroes.strategy.backups.cozystack.io +spec: + group: strategy.backups.cozystack.io + names: + kind: Velero + listKind: VeleroList + plural: veleroes + singular: velero + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Velero defines a backup strategy using Velero as the driver. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: VeleroSpec specifies the desired strategy for backing up + with Velero. + type: object + status: + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {}