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  }