github.com/cloudberrydb/gpbackup@v1.0.3-0.20240118031043-5410fd45eed6/history/history.go (about) 1 package history 2 3 import ( 4 "crypto/sha256" 5 "io" 6 "io/ioutil" 7 "os" 8 "path/filepath" 9 "sort" 10 "time" 11 12 "github.com/cloudberrydb/gp-common-go-libs/gplog" 13 "github.com/cloudberrydb/gp-common-go-libs/operating" 14 "github.com/cloudberrydb/gpbackup/utils" 15 "github.com/nightlyone/lockfile" 16 "gopkg.in/yaml.v2" 17 ) 18 19 type RestorePlanEntry struct { 20 Timestamp string 21 TableFQNs []string 22 } 23 24 const ( 25 BackupStatusSucceed = "Success" 26 BackupStatusFailed = "Failure" 27 ) 28 29 type BackupConfig struct { 30 BackupDir string 31 BackupVersion string 32 Compressed bool 33 CompressionType string 34 DatabaseName string 35 DatabaseVersion string 36 SegmentCount int 37 DataOnly bool 38 DateDeleted string 39 ExcludeRelations []string 40 ExcludeSchemaFiltered bool 41 ExcludeSchemas []string 42 ExcludeTableFiltered bool 43 IncludeRelations []string 44 IncludeSchemaFiltered bool 45 IncludeSchemas []string 46 IncludeTableFiltered bool 47 Incremental bool 48 LeafPartitionData bool 49 MetadataOnly bool 50 Plugin string 51 PluginVersion string 52 RestorePlan []RestorePlanEntry 53 SingleDataFile bool 54 Timestamp string 55 EndTime string 56 WithoutGlobals bool 57 WithStatistics bool 58 Status string 59 } 60 61 func (backup *BackupConfig) Failed() bool { 62 return backup.Status == BackupStatusFailed 63 } 64 65 func ReadConfigFile(filename string) *BackupConfig { 66 config := &BackupConfig{} 67 contents, err := ioutil.ReadFile(filename) 68 gplog.FatalOnError(err) 69 err = yaml.Unmarshal(contents, config) 70 gplog.FatalOnError(err) 71 return config 72 } 73 74 func WriteConfigFile(config *BackupConfig, configFilename string) { 75 configContents, err := yaml.Marshal(config) 76 gplog.FatalOnError(err) 77 _ = utils.WriteToFileAndMakeReadOnly(configFilename, configContents) 78 } 79 80 type History struct { 81 BackupConfigs []BackupConfig 82 } 83 84 func NewHistory(filename string) (*History, [32]byte, error) { 85 history := &History{BackupConfigs: make([]BackupConfig, 0)} 86 contents, err := operating.System.ReadFile(filename) 87 if err != nil { 88 return nil, [32]byte{}, err 89 } 90 fileHash := sha256.Sum256(contents) 91 if err != nil { 92 return nil, [32]byte{}, err 93 } 94 err = yaml.Unmarshal(contents, history) 95 if err != nil { 96 return nil, [32]byte{}, err 97 } 98 return history, fileHash, nil 99 } 100 101 func (history *History) AddBackupConfig(backupConfig *BackupConfig) { 102 history.BackupConfigs = append(history.BackupConfigs, *backupConfig) 103 sort.Slice(history.BackupConfigs, func(i, j int) bool { 104 return history.BackupConfigs[i].Timestamp > history.BackupConfigs[j].Timestamp 105 }) 106 } 107 108 func CurrentTimestamp() string { 109 return operating.System.Now().Format("20060102150405") 110 } 111 112 func WriteBackupHistory(historyFilePath string, currentBackupConfig *BackupConfig) error { 113 114 var oldHistoryLock lockfile.Lockfile 115 currentBackupConfig.EndTime = CurrentTimestamp() 116 history := &History{BackupConfigs: []BackupConfig{*currentBackupConfig}} 117 118 tmpFile, err := ioutil.TempFile(filepath.Dir(historyFilePath), "gpbackup_history*.yaml") 119 if err != nil { 120 return err 121 } 122 123 contents, err := yaml.Marshal(history) 124 if err != nil { 125 return err 126 } 127 128 _, err = tmpFile.Write(contents) 129 if err != nil { 130 return err 131 } 132 133 _, err = os.Stat(historyFilePath) 134 oldHistoryFileExists := err == nil 135 if oldHistoryFileExists { 136 oldHistoryLock = LockHistoryFile() 137 oldHistoryFile, err := os.Open(historyFilePath) 138 if err != nil { 139 return err 140 } 141 _, err = oldHistoryFile.Seek(15, 0) 142 if err != nil { 143 return err 144 } 145 _, err = io.Copy(tmpFile, oldHistoryFile) 146 if err != nil { 147 return err 148 } 149 oldHistoryFile.Close() 150 } else { 151 gplog.Verbose("No existing backups found. Creating new backup history file.") 152 } 153 err = tmpFile.Chmod(0444) 154 if err != nil { 155 return err 156 } 157 err = tmpFile.Sync() 158 if err != nil { 159 return err 160 } 161 err = tmpFile.Close() 162 if err != nil { 163 return err 164 } 165 err = os.Rename(tmpFile.Name(), historyFilePath) 166 if err != nil { 167 return err 168 } 169 if oldHistoryFileExists { 170 _ = oldHistoryLock.Unlock() 171 } 172 return nil 173 } 174 175 func (history *History) RewriteHistoryFile(historyFilePath string) error { 176 lock := LockHistoryFile() 177 defer func() { 178 _ = lock.Unlock() 179 }() 180 181 err := history.WriteToFileAndMakeReadOnly(historyFilePath) 182 return err 183 } 184 185 func LockHistoryFile() lockfile.Lockfile { 186 lock, err := lockfile.New("/tmp/gpbackup_history.yaml.lck") 187 gplog.FatalOnError(err) 188 err = lock.TryLock() 189 for err != nil { 190 time.Sleep(50 * time.Millisecond) 191 err = lock.TryLock() 192 } 193 return lock 194 } 195 196 func (history *History) WriteToFileAndMakeReadOnly(filename string) error { 197 _, err := operating.System.Stat(filename) 198 fileExists := err == nil 199 if fileExists { 200 err = operating.System.Chmod(filename, 0644) 201 if err != nil { 202 return err 203 } 204 } 205 historyFileContents, err := yaml.Marshal(history) 206 207 if err != nil { 208 return err 209 } 210 return utils.WriteToFileAndMakeReadOnly(filename, historyFileContents) 211 } 212 213 func (history *History) FindBackupConfig(timestamp string) *BackupConfig { 214 for _, backupConfig := range history.BackupConfigs { 215 if backupConfig.Timestamp == timestamp && !backupConfig.Failed() { 216 return &backupConfig 217 } 218 } 219 return nil 220 }