Skip to content

Commit 1d97fb1

Browse files
authored
fix(ops): fail fast on missing backup method (#10820)
1 parent 46a44ab commit 1d97fb1

2 files changed

Lines changed: 93 additions & 0 deletions

File tree

pkg/operations/horizontal_scaling.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,9 @@ func (hs horizontalScalingOpsHandler) createRestore(reqCtx intctrlutil.RequestCt
227227
// create restore
228228
restore, err := restoreMGR.BuildPrepareDataRestore(synthesizedComponent, backupObj, getTemplate(templateName))
229229
if err != nil {
230+
if intctrlutil.IsTargetError(err, intctrlutil.ErrorTypeRestoreFailed) {
231+
return intctrlutil.NewFatalError(err.Error())
232+
}
230233
return err
231234
}
232235
scheme, _ := opsv1alpha1.SchemeBuilder.Build()
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
Copyright (C) 2022-2026 ApeCloud Co., Ltd
3+
4+
This file is part of KubeBlocks project
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU Affero General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU Affero General Public License for more details.
15+
16+
You should have received a copy of the GNU Affero General Public License
17+
along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
package operations
21+
22+
import (
23+
"context"
24+
"strings"
25+
"testing"
26+
27+
corev1 "k8s.io/api/core/v1"
28+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+
"k8s.io/apimachinery/pkg/runtime"
30+
"k8s.io/apimachinery/pkg/types"
31+
"sigs.k8s.io/controller-runtime/pkg/client"
32+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
33+
34+
appsv1 "github.com/apecloud/kubeblocks/apis/apps/v1"
35+
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
36+
opsv1alpha1 "github.com/apecloud/kubeblocks/apis/operations/v1alpha1"
37+
"github.com/apecloud/kubeblocks/pkg/constant"
38+
"github.com/apecloud/kubeblocks/pkg/controller/component"
39+
"github.com/apecloud/kubeblocks/pkg/controller/plan"
40+
intctrlutil "github.com/apecloud/kubeblocks/pkg/controllerutil"
41+
)
42+
43+
func TestHorizontalScalingCreateRestoreFailsFastWithoutBackupMethod(t *testing.T) {
44+
ctx := context.Background()
45+
scheme := runtime.NewScheme()
46+
for _, addToScheme := range []func(*runtime.Scheme) error{
47+
corev1.AddToScheme, appsv1.AddToScheme, dpv1alpha1.AddToScheme, opsv1alpha1.AddToScheme,
48+
} {
49+
if err := addToScheme(scheme); err != nil {
50+
t.Fatalf("add scheme: %v", err)
51+
}
52+
}
53+
54+
cluster := &appsv1.Cluster{ObjectMeta: metav1.ObjectMeta{
55+
Name: "cluster", Namespace: "default", UID: types.UID("cluster1"),
56+
}}
57+
opsRequest := &opsv1alpha1.OpsRequest{ObjectMeta: metav1.ObjectMeta{
58+
Name: "scale-out-from-backup", Namespace: "default", UID: types.UID("opsreq1"),
59+
}}
60+
backup := &dpv1alpha1.Backup{
61+
ObjectMeta: metav1.ObjectMeta{Name: "backup", Namespace: "default"},
62+
Status: dpv1alpha1.BackupStatus{Phase: dpv1alpha1.BackupPhaseCompleted},
63+
}
64+
cli := fake.NewClientBuilder().WithScheme(scheme).Build()
65+
restoreMGR := plan.NewRestoreManager(ctx, cli, cluster, scheme, map[string]string{
66+
constant.OpsRequestNameLabelKey: opsRequest.Name,
67+
}, 1, 3)
68+
69+
err := horizontalScalingOpsHandler{}.createRestore(intctrlutil.RequestCtx{Ctx: ctx}, cli,
70+
&OpsResource{Cluster: cluster, OpsRequest: opsRequest},
71+
&component.SynthesizedComponent{Name: "mysql"}, restoreMGR,
72+
&appsv1.ClusterComponentSpec{Name: "mysql"}, backup, "")
73+
if err == nil {
74+
t.Fatal("expected fatal error for completed backup without backup method")
75+
}
76+
if !intctrlutil.IsTargetError(err, intctrlutil.ErrorTypeFatal) {
77+
t.Fatalf("expected fatal error, got %T: %v", err, err)
78+
}
79+
if !strings.Contains(err.Error(), "status.backupMethod") {
80+
t.Fatalf("unexpected error: %v", err)
81+
}
82+
83+
restores := &dpv1alpha1.RestoreList{}
84+
if err := cli.List(ctx, restores, client.InNamespace("default")); err != nil {
85+
t.Fatalf("list restores: %v", err)
86+
}
87+
if len(restores.Items) != 0 {
88+
t.Fatalf("expected no restore to be created, got %d", len(restores.Items))
89+
}
90+
}

0 commit comments

Comments
 (0)