/
backup_controller.go
143 lines (130 loc) · 5.02 KB
/
backup_controller.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package controllers
import (
"context"
"fmt"
"github.com/go-logr/logr"
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"
mysqlv1alpha1 "github.com/blaqkube/mysql-operator/mysql-operator/api/v1alpha1"
)
// BackupReconciler reconciles a Backup object
type BackupReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Properties StatefulSetProperties
}
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch
// +kubebuilder:rbac:groups="apps",resources=statefulsets,verbs=get;list;watch
// +kubebuilder:rbac:groups="",resources=pods,verbs=get;list;watch
// +kubebuilder:rbac:groups=mysql.blaqkube.io,resources=stores,verbs=get;list;watch;create
// +kubebuilder:rbac:groups=mysql.blaqkube.io,resources=instances,verbs=get;list;watch;create
// +kubebuilder:rbac:groups=mysql.blaqkube.io,resources=backups,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=mysql.blaqkube.io,resources=backups/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=mysql.blaqkube.io,resources=backups/finalizers,verbs=update
// Reconcile implement the reconciliation loop for backups
func (r *BackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("backup", req.NamespacedName)
log.Info("Running a reconcile loop")
// Fetch the Backup instance
backup := &mysqlv1alpha1.Backup{}
if err := r.Get(ctx, req.NamespacedName, backup); err != nil {
log.Info("Unable to fetch backup from kubernetes")
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if backup.Status.Reason == mysqlv1alpha1.BackupSucceeded ||
backup.Status.Reason == mysqlv1alpha1.BackupFailed ||
backup.Status.Reason == mysqlv1alpha1.BackupNotImplemented {
return ctrl.Result{}, nil
}
bm := &BackupManager{
Context: ctx,
Reconciler: r,
TimeManager: NewTimeManager(),
}
if backup.Status.Reason == mysqlv1alpha1.BackupRunning {
b, err := bm.MonitorBackup(backup)
condition := metav1.Condition{
Type: "available",
Status: metav1.ConditionFalse,
LastTransitionTime: metav1.Now(),
}
if err != nil {
switch err {
case ErrBackupFailed:
condition.Reason = mysqlv1alpha1.BackupFailed
condition.Message = "Backup Failed, check agent logs for details"
case ErrBackupRunning:
condition.Reason = mysqlv1alpha1.BackupRunning
condition.Message = "Backup is still running"
default:
condition.Reason = mysqlv1alpha1.BackupNotImplemented
condition.Message = "This is not implemented"
}
return bm.setBackupCondition(backup, condition, b)
}
condition.Reason = mysqlv1alpha1.BackupSucceeded
condition.Message = "Backup Succeeded"
condition.Status = metav1.ConditionTrue
return bm.setBackupCondition(backup, condition, b)
}
b, err := bm.CreateBackup(backup)
if err != nil {
condition := metav1.Condition{
Type: "available",
Status: metav1.ConditionFalse,
LastTransitionTime: metav1.Now(),
}
switch err {
case ErrStoreNotFound:
condition.Reason = mysqlv1alpha1.BackupStoreAccessError
condition.Message = "Backup store not found"
case ErrStoreNotReady:
condition.Reason = mysqlv1alpha1.BackupStoreNotReady
condition.Message = "Backup store not ready"
case ErrInstanceNotFound:
condition.Reason = mysqlv1alpha1.BackupInstanceAccessError
condition.Message = "Backup instance not found"
case ErrInstanceNotReady:
condition.Reason = mysqlv1alpha1.BackupInstanceNotReady
condition.Message = "Backup instance not ready"
case ErrPodNotFound, ErrAgentAccessFailed:
condition.Reason = mysqlv1alpha1.BackupAgentNotFound
condition.Message = "Backup agent not found"
case ErrAgentRequestFailed:
condition.Reason = mysqlv1alpha1.BackupAgentFailed
condition.Message = "Backup request failed"
case ErrMissingVariable:
condition.Reason = mysqlv1alpha1.BackupMissingVariable
condition.Message = "Backup environment variable missing from store"
default:
condition.Reason = mysqlv1alpha1.BackupAgentFailed
condition.Message = fmt.Sprintf("Unexpected failure with agent: %v", err)
}
return bm.setBackupCondition(backup, condition, nil)
}
condition := metav1.Condition{
Type: "available",
Status: metav1.ConditionFalse,
LastTransitionTime: metav1.Now(),
Reason: mysqlv1alpha1.BackupRunning,
Message: fmt.Sprintf("Backup started on %s with success. Now monitoring progress", backup.Spec.Instance),
}
return bm.setBackupCondition(
backup,
condition,
&mysqlv1alpha1.BackupDetails{
Identifier: b.Identifier,
Location: b.Location,
StartTime: b.StartTime,
})
}
// SetupWithManager configure type of events the manager should watch
func (r *BackupReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&mysqlv1alpha1.Backup{}).
Complete(r)
}