diff --git a/cmd/instance/main.go b/cmd/instance/main.go index 4eed69c..6808306 100644 --- a/cmd/instance/main.go +++ b/cmd/instance/main.go @@ -62,12 +62,13 @@ func main() { } if err := mgr.Add(&instance.CNPGI{ - Client: mgr.GetClient(), + Client: mgr.GetClient(), + Recorder: mgr.GetEventRecorderFor("cnpg-i"), ClusterObjectKey: client.ObjectKey{ Namespace: namespace, Name: clusterName, }, - BarmanObjectKey: client.ObjectKey{ + WALConfigurationKey: client.ObjectKey{ Namespace: namespace, Name: boName, }, diff --git a/go.mod b/go.mod index 12cb241..1cec2c9 100644 --- a/go.mod +++ b/go.mod @@ -4,9 +4,9 @@ go 1.22.0 require ( github.com/cloudnative-pg/barman-cloud v0.0.0-20240924124724-92831d48562a - github.com/cloudnative-pg/cloudnative-pg v1.24.0 + github.com/cloudnative-pg/cloudnative-pg v1.24.1-0.20241001084914-829808376542 github.com/cloudnative-pg/cnpg-i v0.0.0-20240924030516-c5636170f248 - github.com/cloudnative-pg/cnpg-i-machinery v0.0.0-20240926153929-09e2c6f6689b + github.com/cloudnative-pg/cnpg-i-machinery v0.0.0-20241001135556-db88a95a39eb github.com/cloudnative-pg/machinery v0.0.0-20241001075747-34c8797af80f github.com/onsi/ginkgo/v2 v2.20.2 github.com/onsi/gomega v1.34.2 @@ -114,12 +114,12 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.31.0 // indirect - k8s.io/apiserver v0.31.0 // indirect - k8s.io/component-base v0.31.0 // indirect + k8s.io/apiextensions-apiserver v0.31.1 // indirect + k8s.io/apiserver v0.31.1 // indirect + k8s.io/component-base v0.31.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 // indirect - k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 // indirect + k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect diff --git a/go.sum b/go.sum index e182c0a..30344ab 100644 --- a/go.sum +++ b/go.sum @@ -16,12 +16,12 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cloudnative-pg/barman-cloud v0.0.0-20240924124724-92831d48562a h1:0v1ML9Eibfq3helbT9GtU0EstqFtG91k/MPO9azY5ME= github.com/cloudnative-pg/barman-cloud v0.0.0-20240924124724-92831d48562a/go.mod h1:Jm0tOp5oB7utpt8wz6RfSv31h1mThOtffjfyxVupriE= -github.com/cloudnative-pg/cloudnative-pg v1.24.0 h1:lY9IP/Gnh5ogNcFGoPnbD2eOiHdSdbdEp0PaUdOAMDQ= -github.com/cloudnative-pg/cloudnative-pg v1.24.0/go.mod h1:n7Qqax6os+x3+7Qu/GojUUeKlL1ELGV63dcO/tzIfB4= +github.com/cloudnative-pg/cloudnative-pg v1.24.1-0.20241001084914-829808376542 h1:IXf5lj+m4CBqzckQ9L/9hJ01JUoVw5N0FuPex0sVdVo= +github.com/cloudnative-pg/cloudnative-pg v1.24.1-0.20241001084914-829808376542/go.mod h1:L8M+kTGpz/eaLZj46+4sARvO/vDYlo/m1xOigI/ghBA= github.com/cloudnative-pg/cnpg-i v0.0.0-20240924030516-c5636170f248 h1:eUGzb7YNjVLilwhgZoe4hDOO70fci3oqb/ZzQFbN3xg= github.com/cloudnative-pg/cnpg-i v0.0.0-20240924030516-c5636170f248/go.mod h1:K9/4eAT3rh2bKIWyujoN8BIPRXa4d1Ls+eBY8PE8y6w= -github.com/cloudnative-pg/cnpg-i-machinery v0.0.0-20240926153929-09e2c6f6689b h1:T9G61tzOBoB5yvlDPULUoiUl6QxPmti3pkNFhQYGGQY= -github.com/cloudnative-pg/cnpg-i-machinery v0.0.0-20240926153929-09e2c6f6689b/go.mod h1:dV1+nE7jWENm/fcnKBeKsaScMz685rQPbPCCDydJgsY= +github.com/cloudnative-pg/cnpg-i-machinery v0.0.0-20241001135556-db88a95a39eb h1:fDZ4mOSwgEUKaXJI3a37Bw0bPa8bl3DqZ9nPu/6CiJ4= +github.com/cloudnative-pg/cnpg-i-machinery v0.0.0-20241001135556-db88a95a39eb/go.mod h1:mHEVy/Guae+rij1qlgwHg+lyFKDX48qjTL4lAqE7OJs= github.com/cloudnative-pg/machinery v0.0.0-20241001075747-34c8797af80f h1:RgPmQJkuSu3eTdfd4T2K95RYQi57LHB2+Jfsu/faKOM= github.com/cloudnative-pg/machinery v0.0.0-20241001075747-34c8797af80f/go.mod h1:bWp1Es5zlxElg4Z/c5f0RKOkDcyNvDHdYIvNcPQU4WM= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -286,22 +286,22 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= -k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= -k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= -k8s.io/apiserver v0.31.0 h1:p+2dgJjy+bk+B1Csz+mc2wl5gHwvNkC9QJV+w55LVrY= -k8s.io/apiserver v0.31.0/go.mod h1:KI9ox5Yu902iBnnyMmy7ajonhKnkeZYJhTZ/YI+WEMk= +k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= +k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= -k8s.io/component-base v0.31.0 h1:/KIzGM5EvPNQcYgwq5NwoQBaOlVFrghoVGr8lG6vNRs= -k8s.io/component-base v0.31.0/go.mod h1:TYVuzI1QmN4L5ItVdMSXKvH7/DtvIuas5/mm8YT3rTo= +k8s.io/component-base v0.31.1 h1:UpOepcrX3rQ3ab5NB6g5iP0tvsgJWzxTyAo20sgYSy8= +k8s.io/component-base v0.31.1/go.mod h1:WGeaw7t/kTsqpVTaCoVEtillbqAhF2/JgvO0LDOMa0w= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38 h1:1dWzkmJrrprYvjGwh9kEUxmcUV/CtNU8QM7h1FLWQOo= k8s.io/kube-openapi v0.0.0-20240903163716-9e1beecbcb38/go.mod h1:coRQXBK9NxO98XUv3ZD6AK3xzHCxV6+b7lrquKwaKzA= -k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3 h1:b2FmK8YH+QEwq/Sy2uAEhmqL5nPfGYbJOcaqjeYYZoA= -k8s.io/utils v0.0.0-20240902221715-702e33fdd3c3/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI= +k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3 h1:2770sDpzrjjsAtVhSeUFseziht227YAWYHLGNM8QPwY= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.30.3/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= diff --git a/internal/cnpgi/instance/backup.go b/internal/cnpgi/instance/backup.go index 3521697..d76c5b9 100644 --- a/internal/cnpgi/instance/backup.go +++ b/internal/cnpgi/instance/backup.go @@ -2,13 +2,30 @@ package instance import ( "context" + "os" + 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/conditions" + "github.com/cloudnative-pg/cloudnative-pg/pkg/postgres" + "github.com/cloudnative-pg/cloudnative-pg/pkg/resources" + "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/decoder" "github.com/cloudnative-pg/cnpg-i/pkg/backup" + "github.com/cloudnative-pg/machinery/pkg/fileutils" + "github.com/cloudnative-pg/machinery/pkg/log" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" ) // BackupServiceImplementation is the implementation // of the Backup CNPG capability type BackupServiceImplementation struct { + Client client.Client + Recorder record.EventRecorder + InstanceName string backup.UnimplementedBackupServer } @@ -30,7 +47,98 @@ func (b BackupServiceImplementation) GetCapabilities( } // Backup implements the Backup interface -func (b BackupServiceImplementation) Backup(_ context.Context, _ *backup.BackupRequest) (*backup.BackupResult, error) { - // TODO implement me - panic("implement me") +func (b BackupServiceImplementation) Backup( + ctx context.Context, + req *backup.BackupRequest, +) (*backup.BackupResult, error) { + contextLogger := log.FromContext(ctx) + backupObj, err := decoder.DecodeBackup(req.BackupDefinition) + if err != nil { + return nil, err + } + cluster, err := decoder.DecodeClusterJSON(req.ClusterDefinition) + if err != nil { + return nil, err + } + + // Update backup status in cluster conditions on startup + if err := b.retryWithRefreshedCluster(ctx, cluster, func() error { + // TODO: this condition is set only here, never removed or handled? + return conditions.Patch(ctx, b.Client, cluster, cnpgv1.BackupStartingCondition) + }); err != nil { + contextLogger.Error(err, "Error changing backup condition (backup started)") + // We do not terminate here because we could still have a good backup + // even if we are unable to communicate with the Kubernetes API server + } + + 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 { + return nil, err + } + backupCmd := barmanBackup.NewBackupCommand( + cluster.Spec.Backup.BarmanObjectStore, + capabilities, + ) + env := os.Environ() + env, err = barmanCredentials.EnvSetBackupCloudCredentials( + ctx, + b.Client, + cluster.Namespace, + cluster.Spec.Backup.BarmanObjectStore, + env) + if err != nil { + return nil, err + } + + if err = backupCmd.Take( + ctx, + backupObj.Status.BackupName, + backupObj.Status.ServerName, + env, + cluster, + postgres.BackupTemporaryDirectory, + ); err != nil { + return nil, err + } + + contextLogger.Info("Backup completed") + b.Recorder.Event(backupObj, "Normal", "Completed", "Backup completed") + + // Set the status to completed + backupObj.Status.SetAsCompleted() + + executedBackupInfo, err := backupCmd.GetExecutedBackupInfo( + ctx, backupObj.Status.BackupName, backupObj.Status.ServerName, cluster, env) + if err != nil { + return nil, err + } + + 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, + BackupLabelFile: nil, + TablespaceMapFile: nil, + InstanceId: b.InstanceName, + Online: true, + Metadata: nil, + }, nil +} + +func (b *BackupServiceImplementation) retryWithRefreshedCluster( + ctx context.Context, + cluster *cnpgv1.Cluster, + cb func() error, +) error { + return resources.RetryWithRefreshedResource(ctx, b.Client, cluster, cb) } diff --git a/internal/cnpgi/instance/identity.go b/internal/cnpgi/instance/identity.go index 1e29d6b..8393f9a 100644 --- a/internal/cnpgi/instance/identity.go +++ b/internal/cnpgi/instance/identity.go @@ -14,8 +14,8 @@ import ( // IdentityImplementation implements IdentityServer type IdentityImplementation struct { identity.UnimplementedIdentityServer - BarmanObjectKey client.ObjectKey - Client client.Client + WALConfigurationKey client.ObjectKey + Client client.Client } // GetPluginMetadata implements IdentityServer @@ -57,8 +57,8 @@ func (i IdentityImplementation) Probe( _ *identity.ProbeRequest, ) (*identity.ProbeResponse, error) { var obj barmancloudv1.ObjectStore - if err := i.Client.Get(ctx, i.BarmanObjectKey, &obj); err != nil { - return nil, fmt.Errorf("while fetching object store %s: %w", i.BarmanObjectKey.Name, err) + if err := i.Client.Get(ctx, i.WALConfigurationKey, &obj); err != nil { + return nil, fmt.Errorf("while fetching object store %s: %w", i.WALConfigurationKey.Name, err) } return &identity.ProbeResponse{ diff --git a/internal/cnpgi/instance/start.go b/internal/cnpgi/instance/start.go index f2f549f..91a54fb 100644 --- a/internal/cnpgi/instance/start.go +++ b/internal/cnpgi/instance/start.go @@ -7,20 +7,22 @@ import ( "github.com/cloudnative-pg/cnpg-i/pkg/backup" "github.com/cloudnative-pg/cnpg-i/pkg/wal" "google.golang.org/grpc" + "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" ) // CNPGI is the implementation of the PostgreSQL sidecar type CNPGI struct { - Client client.Client - BarmanObjectKey client.ObjectKey - ClusterObjectKey client.ObjectKey - PGDataPath string - PGWALPath string - SpoolDirectory string - ServerCertPath string - ServerKeyPath string - ClientCertPath string + Client client.Client + Recorder record.EventRecorder + WALConfigurationKey client.ObjectKey + ClusterObjectKey client.ObjectKey + PGDataPath string + PGWALPath string + SpoolDirectory string + ServerCertPath string + ServerKeyPath string + ClientCertPath string // mutually exclusive with pluginPath ServerAddress string // mutually exclusive with serverAddress @@ -32,7 +34,7 @@ type CNPGI struct { func (c *CNPGI) Start(ctx context.Context) error { enrich := func(server *grpc.Server) error { wal.RegisterWALServer(server, WALServiceImplementation{ - BarmanObjectKey: c.BarmanObjectKey, + BarmanObjectKey: c.WALConfigurationKey, ClusterObjectKey: c.ClusterObjectKey, InstanceName: c.InstanceName, Client: c.Client, @@ -40,14 +42,18 @@ func (c *CNPGI) Start(ctx context.Context) error { PGDataPath: c.PGDataPath, PGWALPath: c.PGWALPath, }) - backup.RegisterBackupServer(server, BackupServiceImplementation{}) + backup.RegisterBackupServer(server, BackupServiceImplementation{ + Client: c.Client, + Recorder: c.Recorder, + InstanceName: c.InstanceName, + }) return nil } srv := http.Server{ IdentityImpl: IdentityImplementation{ - Client: c.Client, - BarmanObjectKey: c.BarmanObjectKey, + Client: c.Client, + WALConfigurationKey: c.WALConfigurationKey, }, Enrichers: []http.ServerEnricher{enrich}, ServerCertPath: c.ServerCertPath,