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  }