diff --git a/PROJECT b/PROJECT index 6db14132..dfaec228 100644 --- a/PROJECT +++ b/PROJECT @@ -104,4 +104,13 @@ resources: kind: MultiClusterService path: github.com/Mirantis/hmc/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: hmc.mirantis.com + group: hmc.mirantis.com + kind: Backup + path: github.com/Mirantis/hmc/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/backup_types.go b/api/v1alpha1/backup_types.go new file mode 100644 index 00000000..b41fa06b --- /dev/null +++ b/api/v1alpha1/backup_types.go @@ -0,0 +1,86 @@ +// Copyright 2024 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1alpha1 + +import ( + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// BackupSpec defines the desired state of Backup +type BackupSpec struct { + // Custom Schedule options to fetch parameters from. + // The HMC-required options might override or precede the options from the field. + CustomSchedule *velerov1.ScheduleSpec `json:"customSchedule,omitempty"` + + // +kubebuilder:default="0 */6 * * *" + + // Schedule is a Cron expression defining when to run the Backup. + // A shortcut instead of filling the .customSchedule field up. + // Default value is to backup every 6 hours. + // If both this field and the .customSchedule field + // are given, the schedule from the latter will be utilized. + Schedule string `json:"schedule"` + + // Oneshot indicates whether the Backup should not be scheduled + // and rather created immediately and only once. + // If set to true, the .schedule field is ignored. + // If set to true and the .customSchedule field is given, + // the .spec.template from the latter will be utilized, + // the HMC-required options still might override or precede the options + // from the field. + Oneshot bool `json:"oneshot,omitempty"` +} + +// TODO: optional, restrict the number of backups to be created (if oneshot is not set) + +// BackupStatus defines the observed state of Backup +type BackupStatus struct { + // Reference to the underlying Velero object being managed. + // Might be either Velero Backup or Schedule. + Reference *corev1.ObjectReference `json:"reference,omitempty"` + // Status of the Velero Schedule if .spec.oneshot is set to false. + Schedule *velerov1.ScheduleStatus `json:"schedule,omitempty"` + // Last Velero Backup that has been created. + LastBackup *velerov1.BackupStatus `json:"lastBackup,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// Backup is the Schema for the backups API +type Backup struct { + // TODO: should we make the object cluster-scoped? + + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BackupSpec `json:"spec,omitempty"` + Status BackupStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// BackupList contains a list of Backup +type BackupList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Backup `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Backup{}, &BackupList{}) +} diff --git a/api/v1alpha1/management_types.go b/api/v1alpha1/management_types.go index c09f9d8d..3d3605d3 100644 --- a/api/v1alpha1/management_types.go +++ b/api/v1alpha1/management_types.go @@ -34,6 +34,7 @@ const ( type ManagementSpec struct { // +kubebuilder:validation:MinLength=1 // +kubebuilder:validation:MaxLength=253 + // Release references the Release object. Release string `json:"release"` // Core holds the core Management components that are mandatory. @@ -42,6 +43,8 @@ type ManagementSpec struct { // Providers is the list of supported CAPI providers. Providers []Provider `json:"providers,omitempty"` + + Backup ManagementBackup `json:"backup,omitempty"` } // Core represents a structure describing core Management components. @@ -52,6 +55,18 @@ type Core struct { CAPI Component `json:"capi,omitempty"` } +// ManagementBackup enables a feature to backup HMC objects into a cloud. +type ManagementBackup struct { + // +kubebuilder:default=false + + // Flag to indicate whether the backup feature is enabled. + // If set to true, [Velero] platform will be installed. + // If set to false, creation or modification of Backups/Restores will be blocked. + // + // [Velero]: https://velero.io + Enabled bool `json:"enabled"` +} + // Component represents HMC management component type Component struct { // Config allows to provide parameters for management component customization. @@ -116,6 +131,8 @@ type ManagementStatus struct { CAPIContracts map[string]CompatibilityContracts `json:"capiContracts,omitempty"` // Components indicates the status of installed HMC components and CAPI providers. Components map[string]ComponentStatus `json:"components,omitempty"` + // BackupName is a name of the management cluster scheduled backup. + BackupName string `json:"backupName,omitempty"` // Release indicates the current Release object. Release string `json:"release,omitempty"` // AvailableProviders holds all available CAPI providers. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 48fbe853..7ba198c6 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -20,6 +20,7 @@ package v1alpha1 import ( "github.com/fluxcd/helm-controller/api/v2" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" corev1 "k8s.io/api/core/v1" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -175,6 +176,115 @@ func (in *AvailableUpgrade) DeepCopy() *AvailableUpgrade { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Backup) DeepCopyInto(out *Backup) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Backup. +func (in *Backup) DeepCopy() *Backup { + if in == nil { + return nil + } + out := new(Backup) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Backup) 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 *BackupList) DeepCopyInto(out *BackupList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Backup, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupList. +func (in *BackupList) DeepCopy() *BackupList { + if in == nil { + return nil + } + out := new(BackupList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupList) 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 *BackupSpec) DeepCopyInto(out *BackupSpec) { + *out = *in + if in.CustomSchedule != nil { + in, out := &in.CustomSchedule, &out.CustomSchedule + *out = new(velerov1.ScheduleSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupSpec. +func (in *BackupSpec) DeepCopy() *BackupSpec { + if in == nil { + return nil + } + out := new(BackupSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupStatus) DeepCopyInto(out *BackupStatus) { + *out = *in + if in.Reference != nil { + in, out := &in.Reference, &out.Reference + *out = new(corev1.ObjectReference) + **out = **in + } + if in.Schedule != nil { + in, out := &in.Schedule, &out.Schedule + *out = new(velerov1.ScheduleStatus) + (*in).DeepCopyInto(*out) + } + if in.LastBackup != nil { + in, out := &in.LastBackup, &out.LastBackup + *out = new(velerov1.BackupStatus) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupStatus. +func (in *BackupStatus) DeepCopy() *BackupStatus { + if in == nil { + return nil + } + out := new(BackupStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterTemplate) DeepCopyInto(out *ClusterTemplate) { *out = *in @@ -702,6 +812,21 @@ func (in *Management) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManagementBackup) DeepCopyInto(out *ManagementBackup) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementBackup. +func (in *ManagementBackup) DeepCopy() *ManagementBackup { + if in == nil { + return nil + } + out := new(ManagementBackup) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ManagementList) DeepCopyInto(out *ManagementList) { *out = *in @@ -749,6 +874,7 @@ func (in *ManagementSpec) DeepCopyInto(out *ManagementSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + out.Backup = in.Backup } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManagementSpec. diff --git a/cmd/main.go b/cmd/main.go index a6e1aa80..a341a5e0 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -302,6 +302,13 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "MultiClusterService") os.Exit(1) } + if err = (&controller.BackupReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "Backup") + os.Exit(1) + } // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { diff --git a/go.mod b/go.mod index 609b563c..8ac981a0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/Mirantis/hmc -go 1.22.7 +go 1.22.8 + +toolchain go1.23.3 require ( github.com/Masterminds/semver/v3 v3.3.1 @@ -19,6 +21,7 @@ require ( github.com/projectsveltos/libsveltos v0.43.0 github.com/segmentio/analytics-go v3.1.0+incompatible github.com/stretchr/testify v1.10.0 + github.com/vmware-tanzu/velero v1.15.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.3 k8s.io/api v0.31.3 diff --git a/go.sum b/go.sum index 27ce8ab2..0921cb6b 100644 --- a/go.sum +++ b/go.sum @@ -459,6 +459,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/vmware-tanzu/velero v1.15.0 h1:+S/lNSDwQqlROGWfmNuZnnabopGmco978COIt3AP09c= +github.com/vmware-tanzu/velero v1.15.0/go.mod h1:28VhzPJRBo91GBRkgs4Ird0fx2vCpepBWmhF+5Pn/WQ= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -620,7 +622,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 h1:N9BgCIAUvn/M+p4NJccWPWb3BWh88+zyL0ll9HgbEeM= diff --git a/internal/controller/backup_controller.go b/internal/controller/backup_controller.go new file mode 100644 index 00000000..3572b673 --- /dev/null +++ b/internal/controller/backup_controller.go @@ -0,0 +1,60 @@ +// Copyright 2024 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controller + +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" + + hmcmirantiscomv1alpha1 "github.com/Mirantis/hmc/api/v1alpha1" +) + +// BackupReconciler reconciles a Backup object +type BackupReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=hmc.mirantis.com.hmc.mirantis.com,resources=Backups,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=hmc.mirantis.com.hmc.mirantis.com,resources=Backups/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=hmc.mirantis.com.hmc.mirantis.com,resources=Backups/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the Backup object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile +func (*BackupReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *BackupReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&hmcmirantiscomv1alpha1.Backup{}). + Complete(r) +} diff --git a/internal/controller/backup_controller_test.go b/internal/controller/backup_controller_test.go new file mode 100644 index 00000000..5fc61af7 --- /dev/null +++ b/internal/controller/backup_controller_test.go @@ -0,0 +1,81 @@ +// Copyright 2024 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + hmcmirantiscomv1alpha1 "github.com/Mirantis/hmc/api/v1alpha1" +) + +var _ = Describe("Backup Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + backup := &hmcmirantiscomv1alpha1.Backup{} + + BeforeEach(func() { + By("creating the custom resource for the Kind Backup") + err := k8sClient.Get(ctx, typeNamespacedName, backup) + if err != nil && errors.IsNotFound(err) { + resource := &hmcmirantiscomv1alpha1.Backup{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &hmcmirantiscomv1alpha1.Backup{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance Backup") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &BackupReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/templates/provider/hmc/templates/crds/hmc.mirantis.com_backups.yaml b/templates/provider/hmc/templates/crds/hmc.mirantis.com_backups.yaml new file mode 100644 index 00000000..c0924079 --- /dev/null +++ b/templates/provider/hmc/templates/crds/hmc.mirantis.com_backups.yaml @@ -0,0 +1,807 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.3 + name: backups.hmc.mirantis.com +spec: + group: hmc.mirantis.com + names: + kind: Backup + listKind: BackupList + plural: backups + singular: backup + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: Backup is the Schema for the backups API + 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: BackupSpec defines the desired state of Backup + properties: + customSchedule: + description: |- + Custom Schedule options to fetch parameters from. + The HMC-required options might override or precede the options from the field. + properties: + paused: + description: Paused specifies whether the schedule is paused or + not + type: boolean + schedule: + description: |- + Schedule is a Cron expression defining when to run + the Backup. + type: string + skipImmediately: + description: |- + SkipImmediately specifies whether to skip backup if schedule is due immediately from `schedule.status.lastBackup` timestamp when schedule is unpaused or if schedule is new. + If true, backup will be skipped immediately when schedule is unpaused if it is due based on .Status.LastBackupTimestamp or schedule is new, and will run at next schedule time. + If false, backup will not be skipped immediately when schedule is unpaused, but will run at next schedule time. + If empty, will follow server configuration (default: false). + type: boolean + template: + description: |- + Template is the definition of the Backup to be run + on the provided schedule + properties: + csiSnapshotTimeout: + description: |- + CSISnapshotTimeout specifies the time used to wait for CSI VolumeSnapshot status turns to + ReadyToUse during creation, before returning error as timeout. + The default value is 10 minute. + type: string + datamover: + description: |- + DataMover specifies the data mover to be used by the backup. + If DataMover is "" or "velero", the built-in data mover will be used. + type: string + defaultVolumesToFsBackup: + description: |- + DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used + for all volumes by default. + nullable: true + type: boolean + defaultVolumesToRestic: + description: |- + DefaultVolumesToRestic specifies whether restic should be used to take a + backup of all pod volumes by default. + + Deprecated: this field is no longer used and will be removed entirely in future. Use DefaultVolumesToFsBackup instead. + nullable: true + type: boolean + excludedClusterScopedResources: + description: |- + ExcludedClusterScopedResources is a slice of cluster-scoped + resource type names to exclude from the backup. + If set to "*", all cluster-scoped resource types are excluded. + The default value is empty. + items: + type: string + nullable: true + type: array + excludedNamespaceScopedResources: + description: |- + ExcludedNamespaceScopedResources is a slice of namespace-scoped + resource type names to exclude from the backup. + If set to "*", all namespace-scoped resource types are excluded. + The default value is empty. + items: + type: string + nullable: true + type: array + excludedNamespaces: + description: |- + ExcludedNamespaces contains a list of namespaces that are not + included in the backup. + items: + type: string + nullable: true + type: array + excludedResources: + description: |- + ExcludedResources is a slice of resource names that are not + included in the backup. + items: + type: string + nullable: true + type: array + hooks: + description: Hooks represent custom behaviors that should + be executed at different phases of the backup. + properties: + resources: + description: Resources are hooks that should be executed + when backing up individual instances of a resource. + items: + description: |- + BackupResourceHookSpec defines one or more BackupResourceHooks that should be executed based on + the rules defined for namespaces, resources, and label selector. + properties: + excludedNamespaces: + description: ExcludedNamespaces specifies the namespaces + to which this hook spec does not apply. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources specifies the resources + to which this hook spec does not apply. + items: + type: string + nullable: true + type: array + includedNamespaces: + description: |- + IncludedNamespaces specifies the namespaces to which this hook spec applies. If empty, it applies + to all namespaces. + items: + type: string + nullable: true + type: array + includedResources: + description: |- + IncludedResources specifies the resources to which this hook spec applies. If empty, it applies + to all resources. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector, if specified, filters + the resources to which this hook spec applies. + nullable: true + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: Name is the name of this hook. + type: string + post: + description: |- + PostHooks is a list of BackupResourceHooks to execute after storing the item in the backup. + These are executed after all "additional items" from item actions are processed. + items: + description: BackupResourceHook defines a hook + for a resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and + arguments to execute. + items: + type: string + minItems: 1 + type: array + container: + description: |- + Container is the container in the pod where the command should be executed. If not specified, + the pod's first container is used. + type: string + onError: + description: OnError specifies how Velero + should behave if it encounters an error + executing this hook. + enum: + - Continue + - Fail + type: string + timeout: + description: |- + Timeout defines the maximum amount of time Velero should wait for the hook to complete before + considering the execution a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + pre: + description: |- + PreHooks is a list of BackupResourceHooks to execute prior to storing the item in the backup. + These are executed before any "additional items" from item actions are processed. + items: + description: BackupResourceHook defines a hook + for a resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and + arguments to execute. + items: + type: string + minItems: 1 + type: array + container: + description: |- + Container is the container in the pod where the command should be executed. If not specified, + the pod's first container is used. + type: string + onError: + description: OnError specifies how Velero + should behave if it encounters an error + executing this hook. + enum: + - Continue + - Fail + type: string + timeout: + description: |- + Timeout defines the maximum amount of time Velero should wait for the hook to complete before + considering the execution a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + required: + - name + type: object + nullable: true + type: array + type: object + includeClusterResources: + description: |- + IncludeClusterResources specifies whether cluster-scoped resources + should be included for consideration in the backup. + nullable: true + type: boolean + includedClusterScopedResources: + description: |- + IncludedClusterScopedResources is a slice of cluster-scoped + resource type names to include in the backup. + If set to "*", all cluster-scoped resource types are included. + The default value is empty, which means only related + cluster-scoped resources are included. + items: + type: string + nullable: true + type: array + includedNamespaceScopedResources: + description: |- + IncludedNamespaceScopedResources is a slice of namespace-scoped + resource type names to include in the backup. + The default value is "*". + items: + type: string + nullable: true + type: array + includedNamespaces: + description: |- + IncludedNamespaces is a slice of namespace names to include objects + from. If empty, all namespaces are included. + items: + type: string + nullable: true + type: array + includedResources: + description: |- + IncludedResources is a slice of resource names to include + in the backup. If empty, all resources are included. + items: + type: string + nullable: true + type: array + itemOperationTimeout: + description: |- + ItemOperationTimeout specifies the time used to wait for asynchronous BackupItemAction operations + The default value is 4 hour. + type: string + labelSelector: + description: |- + LabelSelector is a metav1.LabelSelector to filter with + when adding individual objects to the backup. If empty + or nil, all objects are included. Optional. + nullable: true + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + metadata: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + orLabelSelectors: + description: |- + OrLabelSelectors is list of metav1.LabelSelector to filter with + when adding individual objects to the backup. If multiple provided + they will be joined by the OR operator. LabelSelector as well as + OrLabelSelectors cannot co-exist in backup request, only one of them + can be used. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + nullable: true + type: array + orderedResources: + additionalProperties: + type: string + description: |- + OrderedResources specifies the backup order of resources of specific Kind. + The map key is the resource name and value is a list of object names separated by commas. + Each resource name has format "namespace/objectname". For cluster resources, simply use "objectname". + nullable: true + type: object + resourcePolicy: + description: ResourcePolicy specifies the referenced resource + policies that backup should follow + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + snapshotMoveData: + description: SnapshotMoveData specifies whether snapshot data + should be moved + nullable: true + type: boolean + snapshotVolumes: + description: |- + SnapshotVolumes specifies whether to take snapshots + of any PV's referenced in the set of objects included + in the Backup. + nullable: true + type: boolean + storageLocation: + description: StorageLocation is a string containing the name + of a BackupStorageLocation where the backup should be stored. + type: string + ttl: + description: |- + TTL is a time.Duration-parseable string describing how long + the Backup should be retained for. + type: string + uploaderConfig: + description: UploaderConfig specifies the configuration for + the uploader. + nullable: true + properties: + parallelFilesUpload: + description: ParallelFilesUpload is the number of files + parallel uploads to perform when using the uploader. + type: integer + type: object + volumeSnapshotLocations: + description: VolumeSnapshotLocations is a list containing + names of VolumeSnapshotLocations associated with this backup. + items: + type: string + type: array + type: object + useOwnerReferencesInBackup: + description: |- + UseOwnerReferencesBackup specifies whether to use + OwnerReferences on backups created by this Schedule. + nullable: true + type: boolean + required: + - schedule + - template + type: object + oneshot: + description: |- + Oneshot indicates whether the Backup should not be scheduled + and rather created immediately and only once. + If set to true, the .schedule field is ignored. + If set to true and the .customSchedule field is given, + the .spec.template from the latter will be utilized, + the HMC-required options still might override or precede the options + from the field. + type: boolean + schedule: + default: 0 */6 * * * + description: |- + Schedule is a Cron expression defining when to run the Backup. + A shortcut instead of filling the .customSchedule field up. + Default value is to backup every 6 hours. + If both this field and the .customSchedule field + are given, the schedule from the latter will be utilized. + type: string + required: + - schedule + type: object + status: + description: BackupStatus defines the observed state of Backup + properties: + lastBackup: + description: Last Velero Backup that has been created. + properties: + backupItemOperationsAttempted: + description: |- + BackupItemOperationsAttempted is the total number of attempted + async BackupItemAction operations for this backup. + type: integer + backupItemOperationsCompleted: + description: |- + BackupItemOperationsCompleted is the total number of successfully completed + async BackupItemAction operations for this backup. + type: integer + backupItemOperationsFailed: + description: |- + BackupItemOperationsFailed is the total number of async + BackupItemAction operations for this backup which ended with an error. + type: integer + completionTimestamp: + description: |- + CompletionTimestamp records the time a backup was completed. + Completion time is recorded even on failed backups. + Completion time is recorded before uploading the backup object. + The server's time is used for CompletionTimestamps + format: date-time + nullable: true + type: string + csiVolumeSnapshotsAttempted: + description: |- + CSIVolumeSnapshotsAttempted is the total number of attempted + CSI VolumeSnapshots for this backup. + type: integer + csiVolumeSnapshotsCompleted: + description: |- + CSIVolumeSnapshotsCompleted is the total number of successfully + completed CSI VolumeSnapshots for this backup. + type: integer + errors: + description: |- + Errors is a count of all error messages that were generated during + execution of the backup. The actual errors are in the backup's log + file in object storage. + type: integer + expiration: + description: Expiration is when this Backup is eligible for garbage-collection. + format: date-time + nullable: true + type: string + failureReason: + description: FailureReason is an error that caused the entire + backup to fail. + type: string + formatVersion: + description: FormatVersion is the backup format version, including + major, minor, and patch version. + type: string + hookStatus: + description: HookStatus contains information about the status + of the hooks. + nullable: true + properties: + hooksAttempted: + description: |- + HooksAttempted is the total number of attempted hooks + Specifically, HooksAttempted represents the number of hooks that failed to execute + and the number of hooks that executed successfully. + type: integer + hooksFailed: + description: HooksFailed is the total number of hooks which + ended with an error + type: integer + type: object + phase: + description: Phase is the current state of the Backup. + enum: + - New + - FailedValidation + - InProgress + - WaitingForPluginOperations + - WaitingForPluginOperationsPartiallyFailed + - Finalizing + - FinalizingPartiallyFailed + - Completed + - PartiallyFailed + - Failed + - Deleting + type: string + progress: + description: |- + Progress contains information about the backup's execution progress. Note + that this information is best-effort only -- if Velero fails to update it + during a backup for any reason, it may be inaccurate/stale. + nullable: true + properties: + itemsBackedUp: + description: |- + ItemsBackedUp is the number of items that have actually been written to the + backup tarball so far. + type: integer + totalItems: + description: |- + TotalItems is the total number of items to be backed up. This number may change + throughout the execution of the backup due to plugins that return additional related + items to back up, the velero.io/exclude-from-backup label, and various other + filters that happen as items are processed. + type: integer + type: object + startTimestamp: + description: |- + StartTimestamp records the time a backup was started. + Separate from CreationTimestamp, since that value changes + on restores. + The server's time is used for StartTimestamps + format: date-time + nullable: true + type: string + validationErrors: + description: |- + ValidationErrors is a slice of all validation errors (if + applicable). + items: + type: string + nullable: true + type: array + version: + description: |- + Version is the backup format major version. + Deprecated: Please see FormatVersion + type: integer + volumeSnapshotsAttempted: + description: |- + VolumeSnapshotsAttempted is the total number of attempted + volume snapshots for this backup. + type: integer + volumeSnapshotsCompleted: + description: |- + VolumeSnapshotsCompleted is the total number of successfully + completed volume snapshots for this backup. + type: integer + warnings: + description: |- + Warnings is a count of all warning messages that were generated during + execution of the backup. The actual warnings are in the backup's log + file in object storage. + type: integer + type: object + reference: + description: |- + Reference to the underlying Velero object being managed. + Might be either Velero Backup or Schedule. + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + schedule: + description: Status of the Velero Schedule if .spec.oneshot is set + to false. + properties: + lastBackup: + description: |- + LastBackup is the last time a Backup was run for this + Schedule schedule + format: date-time + nullable: true + type: string + lastSkipped: + description: LastSkipped is the last time a Schedule was skipped + format: date-time + nullable: true + type: string + phase: + description: Phase is the current phase of the Schedule + enum: + - New + - Enabled + - FailedValidation + type: string + validationErrors: + description: |- + ValidationErrors is a slice of all validation errors (if + applicable) + items: + type: string + type: array + type: object + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/templates/provider/hmc/templates/crds/hmc.mirantis.com_managements.yaml b/templates/provider/hmc/templates/crds/hmc.mirantis.com_managements.yaml index 7ff67958..5f9c5e6e 100644 --- a/templates/provider/hmc/templates/crds/hmc.mirantis.com_managements.yaml +++ b/templates/provider/hmc/templates/crds/hmc.mirantis.com_managements.yaml @@ -42,6 +42,22 @@ spec: spec: description: ManagementSpec defines the desired state of Management properties: + backup: + description: ManagementBackup enables a feature to backup HMC objects + into a cloud. + properties: + enabled: + default: false + description: |- + Flag to indicate whether the backup feature is enabled. + If set to true, [Velero] platform will be installed. + If set to false, creation or modification of Backups/Restores will be blocked. + + [Velero]: https://velero.io + type: boolean + required: + - enabled + type: object core: description: |- Core holds the core Management components that are mandatory. @@ -118,6 +134,10 @@ spec: items: type: string type: array + backupName: + description: BackupName is a name of the management cluster scheduled + backup. + type: string capiContracts: additionalProperties: additionalProperties: diff --git a/templates/provider/hmc/templates/rbac/user-facing/backup-editor.yaml b/templates/provider/hmc/templates/rbac/user-facing/backup-editor.yaml new file mode 100644 index 00000000..6fae7787 --- /dev/null +++ b/templates/provider/hmc/templates/rbac/user-facing/backup-editor.yaml @@ -0,0 +1,14 @@ +# permissions for end users to edit backups. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + hmc.mirantis.com/aggregate-to-global-admin: "true" + name: {{ include "hmc.fullname" . }}-backup-editor-role +rules: +- apiGroups: + - hmc.mirantis.com + resources: + - backups + - backups/status + verbs: {{ include "rbac.editorVerbs" . | nindent 6 }} diff --git a/templates/provider/hmc/templates/rbac/user-facing/backup-viewer.yaml b/templates/provider/hmc/templates/rbac/user-facing/backup-viewer.yaml new file mode 100644 index 00000000..37351134 --- /dev/null +++ b/templates/provider/hmc/templates/rbac/user-facing/backup-viewer.yaml @@ -0,0 +1,14 @@ +# permissions for end users to view backups. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + hmc.mirantis.com/aggregate-to-global-viewer: "true" + name: {{ include "hmc.fullname" . }}-backup-viewer-role +rules: +- apiGroups: + - hmc.mirantis.com + resources: + - backups + - backups/status + verbs: {{ include "rbac.viewerVerbs" . | nindent 6 }}