Skip to content

Commit

Permalink
feat: restore-by-keys (#443)
Browse files Browse the repository at this point in the history
  • Loading branch information
Chiwency authored Sep 24, 2024
1 parent fd7ac58 commit a62d94c
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 64 deletions.
2 changes: 2 additions & 0 deletions docs/user_docs/cli/kbcli_cluster_restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ kbcli cluster restore [flags]
```
--backup string Backup name
-h, --help help for restore
--restore-key string specify the key to restore in kv database, support multiple keys split by comma with wildcard pattern matching
--restore-key-ignore-errors whether or not to ignore errors when restore kv database by keys
--restore-to-time string point in time recovery(PITR)
--volume-restore-policy string the volume claim restore policy, supported values: [Serial, Parallel] (default "Parallel")
```
Expand Down
2 changes: 2 additions & 0 deletions docs/user_docs/cli/kbcli_dataprotection_restore.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ kbcli dataprotection restore [flags]
```
--cluster string The cluster to restore
-h, --help help for restore
--restore-key string specify the key to restore in kv database, support multiple keys split by comma with wildcard pattern matching
--restore-key-ignore-errors whether or not to ignore errors when restore kv database by keys
--restore-to-time string point in time recovery(PITR)
--volume-restore-policy string the volume claim restore policy, supported values: [Serial, Parallel] (default "Parallel")
```
Expand Down
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
cuelang.org/go v0.8.0
github.com/Masterminds/semver/v3 v3.2.1
github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46
github.com/apecloud/kubeblocks v0.9.1-beta.6
github.com/apecloud/kubeblocks v0.9.1-beta.27
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/briandowns/spinner v1.23.0
github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad
Expand Down Expand Up @@ -101,6 +101,7 @@ require (
github.com/c9s/goprocinfo v0.0.0-20170724085704-0010a05ce49f // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/charmbracelet/keygen v0.5.1 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/clbanning/mxj/v2 v2.5.7 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
Expand Down Expand Up @@ -313,7 +314,7 @@ require (
golang.org/x/tools v0.24.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/api v0.171.0 // indirect
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
google.golang.org/grpc v1.64.1 // indirect
Expand Down
10 changes: 6 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df h
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46 h1:+Jcc7IjDGxPgIfIkGX2Q5Yxj35U65zgcfjh0B9rDhjo=
github.com/apecloud/dbctl v0.0.0-20240827084000-68a1980b1a46/go.mod h1:eksJtZ7z1nVcVLqDzAdcN5EfpHwXllIAvHZEks2zWys=
github.com/apecloud/kubeblocks v0.9.1-beta.6 h1:/7k80XnLzJxhW+CaUgiIThm6JlCto+giNqscl6fKU6s=
github.com/apecloud/kubeblocks v0.9.1-beta.6/go.mod h1:kp9nenBgXsO03SbxN7a5S2HdNTsIQlpTcSgY8Mf2KS0=
github.com/apecloud/kubeblocks v0.9.1-beta.27 h1:5Uo2EXLPp5r5SuYHK4usQf1wV4sMZIYdg+0XRi+m79k=
github.com/apecloud/kubeblocks v0.9.1-beta.27/go.mod h1:rUZPPU8lnslgZCjSAH8kH/XUxt7h/toskux2XTh38eQ=
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
Expand Down Expand Up @@ -318,6 +318,8 @@ github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNS
github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad h1:DVxCvjXlmkm4idu4bAbI9P+D99BsVHTKOKbzRYTlFwU=
github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230912020346-a5d89c1c90ad/go.mod h1:Yi/tSmvDrnFgyZN4bsXm3gfXrp3zo1uytHmnPEYfquM=
github.com/charmbracelet/keygen v0.5.1 h1:zBkkYPtmKDVTw+cwUyY6ZwGDhRxXkEp0Oxs9sqMLqxI=
github.com/charmbracelet/keygen v0.5.1/go.mod h1:zznJVmK/GWB6dAtjluqn2qsttiCBhA5MZSiwb80fcHw=
github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
Expand Down Expand Up @@ -1989,8 +1991,8 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw
google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM=
google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/cluster/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ func setBackup(o *CreateOptions, cluster *appsv1alpha1.Cluster) error {
// _ = runtime.DefaultUnstructuredConverter.FromUnstructured(v, &compSpec)
// componentSpecs = append(componentSpecs, compSpec)
// }
restoreAnnotation, err := restore.GetRestoreFromBackupAnnotation(backup, o.VolumeRestorePolicy, restoreTimeStr, false)
restoreAnnotation, err := restore.GetRestoreFromBackupAnnotation(backup, o.VolumeRestorePolicy, restoreTimeStr, nil, false)
if err != nil {
return err
}
Expand Down
24 changes: 23 additions & 1 deletion pkg/cmd/cluster/dataprotection.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,12 @@ var (
`)
)

const TrueValue = "true"
const (
TrueValue = "true"

DPEnvRestoreKeyPatterns = "DP_RESTORE_KEY_PATTERNS"
DPEnvRestoreKeyIgnoreErrors = "DP_RESTORE_KEY_IGNORE_ERRORS"
)

type CreateBackupOptions struct {
BackupSpec appsv1alpha1.Backup `json:"backupSpec"`
Expand Down Expand Up @@ -581,6 +586,9 @@ func (o *CreateRestoreOptions) Validate() error {
}

func NewCreateRestoreCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
restoreKey := ""
restoreKeyIgnoreErrors := false

customOutPut := func(opt *action.CreateOptions) {
output := fmt.Sprintf("Cluster %s created", opt.Name)
printer.PrintLine(output)
Expand All @@ -602,6 +610,18 @@ func NewCreateRestoreCmd(f cmdutil.Factory, streams genericiooptions.IOStreams)
Example: createRestoreExample,
Run: func(cmd *cobra.Command, args []string) {
o.Args = args
if restoreKey != "" {
o.RestoreSpec.Env = append(o.RestoreSpec.Env, corev1.EnvVar{
Name: DPEnvRestoreKeyPatterns,
Value: restoreKey,
})
}
if restoreKeyIgnoreErrors {
o.RestoreSpec.Env = append(o.RestoreSpec.Env, corev1.EnvVar{
Name: DPEnvRestoreKeyIgnoreErrors,
Value: strconv.FormatBool(restoreKeyIgnoreErrors),
})
}
cmdutil.BehaviorOnFatal(printer.FatalWithRedColor)
util.CheckErr(o.Complete())
util.CheckErr(o.Validate())
Expand All @@ -611,6 +631,8 @@ func NewCreateRestoreCmd(f cmdutil.Factory, streams genericiooptions.IOStreams)

cmd.Flags().StringVar(&o.RestoreSpec.BackupName, "backup", "", "Backup name")
cmd.Flags().StringVar(&o.RestoreSpec.RestorePointInTime, "restore-to-time", "", "point in time recovery(PITR)")
cmd.Flags().StringVar(&restoreKey, "restore-key", "", "specify the key to restore in kv database, support multiple keys split by comma with wildcard pattern matching")
cmd.Flags().BoolVar(&restoreKeyIgnoreErrors, "restore-key-ignore-errors", false, "whether or not to ignore errors when restore kv database by keys")
cmd.Flags().StringVar(&o.RestoreSpec.VolumeRestorePolicy, "volume-restore-policy", "Parallel", "the volume claim restore policy, supported values: [Serial, Parallel]")
return cmd
}
Expand Down
19 changes: 19 additions & 0 deletions pkg/cmd/dataprotection/restore.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@ package dataprotection

import (
"fmt"
"strconv"

"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/cli-runtime/pkg/genericiooptions"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/util/templates"
Expand All @@ -41,6 +43,9 @@ var (
)

func newRestoreCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
restoreKey := ""
restoreKeyIgnoreErrors := false

customOutPut := func(opt *action.CreateOptions) {
output := fmt.Sprintf("Cluster %s created", opt.Name)
printer.PrintLine(output)
Expand Down Expand Up @@ -70,6 +75,18 @@ func newRestoreCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *c
if clusterName != "" {
o.Args = []string{clusterName}
}
if restoreKey != "" {
o.RestoreSpec.Env = append(o.RestoreSpec.Env, corev1.EnvVar{
Name: cluster.DPEnvRestoreKeyPatterns,
Value: restoreKey,
})
}
if restoreKeyIgnoreErrors {
o.RestoreSpec.Env = append(o.RestoreSpec.Env, corev1.EnvVar{
Name: cluster.DPEnvRestoreKeyIgnoreErrors,
Value: strconv.FormatBool(restoreKeyIgnoreErrors),
})
}
cmdutil.BehaviorOnFatal(printer.FatalWithRedColor)
util.CheckErr(o.Complete())
util.CheckErr(o.Validate())
Expand All @@ -79,6 +96,8 @@ func newRestoreCommand(f cmdutil.Factory, streams genericiooptions.IOStreams) *c

cmd.Flags().StringVar(&clusterName, "cluster", "", "The cluster to restore")
cmd.Flags().StringVar(&o.RestoreSpec.RestorePointInTime, "restore-to-time", "", "point in time recovery(PITR)")
cmd.Flags().StringVar(&restoreKey, "restore-key", "", "specify the key to restore in kv database, support multiple keys split by comma with wildcard pattern matching")
cmd.Flags().BoolVar(&restoreKeyIgnoreErrors, "restore-key-ignore-errors", false, "whether or not to ignore errors when restore kv database by keys")
cmd.Flags().StringVar(&o.RestoreSpec.VolumeRestorePolicy, "volume-restore-policy", "Parallel", "the volume claim restore policy, supported values: [Serial, Parallel]")
return cmd
}
108 changes: 52 additions & 56 deletions pkg/util/breakingchange/upgradehandlerto0.7.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,18 @@ import (
"fmt"
"strings"

v1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
apitypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/client-go/dynamic"

"github.com/apecloud/kbcli/pkg/types"
dpv1alpha1 "github.com/apecloud/kubeblocks/apis/dataprotection/v1alpha1"
"github.com/apecloud/kubeblocks/pkg/constant"
"github.com/apecloud/kubeblocks/pkg/controller/builder"

"github.com/apecloud/kbcli/pkg/types"
)

var _ upgradeHandler = &upgradeHandlerTo7{}
Expand Down Expand Up @@ -457,57 +453,57 @@ func (u *upgradeHandlerTo7) transformBackup(dynamic dynamic.Interface, obj unstr

func (u *upgradeHandlerTo7) transformStatefulSet(dynamic dynamic.Interface, obj unstructured.Unstructured) error {
// filter objects not managed by KB
labels := obj.GetLabels()
if labels == nil || labels[constant.AppManagedByLabelKey] != constant.AppName {
return nil
}
// create a rsm
matchLabels, _, _ := unstructured.NestedStringMap(obj.Object, "spec", "selector", "matchLabels")
replicas, _, _ := unstructured.NestedInt64(obj.Object, "spec", "replicas")
podManagementPolicy, _, _ := unstructured.NestedString(obj.Object, "spec", "podManagementPolicy")
pvcsUnstructured, _, _ := unstructured.NestedSlice(obj.Object, "spec", "volumeClaimTemplates")
var pvcs []corev1.PersistentVolumeClaim
for _, pvcUnstructured := range pvcsUnstructured {
pvc := &corev1.PersistentVolumeClaim{}
pvcU, _ := pvcUnstructured.(map[string]interface{})
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(pvcU, pvc); err != nil {
return err
}
pvcs = append(pvcs, *pvc)
}
template, _, _ := unstructured.NestedMap(obj.Object, "spec", "template")
podTemplate := &corev1.PodTemplateSpec{}
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(template, podTemplate); err != nil {
return err
}
updateStrategy, _, _ := unstructured.NestedString(obj.Object, "spec", "updateStrategy")
rsm := builder.NewInstanceSetBuilder(obj.GetNamespace(), obj.GetName()).
AddAnnotationsInMap(obj.GetAnnotations()).
AddLabelsInMap(obj.GetLabels()).
AddMatchLabelsInMap(matchLabels).
SetReplicas(int32(replicas)).
SetPodManagementPolicy(v1.PodManagementPolicyType(podManagementPolicy)).
SetVolumeClaimTemplates(pvcs...).
SetTemplate(*podTemplate).
SetUpdateStrategyType(v1.StatefulSetUpdateStrategyType(updateStrategy)).
SetPaused(true).
GetObject()
gvk := schema.GroupVersionKind{
Group: types.RSMGVR().Group,
Version: types.RSMGVR().Version,
Kind: types.KindRSM,
}
rsm.SetGroupVersionKind(gvk)

unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(rsm)
if err != nil {
return err
}
_, err = dynamic.Resource(types.RSMGVR()).Namespace(obj.GetNamespace()).Create(context.TODO(),
&unstructured.Unstructured{Object: unstructuredMap}, metav1.CreateOptions{})
if err != nil && !apierrors.IsAlreadyExists(err) {
return fmt.Errorf("create rsm %s failed: %s", rsm.Name, err.Error())
}
// labels := obj.GetLabels()
// if labels == nil || labels[constant.AppManagedByLabelKey] != constant.AppName {
// return nil
// }
// // create a rsm
// matchLabels, _, _ := unstructured.NestedStringMap(obj.Object, "spec", "selector", "matchLabels")
// replicas, _, _ := unstructured.NestedInt64(obj.Object, "spec", "replicas")
// podManagementPolicy, _, _ := unstructured.NestedString(obj.Object, "spec", "podManagementPolicy")
// pvcsUnstructured, _, _ := unstructured.NestedSlice(obj.Object, "spec", "volumeClaimTemplates")
// var pvcs []corev1.PersistentVolumeClaim
// for _, pvcUnstructured := range pvcsUnstructured {
// pvc := &corev1.PersistentVolumeClaim{}
// pvcU, _ := pvcUnstructured.(map[string]interface{})
// if err := runtime.DefaultUnstructuredConverter.FromUnstructured(pvcU, pvc); err != nil {
// return err
// }
// pvcs = append(pvcs, *pvc)
// }
// template, _, _ := unstructured.NestedMap(obj.Object, "spec", "template")
// podTemplate := &corev1.PodTemplateSpec{}
// if err := runtime.DefaultUnstructuredConverter.FromUnstructured(template, podTemplate); err != nil {
// return err
// }
// updateStrategy, _, _ := unstructured.NestedString(obj.Object, "spec", "updateStrategy")
// rsm := builder.NewInstanceSetBuilder(obj.GetNamespace(), obj.GetName()).
// AddAnnotationsInMap(obj.GetAnnotations()).
// AddLabelsInMap(obj.GetLabels()).
// AddMatchLabelsInMap(matchLabels).
// SetReplicas(int32(replicas)).
// SetPodManagementPolicy(v1.PodManagementPolicyType(podManagementPolicy)).
// SetVolumeClaimTemplates(pvcs...).
// SetTemplate(*podTemplate).
// SetUpdateStrategyType(v1.StatefulSetUpdateStrategyType(updateStrategy)).
// SetPaused(true).
// GetObject()
// gvk := schema.GroupVersionKind{
// Group: types.RSMGVR().Group,
// Version: types.RSMGVR().Version,
// Kind: types.KindRSM,
// }
// rsm.SetGroupVersionKind(gvk)
//
// unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(rsm)
// if err != nil {
// return err
// }
// _, err = dynamic.Resource(types.RSMGVR()).Namespace(obj.GetNamespace()).Create(context.TODO(),
// &unstructured.Unstructured{Object: unstructuredMap}, metav1.CreateOptions{})
// if err != nil && !apierrors.IsAlreadyExists(err) {
// return fmt.Errorf("create rsm %s failed: %s", rsm.Name, err.Error())
// }
return nil
}

Expand Down

0 comments on commit a62d94c

Please sign in to comment.