-
Notifications
You must be signed in to change notification settings - Fork 0
/
reverter.go
169 lines (130 loc) · 3.87 KB
/
reverter.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package a2conf
import (
"fmt"
"io/ioutil"
"os"
"github.com/r2dtools/a2conf/apache"
"github.com/r2dtools/a2conf/logger"
"github.com/unknwon/com"
)
type rollbackError struct {
err error
}
func (re *rollbackError) Error() string {
return fmt.Sprintf("rollback failed: %v", re.err)
}
// Reverter reverts change back for configuration files of virtual hosts
type Reverter struct {
filesToDelete []string
filesToRestore map[string]string
configsToDisable []string
apacheSite *apache.Site
logger logger.Logger
}
// SetLogger sets logger
func (r *Reverter) SetLogger(logger logger.Logger) {
r.logger = logger
}
// AddFileToDeletion marks file to delete on rollback
func (r *Reverter) AddFileToDeletion(filePath string) {
r.filesToDelete = append(r.filesToDelete, filePath)
}
// BackupFiles makes files backups
func (r *Reverter) BackupFiles(filePaths []string) error {
for _, filePath := range filePaths {
if err := r.BackupFile(filePath); err != nil {
return fmt.Errorf("could not make file '%s' backup: %v", filePath, err)
}
}
return nil
}
// BackupFile makes file backup. The file content will be restored on rollback.
func (r *Reverter) BackupFile(filePath string) error {
bFilePath := r.getBackupFilePath(filePath)
if _, ok := r.filesToRestore[filePath]; ok {
r.logger.Debug(fmt.Sprintf("file '%s' is already backed up.", filePath))
return nil
}
// Skip file backup if it should be removed
if com.IsSliceContainsStr(r.filesToDelete, filePath) {
r.logger.Debug(fmt.Sprintf("file '%s' will be removed on rollback. Skip its backup.", filePath))
return nil
}
content, err := ioutil.ReadFile(filePath)
if err != nil {
return err
}
err = ioutil.WriteFile(bFilePath, content, 0644)
if err != nil {
return err
}
if r.filesToRestore == nil {
r.filesToRestore = make(map[string]string)
}
r.filesToRestore[filePath] = bFilePath
return nil
}
// AddSiteConfigToDisable marks apache site config as needed to be disabled on rollback
func (r *Reverter) AddSiteConfigToDisable(siteConfigName string) {
r.configsToDisable = append(r.configsToDisable, siteConfigName)
}
// Rollback rollback all changes
func (r *Reverter) Rollback() error {
// Disable all enabled before sites
// Note: only hosts enabled via a2ensite utility are in this slice
for _, siteConfigToDisable := range r.configsToDisable {
if err := r.apacheSite.Disable(siteConfigToDisable); err != nil {
return &rollbackError{err}
}
}
// remove created files
for _, fileToDelete := range r.filesToDelete {
_, err := os.Stat(fileToDelete)
if os.IsNotExist(err) {
r.logger.Debug(fmt.Sprintf("file '%s' does not exist. Skip its deletion.", fileToDelete))
continue
}
if err != nil {
return &rollbackError{err}
}
err = os.Remove(fileToDelete)
if err != nil {
return &rollbackError{err}
}
}
if r.filesToRestore == nil {
return nil
}
// restore the content of backed up files
for originFilePath, bFilePath := range r.filesToRestore {
bContent, err := ioutil.ReadFile(bFilePath)
if err != nil {
return &rollbackError{err}
}
err = ioutil.WriteFile(originFilePath, bContent, 0644)
if err != nil {
return &rollbackError{err}
}
if err := os.Remove(bFilePath); err != nil {
r.logger.Error(fmt.Sprintf("could not remove file '%s' on reverter rollback: %v", bFilePath, err))
}
delete(r.filesToRestore, originFilePath)
}
return nil
}
// Commit commits changes. All *.back files will be removed.
func (r *Reverter) Commit() error {
for filePath, bFilePath := range r.filesToRestore {
if com.IsFile(bFilePath) {
if err := os.Remove(bFilePath); err != nil {
r.logger.Error(fmt.Sprintf("could not remove file '%s' on reverter commit: %v", bFilePath, err))
}
}
delete(r.filesToRestore, filePath)
}
r.filesToDelete = nil
return nil
}
func (r *Reverter) getBackupFilePath(filePath string) string {
return filePath + ".back"
}