Skip to content

Commit 75da18c

Browse files
authored
Supplement restoring tenant from new types of backup destinations (#586)
1 parent eb898f3 commit 75da18c

File tree

5 files changed

+99
-80
lines changed

5 files changed

+99
-80
lines changed

api/v1alpha1/obtenant_webhook.go

Lines changed: 73 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package v1alpha1
1818

1919
import (
2020
"context"
21+
"errors"
2122
"fmt"
2223
"regexp"
2324
"strings"
@@ -291,55 +292,13 @@ func (r *OBTenant) validateMutation() error {
291292

292293
if res.ArchiveSource == nil && res.BakDataSource == nil && res.SourceUri == "" {
293294
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore"), res, "Restore must have a source option, but both archiveSource, bakDataSource and sourceUri are nil now"))
294-
}
295-
296-
if res.ArchiveSource != nil && res.ArchiveSource.Type == constants.BackupDestTypeOSS {
297-
if res.ArchiveSource.OSSAccessSecret == "" {
298-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("archiveSource").Child("ossAccessSecret"), res.ArchiveSource.OSSAccessSecret, "Tenant restoring from OSS type backup data must have a OSSAccessSecret"))
299-
} else {
300-
secret := &v1.Secret{}
301-
err := tenantClt.Get(context.Background(), types.NamespacedName{
302-
Namespace: r.GetNamespace(),
303-
Name: res.ArchiveSource.OSSAccessSecret,
304-
}, secret)
305-
if err != nil {
306-
if apierrors.IsNotFound(err) {
307-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("archiveSource").Child("ossAccessSecret"), res.ArchiveSource.OSSAccessSecret, "Given OSSAccessSecret not found"))
308-
}
309-
allErrs = append(allErrs, field.InternalError(field.NewPath("spec").Child("source").Child("restore").Child("archiveSource").Child("ossAccessSecret"), err))
310-
} else {
311-
if _, ok := secret.Data["accessId"]; !ok {
312-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("archiveSource").Child("ossAccessSecret"), res.ArchiveSource.OSSAccessSecret, "accessId field not found in given OSSAccessSecret"))
313-
}
314-
if _, ok := secret.Data["accessKey"]; !ok {
315-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("archiveSource").Child("ossAccessSecret"), res.ArchiveSource.OSSAccessSecret, "accessKey field not found in given OSSAccessSecret"))
316-
}
317-
}
318-
}
319-
}
320-
321-
if res.BakDataSource != nil && res.BakDataSource.Type == constants.BackupDestTypeOSS {
322-
if res.BakDataSource.OSSAccessSecret == "" {
323-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("bakDataSource").Child("ossAccessSecret"), res.BakDataSource.OSSAccessSecret, "Tenant restoring from OSS type backup data must have a OSSAccessSecret"))
324-
} else {
325-
secret := &v1.Secret{}
326-
err := tenantClt.Get(context.Background(), types.NamespacedName{
327-
Namespace: r.GetNamespace(),
328-
Name: res.BakDataSource.OSSAccessSecret,
329-
}, secret)
330-
if err != nil {
331-
if apierrors.IsNotFound(err) {
332-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("bakDataSource").Child("ossAccessSecret"), res.BakDataSource.OSSAccessSecret, "Given OSSAccessSecret not found"))
333-
}
334-
allErrs = append(allErrs, field.InternalError(field.NewPath("spec").Child("source").Child("restore").Child("bakDataSource").Child("ossAccessSecret"), err))
335-
} else {
336-
if _, ok := secret.Data["accessId"]; !ok {
337-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("bakDataSource").Child("ossAccessSecret"), res.BakDataSource.OSSAccessSecret, "accessId field not found in given OSSAccessSecret"))
338-
}
339-
if _, ok := secret.Data["accessKey"]; !ok {
340-
allErrs = append(allErrs, field.Invalid(field.NewPath("spec").Child("source").Child("restore").Child("bakDataSource").Child("ossAccessSecret"), res.BakDataSource.OSSAccessSecret, "accessKey field not found in given OSSAccessSecret"))
341-
}
342-
}
295+
} else {
296+
destErrs := errors.Join(
297+
validateBackupDestination(cluster, res.ArchiveSource, "spec", "source", "restore", "archiveSource"),
298+
validateBackupDestination(cluster, res.BakDataSource, "spec", "source", "restore", "bakDataSource"),
299+
)
300+
if destErrs != nil {
301+
return destErrs
343302
}
344303
}
345304
}
@@ -355,3 +314,68 @@ func (r *OBTenant) ValidateDelete() (admission.Warnings, error) {
355314
// TODO(user): fill in your validation logic upon object deletion.
356315
return nil, nil
357316
}
317+
318+
func validateBackupDestination(cluster *OBCluster, dest *apitypes.BackupDestination, paths ...string) error {
319+
var errorPath *field.Path
320+
if len(paths) == 0 {
321+
errorPath = field.NewPath("spec").Child("destination")
322+
} else {
323+
errorPath = field.NewPath("spec").Child(paths[0])
324+
for _, p := range paths[1:] {
325+
errorPath = errorPath.Child(p)
326+
}
327+
}
328+
if dest.Type == constants.BackupDestTypeNFS && cluster.Spec.BackupVolume == nil {
329+
return field.Invalid(errorPath, cluster.Spec.BackupVolume, "backupVolume of obcluster is required when backing up data to NFS")
330+
}
331+
pattern, ok := constants.DestPathPatternMapping[dest.Type]
332+
if !ok {
333+
return field.Invalid(errorPath.Child("destination").Child("type"), dest.Type, "invalid backup destination type")
334+
}
335+
if !pattern.MatchString(dest.Path) {
336+
return field.Invalid(errorPath.Child("destination").Child("path"), dest.Path, "invalid backup destination path, the path format should be "+pattern.String())
337+
}
338+
if dest.Type != constants.BackupDestTypeNFS {
339+
if dest.OSSAccessSecret == "" {
340+
return field.Invalid(errorPath.Child("destination"), dest.OSSAccessSecret, "OSSAccessSecret is required when backing up data to OSS, COS or S3")
341+
}
342+
secret := &v1.Secret{}
343+
err := bakClt.Get(context.Background(), types.NamespacedName{
344+
Namespace: cluster.GetNamespace(),
345+
Name: dest.OSSAccessSecret,
346+
}, secret)
347+
fieldPath := errorPath.Child("destination").Child("ossAccessSecret")
348+
if err != nil {
349+
if apierrors.IsNotFound(err) {
350+
return field.Invalid(fieldPath, dest.OSSAccessSecret, "Given OSSAccessSecret not found")
351+
}
352+
return field.InternalError(fieldPath, err)
353+
}
354+
// All the following types need accessId and accessKey
355+
switch dest.Type {
356+
case
357+
constants.BackupDestTypeCOS,
358+
constants.BackupDestTypeOSS,
359+
constants.BackupDestTypeS3,
360+
constants.BackupDestTypeS3Compatible:
361+
if _, ok := secret.Data["accessId"]; !ok {
362+
return field.Invalid(fieldPath, dest.OSSAccessSecret, "accessId field not found in given OSSAccessSecret")
363+
}
364+
if _, ok := secret.Data["accessKey"]; !ok {
365+
return field.Invalid(fieldPath, dest.OSSAccessSecret, "accessKey field not found in given OSSAccessSecret")
366+
}
367+
}
368+
// The following types need additional fields
369+
switch dest.Type {
370+
case constants.BackupDestTypeCOS:
371+
if _, ok := secret.Data["appId"]; !ok {
372+
return field.Invalid(fieldPath, dest.OSSAccessSecret, "appId field not found in given OSSAccessSecret")
373+
}
374+
case constants.BackupDestTypeS3:
375+
if _, ok := secret.Data["s3Region"]; !ok {
376+
return field.Invalid(fieldPath, dest.OSSAccessSecret, "s3Region field not found in given OSSAccessSecret")
377+
}
378+
}
379+
}
380+
return nil
381+
}

