package instance import ( "context" "fmt" "os" "strconv" "time" barmanBackup "github.com/cloudnative-pg/barman-cloud/pkg/backup" barmanCapabilities "github.com/cloudnative-pg/barman-cloud/pkg/capabilities" barmanCredentials "github.com/cloudnative-pg/barman-cloud/pkg/credentials" cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" "github.com/cloudnative-pg/cloudnative-pg/pkg/postgres" "github.com/cloudnative-pg/cnpg-i/pkg/backup" "github.com/cloudnative-pg/machinery/pkg/fileutils" "github.com/cloudnative-pg/machinery/pkg/log" pgTime "github.com/cloudnative-pg/machinery/pkg/postgres/time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/common" "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" ) // BackupServiceImplementation is the implementation // of the Backup CNPG capability type BackupServiceImplementation struct { BarmanObjectKey client.ObjectKey ClusterObjectKey client.ObjectKey Client client.Client InstanceName string backup.UnimplementedBackupServer } // This is an implementation of the barman executor // that always instruct the barman library to use the // "--name" option for backups. We don't support old // Barman versions that do not implement that option. type barmanCloudExecutor struct{} func (barmanCloudExecutor) ShouldForceLegacyBackup() bool { return false } // GetCapabilities implements the BackupService interface func (b BackupServiceImplementation) GetCapabilities( _ context.Context, _ *backup.BackupCapabilitiesRequest, ) (*backup.BackupCapabilitiesResult, error) { return &backup.BackupCapabilitiesResult{ Capabilities: []*backup.BackupCapability{ { Type: &backup.BackupCapability_Rpc{ Rpc: &backup.BackupCapability_RPC{ Type: backup.BackupCapability_RPC_TYPE_BACKUP, }, }, }, }, }, nil } // Backup implements the Backup interface func (b BackupServiceImplementation) Backup( ctx context.Context, _ *backup.BackupRequest, ) (*backup.BackupResult, error) { contextLogger := log.FromContext(ctx) contextLogger.Info("Starting backup") var cluster cnpgv1.Cluster if err := b.Client.Get(ctx, b.ClusterObjectKey, &cluster); err != nil { return nil, err } var objectStore barmancloudv1.ObjectStore if err := b.Client.Get(ctx, b.BarmanObjectKey, &objectStore); err != nil { contextLogger.Error(err, "while getting object store", "key", b.BarmanObjectKey) return nil, err } if err := fileutils.EnsureDirectoryExists(postgres.BackupTemporaryDirectory); err != nil { contextLogger.Error(err, "Cannot create backup temporary directory", "err", err) return nil, err } capabilities, err := barmanCapabilities.CurrentCapabilities() if err != nil { contextLogger.Error(err, "while getting capabilities") return nil, err } backupCmd := barmanBackup.NewBackupCommand( &objectStore.Spec.Configuration, capabilities, ) // We need to connect to PostgreSQL and to do that we need // PGHOST (and the like) to be available osEnvironment := os.Environ() caBundleEnvironment := common.GetRestoreCABundleEnv(&objectStore.Spec.Configuration) env, err := barmanCredentials.EnvSetBackupCloudCredentials( ctx, b.Client, objectStore.Namespace, &objectStore.Spec.Configuration, common.MergeEnv(osEnvironment, caBundleEnvironment)) if err != nil { contextLogger.Error(err, "while setting backup cloud credentials") return nil, err } serverName := cluster.Name for _, plugin := range cluster.Spec.Plugins { if plugin.IsEnabled() && plugin.Name == metadata.PluginName { if pluginServerName, ok := plugin.Parameters["serverName"]; ok { serverName = pluginServerName } } } backupName := fmt.Sprintf("backup-%v", pgTime.ToCompactISO8601(time.Now())) if err = backupCmd.Take( ctx, backupName, serverName, env, barmanCloudExecutor{}, postgres.BackupTemporaryDirectory, ); err != nil { contextLogger.Error(err, "while taking backup") return nil, err } executedBackupInfo, err := backupCmd.GetExecutedBackupInfo( ctx, backupName, serverName, barmanCloudExecutor{}, env) if err != nil { contextLogger.Error(err, "while getting executed backup info") return nil, err } contextLogger.Info("Backup completed", "backup", executedBackupInfo.ID) return &backup.BackupResult{ BackupId: executedBackupInfo.ID, BackupName: executedBackupInfo.BackupName, StartedAt: metav1.Time{Time: executedBackupInfo.BeginTime}.Unix(), StoppedAt: metav1.Time{Time: executedBackupInfo.EndTime}.Unix(), BeginWal: executedBackupInfo.BeginWal, EndWal: executedBackupInfo.EndWal, BeginLsn: executedBackupInfo.BeginLSN, EndLsn: executedBackupInfo.EndLSN, InstanceId: b.InstanceName, Online: true, Metadata: map[string]string{ "timeline": strconv.Itoa(executedBackupInfo.TimeLine), "version": metadata.Data.Version, "name": metadata.Data.Name, "displayName": metadata.Data.DisplayName, }, }, nil }