Backup all groups and versions with backward compatibility (#2373)
* Backup all API Groups versions while keeping backward compatibility Signed-off-by: Rafael Brito <rbrito@vmware.com> * Backup all API Groups versions while keeping backward compatibility Signed-off-by: Rafael Brito <rbrito@vmware.com> * Adding feature flag to enable backup of multiple API group versions Signed-off-by: Rafael Brito <rbrito@vmware.com>pull/2500/head
parent
e569637dc7
commit
0d97f9400e
|
@ -212,10 +212,15 @@ const (
|
|||
|
||||
// BackupStatus captures the current status of a Velero backup.
|
||||
type BackupStatus struct {
|
||||
// Version is the backup format version.
|
||||
// Version is the backup format major version.
|
||||
// Deprecated: Please see FormatVersion
|
||||
// +optional
|
||||
Version int `json:"version,omitempty"`
|
||||
|
||||
// FormatVersion is the backup format version, including major, minor, and patch version.
|
||||
// +optional
|
||||
FormatVersion string `json:"formatVersion,omitempty"`
|
||||
|
||||
// Expiration is when this Backup is eligible for garbage-collection.
|
||||
// +optional
|
||||
// +nullable
|
||||
|
|
|
@ -39,4 +39,11 @@ const (
|
|||
|
||||
// CSIFeatureFlag is the feature flag string that defines whether or not CSI features are being used.
|
||||
CSIFeatureFlag = "EnableCSI"
|
||||
|
||||
// PreferredVersionDir is the suffix name of the directory containing the preferred version of the API group
|
||||
// resource within a Velero backup.
|
||||
PreferredVersionDir = "-preferredversion"
|
||||
|
||||
// APIGroupVersionsFeatureFlag is the feature flag string that defines whether or not to handle multiple API Group Versions
|
||||
APIGroupVersionsFeatureFlag = "EnableAPIGroupVersions"
|
||||
)
|
||||
|
|
|
@ -40,9 +40,13 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
)
|
||||
|
||||
// BackupVersion is the current backup version for Velero.
|
||||
// BackupVersion is the current backup major version for Velero.
|
||||
// Deprecated, use BackupFormatVersion
|
||||
const BackupVersion = 1
|
||||
|
||||
// BackupFormatVersion is the current backup version for Velero, including major, minor, and patch.
|
||||
const BackupFormatVersion = "1.1.0"
|
||||
|
||||
// Backupper performs backups.
|
||||
type Backupper interface {
|
||||
// Backup takes a backup using the specification in the api.Backup and writes backup and log data
|
||||
|
@ -284,7 +288,7 @@ func (kb *kubernetesBackupper) Backup(log logrus.FieldLogger, backupRequest *Req
|
|||
|
||||
func (kb *kubernetesBackupper) writeBackupVersion(tw *tar.Writer) error {
|
||||
versionFile := filepath.Join(api.MetadataDir, "version")
|
||||
versionString := fmt.Sprintf("%d\n", BackupVersion)
|
||||
versionString := fmt.Sprintf("%s\n", BackupFormatVersion)
|
||||
|
||||
hdr := &tar.Header{
|
||||
Name: versionFile,
|
||||
|
|
|
@ -99,6 +99,16 @@ func TestBackedUpItemsMatchesTarballContents(t *testing.T) {
|
|||
}
|
||||
file = file + "/" + item.name + ".json"
|
||||
expectedFiles = append(expectedFiles, file)
|
||||
|
||||
fileWithVersion := "resources/" + gvkToResource[item.resource]
|
||||
if item.namespace != "" {
|
||||
fileWithVersion = fileWithVersion + "/v1-preferredversion/" + "namespaces/" + item.namespace
|
||||
} else {
|
||||
file = file + "/cluster"
|
||||
fileWithVersion = fileWithVersion + "/v1-preferredversion" + "/cluster"
|
||||
}
|
||||
fileWithVersion = fileWithVersion + "/" + item.name + ".json"
|
||||
expectedFiles = append(expectedFiles, fileWithVersion)
|
||||
}
|
||||
|
||||
assertTarballContents(t, backupFile, append(expectedFiles, "metadata/version")...)
|
||||
|
@ -135,6 +145,10 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -155,6 +169,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -175,6 +191,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -195,6 +213,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -215,6 +235,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -241,6 +263,10 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -266,6 +292,9 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/persistentvolumes/cluster/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/bar.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -290,6 +319,9 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/persistentvolumes/cluster/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/bar.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -314,6 +346,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/persistentvolumes/cluster/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/bar.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -341,6 +375,12 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/persistentvolumes/cluster/bar.json",
|
||||
"resources/persistentvolumes/cluster/baz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/bar.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/baz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -365,6 +405,10 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/ns-2/pod-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -387,6 +431,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -408,6 +454,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -432,6 +480,11 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/ns-3/pod-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -454,6 +507,9 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-1.json",
|
||||
"resources/pods/namespaces/ns-3/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -477,6 +533,11 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/ns-3/pod-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-3/pod-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -499,6 +560,10 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -521,6 +586,10 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -541,6 +610,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -578,6 +649,8 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -599,8 +672,12 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/foo/bar.json",
|
||||
"resources/pods/namespaces/zoo/raz.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -614,6 +691,7 @@ func TestBackupResourceFiltering(t *testing.T) {
|
|||
},
|
||||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -668,6 +746,10 @@ func TestCRDInclusion(t *testing.T) {
|
|||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/test.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/namespaces/foo/vsl-1.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/backups.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/test.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/vsl-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -687,6 +769,7 @@ func TestCRDInclusion(t *testing.T) {
|
|||
},
|
||||
want: []string{
|
||||
"resources/volumesnapshotlocations.velero.io/namespaces/foo/vsl-1.json",
|
||||
"resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/vsl-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -709,6 +792,10 @@ func TestCRDInclusion(t *testing.T) {
|
|||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/test.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/namespaces/foo/vsl-1.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/backups.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/test.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/vsl-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -729,6 +816,8 @@ func TestCRDInclusion(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/namespaces/foo/vsl-1.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/vsl-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -749,6 +838,7 @@ func TestCRDInclusion(t *testing.T) {
|
|||
},
|
||||
want: []string{
|
||||
"resources/volumesnapshotlocations.velero.io/namespaces/foo/vsl-1.json",
|
||||
"resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/vsl-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -772,6 +862,10 @@ func TestCRDInclusion(t *testing.T) {
|
|||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/cluster/test.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/namespaces/foo/vsl-1.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/backups.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/volumesnapshotlocations.velero.io.json",
|
||||
"resources/customresourcedefinitions.apiextensions.k8s.io/v1beta1-preferredversion/cluster/test.velero.io.json",
|
||||
"resources/volumesnapshotlocations.velero.io/v1-preferredversion/namespaces/foo/vsl-1.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -818,6 +912,8 @@ func TestBackupResourceCohabitation(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/deployments.extensions/namespaces/foo/bar.json",
|
||||
"resources/deployments.extensions/namespaces/zoo/raz.json",
|
||||
"resources/deployments.extensions/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.extensions/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -836,6 +932,8 @@ func TestBackupResourceCohabitation(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/deployments.apps/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/namespaces/zoo/raz.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/foo/bar.json",
|
||||
"resources/deployments.apps/v1-preferredversion/namespaces/zoo/raz.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -877,7 +975,7 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) {
|
|||
|
||||
h.backupper.Backup(h.log, backup1, backup1File, nil, nil)
|
||||
|
||||
assertTarballContents(t, backup1File, "metadata/version", "resources/deployments.apps/namespaces/ns-1/deploy-1.json")
|
||||
assertTarballContents(t, backup1File, "metadata/version", "resources/deployments.apps/namespaces/ns-1/deploy-1.json", "resources/deployments.apps/v1-preferredversion/namespaces/ns-1/deploy-1.json")
|
||||
|
||||
// run and verify backup 2
|
||||
backup2 := &Request{
|
||||
|
@ -887,7 +985,7 @@ func TestBackupUsesNewCohabitatingResourcesForEachBackup(t *testing.T) {
|
|||
|
||||
h.backupper.Backup(h.log, backup2, backup2File, nil, nil)
|
||||
|
||||
assertTarballContents(t, backup2File, "metadata/version", "resources/deployments.apps/namespaces/ns-1/deploy-1.json")
|
||||
assertTarballContents(t, backup2File, "metadata/version", "resources/deployments.apps/namespaces/ns-1/deploy-1.json", "resources/deployments.apps/v1-preferredversion/namespaces/ns-1/deploy-1.json")
|
||||
}
|
||||
|
||||
// TestBackupResourceOrdering runs backups of the core API group and ensures that items are backed
|
||||
|
@ -1431,6 +1529,9 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/namespaces/ns-3/pod-3.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-3/pod-3.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1457,6 +1558,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
},
|
||||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1488,6 +1590,9 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1516,6 +1621,7 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
},
|
||||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1546,6 +1652,8 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1575,6 +1683,8 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/persistentvolumes/cluster/pv-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/persistentvolumes/v1-preferredversion/cluster/pv-2.json",
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -1604,6 +1714,8 @@ func TestBackupActionAdditionalItems(t *testing.T) {
|
|||
want: []string{
|
||||
"resources/pods/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/namespaces/ns-3/pod-3.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-3/pod-3.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -2194,6 +2306,8 @@ func TestBackupWithHooks(t *testing.T) {
|
|||
wantBackedUp: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -2243,6 +2357,8 @@ func TestBackupWithHooks(t *testing.T) {
|
|||
wantBackedUp: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-2.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -2297,6 +2413,7 @@ func TestBackupWithHooks(t *testing.T) {
|
|||
},
|
||||
wantBackedUp: []string{
|
||||
"resources/pods/namespaces/ns-1/pod-1.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-1/pod-1.json",
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -2348,6 +2465,7 @@ func TestBackupWithHooks(t *testing.T) {
|
|||
},
|
||||
wantBackedUp: []string{
|
||||
"resources/pods/namespaces/ns-2/pod-2.json",
|
||||
"resources/pods/v1-preferredversion/namespaces/ns-2/pod-2.json",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ func (f *defaultItemBackupperFactory) newItemBackupper(
|
|||
}
|
||||
|
||||
type ItemBackupper interface {
|
||||
backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error)
|
||||
backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource, preferredGVR schema.GroupVersionResource) (bool, error)
|
||||
}
|
||||
|
||||
type defaultItemBackupper struct {
|
||||
|
@ -112,7 +112,7 @@ type defaultItemBackupper struct {
|
|||
// namespaces IncludesExcludes list.
|
||||
// In addition to the error return, backupItem also returns a bool indicating whether the item
|
||||
// was actually backed up.
|
||||
func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource) (bool, error) {
|
||||
func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstructured, groupResource schema.GroupResource, preferredGVR schema.GroupVersionResource) (bool, error) {
|
||||
metadata, err := meta.Accessor(obj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -254,11 +254,27 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
|
|||
return false, kubeerrs.NewAggregate(backupErrs)
|
||||
}
|
||||
|
||||
// group version of this object
|
||||
// Used on filepath to backup up all groups and versions
|
||||
version := resourceVersion(obj)
|
||||
|
||||
// Getting the preferred group version of this resource
|
||||
preferredVersion := preferredGVR.Version
|
||||
|
||||
var filePath string
|
||||
|
||||
// API Group version is now part of path of backup as a subdirectory
|
||||
// it will add a prefix to subdirectory name for the preferred version
|
||||
versionPath := version
|
||||
|
||||
if version == preferredVersion {
|
||||
versionPath = version + api.PreferredVersionDir
|
||||
}
|
||||
|
||||
if namespace != "" {
|
||||
filePath = filepath.Join(api.ResourcesDir, groupResource.String(), api.NamespaceScopedDir, namespace, name+".json")
|
||||
filePath = filepath.Join(api.ResourcesDir, groupResource.String(), versionPath, api.NamespaceScopedDir, namespace, name+".json")
|
||||
} else {
|
||||
filePath = filepath.Join(api.ResourcesDir, groupResource.String(), api.ClusterScopedDir, name+".json")
|
||||
filePath = filepath.Join(api.ResourcesDir, groupResource.String(), versionPath, api.ClusterScopedDir, name+".json")
|
||||
}
|
||||
|
||||
itemBytes, err := json.Marshal(obj.UnstructuredContent())
|
||||
|
@ -282,6 +298,33 @@ func (ib *defaultItemBackupper) backupItem(logger logrus.FieldLogger, obj runtim
|
|||
return false, errors.WithStack(err)
|
||||
}
|
||||
|
||||
// backing up the preferred version backup without API Group version on path - this is for backward compability
|
||||
|
||||
if version == preferredVersion {
|
||||
if namespace != "" {
|
||||
filePath = filepath.Join(api.ResourcesDir, groupResource.String(), api.NamespaceScopedDir, namespace, name+".json")
|
||||
} else {
|
||||
filePath = filepath.Join(api.ResourcesDir, groupResource.String(), api.ClusterScopedDir, name+".json")
|
||||
}
|
||||
|
||||
hdr = &tar.Header{
|
||||
Name: filePath,
|
||||
Size: int64(len(itemBytes)),
|
||||
Typeflag: tar.TypeReg,
|
||||
Mode: 0755,
|
||||
ModTime: time.Now(),
|
||||
}
|
||||
|
||||
if err := ib.tarWriter.WriteHeader(hdr); err != nil {
|
||||
return false, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if _, err := ib.tarWriter.Write(itemBytes); err != nil {
|
||||
return false, errors.WithStack(err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
|
@ -352,7 +395,7 @@ func (ib *defaultItemBackupper) executeActions(
|
|||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
if _, err = ib.additionalItemBackupper.backupItem(log, additionalItem, gvr.GroupResource()); err != nil {
|
||||
if _, err = ib.additionalItemBackupper.backupItem(log, additionalItem, gvr.GroupResource(), gvr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
@ -524,3 +567,10 @@ func resourceKey(obj runtime.Unstructured) string {
|
|||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
return fmt.Sprintf("%s/%s", gvk.GroupVersion().String(), gvk.Kind)
|
||||
}
|
||||
|
||||
// resourceVersion returns a string representing the object's API Version (e.g.
|
||||
// v1 if item belongs to apps/v1
|
||||
func resourceVersion(obj runtime.Unstructured) string {
|
||||
gvk := obj.GetObjectKind().GroupVersionKind()
|
||||
return gvk.Version
|
||||
}
|
||||
|
|
|
@ -110,6 +110,12 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
|
|||
}
|
||||
gr := schema.GroupResource{Group: gv.Group, Resource: resource.Name}
|
||||
|
||||
// Getting the preferred group version of this resource
|
||||
preferredGVR, _, err := rb.discoveryHelper.ResourceFor(gr.WithVersion(""))
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
clusterScoped := !resource.Namespaced
|
||||
|
||||
// If the resource we are backing up is NOT namespaces, and it is cluster-scoped, check to see if
|
||||
|
@ -194,7 +200,7 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
|
|||
continue
|
||||
}
|
||||
|
||||
if _, err := itemBackupper.backupItem(log, unstructured, gr); err != nil {
|
||||
if _, err := itemBackupper.backupItem(log, unstructured, gr, preferredGVR); err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error backing up namespace")
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +239,7 @@ func (rb *defaultResourceBackupper) backupResource(group *metav1.APIResourceList
|
|||
|
||||
// do the backup
|
||||
for _, item := range unstructuredList.Items {
|
||||
if rb.backupItem(log, gr, itemBackupper, &item) {
|
||||
if rb.backupItem(log, gr, itemBackupper, &item, preferredGVR) {
|
||||
backedUpItem = true
|
||||
}
|
||||
}
|
||||
|
@ -254,6 +260,7 @@ func (rb *defaultResourceBackupper) backupItem(
|
|||
gr schema.GroupResource,
|
||||
itemBackupper ItemBackupper,
|
||||
unstructured runtime.Unstructured,
|
||||
preferredGVR schema.GroupVersionResource,
|
||||
) bool {
|
||||
metadata, err := meta.Accessor(unstructured)
|
||||
if err != nil {
|
||||
|
@ -271,7 +278,7 @@ func (rb *defaultResourceBackupper) backupItem(
|
|||
return false
|
||||
}
|
||||
|
||||
backedUpItem, err := itemBackupper.backupItem(log, unstructured, gr)
|
||||
backedUpItem, err := itemBackupper.backupItem(log, unstructured, gr, preferredGVR)
|
||||
if aggregate, ok := err.(kubeerrs.Aggregate); ok {
|
||||
log.Infof("%d errors encountered backup up item", len(aggregate.Errors()))
|
||||
// log each error separately so we get error location info in the log, and an
|
||||
|
@ -324,7 +331,7 @@ func (rb *defaultResourceBackupper) backupCRD(log logrus.FieldLogger, gr schema.
|
|||
}
|
||||
log.Infof("Found associated CRD %s to add to backup", gr.String())
|
||||
|
||||
rb.backupItem(log, gvr.GroupResource(), itemBackupper, unstructured)
|
||||
rb.backupItem(log, gvr.GroupResource(), itemBackupper, unstructured, gvr)
|
||||
}
|
||||
|
||||
// getNamespacesToList examines ie and resolves the includes and excludes to a full list of
|
||||
|
|
|
@ -320,9 +320,12 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
|
|||
Backup: backup.DeepCopy(), // don't modify items in the cache
|
||||
}
|
||||
|
||||
// set backup version
|
||||
// set backup major version - deprecated, use Status.FormatVersion
|
||||
request.Status.Version = pkgbackup.BackupVersion
|
||||
|
||||
// set backup major, minor, and patch version
|
||||
request.Status.FormatVersion = pkgbackup.BackupFormatVersion
|
||||
|
||||
if request.Spec.TTL.Duration == 0 {
|
||||
// set default backup TTL
|
||||
request.Spec.TTL.Duration = c.defaultBackupTTL
|
||||
|
|
|
@ -374,6 +374,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
|
@ -407,6 +408,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
|
@ -443,6 +445,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
|
@ -477,6 +480,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
Expiration: &metav1.Time{now.Add(10 * time.Minute)},
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
|
@ -511,6 +515,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
|
@ -547,6 +552,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
|
@ -581,6 +587,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
Version: 1,
|
||||
FormatVersion: "1.1.0",
|
||||
StartTimestamp: ×tamp,
|
||||
CompletionTimestamp: ×tamp,
|
||||
Expiration: ×tamp,
|
||||
|
|
|
@ -29,6 +29,8 @@ import (
|
|||
"k8s.io/client-go/discovery"
|
||||
"k8s.io/client-go/restmapper"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/features"
|
||||
kcmdutil "github.com/vmware-tanzu/velero/third_party/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
|
@ -57,7 +59,11 @@ type Helper interface {
|
|||
}
|
||||
|
||||
type serverResourcesInterface interface {
|
||||
// ServerPreferredResources() is used to populate Resources() with only Preferred Versions - this is the default
|
||||
ServerPreferredResources() ([]*metav1.APIResourceList, error)
|
||||
// ServerGroupsAndResources returns supported groups and resources for *all* groups and versions
|
||||
// Used to populate Resources() if feature flag is passed
|
||||
ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error)
|
||||
}
|
||||
|
||||
type helper struct {
|
||||
|
@ -112,14 +118,28 @@ func (h *helper) Refresh() error {
|
|||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
preferredResources, err := refreshServerPreferredResources(h.discoveryClient, h.logger)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
var serverResources []*metav1.APIResourceList
|
||||
|
||||
if features.IsEnabled(velerov1api.APIGroupVersionsFeatureFlag) {
|
||||
// ServerGroupsAndResources returns all APIGroup and APIResouceList - not only preferred versions
|
||||
_, serverAllResources, err := refreshServerGroupsAndResources(h.discoveryClient, h.logger)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
h.logger.Info("The '%s' feature flag was specified, using all API group versions.", velerov1api.APIGroupVersionsFeatureFlag)
|
||||
serverResources = serverAllResources
|
||||
} else {
|
||||
// ServerPreferredResources() returns only preferred APIGroup - this is the default since no feature flag has been passed
|
||||
serverPreferredResources, err := refreshServerPreferredResources(h.discoveryClient, h.logger)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
serverResources = serverPreferredResources
|
||||
}
|
||||
|
||||
h.resources = discovery.FilteredBy(
|
||||
discovery.ResourcePredicateFunc(filterByVerbs),
|
||||
preferredResources,
|
||||
serverResources,
|
||||
)
|
||||
|
||||
sortResources(h.resources)
|
||||
|
@ -172,6 +192,19 @@ func refreshServerPreferredResources(discoveryClient serverResourcesInterface, l
|
|||
return preferredResources, err
|
||||
}
|
||||
|
||||
func refreshServerGroupsAndResources(discoveryClient serverResourcesInterface, logger logrus.FieldLogger) ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
|
||||
serverGroups, serverResources, err := discoveryClient.ServerGroupsAndResources()
|
||||
if err != nil {
|
||||
if discoveryErr, ok := err.(*discovery.ErrGroupDiscoveryFailed); ok {
|
||||
for groupVersion, err := range discoveryErr.Groups {
|
||||
logger.WithError(err).Warnf("Failed to discover group: %v", groupVersion)
|
||||
}
|
||||
return serverGroups, serverResources, nil
|
||||
}
|
||||
}
|
||||
return serverGroups, serverResources, err
|
||||
}
|
||||
|
||||
func filterByVerbs(groupVersion string, r *metav1.APIResource) bool {
|
||||
return discovery.SupportsAllVerbs{Verbs: []string{"list", "create", "get", "delete"}}.Match(groupVersion, r)
|
||||
}
|
||||
|
|
|
@ -151,6 +151,7 @@ func TestRefreshServerPreferredResources(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
resourceList []*metav1.APIResourceList
|
||||
apiGroup []*metav1.APIGroup
|
||||
failedGroups map[schema.GroupVersion]error
|
||||
returnError error
|
||||
}{
|
||||
|
@ -183,7 +184,7 @@ func TestRefreshServerPreferredResources(t *testing.T) {
|
|||
formatFlag := logging.FormatText
|
||||
|
||||
for _, test := range tests {
|
||||
fakeServer := velerotest.NewFakeServerResourcesInterface(test.resourceList, test.failedGroups, test.returnError)
|
||||
fakeServer := velerotest.NewFakeServerResourcesInterface(test.resourceList, test.apiGroup, test.failedGroups, test.returnError)
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
resources, err := refreshServerPreferredResources(fakeServer, logging.DefaultLogger(logrus.DebugLevel, formatFlag))
|
||||
if test.returnError != nil {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -340,6 +340,10 @@ spec:
|
|||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
formatVersion:
|
||||
description: FormatVersion is the backup format version, including major,
|
||||
minor, and patch version.
|
||||
type: string
|
||||
phase:
|
||||
description: Phase is the current state of the Backup.
|
||||
enum:
|
||||
|
@ -366,7 +370,8 @@ spec:
|
|||
nullable: true
|
||||
type: array
|
||||
version:
|
||||
description: Version is the backup format version.
|
||||
description: 'Version is the backup format major version. Deprecated:
|
||||
Please see FormatVersion'
|
||||
type: integer
|
||||
volumeSnapshotsAttempted:
|
||||
description: VolumeSnapshotsAttempted is the total number of attempted
|
||||
|
|
|
@ -145,6 +145,7 @@ func (dh *FakeDiscoveryHelper) APIGroups() []metav1.APIGroup {
|
|||
|
||||
type FakeServerResourcesInterface struct {
|
||||
ResourceList []*metav1.APIResourceList
|
||||
ApiGroup []*metav1.APIGroup
|
||||
FailedGroups map[schema.GroupVersion]error
|
||||
ReturnError error
|
||||
}
|
||||
|
@ -159,9 +160,20 @@ func (di *FakeServerResourcesInterface) ServerPreferredResources() ([]*metav1.AP
|
|||
return di.ResourceList, &discovery.ErrGroupDiscoveryFailed{Groups: di.FailedGroups}
|
||||
}
|
||||
|
||||
func NewFakeServerResourcesInterface(resourceList []*metav1.APIResourceList, failedGroups map[schema.GroupVersion]error, returnError error) *FakeServerResourcesInterface {
|
||||
func (di *FakeServerResourcesInterface) ServerGroupsAndResources() ([]*metav1.APIGroup, []*metav1.APIResourceList, error) {
|
||||
if di.ReturnError != nil {
|
||||
return di.ApiGroup, di.ResourceList, di.ReturnError
|
||||
}
|
||||
if di.FailedGroups == nil || len(di.FailedGroups) == 0 {
|
||||
return di.ApiGroup, di.ResourceList, nil
|
||||
}
|
||||
return di.ApiGroup, di.ResourceList, &discovery.ErrGroupDiscoveryFailed{Groups: di.FailedGroups}
|
||||
}
|
||||
|
||||
func NewFakeServerResourcesInterface(resourceList []*metav1.APIResourceList, apiGroup []*metav1.APIGroup, failedGroups map[schema.GroupVersion]error, returnError error) *FakeServerResourcesInterface {
|
||||
helper := &FakeServerResourcesInterface{
|
||||
ResourceList: resourceList,
|
||||
ApiGroup: apiGroup,
|
||||
FailedGroups: failedGroups,
|
||||
ReturnError: returnError,
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ rootBucket/
|
|||
},
|
||||
"status": {
|
||||
"version": 1,
|
||||
"formatVersion": "1.1.0",
|
||||
"expiration": "2017-08-01T13:39:15Z",
|
||||
"phase": "Completed",
|
||||
"volumeBackups": {
|
||||
|
@ -57,7 +58,129 @@ rootBucket/
|
|||
```
|
||||
Note that this file includes detailed info about your volume snapshots in the `status.volumeBackups` field, which can be helpful if you want to manually check them in your cloud provider GUI.
|
||||
|
||||
## file format version: 1
|
||||
## Output File Format Versioning
|
||||
|
||||
The Velero output file format is intended to be relatively stable, but may change over time in order to support new features.
|
||||
|
||||
In order to accommodate this, Velero follows [Semantic Versioning](http://semver.org/) for the file format version.
|
||||
|
||||
Minor and patch versions will indicate backwards-compatible changes that previous versions of Velero can restore, including new directories or files.
|
||||
|
||||
A major version would indicate that a version of Velero older than the version that created the backup could not restore it, usually because of moved or renamed directories or files.
|
||||
|
||||
Major versions of the file format will be incremented with major version releases of Velero.
|
||||
However, a major version release of Velero does not necessarily mean that the backup format version changed - Velero 3.0 could still use backup file format 2.0, as an example.
|
||||
|
||||
## Versions
|
||||
|
||||
### File Format Version: 1.1 (Current)
|
||||
|
||||
In version 1.1, we have added the support of API groups versions as part of the backup (previously, only the preferred version of each API Groups was backed up). Each resource has one or more sub-directories, one sub-directory for each supported version of the API group. The preferred version API Group of each resource has the suffix "-preferredversion" as part of the sub-directory name. For backward compatibility, we kept the classic directory structure without the API Group version, which sits on the same level as the API Group sub-directory versions.
|
||||
By default, only the preferred API group of each resource is backed up.
|
||||
In order to take a backup of all API group versions, you need to run the Velero server with `--features=EnableAPIGroupVersions` feature flag. This is an experimental flag and the restore logic to handle multiple API Group Versions will be added in the future.
|
||||
|
||||
|
||||
When unzipped, a typical backup directory (e.g. `backup1234.tar.gz`) taken with this file format version looks like the following (with the feature flag):
|
||||
|
||||
```
|
||||
resources/
|
||||
persistentvolumes/
|
||||
cluster/
|
||||
pv01.json
|
||||
...
|
||||
v1-preferredversion/
|
||||
cluster/
|
||||
pv01.json
|
||||
...
|
||||
configmaps/
|
||||
namespaces/
|
||||
namespace1/
|
||||
myconfigmap.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v1-preferredversion/
|
||||
namespaces/
|
||||
namespace1/
|
||||
myconfigmap.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
pods/
|
||||
namespaces/
|
||||
namespace1/
|
||||
mypod.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v1-preferredversion/
|
||||
namespaces/
|
||||
namespace1/
|
||||
mypod.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
jobs.batch/
|
||||
namespaces/
|
||||
namespace1/
|
||||
awesome-job.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v1-preferredversion/
|
||||
namespaces/
|
||||
namespace1/
|
||||
awesome-job.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
deployments/
|
||||
namespaces/
|
||||
namespace1/
|
||||
cool-deployment.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v1-preferredversion/
|
||||
namespaces/
|
||||
namespace1/
|
||||
cool-deployment.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
horizontalpodautoscalers.autoscaling/
|
||||
namespaces/
|
||||
namespace1/
|
||||
hpa-to-the-rescue.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v1-preferredversion/
|
||||
namespaces/
|
||||
namespace1/
|
||||
hpa-to-the-rescue.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v2beta1/
|
||||
namespaces/
|
||||
namespace1/
|
||||
hpa-to-the-rescue.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
v2beta2/
|
||||
namespaces/
|
||||
namespace1/
|
||||
hpa-to-the-rescue.json
|
||||
...
|
||||
namespace2/
|
||||
...
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
### File Format Version: 1
|
||||
|
||||
When unzipped, a typical backup directory (e.g. `backup1234.tar.gz`) looks like the following:
|
||||
|
||||
|
@ -97,17 +220,3 @@ resources/
|
|||
...
|
||||
...
|
||||
```
|
||||
|
||||
## Output File Format Versioning
|
||||
|
||||
The Velero output file format is intended to be relatively stable, but may change over time in order to support new features.
|
||||
|
||||
In order to accommodate this, Velero follows [Semantic Versioning](http://semver.org/) for the file format version.
|
||||
|
||||
Minor and patch versions will indicate backwards-compatible changes that previous versions of Velero can restore, including new directories or files.
|
||||
|
||||
A major version would indicate that a version of Velero older than the version that created the backup could not restore it, usually because of moved or renamed directories or files.
|
||||
|
||||
Major versions of the file format will be incremented with major version releases of Velero.
|
||||
However, a major version release of Velero does not necessarily mean that the backup format version changed - Velero 3.0 could still use backup file format 2.0, as an example.
|
||||
|
||||
|
|
Loading…
Reference in New Issue