build/Dockerfile.dashboard

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
FROM node:18-alpine as builder-fe
1+
FROM node:18-alpine AS builder-fe
22
WORKDIR /workspace
33
COPY ./ui .
44
ENV NODE_OPTIONS=--max_old_space_size=5120
55
RUN yarn
66
RUN yarn build
77

8-
FROM golang:1.22 as builder-be
8+
FROM golang:1.22 AS builder-be
99
ARG GOPROXY=https://goproxy.io,direct
1010
ARG GOSUMDB=sum.golang.org
1111
ARG COMMIT_HASH=unknown

build/Dockerfile.obhelper

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.22 as builder
1+
FROM golang:1.22 AS builder
22
ARG GOPROXY=https://goproxy.io,direct
33
WORKDIR /workspace
44
COPY . .

build/Dockerfile.operator

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Build the manager binary
2-
FROM golang:1.22 as builder
2+
FROM golang:1.22 AS builder
33

44
ARG GOPROXY
55
ARG GOSUMDB

internal/resource/obtenantrestore/utils.go

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"k8s.io/apimachinery/pkg/types"
2222

2323
"github.com/oceanbase/ob-operator/api/constants"
24+
apitypes "github.com/oceanbase/ob-operator/api/types"
2425
v1alpha1 "github.com/oceanbase/ob-operator/api/v1alpha1"
2526
oceanbaseconst "github.com/oceanbase/ob-operator/internal/const/oceanbase"
2627
resourceutils "github.com/oceanbase/ob-operator/internal/resource/utils"
@@ -49,25 +50,8 @@ func (m *ObTenantRestoreManager) getSourceUri() (string, error) {
4950
return source.SourceUri, nil
5051
}
5152
var bakPath, archivePath string
52-
if source.BakDataSource != nil && source.BakDataSource.Type == constants.BackupDestTypeOSS {
53-
accessId, accessKey, err := m.readAccessCredentials(source.BakDataSource.OSSAccessSecret)
54-
if err != nil {
55-
return "", err
56-
}
57-
bakPath = strings.Join([]string{source.BakDataSource.Path, "access_id=" + accessId, "access_key=" + accessKey}, "&")
58-
} else {
59-
bakPath = "file://" + path.Join(oceanbaseconst.BackupPath, source.BakDataSource.Path)
60-
}
61-
62-
if source.ArchiveSource != nil && source.ArchiveSource.Type == constants.BackupDestTypeOSS {
63-
accessId, accessKey, err := m.readAccessCredentials(source.ArchiveSource.OSSAccessSecret)
64-
if err != nil {
65-
return "", err
66-
}
67-
archivePath = strings.Join([]string{source.ArchiveSource.Path, "access_id=" + accessId, "access_key=" + accessKey}, "&")
68-
} else {
69-
archivePath = "file://" + path.Join(oceanbaseconst.BackupPath, source.ArchiveSource.Path)
70-
}
53+
bakPath = m.getDestPath(source.BakDataSource)
54+
archivePath = m.getDestPath(source.ArchiveSource)
7155

