restoresession init

This commit is contained in:
jandre 2021-02-03 22:19:46 +01:00
parent 8b1259c8ba
commit c20da00e0d
10 changed files with 362 additions and 24 deletions

View File

@ -23,16 +23,6 @@ import (
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
type BackupState string
const (
New BackupState = "New"
Running BackupState = "Running"
Success BackupState = "Success"
Failure BackupState = "Failure"
Deleted BackupState = "Deleted"
)
type Ref struct {
Name string `json:"name"`
}
@ -46,19 +36,6 @@ type BackupSessionSpec struct {
Ref `json:"ref"`
}
type TargetStatus struct {
Name string `json:"name"`
Kind string `json:"kind"`
// +optional
BackupState `json:"state,omitempty"`
// +optional
SnapshotId string `json:"snapshotId,omitempty"`
// +optional
StartTime *metav1.Time `json:"startTime,omitempty"`
// +optional
Duration *metav1.Duration `json:"duration,omitempty"`
}
// BackupSessionStatus defines the observed state of BackupSession
type BackupSessionStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
@ -66,7 +43,7 @@ type BackupSessionStatus struct {
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
BackupState `json:"state,omitempty"`
SessionState `json:"state,omitempty"`
// +optional
StartTime *metav1.Time `json:"startTime,omitempty"`
// +optional

View File

@ -0,0 +1,71 @@
/*
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 (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
// RestoreSessionSpec defines the desired state of RestoreSession
type RestoreSessionSpec struct {
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
// Important: Run "make" to regenerate code after modifying this file
BackupSessionRef metav1.ObjectMeta `json:"backupSessionRef"`
}
// RestoreSessionStatus defines the observed state of RestoreSession
type RestoreSessionStatus struct {
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
// Important: Run "make" to regenerate code after modifying this file
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// +optional
SessionState `json:"state,omitempty"`
// +optional
StartTime *metav1.Time `json:"startTime,omitempty"`
// +optional
Targets []TargetStatus `json:"target,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:resource:shortName="rs"
// +kubebuilder:subresource:status
// RestoreSession is the Schema for the restoresessions API
type RestoreSession struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec RestoreSessionSpec `json:"spec,omitempty"`
Status RestoreSessionStatus `json:"status,omitempty"`
}
// +kubebuilder:object:root=true
// RestoreSessionList contains a list of RestoreSession
type RestoreSessionList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []RestoreSession `json:"items"`
}
func init() {
SchemeBuilder.Register(&RestoreSession{}, &RestoreSessionList{})
}

View File

@ -458,6 +458,107 @@ func (in *Repository) DeepCopy() *Repository {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RestoreSession) DeepCopyInto(out *RestoreSession) {
*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 RestoreSession.
func (in *RestoreSession) DeepCopy() *RestoreSession {
if in == nil {
return nil
}
out := new(RestoreSession)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *RestoreSession) 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 *RestoreSessionList) DeepCopyInto(out *RestoreSessionList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]RestoreSession, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreSessionList.
func (in *RestoreSessionList) DeepCopy() *RestoreSessionList {
if in == nil {
return nil
}
out := new(RestoreSessionList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *RestoreSessionList) 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 *RestoreSessionSpec) DeepCopyInto(out *RestoreSessionSpec) {
*out = *in
in.BackupSessionRef.DeepCopyInto(&out.BackupSessionRef)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreSessionSpec.
func (in *RestoreSessionSpec) DeepCopy() *RestoreSessionSpec {
if in == nil {
return nil
}
out := new(RestoreSessionSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RestoreSessionStatus) DeepCopyInto(out *RestoreSessionStatus) {
*out = *in
if in.StartTime != nil {
in, out := &in.StartTime, &out.StartTime
*out = (*in).DeepCopy()
}
if in.Targets != nil {
in, out := &in.Targets, &out.Targets
*out = make([]TargetStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RestoreSessionStatus.
func (in *RestoreSessionStatus) DeepCopy() *RestoreSessionStatus {
if in == nil {
return nil
}
out := new(RestoreSessionStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *S3) DeepCopyInto(out *S3) {
*out = *in

View File

@ -6,6 +6,7 @@ resources:
- bases/formol.desmojim.fr_backupconfigurations.yaml
- bases/formol.desmojim.fr_backupsessions.yaml
- bases/formol.desmojim.fr_repoes.yaml
- bases/formol.desmojim.fr_restoresessions.yaml
# +kubebuilder:scaffold:crdkustomizeresource
patchesStrategicMerge:
@ -16,6 +17,7 @@ patchesStrategicMerge:
#- patches/webhook_in_backupconfigurations.yaml
- patches/webhook_in_backupsessions.yaml
#- patches/webhook_in_repoes.yaml
#- patches/webhook_in_restoresessions.yaml
# +kubebuilder:scaffold:crdkustomizewebhookpatch
# [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
@ -24,6 +26,7 @@ patchesStrategicMerge:
- patches/cainjection_in_backupconfigurations.yaml
- patches/cainjection_in_backupsessions.yaml
#- patches/cainjection_in_repoes.yaml
#- patches/cainjection_in_restoresessions.yaml
# +kubebuilder:scaffold:crdkustomizecainjectionpatch
# the following config is for teaching kustomize how to do kustomization for CRDs.

View File

@ -0,0 +1,8 @@
# The following patch adds a directive for certmanager to inject CA into the CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
name: restoresessions.formol.desmojim.fr.desmojim.fr

View File

@ -0,0 +1,17 @@
# The following patch enables conversion webhook for CRD
# CRD conversion requires k8s 1.13 or later.
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: restoresessions.formol.desmojim.fr.desmojim.fr
spec:
conversion:
strategy: Webhook
webhookClientConfig:
# this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank,
# but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager)
caBundle: Cg==
service:
namespace: system
name: webhook-service
path: /convert

View File

@ -0,0 +1,7 @@
apiVersion: formol.desmojim.fr.desmojim.fr/v1alpha1
kind: RestoreSession
metadata:
name: restoresession-sample
spec:
# Add fields here
foo: bar

View File

@ -0,0 +1,140 @@
/*
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 controllers
import (
"context"
"time"
"github.com/go-logr/logr"
batchv1 "k8s.io/api/batch/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
)
// RestoreSessionReconciler reconciles a RestoreSession object
type RestoreSessionReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
RestoreSession *formolv1alpha1.RestoreSession
BackupSession *formolv1alpha1.BackupSession
BackupConf *formolv1alpha1.BackupConfiguration
}
func (r *RestoreSessionReconciler) CreateRestoreJob(target formolv1alpha1.Target) error {
return nil
}
func (r *RestoreSessionReconciler) StatusUpdate() error {
log := r.Log.WithValues("statusupdate", r.RestoreSession.Name)
ctx := context.Background()
startNextTask := func() (*formolv1alpha1.TargetStatus, error) {
nextTarget := len(r.RestoreSession.Status.Targets)
if nextTarget < len(r.BackupConf.Spec.Targets) {
target := r.BackupConf.Spec.Targets[len(r.BackupConf.Spec.Targets)-1-nextTarget]
targetStatus := formolv1alpha1.TargetStatus{
Name: target.Name,
Kind: target.Kind,
SessionState: formolv1alpha1.New,
}
r.RestoreSession.Status.Targets = append(r.RestoreSession.Status.Targets, targetStatus)
switch target.Kind {
case "Task":
if err := r.CreateRestoreJob(target); err != nil {
log.V(0).Info("unable to create restore task", "task", target)
targetStatus.SessionState = formolv1alpha1.Failure
return nil, err
}
}
return &targetStatus, nil
} else {
return nil, nil
}
}
var ret error
switch r.RestoreSession.Status.SessionState {
case formolv1alpha1.New:
r.RestoreSession.Status.SessionState = formolv1alpha1.Running
targetStatus, err := startNextTask()
if err != nil {
return err
}
log.V(0).Info("New restore. Start the first task", "task", targetStatus.Name)
}
if ret = r.Status().Update(ctx, r.RestoreSession); ret != nil {
log.Error(ret, "unable to update restoresession")
}
return ret
}
// +kubebuilder:rbac:groups=formol.desmojim.fr,resources=restoresessions,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=formol.desmojim.fr,resources=restoresessions/status,verbs=get;update;patch
func (r *RestoreSessionReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
time.Sleep(100 * time.Millisecond)
ctx := context.Background()
log := r.Log.WithValues("restoresession", req.NamespacedName)
r.RestoreSession = &formolv1alpha1.RestoreSession{}
if err := r.Get(ctx, req.NamespacedName, r.RestoreSession); err != nil {
log.Error(err, "unable to get restoresession")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if r.RestoreSession.Status.ObservedGeneration == r.RestoreSession.ObjectMeta.Generation {
// status update
log.V(0).Info("status update")
return ctrl.Result{}, r.StatusUpdate()
}
r.RestoreSession.Status.ObservedGeneration = r.RestoreSession.ObjectMeta.Generation
r.RestoreSession.Status.SessionState = formolv1alpha1.New
r.RestoreSession.Status.StartTime = &metav1.Time{Time: time.Now()}
reschedule := ctrl.Result{RequeueAfter: 5 * time.Second}
r.BackupSession = &formolv1alpha1.BackupSession{}
if err := r.Get(ctx, client.ObjectKey{
Namespace: r.RestoreSession.Spec.BackupSessionRef.Namespace,
Name: r.RestoreSession.Spec.BackupSessionRef.Name}, r.BackupSession); err != nil {
log.Error(err, "unable to get backupsession")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
r.BackupConf = &formolv1alpha1.BackupConfiguration{}
if err := r.Get(ctx, client.ObjectKey{
Namespace: r.BackupSession.Namespace,
Name: r.BackupSession.Spec.Ref.Name}, r.BackupConf); err != nil {
log.Error(err, "unable to get backupConfiguration")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if err := r.Status().Update(ctx, r.RestoreSession); err != nil {
log.Error(err, "unable to update restoresession")
return ctrl.Result{}, err
}
return reschedule, nil
}
func (r *RestoreSessionReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&formolv1alpha1.RestoreSession{}).
Owns(&batchv1.Job{}).
Complete(r)
}

View File

@ -30,6 +30,7 @@ import (
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
formoldesmojimfrv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
// +kubebuilder:scaffold:imports
)
@ -68,6 +69,9 @@ var _ = BeforeSuite(func(done Done) {
err = formolv1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = formoldesmojimfrv1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
// +kubebuilder:scaffold:scheme
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})

10
main.go
View File

@ -26,6 +26,7 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
formoldesmojimfrv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
formolv1alpha1 "github.com/desmo999r/formol/api/v1alpha1"
"github.com/desmo999r/formol/controllers"
// +kubebuilder:scaffold:imports
@ -40,6 +41,7 @@ func init() {
_ = clientgoscheme.AddToScheme(scheme)
_ = formolv1alpha1.AddToScheme(scheme)
_ = formoldesmojimfrv1alpha1.AddToScheme(scheme)
// +kubebuilder:scaffold:scheme
}
@ -88,6 +90,14 @@ func main() {
os.Exit(1)
}
}
if err = (&controllers.RestoreSessionReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("RestoreSession"),
Scheme: mgr.GetScheme(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "RestoreSession")
os.Exit(1)
}
// +kubebuilder:scaffold:builder
setupLog.Info("starting manager")