github.com/percona/percona-xtradb-cluster-operator@v1.14.0/pkg/controller/pxcrestore/restore_test.go (about) 1 package pxcrestore 2 3 import ( 4 "context" 5 "testing" 6 7 "k8s.io/apimachinery/pkg/runtime" 8 logf "sigs.k8s.io/controller-runtime/pkg/log" 9 10 api "github.com/percona/percona-xtradb-cluster-operator/pkg/apis/pxc/v1" 11 "github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/backup/storage" 12 fakestorage "github.com/percona/percona-xtradb-cluster-operator/pkg/pxc/backup/storage/fake" 13 "github.com/percona/percona-xtradb-cluster-operator/version" 14 "github.com/pkg/errors" 15 ) 16 17 func TestValidate(t *testing.T) { 18 ctx := context.Background() 19 20 const clusterName = "test-cluster" 21 const namespace = "namespace" 22 const backupName = clusterName + "-backup" 23 const restoreName = clusterName + "-restore" 24 const s3SecretName = "my-cluster-name-backup-s3" 25 const azureSecretName = "azure-secret" 26 27 cluster := readDefaultCR(t, clusterName, namespace) 28 s3Bcp := readDefaultBackup(t, backupName, namespace) 29 s3Bcp.Spec.StorageName = "s3-us-west" 30 s3Bcp.Status.Destination.SetS3Destination("some-dest", "dest") 31 s3Bcp.Status.S3 = &api.BackupStorageS3Spec{ 32 Bucket: "some-bucket", 33 CredentialsSecret: s3SecretName, 34 } 35 s3Bcp.Status.State = api.BackupSucceeded 36 azureBcp := readDefaultBackup(t, backupName, namespace) 37 azureBcp.Spec.StorageName = "azure-blob" 38 azureBcp.Status.Destination.SetAzureDestination("some-dest", "dest") 39 azureBcp.Status.Azure = &api.BackupStorageAzureSpec{ 40 ContainerPath: "some-bucket", 41 CredentialsSecret: azureSecretName, 42 } 43 azureBcp.Status.State = api.BackupSucceeded 44 cr := readDefaultRestore(t, restoreName, namespace) 45 cr.Spec.BackupName = backupName 46 crSecret := readDefaultCRSecret(t, clusterName+"-secrets", namespace) 47 s3Secret := readDefaultS3Secret(t, s3SecretName, namespace) 48 azureSecret := readDefaultAzureSecret(t, azureSecretName, namespace) 49 50 tests := []struct { 51 name string 52 cr *api.PerconaXtraDBClusterRestore 53 bcp *api.PerconaXtraDBClusterBackup 54 cluster *api.PerconaXtraDBCluster 55 objects []runtime.Object 56 expectedErr string 57 fakeStorageClientFunc storage.NewClientFunc 58 }{ 59 { 60 name: "s3", 61 cr: cr.DeepCopy(), 62 cluster: cluster.DeepCopy(), 63 bcp: s3Bcp, 64 objects: []runtime.Object{ 65 crSecret, 66 s3Secret, 67 }, 68 }, 69 { 70 name: "s3 without secrets", 71 cr: cr.DeepCopy(), 72 cluster: cluster.DeepCopy(), 73 bcp: s3Bcp, 74 expectedErr: "failed to validate job: secrets my-cluster-name-backup-s3, test-cluster-secrets not found", 75 }, 76 { 77 name: "s3 without credentialsSecret", 78 cr: cr.DeepCopy(), 79 bcp: s3Bcp, 80 objects: []runtime.Object{ 81 crSecret, 82 s3Secret, 83 }, 84 cluster: updateResource(cluster, func(cluster *api.PerconaXtraDBCluster) { 85 cluster.Spec.Backup.Storages["s3-us-west"].S3.CredentialsSecret = "" 86 }), 87 expectedErr: "", 88 }, 89 { 90 name: "s3 with failing storage client", 91 cr: cr.DeepCopy(), 92 cluster: cluster.DeepCopy(), 93 bcp: s3Bcp, 94 expectedErr: "failed to validate backup existence: failed to list objects: failListObjects", 95 objects: []runtime.Object{ 96 crSecret, 97 s3Secret, 98 }, 99 fakeStorageClientFunc: func(_ context.Context, opts storage.Options) (storage.Storage, error) { 100 return &fakeStorageClient{failListObjects: true}, nil 101 }, 102 }, 103 { 104 name: "s3 without provided bucket", 105 cr: updateResource(cr, func(cr *api.PerconaXtraDBClusterRestore) { 106 cr.Spec.BackupName = "" 107 cr.Spec.BackupSource = &api.PXCBackupStatus{ 108 Destination: s3Bcp.Status.Destination, 109 StorageType: api.BackupStorageS3, 110 S3: s3Bcp.Status.S3, 111 } 112 cr.Spec.BackupSource.S3.Bucket = "" 113 }, 114 ), 115 cluster: cluster.DeepCopy(), 116 objects: []runtime.Object{ 117 crSecret, 118 s3Secret, 119 }, 120 }, 121 { 122 name: "s3 with empty bucket", 123 cr: cr.DeepCopy(), 124 cluster: cluster.DeepCopy(), 125 bcp: s3Bcp, 126 expectedErr: "failed to validate backup existence: backup not found", 127 objects: []runtime.Object{ 128 crSecret, 129 s3Secret, 130 }, 131 fakeStorageClientFunc: func(_ context.Context, opts storage.Options) (storage.Storage, error) { 132 return &fakeStorageClient{emptyListObjects: true}, nil 133 }, 134 }, 135 { 136 name: "s3 pitr", 137 bcp: s3Bcp, 138 cr: updateResource(cr, func(cr *api.PerconaXtraDBClusterRestore) { 139 cr.Spec.PITR = &api.PITR{ 140 BackupSource: &api.PXCBackupStatus{ 141 StorageName: s3Bcp.Spec.StorageName, 142 }, 143 } 144 }), 145 cluster: updateResource(cluster, func(cluster *api.PerconaXtraDBCluster) { 146 cluster.Spec.Backup.PITR = api.PITRSpec{ 147 Enabled: true, 148 StorageName: s3Bcp.Spec.StorageName, 149 } 150 }), 151 objects: []runtime.Object{ 152 crSecret, 153 s3Secret, 154 }, 155 }, 156 { 157 name: "azure", 158 bcp: azureBcp, 159 cr: cr.DeepCopy(), 160 cluster: cluster.DeepCopy(), 161 objects: []runtime.Object{ 162 crSecret, 163 azureSecret, 164 }, 165 }, 166 { 167 name: "azure without secrets", 168 cr: cr.DeepCopy(), 169 cluster: cluster.DeepCopy(), 170 bcp: azureBcp, 171 expectedErr: "failed to validate job: secrets azure-secret, test-cluster-secrets not found", 172 }, 173 { 174 name: "azure pitr", 175 bcp: azureBcp, 176 cr: updateResource(cr, func(cr *api.PerconaXtraDBClusterRestore) { 177 cr.Spec.PITR = &api.PITR{ 178 BackupSource: &api.PXCBackupStatus{ 179 StorageName: azureBcp.Spec.StorageName, 180 }, 181 } 182 }), 183 cluster: updateResource(cluster, func(cluster *api.PerconaXtraDBCluster) { 184 cluster.Spec.Backup.PITR = api.PITRSpec{ 185 Enabled: true, 186 StorageName: azureBcp.Spec.StorageName, 187 } 188 }), 189 objects: []runtime.Object{ 190 crSecret, 191 azureSecret, 192 }, 193 }, 194 { 195 name: "azure with failing storage client", 196 cr: cr.DeepCopy(), 197 cluster: cluster.DeepCopy(), 198 bcp: azureBcp, 199 expectedErr: "failed to validate backup existence: list blobs: failListObjects", 200 objects: []runtime.Object{ 201 crSecret, 202 azureSecret, 203 }, 204 fakeStorageClientFunc: func(_ context.Context, opts storage.Options) (storage.Storage, error) { 205 return &fakeStorageClient{failListObjects: true}, nil 206 }, 207 }, 208 { 209 name: "azure without provided bucket", 210 cr: updateResource(cr, func(cr *api.PerconaXtraDBClusterRestore) { 211 cr.Spec.BackupName = "" 212 cr.Spec.BackupSource = &api.PXCBackupStatus{ 213 Destination: azureBcp.Status.Destination, 214 StorageType: api.BackupStorageAzure, 215 Azure: azureBcp.Status.Azure, 216 } 217 cr.Spec.BackupSource.Azure.ContainerPath = "" 218 }, 219 ), 220 cluster: cluster.DeepCopy(), 221 objects: []runtime.Object{ 222 crSecret, 223 azureSecret, 224 }, 225 }, 226 { 227 name: "azure with empty bucket", 228 cr: cr.DeepCopy(), 229 cluster: cluster.DeepCopy(), 230 bcp: azureBcp, 231 expectedErr: "failed to validate backup existence: no backups found", 232 objects: []runtime.Object{ 233 crSecret, 234 azureSecret, 235 }, 236 fakeStorageClientFunc: func(_ context.Context, opts storage.Options) (storage.Storage, error) { 237 return &fakeStorageClient{emptyListObjects: true}, nil 238 }, 239 }, 240 } 241 242 for _, tt := range tests { 243 t.Run(tt.name, func(t *testing.T) { 244 if tt.fakeStorageClientFunc == nil { 245 tt.fakeStorageClientFunc = func(ctx context.Context, opts storage.Options) (storage.Storage, error) { 246 defaultFakeClient, err := fakestorage.NewFakeClient(ctx, opts) 247 if err != nil { 248 return nil, err 249 } 250 return &fakeStorageClient{defaultFakeClient, false, false}, nil 251 } 252 } 253 254 if err := tt.cr.CheckNsetDefaults(); err != nil { 255 t.Fatal(err) 256 } 257 if err := tt.cluster.CheckNSetDefaults(new(version.ServerVersion), logf.FromContext(ctx)); err != nil { 258 t.Fatal(err) 259 } 260 if tt.bcp != nil { 261 tt.objects = append(tt.objects, tt.bcp) 262 } 263 tt.objects = append(tt.objects, tt.cr, tt.cluster) 264 265 cl := buildFakeClient(tt.objects...) 266 r := reconciler(cl) 267 r.newStorageClientFunc = tt.fakeStorageClientFunc 268 269 bcp, err := r.getBackup(ctx, tt.cr) 270 if err != nil { 271 t.Fatal(err) 272 } 273 err = r.validate(ctx, tt.cr, bcp, tt.cluster) 274 errStr := "" 275 if err != nil { 276 errStr = err.Error() 277 } 278 if errStr != tt.expectedErr { 279 t.Fatal("expected err:", tt.expectedErr, "; got:", errStr) 280 } 281 }) 282 } 283 } 284 285 type fakeStorageClient struct { 286 storage.Storage 287 failListObjects bool 288 emptyListObjects bool 289 } 290 291 func (c *fakeStorageClient) ListObjects(_ context.Context, _ string) ([]string, error) { 292 switch { 293 case c.emptyListObjects: 294 return nil, nil 295 case c.failListObjects: 296 return nil, errors.New("failListObjects") 297 } 298 return []string{"some-dest/backup1", "some-dest/backup2"}, nil 299 }