/* Lists all the backup files at the desired location. The {where} should be "local" or "remote". The "local" option finds all the backup files on the local filesystem. The "remote" option will display all of the backup files on the remote storage, such as S3. Backups are returned in json format. The request must be a GET. Form values: "fmt", "filename" is the only supported value at present. Defaults to "filename" if absent or if left blank "dbname": the name of the database for which to query backups of. If left blank, returns the backups for all databases. "globals": set to the string "true" if files with the suffix ".globals" should appear in the output.*/ func BackupListHandler(w http.ResponseWriter, request *http.Request) { printFormat := "filename" if request.FormValue("fmt") != "" { printFormat = request.FormValue("fmt") } showGlobals := false if request.FormValue("globals") == "true" { showGlobals = true } backupList := []backup.DatabaseBackupList{} // If the dbname wasn't specified of if the field is blank, then return the backups of // all databases. dbname := request.FormValue("dbname") // Where are we getting the files from? vars := mux.Vars(request) var err error switch vars["where"] { case "local": backupList, err = backup.LocalListing(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } case "remote": rdpgs3.ReinitializeS3Credentials() if !rdpgs3.Configured { w.WriteHeader(http.StatusGone) w.Write([]byte("Remote storage has not been configured")) } backupList, err = backup.RemoteListing(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } default: w.WriteHeader(http.StatusNotFound) return } switch printFormat { case "filename": w.Header().Set("Content-Type", "application/json") w.Write([]byte(formatFilename(backupList))) //case "timestamp": TODO default: log.Debug(fmt.Sprintf(`api.BackupListHandler() Requested unsupported format.`)) w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Unsupported Format Requested")) } }
// Returns a listing of backup files on the remote storage for the specified // database. // @param dbname // The name of the database on this cluster to return backups for. If // the dbname given is an empty string, the backups for all databases // this cluster will be returned. // @return // A slice of DatabaseBackupList objects where each "Database" entry // contains the name of the database to which the backups refer, and // the Backups slice within contains entries with information about // each backup for that database. // The return slice is sorted ascendingly by database name, and the backups // within are sorted by their filenames (aka timestamps). func RemoteListing(dbname string, showGlobals bool) (backupList []DatabaseBackupList, err error) { log.Trace("In admin.backup.handleRemoteListing") err = rdpgs3.ReinitializeS3Credentials() if err != nil { log.Error("admin.backup.handleRemoteListing ! Failure in ReinitializeS3Credentials.") return } var ourPrefix string //If no database specified, look for all databases if dbname == "" { ourPrefix = strings.TrimPrefix(localBackupLocation(), "/") } else { ourPrefix = strings.TrimPrefix(localBackupLocation(), "/") + dbname + "/" } done := false input := &s3.ListObjectsInput{ Bucket: &rdpgs3.BucketName, Prefix: &ourPrefix, } backupList = []DatabaseBackupList{} for !done { output, err := remoteHelper(&backupList, input, showGlobals) if err != nil { log.Error(fmt.Sprintf("utils/backup.RemoteListing ! %s", err.Error())) return nil, err } done = !(*output.IsTruncated) if !done { input = &s3.ListObjectsInput{ Bucket: &rdpgs3.BucketName, Prefix: &ourPrefix, Marker: output.Contents[len(output.Contents)-1].Key, } } } sortDBList(&backupList) return }
func BackupDiffHandler(w http.ResponseWriter, request *http.Request) { //Default printing format to print pretty timestamps. So pretty. printFormat := "filename" if request.Method == "POST" && request.FormValue("fmt") != "" { printFormat = request.FormValue("fmt") } showGlobals := false if request.FormValue("globals") == "true" { showGlobals = true } // If the dbname wasn't specified of if the field is blank, then return the backups of // all databases. dbname := request.FormValue("dbname") // Where are we getting the files from? vars := mux.Vars(request) //Do we actually have s3? rdpgs3.ReinitializeS3Credentials() if !rdpgs3.Configured { w.WriteHeader(http.StatusGone) w.Write([]byte("Remote storage has not been configured")) } var localDiff, remoteDiff []backup.DatabaseBackupList var err error //The "both" path uses a different function if vars["where"] != "both" { localDiff, remoteDiff, err = backup.Diff(dbname, showGlobals) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } } //====Begin inner function formatter := func(localList []backup.DatabaseBackupList, remoteList []backup.DatabaseBackupList, printFormat string) (outputString string, err error) { if localList == nil && remoteList == nil { errorMessage := fmt.Sprintf("api.BackupDiffHandler ! Can't handle formatting without input") log.Error(errorMessage) return "", errors.New(errorMessage) } switch printFormat { case "filename": if localList == nil { // only/remote outputString = formatFilename(remoteList) } else if remoteList == nil { //only/local outputString = formatFilename(localList) } else { //only/both outputString = `{ "local": ` + formatFilename(localList) + `, "remote": ` + formatFilename(remoteList) + ` }` } default: log.Debug(fmt.Sprintf(`api.BackupDiffHandler() Requested unsupported format.`)) return "", errors.New(fmt.Sprintf("Unsupported Printing Format: %s. Valid format is 'filename'", printFormat)) } return } //====End inner function var output string switch vars["where"] { case "local": output, err = formatter(localDiff, nil, printFormat) case "remote": output, err = formatter(nil, remoteDiff, printFormat) case "diff": output, err = formatter(localDiff, remoteDiff, printFormat) case "both": bothList, err := backup.Both(dbname, false) if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte(err.Error())) return } output, err = formatter(bothList, nil, printFormat) default: w.WriteHeader(http.StatusNotFound) } if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) return } w.Header().Set("Content-Type", "application/json") w.Write([]byte(output)) }