forked from kyma-project/kyma
/
recorded_executor.go
112 lines (92 loc) · 3.32 KB
/
recorded_executor.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
package backup
import (
"github.com/pkg/errors"
coreTypes "k8s.io/api/core/v1"
apiErrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
const absBackupCfgMapKeyName = "abs-backup-file-path-from-last-success"
//go:generate mockery -name=configMapClient -output=automock -outpkg=automock -case=underscore
//go:generate mockery -name=singleBackupExecutor -output=automock -outpkg=automock -case=underscore
type (
configMapClient interface {
Create(*coreTypes.ConfigMap) (*coreTypes.ConfigMap, error)
Update(*coreTypes.ConfigMap) (*coreTypes.ConfigMap, error)
Get(name string, options metav1.GetOptions) (*coreTypes.ConfigMap, error)
}
singleBackupExecutor interface {
SingleBackup(stopCh <-chan struct{}, blobPrefix string) (*SingleBackupOutput, error)
}
)
// RecordedExecutor saves in k8s ConfigMap path to the backup file when backup process ends without errors.
type RecordedExecutor struct {
underlying singleBackupExecutor
cfgMapCli configMapClient
cfgMapName string
}
// NewRecordedExecutor returns new instance of RecordedExecutor
func NewRecordedExecutor(underlying singleBackupExecutor, cfgMapName string, cfgMapCli configMapClient) *RecordedExecutor {
return &RecordedExecutor{
underlying: underlying,
cfgMapCli: cfgMapCli,
cfgMapName: cfgMapName,
}
}
// SingleBackup executes underlying SingleBackup method and if process ends without errors then
// path to the backup file is save in k8s ConfigMap
// BEWARE: It can return own error when it cannot save information to ConfigMap.
func (r *RecordedExecutor) SingleBackup(stopCh <-chan struct{}, blobPrefix string) (*SingleBackupOutput, error) {
out, err := r.underlying.SingleBackup(stopCh, blobPrefix)
if err != nil {
return out, err
}
if err := r.upsertABSBackupPathToCfgMap(out.ABSBackupPath); err != nil {
return out, errors.Wrap(err, "while upserting the newest ABS backup path to config map")
}
return out, nil
}
func (r *RecordedExecutor) upsertABSBackupPathToCfgMap(path string) error {
err := r.createCfgMapWithABSBackupPath(path)
switch {
case err == nil:
case apiErrors.IsAlreadyExists(err):
r.updateCfgMapWithABSBackupPath(path)
default:
return errors.Wrapf(err, "while creating %s config map", r.cfgMapName)
}
return nil
}
func (r *RecordedExecutor) createCfgMapWithABSBackupPath(path string) error {
_, err := r.cfgMapCli.Create(&coreTypes.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: r.cfgMapName,
},
Data: map[string]string{
absBackupCfgMapKeyName: path,
},
})
return err
}
func (r *RecordedExecutor) updateCfgMapWithABSBackupPath(path string) error {
oldCfg, err := r.cfgMapCli.Get(r.cfgMapName, metav1.GetOptions{})
if err != nil {
return errors.Wrapf(err, "while getting %s config map", r.cfgMapName)
}
cfgCopy := oldCfg.DeepCopy()
cfgCopy.Data = r.ensureMapIsInitiated(cfgCopy.Data)
cfgCopy.Data[absBackupCfgMapKeyName] = path
if _, err := r.cfgMapCli.Update(cfgCopy); err != nil {
return errors.Wrapf(err, "while updating %s config map", r.cfgMapName)
}
return nil
}
// ensureMapIsInitiated ensures that given map is initiated.
// - returns given map if it's already allocated
// - otherwise returns empty map
func (r *RecordedExecutor) ensureMapIsInitiated(m map[string]string) map[string]string {
if m == nil {
empty := make(map[string]string)
return empty
}
return m
}