7256
if bakPath == "" || archivePath == "" {
7357
return "", errors.New("Unexpected error: both bakPath and archivePath must be set")
@@ -76,16 +60,27 @@ func (m *ObTenantRestoreManager) getSourceUri() (string, error) {
7660
return strings.Join([]string{bakPath, archivePath}, ","), nil
7761
}
7862

79-
func (m *ObTenantRestoreManager) readAccessCredentials(secretName string) (accessId, accessKey string, err error) {
63+
func (m *ObTenantRestoreManager) getDestPath(dest *apitypes.BackupDestination) string {
64+
if dest.Type == constants.BackupDestTypeNFS || resourceutils.IsZero(dest.Type) {
65+
return "file://" + path.Join(oceanbaseconst.BackupPath, dest.Path)
66+
}
67+
if dest.OSSAccessSecret == "" {
68+
return ""
69+
}
8070
secret := &v1.Secret{}
81-
err = m.Client.Get(m.Ctx, types.NamespacedName{
82-
Namespace: m.Resource.Namespace,
83-
Name: secretName,
71+
err := m.Client.Get(m.Ctx, types.NamespacedName{
72+
Namespace: m.Resource.GetNamespace(),
73+
Name: dest.OSSAccessSecret,
8474
}, secret)
8575
if err != nil {
86-
return "", "", err
76+
m.PrintErrEvent(err)
77+
return ""
78+
}
79+
destPath := strings.Join([]string{dest.Path, "access_id=" + string(secret.Data["accessId"]), "access_key=" + string(secret.Data["accessKey"])}, "&")
80+
if dest.Type == constants.BackupDestTypeCOS {
81+
destPath += ("&appid=" + string(secret.Data["appId"]))
82+
} else if dest.Type == constants.BackupDestTypeS3 {
83+
destPath += ("&s3_region=" + string(secret.Data["s3Region"]))
8784
}
88-
accessId = string(secret.Data["accessId"])
89-
accessKey = string(secret.Data["accessKey"])
90-
return accessId, accessKey, nil
85+
return destPath
9186
}

0 commit comments

Comments
 (0)