Enhance the restore priorities list to support specifying the low prioritized resources that need to be restored in the last
Enhance the restore priorities list to support specifying the low prioritized resources that need to be r estored in the last Signed-off-by: Wenkai Yin(尹文开) <yinw@vmware.com>pull/5529/head
parent
6358507796
commit
6750836d69
|
@ -0,0 +1 @@
|
|||
Enhance the restore priorities list to support specifying the low prioritized resources that need to be restored in the last
|
|
@ -114,7 +114,7 @@ type serverConfig struct {
|
|||
pluginDir, metricsAddress, defaultBackupLocation string
|
||||
backupSyncPeriod, podVolumeOperationTimeout, resourceTerminatingTimeout time.Duration
|
||||
defaultBackupTTL, storeValidationFrequency, defaultCSISnapshotTimeout time.Duration
|
||||
restoreResourcePriorities []string
|
||||
restoreResourcePriorities restore.Priorities
|
||||
defaultVolumeSnapshotLocations map[string]string
|
||||
restoreOnly bool
|
||||
disabledControllers []string
|
||||
|
@ -208,7 +208,7 @@ func NewCommand(f client.Factory) *cobra.Command {
|
|||
command.Flags().DurationVar(&config.podVolumeOperationTimeout, "restic-timeout", config.podVolumeOperationTimeout, "How long backups/restores of pod volumes should be allowed to run before timing out.")
|
||||
command.Flags().BoolVar(&config.restoreOnly, "restore-only", config.restoreOnly, "Run in a mode where only restores are allowed; backups, schedules, and garbage-collection are all disabled. DEPRECATED: this flag will be removed in v2.0. Use read-only backup storage locations instead.")
|
||||
command.Flags().StringSliceVar(&config.disabledControllers, "disable-controllers", config.disabledControllers, fmt.Sprintf("List of controllers to disable on startup. Valid values are %s", strings.Join(controller.DisableableControllers, ",")))
|
||||
command.Flags().StringSliceVar(&config.restoreResourcePriorities, "restore-resource-priorities", config.restoreResourcePriorities, "Desired order of resource restores; any resource not in the list will be restored alphabetically after the prioritized resources.")
|
||||
command.Flags().Var(&config.restoreResourcePriorities, "restore-resource-priorities", "Desired order of resource restores, the priority list contains two parts which are split by \"-\" element. The resources before \"-\" element are restored first as high priorities, the resources after \"-\" element are restored last as low priorities, and any resource not in the list will be restored alphabetically between the high and low priorities.")
|
||||
command.Flags().StringVar(&config.defaultBackupLocation, "default-backup-storage-location", config.defaultBackupLocation, "Name of the default backup storage location. DEPRECATED: this flag will be removed in v2.0. Use \"velero backup-location set --default\" instead.")
|
||||
command.Flags().DurationVar(&config.storeValidationFrequency, "store-validation-frequency", config.storeValidationFrequency, "How often to verify if the storage is valid. Optional. Set this to `0s` to disable sync. Default 1 minute.")
|
||||
command.Flags().Var(&volumeSnapshotLocations, "default-volume-snapshot-locations", "List of unique volume providers and default volume snapshot location (provider1:location-01,provider2:location-02,...)")
|
||||
|
@ -467,6 +467,7 @@ func (s *server) veleroResourcesExist() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// High priorities:
|
||||
// - Custom Resource Definitions come before Custom Resource so that they can be
|
||||
// restored with their corresponding CRD.
|
||||
// - Namespaces go second because all namespaced resources depend on them.
|
||||
|
@ -489,28 +490,36 @@ func (s *server) veleroResourcesExist() error {
|
|||
// - CAPI Clusters come before ClusterResourceSets because failing to do so means the CAPI controller-manager will panic.
|
||||
// Both Clusters and ClusterResourceSets need to come before ClusterResourceSetBinding in order to properly restore workload clusters.
|
||||
// See https://github.com/kubernetes-sigs/cluster-api/issues/4105
|
||||
var defaultRestorePriorities = []string{
|
||||
"customresourcedefinitions",
|
||||
"namespaces",
|
||||
"storageclasses",
|
||||
"volumesnapshotclass.snapshot.storage.k8s.io",
|
||||
"volumesnapshotcontents.snapshot.storage.k8s.io",
|
||||
"volumesnapshots.snapshot.storage.k8s.io",
|
||||
"persistentvolumes",
|
||||
"persistentvolumeclaims",
|
||||
"secrets",
|
||||
"configmaps",
|
||||
"serviceaccounts",
|
||||
"limitranges",
|
||||
"pods",
|
||||
// we fully qualify replicasets.apps because prior to Kubernetes 1.16, replicasets also
|
||||
// existed in the extensions API group, but we back up replicasets from "apps" so we want
|
||||
// to ensure that we prioritize restoring from "apps" too, since this is how they're stored
|
||||
// in the backup.
|
||||
"replicasets.apps",
|
||||
"clusterclasses.cluster.x-k8s.io",
|
||||
"clusters.cluster.x-k8s.io",
|
||||
"clusterresourcesets.addons.cluster.x-k8s.io",
|
||||
//
|
||||
// Low priorities:
|
||||
// - Tanzu ClusterBootstrap go last as it can reference any other kind of resources
|
||||
var defaultRestorePriorities = restore.Priorities{
|
||||
HighPriorities: []string{
|
||||
"customresourcedefinitions",
|
||||
"namespaces",
|
||||
"storageclasses",
|
||||
"volumesnapshotclass.snapshot.storage.k8s.io",
|
||||
"volumesnapshotcontents.snapshot.storage.k8s.io",
|
||||
"volumesnapshots.snapshot.storage.k8s.io",
|
||||
"persistentvolumes",
|
||||
"persistentvolumeclaims",
|
||||
"secrets",
|
||||
"configmaps",
|
||||
"serviceaccounts",
|
||||
"limitranges",
|
||||
"pods",
|
||||
// we fully qualify replicasets.apps because prior to Kubernetes 1.16, replicasets also
|
||||
// existed in the extensions API group, but we back up replicasets from "apps" so we want
|
||||
// to ensure that we prioritize restoring from "apps" too, since this is how they're stored
|
||||
// in the backup.
|
||||
"replicasets.apps",
|
||||
"clusterclasses.cluster.x-k8s.io",
|
||||
"clusters.cluster.x-k8s.io",
|
||||
"clusterresourcesets.addons.cluster.x-k8s.io",
|
||||
},
|
||||
LowPriorities: []string{
|
||||
"clusterbootstraps.run.tanzu.vmware.com",
|
||||
},
|
||||
}
|
||||
|
||||
func (s *server) initRestic() error {
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Copyright The Velero Contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package restore
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
prioritySeparator = "-"
|
||||
)
|
||||
|
||||
// Priorities defines the desired order of resource operations:
|
||||
// Resources in the HighPriorities list will be handled first
|
||||
// Resources in the LowPriorities list will be handled last
|
||||
// Other resources will be handled alphabetically after the high prioritized resources and before the low prioritized resources
|
||||
type Priorities struct {
|
||||
HighPriorities []string
|
||||
LowPriorities []string
|
||||
}
|
||||
|
||||
// String returns a string representation of Priority.
|
||||
func (p *Priorities) String() string {
|
||||
priorities := p.HighPriorities
|
||||
if len(p.LowPriorities) > 0 {
|
||||
priorities = append(priorities, prioritySeparator)
|
||||
priorities = append(priorities, p.LowPriorities...)
|
||||
}
|
||||
return strings.Join(priorities, ",")
|
||||
}
|
||||
|
||||
// Set parses the provided string to the priority object
|
||||
func (p *Priorities) Set(s string) error {
|
||||
if len(s) == 0 {
|
||||
return nil
|
||||
}
|
||||
strs := strings.Split(s, ",")
|
||||
separatorIndex := -1
|
||||
for i, str := range strs {
|
||||
if str == prioritySeparator {
|
||||
if separatorIndex > -1 {
|
||||
return fmt.Errorf("multiple priority separator %q found", prioritySeparator)
|
||||
}
|
||||
separatorIndex = i
|
||||
}
|
||||
}
|
||||
// has no separator
|
||||
if separatorIndex == -1 {
|
||||
p.HighPriorities = strs
|
||||
return nil
|
||||
}
|
||||
// start with separator
|
||||
if separatorIndex == 0 {
|
||||
// contain only separator
|
||||
if len(strs) == 1 {
|
||||
return nil
|
||||
}
|
||||
p.LowPriorities = strs[1:]
|
||||
return nil
|
||||
}
|
||||
// end with separator
|
||||
if separatorIndex == len(strs)-1 {
|
||||
p.HighPriorities = strs[:len(strs)-1]
|
||||
return nil
|
||||
}
|
||||
|
||||
// separator in the middle
|
||||
p.HighPriorities = strs[:separatorIndex]
|
||||
p.LowPriorities = strs[separatorIndex+1:]
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type specifies the flag type
|
||||
func (p *Priorities) Type() string {
|
||||
return "stringArray"
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright The Velero Contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package restore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStringOfPriorities(t *testing.T) {
|
||||
priority := Priorities{
|
||||
HighPriorities: []string{"high"},
|
||||
}
|
||||
assert.Equal(t, "high", priority.String())
|
||||
|
||||
priority = Priorities{
|
||||
HighPriorities: []string{"high"},
|
||||
LowPriorities: []string{"low"},
|
||||
}
|
||||
assert.Equal(t, "high,-,low", priority.String())
|
||||
}
|
||||
|
||||
func TestSetOfPriority(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
priorities Priorities
|
||||
hasErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty input",
|
||||
input: "",
|
||||
priorities: Priorities{},
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "only high priorities",
|
||||
input: "p0",
|
||||
priorities: Priorities{
|
||||
HighPriorities: []string{"p0"},
|
||||
},
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "only low priorities",
|
||||
input: "-,p9",
|
||||
priorities: Priorities{
|
||||
LowPriorities: []string{"p9"},
|
||||
},
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "only separator",
|
||||
input: "-",
|
||||
priorities: Priorities{},
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "multiple separators",
|
||||
input: "-,-",
|
||||
priorities: Priorities{},
|
||||
hasErr: true,
|
||||
},
|
||||
{
|
||||
name: "contain both high and low priorities",
|
||||
input: "p0,p1,p2,-,p9",
|
||||
priorities: Priorities{
|
||||
HighPriorities: []string{"p0", "p1", "p2"},
|
||||
LowPriorities: []string{"p9"},
|
||||
},
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "end with separator",
|
||||
input: "p0,-",
|
||||
priorities: Priorities{
|
||||
HighPriorities: []string{"p0"},
|
||||
},
|
||||
hasErr: false,
|
||||
},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
p := Priorities{}
|
||||
err := p.Set(c.input)
|
||||
if c.hasErr {
|
||||
require.NotNil(t, err)
|
||||
} else {
|
||||
require.Nil(t, err)
|
||||
}
|
||||
assert.Equal(t, c.priorities, p)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -107,7 +107,7 @@ type kubernetesRestorer struct {
|
|||
resticRestorerFactory restic.RestorerFactory
|
||||
resticTimeout time.Duration
|
||||
resourceTerminatingTimeout time.Duration
|
||||
resourcePriorities []string
|
||||
resourcePriorities Priorities
|
||||
fileSystem filesystem.Interface
|
||||
pvRenamer func(string) (string, error)
|
||||
logger logrus.FieldLogger
|
||||
|
@ -120,7 +120,7 @@ func NewKubernetesRestorer(
|
|||
restoreClient velerov1client.RestoresGetter,
|
||||
discoveryHelper discovery.Helper,
|
||||
dynamicFactory client.DynamicFactory,
|
||||
resourcePriorities []string,
|
||||
resourcePriorities Priorities,
|
||||
namespaceClient corev1.NamespaceInterface,
|
||||
resticRestorerFactory restic.RestorerFactory,
|
||||
resticTimeout time.Duration,
|
||||
|
@ -351,7 +351,7 @@ type restoreContext struct {
|
|||
renamedPVs map[string]string
|
||||
pvRenamer func(string) (string, error)
|
||||
discoveryHelper discovery.Helper
|
||||
resourcePriorities []string
|
||||
resourcePriorities Priorities
|
||||
hooksWaitGroup sync.WaitGroup
|
||||
hooksErrs chan error
|
||||
resourceRestoreHooks []hook.ResourceRestoreHook
|
||||
|
@ -367,19 +367,31 @@ type resourceClientKey struct {
|
|||
|
||||
// getOrderedResources returns an ordered list of resource identifiers to restore,
|
||||
// based on the provided resource priorities and backup contents. The returned list
|
||||
// begins with all of the prioritized resources (in order), and appends to that
|
||||
// an alphabetized list of all resources in the backup.
|
||||
func getOrderedResources(resourcePriorities []string, backupResources map[string]*archive.ResourceItems) []string {
|
||||
// alphabetize resources in the backup
|
||||
orderedBackupResources := make([]string, 0, len(backupResources))
|
||||
// begins with all of the high prioritized resources (in order), ends with all of
|
||||
// the low prioritized resources(in order), and an alphabetized list of resources
|
||||
// in the backup(pick out the prioritized resources) is put in the middle.
|
||||
func getOrderedResources(resourcePriorities Priorities, backupResources map[string]*archive.ResourceItems) []string {
|
||||
priorities := map[string]struct{}{}
|
||||
for _, priority := range resourcePriorities.HighPriorities {
|
||||
priorities[priority] = struct{}{}
|
||||
}
|
||||
for _, priority := range resourcePriorities.LowPriorities {
|
||||
priorities[priority] = struct{}{}
|
||||
}
|
||||
|
||||
// pick the prioritized resources out
|
||||
var orderedBackupResources []string
|
||||
for resource := range backupResources {
|
||||
if _, exist := priorities[resource]; exist {
|
||||
continue
|
||||
}
|
||||
orderedBackupResources = append(orderedBackupResources, resource)
|
||||
}
|
||||
// alphabetize resources in the backup
|
||||
sort.Strings(orderedBackupResources)
|
||||
|
||||
// Main list: everything in resource priorities, followed by what's in the
|
||||
// backup (alphabetized).
|
||||
return append(resourcePriorities, orderedBackupResources...)
|
||||
list := append(resourcePriorities.HighPriorities, orderedBackupResources...)
|
||||
return append(list, resourcePriorities.LowPriorities...)
|
||||
}
|
||||
|
||||
type progressUpdate struct {
|
||||
|
@ -466,7 +478,7 @@ func (ctx *restoreContext) execute() (Result, Result) {
|
|||
backupResources,
|
||||
make([]restoreableResource, 0),
|
||||
sets.NewString(),
|
||||
[]string{"customresourcedefinitions"},
|
||||
Priorities{HighPriorities: []string{"customresourcedefinitions"}},
|
||||
false,
|
||||
)
|
||||
warnings.Merge(&w)
|
||||
|
@ -1790,7 +1802,7 @@ func (ctx *restoreContext) getOrderedResourceCollection(
|
|||
backupResources map[string]*archive.ResourceItems,
|
||||
restoreResourceCollection []restoreableResource,
|
||||
processedResources sets.String,
|
||||
resourcePriorities []string,
|
||||
resourcePriorities Priorities,
|
||||
includeAllResources bool,
|
||||
) ([]restoreableResource, sets.String, Result, Result) {
|
||||
var warnings, errs Result
|
||||
|
@ -1812,7 +1824,7 @@ func (ctx *restoreContext) getOrderedResourceCollection(
|
|||
if includeAllResources {
|
||||
resourceList = getOrderedResources(resourcePriorities, backupResources)
|
||||
} else {
|
||||
resourceList = resourcePriorities
|
||||
resourceList = resourcePriorities.HighPriorities
|
||||
}
|
||||
for _, resource := range resourceList {
|
||||
// try to resolve the resource via discovery to a complete group/version/resource
|
||||
|
|
|
@ -679,7 +679,7 @@ func TestRestoreResourcePriorities(t *testing.T) {
|
|||
backup *velerov1api.Backup
|
||||
apiResources []*test.APIResource
|
||||
tarball io.Reader
|
||||
resourcePriorities []string
|
||||
resourcePriorities Priorities
|
||||
}{
|
||||
{
|
||||
name: "resources are restored according to the specified resource priorities",
|
||||
|
@ -713,7 +713,10 @@ func TestRestoreResourcePriorities(t *testing.T) {
|
|||
test.Deployments(),
|
||||
test.ServiceAccounts(),
|
||||
},
|
||||
resourcePriorities: []string{"persistentvolumes", "serviceaccounts", "pods", "deployments.apps"},
|
||||
resourcePriorities: Priorities{
|
||||
HighPriorities: []string{"persistentvolumes", "persistentvolumeclaims", "serviceaccounts"},
|
||||
LowPriorities: []string{"deployments.apps"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -745,7 +748,7 @@ func TestRestoreResourcePriorities(t *testing.T) {
|
|||
)
|
||||
|
||||
assertEmptyResults(t, warnings, errs)
|
||||
assertResourceCreationOrder(t, tc.resourcePriorities, recorder.resources)
|
||||
assertResourceCreationOrder(t, []string{"persistentvolumes", "persistentvolumeclaims", "serviceaccounts", "pods", "deployments.apps"}, recorder.resources)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2622,7 +2625,7 @@ func TestRestorePersistentVolumes(t *testing.T) {
|
|||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
h := newHarness(t)
|
||||
h.restorer.resourcePriorities = []string{"persistentvolumes", "persistentvolumeclaims"}
|
||||
h.restorer.resourcePriorities = Priorities{HighPriorities: []string{"persistentvolumes", "persistentvolumeclaims"}}
|
||||
h.restorer.pvRenamer = func(oldName string) (string, error) {
|
||||
renamed := "renamed-" + oldName
|
||||
return renamed, nil
|
||||
|
@ -2940,19 +2943,19 @@ func TestIsCompleted(t *testing.T) {
|
|||
func Test_getOrderedResources(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
resourcePriorities []string
|
||||
resourcePriorities Priorities
|
||||
backupResources map[string]*archive.ResourceItems
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "when only priorities are specified, they're returned in order",
|
||||
resourcePriorities: []string{"prio-3", "prio-2", "prio-1"},
|
||||
resourcePriorities: Priorities{HighPriorities: []string{"prio-3", "prio-2", "prio-1"}},
|
||||
backupResources: nil,
|
||||
want: []string{"prio-3", "prio-2", "prio-1"},
|
||||
},
|
||||
{
|
||||
name: "when only backup resources are specified, they're returned in alphabetical order",
|
||||
resourcePriorities: nil,
|
||||
resourcePriorities: Priorities{},
|
||||
backupResources: map[string]*archive.ResourceItems{
|
||||
"backup-resource-3": nil,
|
||||
"backup-resource-2": nil,
|
||||
|
@ -2962,14 +2965,26 @@ func Test_getOrderedResources(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "when priorities and backup resources are specified, they're returned in the correct order",
|
||||
resourcePriorities: []string{"prio-3", "prio-2", "prio-1"},
|
||||
resourcePriorities: Priorities{HighPriorities: []string{"prio-3", "prio-2", "prio-1"}},
|
||||
backupResources: map[string]*archive.ResourceItems{
|
||||
"prio-3": nil,
|
||||
"backup-resource-3": nil,
|
||||
"backup-resource-2": nil,
|
||||
"backup-resource-1": nil,
|
||||
},
|
||||
want: []string{"prio-3", "prio-2", "prio-1", "backup-resource-1", "backup-resource-2", "backup-resource-3", "prio-3"},
|
||||
want: []string{"prio-3", "prio-2", "prio-1", "backup-resource-1", "backup-resource-2", "backup-resource-3"},
|
||||
},
|
||||
{
|
||||
name: "when priorities and backup resources are specified, they're returned in the correct order",
|
||||
resourcePriorities: Priorities{HighPriorities: []string{"prio-3", "prio-2", "prio-1"}, LowPriorities: []string{"prio-0"}},
|
||||
backupResources: map[string]*archive.ResourceItems{
|
||||
"prio-3": nil,
|
||||
"prio-0": nil,
|
||||
"backup-resource-3": nil,
|
||||
"backup-resource-2": nil,
|
||||
"backup-resource-1": nil,
|
||||
},
|
||||
want: []string{"prio-3", "prio-2", "prio-1", "backup-resource-1", "backup-resource-2", "backup-resource-3", "prio-0"},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue