mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-03 03:35:22 +00:00
store volume snapshot info as JSON in backup storage
Signed-off-by: Steve Kriss <steve@heptio.com>
This commit is contained in:
@@ -177,21 +177,6 @@ spec:
|
|||||||
plural: volumesnapshotlocations
|
plural: volumesnapshotlocations
|
||||||
kind: VolumeSnapshotLocation
|
kind: VolumeSnapshotLocation
|
||||||
|
|
||||||
---
|
|
||||||
apiVersion: apiextensions.k8s.io/v1beta1
|
|
||||||
kind: CustomResourceDefinition
|
|
||||||
metadata:
|
|
||||||
name: volumesnapshots.ark.heptio.com
|
|
||||||
labels:
|
|
||||||
component: ark
|
|
||||||
spec:
|
|
||||||
group: ark.heptio.com
|
|
||||||
version: v1
|
|
||||||
scope: Namespaced
|
|
||||||
names:
|
|
||||||
plural: volumesnapshots
|
|
||||||
kind: VolumeSnapshot
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Namespace
|
kind: Namespace
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ func CustomResources() map[string]typeInfo {
|
|||||||
"ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}),
|
"ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}),
|
||||||
"BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}),
|
"BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}),
|
||||||
"VolumeSnapshotLocation": newTypeInfo("volumesnapshotlocations", &VolumeSnapshotLocation{}, &VolumeSnapshotLocationList{}),
|
"VolumeSnapshotLocation": newTypeInfo("volumesnapshotlocations", &VolumeSnapshotLocation{}, &VolumeSnapshotLocationList{}),
|
||||||
"VolumeSnapshot": newTypeInfo("volumesnapshots", &VolumeSnapshot{}, &VolumeSnapshotList{}),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 the Heptio Ark 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 v1
|
|
||||||
|
|
||||||
import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
|
|
||||||
// +genclient
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
|
||||||
|
|
||||||
// VolumeSnapshot represents a snapshot of a persistent volume
|
|
||||||
type VolumeSnapshot struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ObjectMeta `json:"metadata"`
|
|
||||||
|
|
||||||
Spec VolumeSnapshotSpec `json:"spec"`
|
|
||||||
Status VolumeSnapshotStatus `json:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
|
||||||
|
|
||||||
// VolumeSnapshotList is a list of VolumeSnapshots.
|
|
||||||
type VolumeSnapshotList struct {
|
|
||||||
metav1.TypeMeta `json:",inline"`
|
|
||||||
metav1.ListMeta `json:"metadata"`
|
|
||||||
Items []VolumeSnapshot `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshotSpec defines the specification for an Ark VolumeSnapshot.
|
|
||||||
type VolumeSnapshotSpec struct {
|
|
||||||
// Type is the type of the disk/volume in the cloud provider
|
|
||||||
// API.
|
|
||||||
Type string `json:"type"`
|
|
||||||
|
|
||||||
// AvailabilityZone is the where the volume is provisioned
|
|
||||||
// in the cloud provider.
|
|
||||||
AvailabilityZone string `json:"availabilityZone,omitempty"`
|
|
||||||
|
|
||||||
// Iops is the optional value of provisioned IOPS for the
|
|
||||||
// disk/volume in the cloud provider API.
|
|
||||||
Iops *int64 `json:"iops,omitempty"`
|
|
||||||
|
|
||||||
// Backup is a string containing the name of name of the Ark backup this snapshot is associated with.
|
|
||||||
Backup string `json:"backup"`
|
|
||||||
|
|
||||||
// Location is the name of the VolumeSnapshotLocation where this snapshot is stored.
|
|
||||||
Location string `json:"location"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshotStatus captures the current status of an Ark VolumeSnapshot.
|
|
||||||
type VolumeSnapshotStatus struct {
|
|
||||||
// SnapshotID is the UUID generated by Ark.
|
|
||||||
SnapshotID string `json:"snapshotID"`
|
|
||||||
|
|
||||||
// ProviderSnapshotID is the ID of the snapshot taken in the cloud
|
|
||||||
// provider API of this volume.
|
|
||||||
ProviderSnapshotID string `json:"providerSnapshotID"`
|
|
||||||
|
|
||||||
// Phase is the current state of the VolumeSnapshot.
|
|
||||||
Phase VolumeSnapshotPhase `json:"phase,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshotPhase is the lifecyle phase of an Ark VolumeSnapshot.
|
|
||||||
type VolumeSnapshotPhase string
|
|
||||||
|
|
||||||
const (
|
|
||||||
// VolumeSnapshotPhaseNew means the volume snapshot has been created but not
|
|
||||||
// yet processed by the VolumeSnapshotController.
|
|
||||||
VolumeSnapshotPhaseNew VolumeSnapshotPhase = "New"
|
|
||||||
|
|
||||||
// VolumeSnapshotPhaseCompleted means the volume snapshot was successfully created and can be restored from..
|
|
||||||
VolumeSnapshotPhaseCompleted VolumeSnapshotPhase = "Completed"
|
|
||||||
|
|
||||||
// VolumeSnapshotPhaseFailed means the volume snapshot was unable to execute.
|
|
||||||
VolumeSnapshotPhaseFailed VolumeSnapshotPhase = "Failed"
|
|
||||||
)
|
|
||||||
@@ -1376,67 +1376,6 @@ func (in *VolumeBackupInfo) DeepCopy() *VolumeBackupInfo {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *VolumeSnapshot) DeepCopyInto(out *VolumeSnapshot) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
|
||||||
in.Spec.DeepCopyInto(&out.Spec)
|
|
||||||
out.Status = in.Status
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshot.
|
|
||||||
func (in *VolumeSnapshot) DeepCopy() *VolumeSnapshot {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(VolumeSnapshot)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *VolumeSnapshot) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *VolumeSnapshotList) DeepCopyInto(out *VolumeSnapshotList) {
|
|
||||||
*out = *in
|
|
||||||
out.TypeMeta = in.TypeMeta
|
|
||||||
out.ListMeta = in.ListMeta
|
|
||||||
if in.Items != nil {
|
|
||||||
in, out := &in.Items, &out.Items
|
|
||||||
*out = make([]VolumeSnapshot, len(*in))
|
|
||||||
for i := range *in {
|
|
||||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotList.
|
|
||||||
func (in *VolumeSnapshotList) DeepCopy() *VolumeSnapshotList {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(VolumeSnapshotList)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
|
||||||
func (in *VolumeSnapshotList) DeepCopyObject() runtime.Object {
|
|
||||||
if c := in.DeepCopy(); c != nil {
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||||
func (in *VolumeSnapshotLocation) DeepCopyInto(out *VolumeSnapshotLocation) {
|
func (in *VolumeSnapshotLocation) DeepCopyInto(out *VolumeSnapshotLocation) {
|
||||||
*out = *in
|
*out = *in
|
||||||
@@ -1536,44 +1475,3 @@ func (in *VolumeSnapshotLocationStatus) DeepCopy() *VolumeSnapshotLocationStatus
|
|||||||
in.DeepCopyInto(out)
|
in.DeepCopyInto(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *VolumeSnapshotSpec) DeepCopyInto(out *VolumeSnapshotSpec) {
|
|
||||||
*out = *in
|
|
||||||
if in.Iops != nil {
|
|
||||||
in, out := &in.Iops, &out.Iops
|
|
||||||
if *in == nil {
|
|
||||||
*out = nil
|
|
||||||
} else {
|
|
||||||
*out = new(int64)
|
|
||||||
**out = **in
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotSpec.
|
|
||||||
func (in *VolumeSnapshotSpec) DeepCopy() *VolumeSnapshotSpec {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(VolumeSnapshotSpec)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
|
||||||
func (in *VolumeSnapshotStatus) DeepCopyInto(out *VolumeSnapshotStatus) {
|
|
||||||
*out = *in
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeSnapshotStatus.
|
|
||||||
func (in *VolumeSnapshotStatus) DeepCopy() *VolumeSnapshotStatus {
|
|
||||||
if in == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
out := new(VolumeSnapshotStatus)
|
|
||||||
in.DeepCopyInto(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import (
|
|||||||
"github.com/heptio/ark/pkg/kuberesource"
|
"github.com/heptio/ark/pkg/kuberesource"
|
||||||
"github.com/heptio/ark/pkg/podexec"
|
"github.com/heptio/ark/pkg/podexec"
|
||||||
"github.com/heptio/ark/pkg/restic"
|
"github.com/heptio/ark/pkg/restic"
|
||||||
|
"github.com/heptio/ark/pkg/volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
type itemBackupperFactory interface {
|
type itemBackupperFactory interface {
|
||||||
@@ -401,19 +402,14 @@ func (ib *defaultItemBackupper) takePVSnapshot(obj runtime.Unstructured, log log
|
|||||||
return errors.WithStack(err)
|
return errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
name := metadata.GetName()
|
pvFailureDomainZone := metadata.GetLabels()[zoneLabel]
|
||||||
var pvFailureDomainZone string
|
if pvFailureDomainZone == "" {
|
||||||
labels := metadata.GetLabels()
|
|
||||||
|
|
||||||
if labels[zoneLabel] != "" {
|
|
||||||
pvFailureDomainZone = labels[zoneLabel]
|
|
||||||
} else {
|
|
||||||
log.Infof("label %q is not present on PersistentVolume", zoneLabel)
|
log.Infof("label %q is not present on PersistentVolume", zoneLabel)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
volumeID string
|
volumeID, location string
|
||||||
blockStore cloudprovider.BlockStore
|
blockStore cloudprovider.BlockStore
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, snapshotLocation := range ib.backupRequest.SnapshotLocations {
|
for _, snapshotLocation := range ib.backupRequest.SnapshotLocations {
|
||||||
@@ -439,6 +435,7 @@ func (ib *defaultItemBackupper) takePVSnapshot(obj runtime.Unstructured, log log
|
|||||||
|
|
||||||
log.Infof("Got volume ID for persistent volume")
|
log.Infof("Got volume ID for persistent volume")
|
||||||
blockStore = bs
|
blockStore = bs
|
||||||
|
location = snapshotLocation.Name
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,30 +451,45 @@ func (ib *defaultItemBackupper) takePVSnapshot(obj runtime.Unstructured, log log
|
|||||||
"ark.heptio.com/pv": metadata.GetName(),
|
"ark.heptio.com/pv": metadata.GetName(),
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("Snapshotting PersistentVolume")
|
log.Info("Getting volume information")
|
||||||
snapshotID, err := blockStore.CreateSnapshot(volumeID, pvFailureDomainZone, tags)
|
|
||||||
if err != nil {
|
|
||||||
// log+error on purpose - log goes to the per-backup log file, error goes to the backup
|
|
||||||
log.WithError(err).Error("error creating snapshot")
|
|
||||||
return errors.WithMessage(err, "error creating snapshot")
|
|
||||||
}
|
|
||||||
|
|
||||||
volumeType, iops, err := blockStore.GetVolumeInfo(volumeID, pvFailureDomainZone)
|
volumeType, iops, err := blockStore.GetVolumeInfo(volumeID, pvFailureDomainZone)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithError(err).Error("error getting volume info")
|
log.WithError(err).Error("error getting volume info")
|
||||||
return errors.WithMessage(err, "error getting volume info")
|
return errors.WithMessage(err, "error getting volume info")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ib.backupRequest.Status.VolumeBackups == nil {
|
log.Info("Snapshotting PersistentVolume")
|
||||||
ib.backupRequest.Status.VolumeBackups = make(map[string]*api.VolumeBackupInfo)
|
snapshot := volumeSnapshot(ib.backupRequest.Backup, volumeID, volumeType, pvFailureDomainZone, location, iops)
|
||||||
}
|
|
||||||
|
|
||||||
ib.backupRequest.Status.VolumeBackups[name] = &api.VolumeBackupInfo{
|
var errs []error
|
||||||
SnapshotID: snapshotID,
|
snapshotID, err := blockStore.CreateSnapshot(snapshot.Spec.ProviderVolumeID, snapshot.Spec.VolumeAZ, tags)
|
||||||
Type: volumeType,
|
if err != nil {
|
||||||
Iops: iops,
|
log.WithError(err).Error("error creating snapshot")
|
||||||
AvailabilityZone: pvFailureDomainZone,
|
errs = append(errs, errors.Wrap(err, "error taking snapshot of volume"))
|
||||||
|
snapshot.Status.Phase = volume.SnapshotPhaseFailed
|
||||||
|
} else {
|
||||||
|
snapshot.Status.Phase = volume.SnapshotPhaseCompleted
|
||||||
|
snapshot.Status.ProviderSnapshotID = snapshotID
|
||||||
}
|
}
|
||||||
|
ib.backupRequest.VolumeSnapshots = append(ib.backupRequest.VolumeSnapshots, snapshot)
|
||||||
|
|
||||||
return nil
|
// nil errors are automatically removed
|
||||||
|
return kubeerrs.NewAggregate(errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func volumeSnapshot(backup *api.Backup, volumeID, volumeType, az, location string, iops *int64) *volume.Snapshot {
|
||||||
|
return &volume.Snapshot{
|
||||||
|
Spec: volume.SnapshotSpec{
|
||||||
|
BackupName: backup.Name,
|
||||||
|
BackupUID: string(backup.UID),
|
||||||
|
Location: location,
|
||||||
|
ProviderVolumeID: volumeID,
|
||||||
|
VolumeType: volumeType,
|
||||||
|
VolumeAZ: az,
|
||||||
|
VolumeIOPS: iops,
|
||||||
|
},
|
||||||
|
Status: volume.SnapshotStatus{
|
||||||
|
Phase: volume.SnapshotPhaseNew,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -524,18 +524,16 @@ func TestBackupItemNoSkips(t *testing.T) {
|
|||||||
|
|
||||||
if test.snapshottableVolumes != nil {
|
if test.snapshottableVolumes != nil {
|
||||||
require.Equal(t, len(test.snapshottableVolumes), len(blockStore.SnapshotsTaken))
|
require.Equal(t, len(test.snapshottableVolumes), len(blockStore.SnapshotsTaken))
|
||||||
|
}
|
||||||
|
|
||||||
var expectedBackups []api.VolumeBackupInfo
|
if len(test.snapshottableVolumes) > 0 {
|
||||||
for _, vbi := range test.snapshottableVolumes {
|
require.Len(t, backup.VolumeSnapshots, 1)
|
||||||
expectedBackups = append(expectedBackups, vbi)
|
snapshot := backup.VolumeSnapshots[0]
|
||||||
}
|
|
||||||
|
|
||||||
var actualBackups []api.VolumeBackupInfo
|
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].SnapshotID, snapshot.Status.ProviderSnapshotID)
|
||||||
for _, vbi := range backup.Status.VolumeBackups {
|
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].Type, snapshot.Spec.VolumeType)
|
||||||
actualBackups = append(actualBackups, *vbi)
|
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].Iops, snapshot.Spec.VolumeIOPS)
|
||||||
}
|
assert.Equal(t, test.snapshottableVolumes["vol-abc123"].AvailabilityZone, snapshot.Spec.VolumeAZ)
|
||||||
|
|
||||||
assert.Equal(t, expectedBackups, actualBackups)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if test.expectedTrackedPVCs != nil {
|
if test.expectedTrackedPVCs != nil {
|
||||||
@@ -718,7 +716,6 @@ func TestTakePVSnapshot(t *testing.T) {
|
|||||||
expectError bool
|
expectError bool
|
||||||
expectedVolumeID string
|
expectedVolumeID string
|
||||||
expectedSnapshotsTaken int
|
expectedSnapshotsTaken int
|
||||||
existingVolumeBackups map[string]*v1.VolumeBackupInfo
|
|
||||||
volumeInfo map[string]v1.VolumeBackupInfo
|
volumeInfo map[string]v1.VolumeBackupInfo
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
@@ -756,21 +753,6 @@ func TestTakePVSnapshot(t *testing.T) {
|
|||||||
"vol-abc123": {Type: "io1", Iops: &iops, SnapshotID: "snap-1", AvailabilityZone: "us-east-1c"},
|
"vol-abc123": {Type: "io1", Iops: &iops, SnapshotID: "snap-1", AvailabilityZone: "us-east-1c"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "preexisting volume backup info in backup status",
|
|
||||||
snapshotEnabled: true,
|
|
||||||
pv: `{"apiVersion": "v1", "kind": "PersistentVolume", "metadata": {"name": "mypv"}, "spec": {"gcePersistentDisk": {"pdName": "pd-abc123"}}}`,
|
|
||||||
expectError: false,
|
|
||||||
expectedSnapshotsTaken: 1,
|
|
||||||
expectedVolumeID: "pd-abc123",
|
|
||||||
ttl: 5 * time.Minute,
|
|
||||||
existingVolumeBackups: map[string]*v1.VolumeBackupInfo{
|
|
||||||
"anotherpv": {SnapshotID: "anothersnap"},
|
|
||||||
},
|
|
||||||
volumeInfo: map[string]v1.VolumeBackupInfo{
|
|
||||||
"pd-abc123": {Type: "gp", SnapshotID: "snap-1"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "create snapshot error",
|
name: "create snapshot error",
|
||||||
snapshotEnabled: true,
|
snapshotEnabled: true,
|
||||||
@@ -803,9 +785,6 @@ func TestTakePVSnapshot(t *testing.T) {
|
|||||||
SnapshotVolumes: &test.snapshotEnabled,
|
SnapshotVolumes: &test.snapshotEnabled,
|
||||||
TTL: metav1.Duration{Duration: test.ttl},
|
TTL: metav1.Duration{Duration: test.ttl},
|
||||||
},
|
},
|
||||||
Status: v1.BackupStatus{
|
|
||||||
VolumeBackups: test.existingVolumeBackups,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockStore := &arktest.FakeBlockStore{
|
blockStore := &arktest.FakeBlockStore{
|
||||||
@@ -843,29 +822,18 @@ func TestTakePVSnapshot(t *testing.T) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
expectedVolumeBackups := test.existingVolumeBackups
|
// we should have exactly one snapshot taken
|
||||||
if expectedVolumeBackups == nil {
|
|
||||||
expectedVolumeBackups = make(map[string]*v1.VolumeBackupInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// we should have one snapshot taken exactly
|
|
||||||
require.Equal(t, test.expectedSnapshotsTaken, blockStore.SnapshotsTaken.Len())
|
require.Equal(t, test.expectedSnapshotsTaken, blockStore.SnapshotsTaken.Len())
|
||||||
|
|
||||||
if test.expectedSnapshotsTaken > 0 {
|
if test.expectedSnapshotsTaken > 0 {
|
||||||
// the snapshotID should be the one in the entry in blockStore.SnapshottableVolumes
|
require.Len(t, ib.backupRequest.VolumeSnapshots, 1)
|
||||||
// for the volume we ran the test for
|
snapshot := ib.backupRequest.VolumeSnapshots[0]
|
||||||
|
|
||||||
snapshotID, _ := blockStore.SnapshotsTaken.PopAny()
|
snapshotID, _ := blockStore.SnapshotsTaken.PopAny()
|
||||||
|
assert.Equal(t, snapshotID, snapshot.Status.ProviderSnapshotID)
|
||||||
expectedVolumeBackups["mypv"] = &v1.VolumeBackupInfo{
|
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].Type, snapshot.Spec.VolumeType)
|
||||||
SnapshotID: snapshotID,
|
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].Iops, snapshot.Spec.VolumeIOPS)
|
||||||
Type: test.volumeInfo[test.expectedVolumeID].Type,
|
assert.Equal(t, test.volumeInfo[test.expectedVolumeID].AvailabilityZone, snapshot.Spec.VolumeAZ)
|
||||||
Iops: test.volumeInfo[test.expectedVolumeID].Iops,
|
|
||||||
AvailabilityZone: test.volumeInfo[test.expectedVolumeID].AvailabilityZone,
|
|
||||||
}
|
|
||||||
|
|
||||||
if e, a := expectedVolumeBackups, backup.Status.VolumeBackups; !reflect.DeepEqual(e, a) {
|
|
||||||
t.Errorf("backup.status.VolumeBackups: expected %v, got %v", e, a)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package backup
|
|||||||
import (
|
import (
|
||||||
arkv1api "github.com/heptio/ark/pkg/apis/ark/v1"
|
arkv1api "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||||
"github.com/heptio/ark/pkg/util/collections"
|
"github.com/heptio/ark/pkg/util/collections"
|
||||||
|
"github.com/heptio/ark/pkg/volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Request is a request for a backup, with all references to other objects
|
// Request is a request for a backup, with all references to other objects
|
||||||
@@ -16,4 +17,6 @@ type Request struct {
|
|||||||
ResourceIncludesExcludes *collections.IncludesExcludes
|
ResourceIncludesExcludes *collections.IncludesExcludes
|
||||||
ResourceHooks []resourceHook
|
ResourceHooks []resourceHook
|
||||||
ResolvedActions []resolvedAction
|
ResolvedActions []resolvedAction
|
||||||
|
|
||||||
|
VolumeSnapshots []*volume.Snapshot
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -448,6 +448,7 @@ func TestBackupResourceCohabitation(t *testing.T) {
|
|||||||
mock.Anything, // restic backupper
|
mock.Anything, // restic backupper
|
||||||
mock.Anything, // pvc snapshot tracker
|
mock.Anything, // pvc snapshot tracker
|
||||||
nil,
|
nil,
|
||||||
|
mock.Anything,
|
||||||
).Return(itemBackupper)
|
).Return(itemBackupper)
|
||||||
|
|
||||||
client := &arktest.FakeDynamicClient{}
|
client := &arktest.FakeDynamicClient{}
|
||||||
|
|||||||
@@ -385,53 +385,76 @@ func (c *backupController) runBackup(backup *pkgbackup.Request) error {
|
|||||||
// Do the actual backup
|
// Do the actual backup
|
||||||
if err := c.backupper.Backup(log, backup, backupFile, actions, pluginManager); err != nil {
|
if err := c.backupper.Backup(log, backup, backupFile, actions, pluginManager); err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
||||||
backup.Status.Phase = api.BackupPhaseFailed
|
backup.Status.Phase = api.BackupPhaseFailed
|
||||||
} else {
|
} else {
|
||||||
backup.Status.Phase = api.BackupPhaseCompleted
|
backup.Status.Phase = api.BackupPhaseCompleted
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark completion timestamp before serializing and uploading.
|
|
||||||
// Otherwise, the JSON file in object storage has a CompletionTimestamp of 'null'.
|
|
||||||
backup.Status.CompletionTimestamp.Time = c.clock.Now()
|
|
||||||
|
|
||||||
var backupJSONToUpload, backupFileToUpload io.Reader
|
|
||||||
backupJSON := new(bytes.Buffer)
|
|
||||||
if err := encode.EncodeTo(backup.Backup, "json", backupJSON); err != nil {
|
|
||||||
errs = append(errs, errors.Wrap(err, "error encoding backup"))
|
|
||||||
} else {
|
|
||||||
// Only upload the json and backup tarball if encoding to json succeeded.
|
|
||||||
backupJSONToUpload = backupJSON
|
|
||||||
backupFileToUpload = backupFile
|
|
||||||
}
|
|
||||||
|
|
||||||
var backupSizeBytes int64
|
|
||||||
if backupFileStat, err := backupFile.Stat(); err != nil {
|
|
||||||
errs = append(errs, errors.Wrap(err, "error getting file info"))
|
|
||||||
} else {
|
|
||||||
backupSizeBytes = backupFileStat.Size()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := gzippedLogFile.Close(); err != nil {
|
if err := gzippedLogFile.Close(); err != nil {
|
||||||
c.logger.WithError(err).Error("error closing gzippedLogFile")
|
c.logger.WithError(err).Error("error closing gzippedLogFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := backupStore.PutBackup(backup.Name, backupJSONToUpload, backupFileToUpload, logFile); err != nil {
|
// Mark completion timestamp before serializing and uploading.
|
||||||
errs = append(errs, err)
|
// Otherwise, the JSON file in object storage has a CompletionTimestamp of 'null'.
|
||||||
}
|
backup.Status.CompletionTimestamp.Time = c.clock.Now()
|
||||||
|
|
||||||
backupScheduleName := backup.GetLabels()["ark-schedule"]
|
errs = append(errs, persistBackup(backup, backupFile, logFile, backupStore, c.logger)...)
|
||||||
c.metrics.SetBackupTarballSizeBytesGauge(backupScheduleName, backupSizeBytes)
|
errs = append(errs, recordBackupMetrics(backup.Backup, backupFile, c.metrics))
|
||||||
|
|
||||||
backupDuration := backup.Status.CompletionTimestamp.Time.Sub(backup.Status.StartTimestamp.Time)
|
|
||||||
backupDurationSeconds := float64(backupDuration / time.Second)
|
|
||||||
c.metrics.RegisterBackupDuration(backupScheduleName, backupDurationSeconds)
|
|
||||||
|
|
||||||
log.Info("Backup completed")
|
log.Info("Backup completed")
|
||||||
|
|
||||||
return kerrors.NewAggregate(errs)
|
return kerrors.NewAggregate(errs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func recordBackupMetrics(backup *api.Backup, backupFile *os.File, serverMetrics *metrics.ServerMetrics) error {
|
||||||
|
backupScheduleName := backup.GetLabels()["ark-schedule"]
|
||||||
|
|
||||||
|
var backupSizeBytes int64
|
||||||
|
var err error
|
||||||
|
if backupFileStat, err := backupFile.Stat(); err != nil {
|
||||||
|
err = errors.Wrap(err, "error getting file info")
|
||||||
|
} else {
|
||||||
|
backupSizeBytes = backupFileStat.Size()
|
||||||
|
}
|
||||||
|
serverMetrics.SetBackupTarballSizeBytesGauge(backupScheduleName, backupSizeBytes)
|
||||||
|
|
||||||
|
backupDuration := backup.Status.CompletionTimestamp.Time.Sub(backup.Status.StartTimestamp.Time)
|
||||||
|
backupDurationSeconds := float64(backupDuration / time.Second)
|
||||||
|
serverMetrics.RegisterBackupDuration(backupScheduleName, backupDurationSeconds)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func persistBackup(backup *pkgbackup.Request, backupContents, backupLog *os.File, backupStore persistence.BackupStore, log logrus.FieldLogger) []error {
|
||||||
|
errs := []error{}
|
||||||
|
backupJSON := new(bytes.Buffer)
|
||||||
|
|
||||||
|
if err := encode.EncodeTo(backup.Backup, "json", backupJSON); err != nil {
|
||||||
|
errs = append(errs, errors.Wrap(err, "error encoding backup"))
|
||||||
|
}
|
||||||
|
|
||||||
|
volumeSnapshots := new(bytes.Buffer)
|
||||||
|
gzw := gzip.NewWriter(volumeSnapshots)
|
||||||
|
defer gzw.Close()
|
||||||
|
|
||||||
|
if err := json.NewEncoder(gzw).Encode(backup.VolumeSnapshots); err != nil {
|
||||||
|
errs = append(errs, errors.Wrap(err, "error encoding list of volume snapshots"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) > 0 {
|
||||||
|
// Don't upload the JSON files or backup tarball if encoding to json fails.
|
||||||
|
backupJSON = nil
|
||||||
|
backupContents = nil
|
||||||
|
volumeSnapshots = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := backupStore.PutBackup(backup.Name, backupJSON, backupContents, backupLog, volumeSnapshots); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
func closeAndRemoveFile(file *os.File, log logrus.FieldLogger) {
|
func closeAndRemoveFile(file *os.File, log logrus.FieldLogger) {
|
||||||
if err := file.Close(); err != nil {
|
if err := file.Close(); err != nil {
|
||||||
log.WithError(err).WithField("file", file.Name()).Error("error closing file")
|
log.WithError(err).WithField("file", file.Name()).Error("error closing file")
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||||||
completionTimestampIsPresent := func(buf *bytes.Buffer) bool {
|
completionTimestampIsPresent := func(buf *bytes.Buffer) bool {
|
||||||
return strings.Contains(buf.String(), `"completionTimestamp": "2006-01-02T22:04:05Z"`)
|
return strings.Contains(buf.String(), `"completionTimestamp": "2006-01-02T22:04:05Z"`)
|
||||||
}
|
}
|
||||||
backupStore.On("PutBackup", test.backup.Name, mock.MatchedBy(completionTimestampIsPresent), mock.Anything, mock.Anything).Return(nil)
|
backupStore.On("PutBackup", test.backup.Name, mock.MatchedBy(completionTimestampIsPresent), mock.Anything, mock.Anything, mock.Anything).Return(nil)
|
||||||
|
|
||||||
// add the test's backup to the informer/lister store
|
// add the test's backup to the informer/lister store
|
||||||
require.NotNil(t, test.backup)
|
require.NotNil(t, test.backup)
|
||||||
|
|||||||
@@ -198,8 +198,10 @@ func (c *backupSyncController) run() {
|
|||||||
switch {
|
switch {
|
||||||
case err != nil && kuberrs.IsAlreadyExists(err):
|
case err != nil && kuberrs.IsAlreadyExists(err):
|
||||||
log.Debug("Backup already exists in cluster")
|
log.Debug("Backup already exists in cluster")
|
||||||
|
continue
|
||||||
case err != nil && !kuberrs.IsAlreadyExists(err):
|
case err != nil && !kuberrs.IsAlreadyExists(err):
|
||||||
log.WithError(errors.WithStack(err)).Error("Error syncing backup into cluster")
|
log.WithError(errors.WithStack(err)).Error("Error syncing backup into cluster")
|
||||||
|
continue
|
||||||
default:
|
default:
|
||||||
log.Debug("Synced backup into cluster")
|
log.Debug("Synced backup into cluster")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ type ArkV1Interface interface {
|
|||||||
ResticRepositoriesGetter
|
ResticRepositoriesGetter
|
||||||
RestoresGetter
|
RestoresGetter
|
||||||
SchedulesGetter
|
SchedulesGetter
|
||||||
VolumeSnapshotsGetter
|
|
||||||
VolumeSnapshotLocationsGetter
|
VolumeSnapshotLocationsGetter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,10 +85,6 @@ func (c *ArkV1Client) Schedules(namespace string) ScheduleInterface {
|
|||||||
return newSchedules(c, namespace)
|
return newSchedules(c, namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ArkV1Client) VolumeSnapshots(namespace string) VolumeSnapshotInterface {
|
|
||||||
return newVolumeSnapshots(c, namespace)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ArkV1Client) VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationInterface {
|
func (c *ArkV1Client) VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationInterface {
|
||||||
return newVolumeSnapshotLocations(c, namespace)
|
return newVolumeSnapshotLocations(c, namespace)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,10 +68,6 @@ func (c *FakeArkV1) Schedules(namespace string) v1.ScheduleInterface {
|
|||||||
return &FakeSchedules{c, namespace}
|
return &FakeSchedules{c, namespace}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *FakeArkV1) VolumeSnapshots(namespace string) v1.VolumeSnapshotInterface {
|
|
||||||
return &FakeVolumeSnapshots{c, namespace}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *FakeArkV1) VolumeSnapshotLocations(namespace string) v1.VolumeSnapshotLocationInterface {
|
func (c *FakeArkV1) VolumeSnapshotLocations(namespace string) v1.VolumeSnapshotLocationInterface {
|
||||||
return &FakeVolumeSnapshotLocations{c, namespace}
|
return &FakeVolumeSnapshotLocations{c, namespace}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 the Heptio Ark 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Code generated by client-gen. DO NOT EDIT.
|
|
||||||
|
|
||||||
package fake
|
|
||||||
|
|
||||||
import (
|
|
||||||
ark_v1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
|
||||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
labels "k8s.io/apimachinery/pkg/labels"
|
|
||||||
schema "k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
types "k8s.io/apimachinery/pkg/types"
|
|
||||||
watch "k8s.io/apimachinery/pkg/watch"
|
|
||||||
testing "k8s.io/client-go/testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FakeVolumeSnapshots implements VolumeSnapshotInterface
|
|
||||||
type FakeVolumeSnapshots struct {
|
|
||||||
Fake *FakeArkV1
|
|
||||||
ns string
|
|
||||||
}
|
|
||||||
|
|
||||||
var volumesnapshotsResource = schema.GroupVersionResource{Group: "ark.heptio.com", Version: "v1", Resource: "volumesnapshots"}
|
|
||||||
|
|
||||||
var volumesnapshotsKind = schema.GroupVersionKind{Group: "ark.heptio.com", Version: "v1", Kind: "VolumeSnapshot"}
|
|
||||||
|
|
||||||
// Get takes name of the volumeSnapshot, and returns the corresponding volumeSnapshot object, and an error if there is any.
|
|
||||||
func (c *FakeVolumeSnapshots) Get(name string, options v1.GetOptions) (result *ark_v1.VolumeSnapshot, err error) {
|
|
||||||
obj, err := c.Fake.
|
|
||||||
Invokes(testing.NewGetAction(volumesnapshotsResource, c.ns, name), &ark_v1.VolumeSnapshot{})
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.(*ark_v1.VolumeSnapshot), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// List takes label and field selectors, and returns the list of VolumeSnapshots that match those selectors.
|
|
||||||
func (c *FakeVolumeSnapshots) List(opts v1.ListOptions) (result *ark_v1.VolumeSnapshotList, err error) {
|
|
||||||
obj, err := c.Fake.
|
|
||||||
Invokes(testing.NewListAction(volumesnapshotsResource, volumesnapshotsKind, c.ns, opts), &ark_v1.VolumeSnapshotList{})
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
label, _, _ := testing.ExtractFromListOptions(opts)
|
|
||||||
if label == nil {
|
|
||||||
label = labels.Everything()
|
|
||||||
}
|
|
||||||
list := &ark_v1.VolumeSnapshotList{ListMeta: obj.(*ark_v1.VolumeSnapshotList).ListMeta}
|
|
||||||
for _, item := range obj.(*ark_v1.VolumeSnapshotList).Items {
|
|
||||||
if label.Matches(labels.Set(item.Labels)) {
|
|
||||||
list.Items = append(list.Items, item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return list, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch returns a watch.Interface that watches the requested volumeSnapshots.
|
|
||||||
func (c *FakeVolumeSnapshots) Watch(opts v1.ListOptions) (watch.Interface, error) {
|
|
||||||
return c.Fake.
|
|
||||||
InvokesWatch(testing.NewWatchAction(volumesnapshotsResource, c.ns, opts))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create takes the representation of a volumeSnapshot and creates it. Returns the server's representation of the volumeSnapshot, and an error, if there is any.
|
|
||||||
func (c *FakeVolumeSnapshots) Create(volumeSnapshot *ark_v1.VolumeSnapshot) (result *ark_v1.VolumeSnapshot, err error) {
|
|
||||||
obj, err := c.Fake.
|
|
||||||
Invokes(testing.NewCreateAction(volumesnapshotsResource, c.ns, volumeSnapshot), &ark_v1.VolumeSnapshot{})
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.(*ark_v1.VolumeSnapshot), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update takes the representation of a volumeSnapshot and updates it. Returns the server's representation of the volumeSnapshot, and an error, if there is any.
|
|
||||||
func (c *FakeVolumeSnapshots) Update(volumeSnapshot *ark_v1.VolumeSnapshot) (result *ark_v1.VolumeSnapshot, err error) {
|
|
||||||
obj, err := c.Fake.
|
|
||||||
Invokes(testing.NewUpdateAction(volumesnapshotsResource, c.ns, volumeSnapshot), &ark_v1.VolumeSnapshot{})
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.(*ark_v1.VolumeSnapshot), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateStatus was generated because the type contains a Status member.
|
|
||||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
|
||||||
func (c *FakeVolumeSnapshots) UpdateStatus(volumeSnapshot *ark_v1.VolumeSnapshot) (*ark_v1.VolumeSnapshot, error) {
|
|
||||||
obj, err := c.Fake.
|
|
||||||
Invokes(testing.NewUpdateSubresourceAction(volumesnapshotsResource, "status", c.ns, volumeSnapshot), &ark_v1.VolumeSnapshot{})
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.(*ark_v1.VolumeSnapshot), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete takes name of the volumeSnapshot and deletes it. Returns an error if one occurs.
|
|
||||||
func (c *FakeVolumeSnapshots) Delete(name string, options *v1.DeleteOptions) error {
|
|
||||||
_, err := c.Fake.
|
|
||||||
Invokes(testing.NewDeleteAction(volumesnapshotsResource, c.ns, name), &ark_v1.VolumeSnapshot{})
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteCollection deletes a collection of objects.
|
|
||||||
func (c *FakeVolumeSnapshots) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error {
|
|
||||||
action := testing.NewDeleteCollectionAction(volumesnapshotsResource, c.ns, listOptions)
|
|
||||||
|
|
||||||
_, err := c.Fake.Invokes(action, &ark_v1.VolumeSnapshotList{})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch applies the patch and returns the patched volumeSnapshot.
|
|
||||||
func (c *FakeVolumeSnapshots) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *ark_v1.VolumeSnapshot, err error) {
|
|
||||||
obj, err := c.Fake.
|
|
||||||
Invokes(testing.NewPatchSubresourceAction(volumesnapshotsResource, c.ns, name, data, subresources...), &ark_v1.VolumeSnapshot{})
|
|
||||||
|
|
||||||
if obj == nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return obj.(*ark_v1.VolumeSnapshot), err
|
|
||||||
}
|
|
||||||
@@ -38,6 +38,4 @@ type RestoreExpansion interface{}
|
|||||||
|
|
||||||
type ScheduleExpansion interface{}
|
type ScheduleExpansion interface{}
|
||||||
|
|
||||||
type VolumeSnapshotExpansion interface{}
|
|
||||||
|
|
||||||
type VolumeSnapshotLocationExpansion interface{}
|
type VolumeSnapshotLocationExpansion interface{}
|
||||||
|
|||||||
@@ -1,174 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 the Heptio Ark 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Code generated by client-gen. DO NOT EDIT.
|
|
||||||
|
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
v1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
|
||||||
scheme "github.com/heptio/ark/pkg/generated/clientset/versioned/scheme"
|
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
types "k8s.io/apimachinery/pkg/types"
|
|
||||||
watch "k8s.io/apimachinery/pkg/watch"
|
|
||||||
rest "k8s.io/client-go/rest"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VolumeSnapshotsGetter has a method to return a VolumeSnapshotInterface.
|
|
||||||
// A group's client should implement this interface.
|
|
||||||
type VolumeSnapshotsGetter interface {
|
|
||||||
VolumeSnapshots(namespace string) VolumeSnapshotInterface
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshotInterface has methods to work with VolumeSnapshot resources.
|
|
||||||
type VolumeSnapshotInterface interface {
|
|
||||||
Create(*v1.VolumeSnapshot) (*v1.VolumeSnapshot, error)
|
|
||||||
Update(*v1.VolumeSnapshot) (*v1.VolumeSnapshot, error)
|
|
||||||
UpdateStatus(*v1.VolumeSnapshot) (*v1.VolumeSnapshot, error)
|
|
||||||
Delete(name string, options *meta_v1.DeleteOptions) error
|
|
||||||
DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error
|
|
||||||
Get(name string, options meta_v1.GetOptions) (*v1.VolumeSnapshot, error)
|
|
||||||
List(opts meta_v1.ListOptions) (*v1.VolumeSnapshotList, error)
|
|
||||||
Watch(opts meta_v1.ListOptions) (watch.Interface, error)
|
|
||||||
Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.VolumeSnapshot, err error)
|
|
||||||
VolumeSnapshotExpansion
|
|
||||||
}
|
|
||||||
|
|
||||||
// volumeSnapshots implements VolumeSnapshotInterface
|
|
||||||
type volumeSnapshots struct {
|
|
||||||
client rest.Interface
|
|
||||||
ns string
|
|
||||||
}
|
|
||||||
|
|
||||||
// newVolumeSnapshots returns a VolumeSnapshots
|
|
||||||
func newVolumeSnapshots(c *ArkV1Client, namespace string) *volumeSnapshots {
|
|
||||||
return &volumeSnapshots{
|
|
||||||
client: c.RESTClient(),
|
|
||||||
ns: namespace,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get takes name of the volumeSnapshot, and returns the corresponding volumeSnapshot object, and an error if there is any.
|
|
||||||
func (c *volumeSnapshots) Get(name string, options meta_v1.GetOptions) (result *v1.VolumeSnapshot, err error) {
|
|
||||||
result = &v1.VolumeSnapshot{}
|
|
||||||
err = c.client.Get().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
Name(name).
|
|
||||||
VersionedParams(&options, scheme.ParameterCodec).
|
|
||||||
Do().
|
|
||||||
Into(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// List takes label and field selectors, and returns the list of VolumeSnapshots that match those selectors.
|
|
||||||
func (c *volumeSnapshots) List(opts meta_v1.ListOptions) (result *v1.VolumeSnapshotList, err error) {
|
|
||||||
result = &v1.VolumeSnapshotList{}
|
|
||||||
err = c.client.Get().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
VersionedParams(&opts, scheme.ParameterCodec).
|
|
||||||
Do().
|
|
||||||
Into(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Watch returns a watch.Interface that watches the requested volumeSnapshots.
|
|
||||||
func (c *volumeSnapshots) Watch(opts meta_v1.ListOptions) (watch.Interface, error) {
|
|
||||||
opts.Watch = true
|
|
||||||
return c.client.Get().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
VersionedParams(&opts, scheme.ParameterCodec).
|
|
||||||
Watch()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create takes the representation of a volumeSnapshot and creates it. Returns the server's representation of the volumeSnapshot, and an error, if there is any.
|
|
||||||
func (c *volumeSnapshots) Create(volumeSnapshot *v1.VolumeSnapshot) (result *v1.VolumeSnapshot, err error) {
|
|
||||||
result = &v1.VolumeSnapshot{}
|
|
||||||
err = c.client.Post().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
Body(volumeSnapshot).
|
|
||||||
Do().
|
|
||||||
Into(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update takes the representation of a volumeSnapshot and updates it. Returns the server's representation of the volumeSnapshot, and an error, if there is any.
|
|
||||||
func (c *volumeSnapshots) Update(volumeSnapshot *v1.VolumeSnapshot) (result *v1.VolumeSnapshot, err error) {
|
|
||||||
result = &v1.VolumeSnapshot{}
|
|
||||||
err = c.client.Put().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
Name(volumeSnapshot.Name).
|
|
||||||
Body(volumeSnapshot).
|
|
||||||
Do().
|
|
||||||
Into(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateStatus was generated because the type contains a Status member.
|
|
||||||
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
|
|
||||||
|
|
||||||
func (c *volumeSnapshots) UpdateStatus(volumeSnapshot *v1.VolumeSnapshot) (result *v1.VolumeSnapshot, err error) {
|
|
||||||
result = &v1.VolumeSnapshot{}
|
|
||||||
err = c.client.Put().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
Name(volumeSnapshot.Name).
|
|
||||||
SubResource("status").
|
|
||||||
Body(volumeSnapshot).
|
|
||||||
Do().
|
|
||||||
Into(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete takes name of the volumeSnapshot and deletes it. Returns an error if one occurs.
|
|
||||||
func (c *volumeSnapshots) Delete(name string, options *meta_v1.DeleteOptions) error {
|
|
||||||
return c.client.Delete().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
Name(name).
|
|
||||||
Body(options).
|
|
||||||
Do().
|
|
||||||
Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteCollection deletes a collection of objects.
|
|
||||||
func (c *volumeSnapshots) DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error {
|
|
||||||
return c.client.Delete().
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
VersionedParams(&listOptions, scheme.ParameterCodec).
|
|
||||||
Body(options).
|
|
||||||
Do().
|
|
||||||
Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Patch applies the patch and returns the patched volumeSnapshot.
|
|
||||||
func (c *volumeSnapshots) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.VolumeSnapshot, err error) {
|
|
||||||
result = &v1.VolumeSnapshot{}
|
|
||||||
err = c.client.Patch(pt).
|
|
||||||
Namespace(c.ns).
|
|
||||||
Resource("volumesnapshots").
|
|
||||||
SubResource(subresources...).
|
|
||||||
Name(name).
|
|
||||||
Body(data).
|
|
||||||
Do().
|
|
||||||
Into(result)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
@@ -44,8 +44,6 @@ type Interface interface {
|
|||||||
Restores() RestoreInformer
|
Restores() RestoreInformer
|
||||||
// Schedules returns a ScheduleInformer.
|
// Schedules returns a ScheduleInformer.
|
||||||
Schedules() ScheduleInformer
|
Schedules() ScheduleInformer
|
||||||
// VolumeSnapshots returns a VolumeSnapshotInformer.
|
|
||||||
VolumeSnapshots() VolumeSnapshotInformer
|
|
||||||
// VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer.
|
// VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer.
|
||||||
VolumeSnapshotLocations() VolumeSnapshotLocationInformer
|
VolumeSnapshotLocations() VolumeSnapshotLocationInformer
|
||||||
}
|
}
|
||||||
@@ -111,11 +109,6 @@ func (v *version) Schedules() ScheduleInformer {
|
|||||||
return &scheduleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
return &scheduleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
||||||
}
|
}
|
||||||
|
|
||||||
// VolumeSnapshots returns a VolumeSnapshotInformer.
|
|
||||||
func (v *version) VolumeSnapshots() VolumeSnapshotInformer {
|
|
||||||
return &volumeSnapshotInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer.
|
// VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer.
|
||||||
func (v *version) VolumeSnapshotLocations() VolumeSnapshotLocationInformer {
|
func (v *version) VolumeSnapshotLocations() VolumeSnapshotLocationInformer {
|
||||||
return &volumeSnapshotLocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
return &volumeSnapshotLocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions}
|
||||||
|
|||||||
@@ -1,89 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 the Heptio Ark 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Code generated by informer-gen. DO NOT EDIT.
|
|
||||||
|
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
time "time"
|
|
||||||
|
|
||||||
ark_v1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
|
||||||
versioned "github.com/heptio/ark/pkg/generated/clientset/versioned"
|
|
||||||
internalinterfaces "github.com/heptio/ark/pkg/generated/informers/externalversions/internalinterfaces"
|
|
||||||
v1 "github.com/heptio/ark/pkg/generated/listers/ark/v1"
|
|
||||||
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
|
||||||
watch "k8s.io/apimachinery/pkg/watch"
|
|
||||||
cache "k8s.io/client-go/tools/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VolumeSnapshotInformer provides access to a shared informer and lister for
|
|
||||||
// VolumeSnapshots.
|
|
||||||
type VolumeSnapshotInformer interface {
|
|
||||||
Informer() cache.SharedIndexInformer
|
|
||||||
Lister() v1.VolumeSnapshotLister
|
|
||||||
}
|
|
||||||
|
|
||||||
type volumeSnapshotInformer struct {
|
|
||||||
factory internalinterfaces.SharedInformerFactory
|
|
||||||
tweakListOptions internalinterfaces.TweakListOptionsFunc
|
|
||||||
namespace string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewVolumeSnapshotInformer constructs a new informer for VolumeSnapshot type.
|
|
||||||
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
|
||||||
// one. This reduces memory footprint and number of connections to the server.
|
|
||||||
func NewVolumeSnapshotInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer {
|
|
||||||
return NewFilteredVolumeSnapshotInformer(client, namespace, resyncPeriod, indexers, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewFilteredVolumeSnapshotInformer constructs a new informer for VolumeSnapshot type.
|
|
||||||
// Always prefer using an informer factory to get a shared informer instead of getting an independent
|
|
||||||
// one. This reduces memory footprint and number of connections to the server.
|
|
||||||
func NewFilteredVolumeSnapshotInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer {
|
|
||||||
return cache.NewSharedIndexInformer(
|
|
||||||
&cache.ListWatch{
|
|
||||||
ListFunc: func(options meta_v1.ListOptions) (runtime.Object, error) {
|
|
||||||
if tweakListOptions != nil {
|
|
||||||
tweakListOptions(&options)
|
|
||||||
}
|
|
||||||
return client.ArkV1().VolumeSnapshots(namespace).List(options)
|
|
||||||
},
|
|
||||||
WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) {
|
|
||||||
if tweakListOptions != nil {
|
|
||||||
tweakListOptions(&options)
|
|
||||||
}
|
|
||||||
return client.ArkV1().VolumeSnapshots(namespace).Watch(options)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&ark_v1.VolumeSnapshot{},
|
|
||||||
resyncPeriod,
|
|
||||||
indexers,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *volumeSnapshotInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer {
|
|
||||||
return NewFilteredVolumeSnapshotInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *volumeSnapshotInformer) Informer() cache.SharedIndexInformer {
|
|
||||||
return f.factory.InformerFor(&ark_v1.VolumeSnapshot{}, f.defaultInformer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *volumeSnapshotInformer) Lister() v1.VolumeSnapshotLister {
|
|
||||||
return v1.NewVolumeSnapshotLister(f.Informer().GetIndexer())
|
|
||||||
}
|
|
||||||
@@ -73,8 +73,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource
|
|||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().Restores().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().Restores().Informer()}, nil
|
||||||
case v1.SchemeGroupVersion.WithResource("schedules"):
|
case v1.SchemeGroupVersion.WithResource("schedules"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().Schedules().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().Schedules().Informer()}, nil
|
||||||
case v1.SchemeGroupVersion.WithResource("volumesnapshots"):
|
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().VolumeSnapshots().Informer()}, nil
|
|
||||||
case v1.SchemeGroupVersion.WithResource("volumesnapshotlocations"):
|
case v1.SchemeGroupVersion.WithResource("volumesnapshotlocations"):
|
||||||
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().VolumeSnapshotLocations().Informer()}, nil
|
return &genericInformer{resource: resource.GroupResource(), informer: f.Ark().V1().VolumeSnapshotLocations().Informer()}, nil
|
||||||
|
|
||||||
|
|||||||
@@ -98,14 +98,6 @@ type ScheduleListerExpansion interface{}
|
|||||||
// ScheduleNamespaceLister.
|
// ScheduleNamespaceLister.
|
||||||
type ScheduleNamespaceListerExpansion interface{}
|
type ScheduleNamespaceListerExpansion interface{}
|
||||||
|
|
||||||
// VolumeSnapshotListerExpansion allows custom methods to be added to
|
|
||||||
// VolumeSnapshotLister.
|
|
||||||
type VolumeSnapshotListerExpansion interface{}
|
|
||||||
|
|
||||||
// VolumeSnapshotNamespaceListerExpansion allows custom methods to be added to
|
|
||||||
// VolumeSnapshotNamespaceLister.
|
|
||||||
type VolumeSnapshotNamespaceListerExpansion interface{}
|
|
||||||
|
|
||||||
// VolumeSnapshotLocationListerExpansion allows custom methods to be added to
|
// VolumeSnapshotLocationListerExpansion allows custom methods to be added to
|
||||||
// VolumeSnapshotLocationLister.
|
// VolumeSnapshotLocationLister.
|
||||||
type VolumeSnapshotLocationListerExpansion interface{}
|
type VolumeSnapshotLocationListerExpansion interface{}
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2018 the Heptio Ark 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Code generated by lister-gen. DO NOT EDIT.
|
|
||||||
|
|
||||||
package v1
|
|
||||||
|
|
||||||
import (
|
|
||||||
v1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VolumeSnapshotLister helps list VolumeSnapshots.
|
|
||||||
type VolumeSnapshotLister interface {
|
|
||||||
// List lists all VolumeSnapshots in the indexer.
|
|
||||||
List(selector labels.Selector) (ret []*v1.VolumeSnapshot, err error)
|
|
||||||
// VolumeSnapshots returns an object that can list and get VolumeSnapshots.
|
|
||||||
VolumeSnapshots(namespace string) VolumeSnapshotNamespaceLister
|
|
||||||
VolumeSnapshotListerExpansion
|
|
||||||
}
|
|
||||||
|
|
||||||
// volumeSnapshotLister implements the VolumeSnapshotLister interface.
|
|
||||||
type volumeSnapshotLister struct {
|
|
||||||
indexer cache.Indexer
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewVolumeSnapshotLister returns a new VolumeSnapshotLister.
|
|
||||||
func NewVolumeSnapshotLister(indexer cache.Indexer) VolumeSnapshotLister {
|
|
||||||
return &volumeSnapshotLister{indexer: indexer}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List lists all VolumeSnapshots in the indexer.
|
|
||||||
func (s *volumeSnapshotLister) List(selector labels.Selector) (ret []*v1.VolumeSnapshot, err error) {
|
|
||||||
err = cache.ListAll(s.indexer, selector, func(m interface{}) {
|
|
||||||
ret = append(ret, m.(*v1.VolumeSnapshot))
|
|
||||||
})
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshots returns an object that can list and get VolumeSnapshots.
|
|
||||||
func (s *volumeSnapshotLister) VolumeSnapshots(namespace string) VolumeSnapshotNamespaceLister {
|
|
||||||
return volumeSnapshotNamespaceLister{indexer: s.indexer, namespace: namespace}
|
|
||||||
}
|
|
||||||
|
|
||||||
// VolumeSnapshotNamespaceLister helps list and get VolumeSnapshots.
|
|
||||||
type VolumeSnapshotNamespaceLister interface {
|
|
||||||
// List lists all VolumeSnapshots in the indexer for a given namespace.
|
|
||||||
List(selector labels.Selector) (ret []*v1.VolumeSnapshot, err error)
|
|
||||||
// Get retrieves the VolumeSnapshot from the indexer for a given namespace and name.
|
|
||||||
Get(name string) (*v1.VolumeSnapshot, error)
|
|
||||||
VolumeSnapshotNamespaceListerExpansion
|
|
||||||
}
|
|
||||||
|
|
||||||
// volumeSnapshotNamespaceLister implements the VolumeSnapshotNamespaceLister
|
|
||||||
// interface.
|
|
||||||
type volumeSnapshotNamespaceLister struct {
|
|
||||||
indexer cache.Indexer
|
|
||||||
namespace string
|
|
||||||
}
|
|
||||||
|
|
||||||
// List lists all VolumeSnapshots in the indexer for a given namespace.
|
|
||||||
func (s volumeSnapshotNamespaceLister) List(selector labels.Selector) (ret []*v1.VolumeSnapshot, err error) {
|
|
||||||
err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) {
|
|
||||||
ret = append(ret, m.(*v1.VolumeSnapshot))
|
|
||||||
})
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get retrieves the VolumeSnapshot from the indexer for a given namespace and name.
|
|
||||||
func (s volumeSnapshotNamespaceLister) Get(name string) (*v1.VolumeSnapshot, error) {
|
|
||||||
obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !exists {
|
|
||||||
return nil, errors.NewNotFound(v1.Resource("volumesnapshot"), name)
|
|
||||||
}
|
|
||||||
return obj.(*v1.VolumeSnapshot), nil
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,7 @@ import io "io"
|
|||||||
import mock "github.com/stretchr/testify/mock"
|
import mock "github.com/stretchr/testify/mock"
|
||||||
|
|
||||||
import v1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
import v1 "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||||
|
import volume "github.com/heptio/ark/pkg/volume"
|
||||||
|
|
||||||
// BackupStore is an autogenerated mock type for the BackupStore type
|
// BackupStore is an autogenerated mock type for the BackupStore type
|
||||||
type BackupStore struct {
|
type BackupStore struct {
|
||||||
@@ -85,6 +86,29 @@ func (_m *BackupStore) GetBackupMetadata(name string) (*v1.Backup, error) {
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBackupVolumeSnapshots provides a mock function with given fields: name
|
||||||
|
func (_m *BackupStore) GetBackupVolumeSnapshots(name string) ([]*volume.Snapshot, error) {
|
||||||
|
ret := _m.Called(name)
|
||||||
|
|
||||||
|
var r0 []*volume.Snapshot
|
||||||
|
if rf, ok := ret.Get(0).(func(string) []*volume.Snapshot); ok {
|
||||||
|
r0 = rf(name)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).([]*volume.Snapshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||||
|
r1 = rf(name)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return r0, r1
|
||||||
|
}
|
||||||
|
|
||||||
// GetDownloadURL provides a mock function with given fields: target
|
// GetDownloadURL provides a mock function with given fields: target
|
||||||
func (_m *BackupStore) GetDownloadURL(target v1.DownloadTarget) (string, error) {
|
func (_m *BackupStore) GetDownloadURL(target v1.DownloadTarget) (string, error) {
|
||||||
ret := _m.Called(target)
|
ret := _m.Called(target)
|
||||||
@@ -164,13 +188,13 @@ func (_m *BackupStore) ListBackups() ([]string, error) {
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// PutBackup provides a mock function with given fields: name, metadata, contents, log
|
// PutBackup provides a mock function with given fields: name, metadata, contents, log, volumeSnapshots
|
||||||
func (_m *BackupStore) PutBackup(name string, metadata io.Reader, contents io.Reader, log io.Reader) error {
|
func (_m *BackupStore) PutBackup(name string, metadata io.Reader, contents io.Reader, log io.Reader, volumeSnapshots io.Reader) error {
|
||||||
ret := _m.Called(name, metadata, contents, log)
|
ret := _m.Called(name, metadata, contents, log, volumeSnapshots)
|
||||||
|
|
||||||
var r0 error
|
var r0 error
|
||||||
if rf, ok := ret.Get(0).(func(string, io.Reader, io.Reader, io.Reader) error); ok {
|
if rf, ok := ret.Get(0).(func(string, io.Reader, io.Reader, io.Reader, io.Reader) error); ok {
|
||||||
r0 = rf(name, metadata, contents, log)
|
r0 = rf(name, metadata, contents, log, volumeSnapshots)
|
||||||
} else {
|
} else {
|
||||||
r0 = ret.Error(0)
|
r0 = ret.Error(0)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ limitations under the License.
|
|||||||
package persistence
|
package persistence
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -31,6 +32,7 @@ import (
|
|||||||
arkv1api "github.com/heptio/ark/pkg/apis/ark/v1"
|
arkv1api "github.com/heptio/ark/pkg/apis/ark/v1"
|
||||||
"github.com/heptio/ark/pkg/cloudprovider"
|
"github.com/heptio/ark/pkg/cloudprovider"
|
||||||
"github.com/heptio/ark/pkg/generated/clientset/versioned/scheme"
|
"github.com/heptio/ark/pkg/generated/clientset/versioned/scheme"
|
||||||
|
"github.com/heptio/ark/pkg/volume"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BackupStore defines operations for creating, retrieving, and deleting
|
// BackupStore defines operations for creating, retrieving, and deleting
|
||||||
@@ -41,8 +43,9 @@ type BackupStore interface {
|
|||||||
|
|
||||||
ListBackups() ([]string, error)
|
ListBackups() ([]string, error)
|
||||||
|
|
||||||
PutBackup(name string, metadata, contents, log io.Reader) error
|
PutBackup(name string, metadata, contents, log, volumeSnapshots io.Reader) error
|
||||||
GetBackupMetadata(name string) (*arkv1api.Backup, error)
|
GetBackupMetadata(name string) (*arkv1api.Backup, error)
|
||||||
|
GetBackupVolumeSnapshots(name string) ([]*volume.Snapshot, error)
|
||||||
GetBackupContents(name string) (io.ReadCloser, error)
|
GetBackupContents(name string) (io.ReadCloser, error)
|
||||||
DeleteBackup(name string) error
|
DeleteBackup(name string) error
|
||||||
|
|
||||||
@@ -159,7 +162,7 @@ func (s *objectBackupStore) ListBackups() ([]string, error) {
|
|||||||
return output, nil
|
return output, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *objectBackupStore) PutBackup(name string, metadata io.Reader, contents io.Reader, log io.Reader) error {
|
func (s *objectBackupStore) PutBackup(name string, metadata, contents, log, volumeSnapshots io.Reader) error {
|
||||||
if err := seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupLogKey(name), log); err != nil {
|
if err := seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupLogKey(name), log); err != nil {
|
||||||
// Uploading the log file is best-effort; if it fails, we log the error but it doesn't impact the
|
// Uploading the log file is best-effort; if it fails, we log the error but it doesn't impact the
|
||||||
// backup's status.
|
// backup's status.
|
||||||
@@ -183,6 +186,18 @@ func (s *objectBackupStore) PutBackup(name string, metadata io.Reader, contents
|
|||||||
return kerrors.NewAggregate([]error{err, deleteErr})
|
return kerrors.NewAggregate([]error{err, deleteErr})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := seekAndPutObject(s.objectStore, s.bucket, s.layout.getBackupVolumeSnapshotsKey(name), volumeSnapshots); err != nil {
|
||||||
|
errs := []error{err}
|
||||||
|
|
||||||
|
deleteErr := s.objectStore.DeleteObject(s.bucket, s.layout.getBackupContentsKey(name))
|
||||||
|
errs = append(errs, deleteErr)
|
||||||
|
|
||||||
|
deleteErr = s.objectStore.DeleteObject(s.bucket, s.layout.getBackupMetadataKey(name))
|
||||||
|
errs = append(errs, deleteErr)
|
||||||
|
|
||||||
|
return kerrors.NewAggregate(errs)
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.putRevision(); err != nil {
|
if err := s.putRevision(); err != nil {
|
||||||
s.logger.WithField("backup", name).WithError(err).Warn("Error updating backup store revision")
|
s.logger.WithField("backup", name).WithError(err).Warn("Error updating backup store revision")
|
||||||
}
|
}
|
||||||
@@ -216,7 +231,23 @@ func (s *objectBackupStore) GetBackupMetadata(name string) (*arkv1api.Backup, er
|
|||||||
}
|
}
|
||||||
|
|
||||||
return backupObj, nil
|
return backupObj, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *objectBackupStore) GetBackupVolumeSnapshots(name string) ([]*volume.Snapshot, error) {
|
||||||
|
key := s.layout.getBackupVolumeSnapshotsKey(name)
|
||||||
|
|
||||||
|
res, err := s.objectStore.GetObject(s.bucket, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer res.Close()
|
||||||
|
|
||||||
|
var volumeSnapshots []*volume.Snapshot
|
||||||
|
if err := json.NewDecoder(res).Decode(&volumeSnapshots); err != nil {
|
||||||
|
return nil, errors.Wrap(err, "error decoding object data")
|
||||||
|
}
|
||||||
|
|
||||||
|
return volumeSnapshots, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *objectBackupStore) GetBackupContents(name string) (io.ReadCloser, error) {
|
func (s *objectBackupStore) GetBackupContents(name string) (io.ReadCloser, error) {
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ func (l *ObjectStoreLayout) getBackupLogKey(backup string) string {
|
|||||||
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-logs.gz", backup))
|
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-logs.gz", backup))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *ObjectStoreLayout) getBackupVolumeSnapshotsKey(backup string) string {
|
||||||
|
return path.Join(l.subdirs["backups"], backup, fmt.Sprintf("%s-volumesnapshots.json.gz", backup))
|
||||||
|
}
|
||||||
|
|
||||||
func (l *ObjectStoreLayout) getRestoreLogKey(restore string) string {
|
func (l *ObjectStoreLayout) getRestoreLogKey(restore string) string {
|
||||||
return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("restore-%s-logs.gz", restore))
|
return path.Join(l.subdirs["restores"], restore, fmt.Sprintf("restore-%s-logs.gz", restore))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,31 +211,47 @@ func TestPutBackup(t *testing.T) {
|
|||||||
metadata io.Reader
|
metadata io.Reader
|
||||||
contents io.Reader
|
contents io.Reader
|
||||||
log io.Reader
|
log io.Reader
|
||||||
|
snapshots io.Reader
|
||||||
expectedErr string
|
expectedErr string
|
||||||
expectedKeys []string
|
expectedKeys []string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "normal case",
|
name: "normal case",
|
||||||
metadata: newStringReadSeeker("metadata"),
|
metadata: newStringReadSeeker("metadata"),
|
||||||
contents: newStringReadSeeker("contents"),
|
contents: newStringReadSeeker("contents"),
|
||||||
log: newStringReadSeeker("log"),
|
log: newStringReadSeeker("log"),
|
||||||
expectedErr: "",
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
expectedKeys: []string{"backups/backup-1/ark-backup.json", "backups/backup-1/backup-1.tar.gz", "backups/backup-1/backup-1-logs.gz", "metadata/revision"},
|
expectedErr: "",
|
||||||
|
expectedKeys: []string{
|
||||||
|
"backups/backup-1/ark-backup.json",
|
||||||
|
"backups/backup-1/backup-1.tar.gz",
|
||||||
|
"backups/backup-1/backup-1-logs.gz",
|
||||||
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
|
"metadata/revision",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "normal case with backup store prefix",
|
name: "normal case with backup store prefix",
|
||||||
prefix: "prefix-1/",
|
prefix: "prefix-1/",
|
||||||
metadata: newStringReadSeeker("metadata"),
|
metadata: newStringReadSeeker("metadata"),
|
||||||
contents: newStringReadSeeker("contents"),
|
contents: newStringReadSeeker("contents"),
|
||||||
log: newStringReadSeeker("log"),
|
log: newStringReadSeeker("log"),
|
||||||
expectedErr: "",
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
expectedKeys: []string{"prefix-1/backups/backup-1/ark-backup.json", "prefix-1/backups/backup-1/backup-1.tar.gz", "prefix-1/backups/backup-1/backup-1-logs.gz", "prefix-1/metadata/revision"},
|
expectedErr: "",
|
||||||
|
expectedKeys: []string{
|
||||||
|
"prefix-1/backups/backup-1/ark-backup.json",
|
||||||
|
"prefix-1/backups/backup-1/backup-1.tar.gz",
|
||||||
|
"prefix-1/backups/backup-1/backup-1-logs.gz",
|
||||||
|
"prefix-1/backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
|
"prefix-1/metadata/revision",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error on metadata upload does not upload data",
|
name: "error on metadata upload does not upload data",
|
||||||
metadata: new(errorReader),
|
metadata: new(errorReader),
|
||||||
contents: newStringReadSeeker("contents"),
|
contents: newStringReadSeeker("contents"),
|
||||||
log: newStringReadSeeker("log"),
|
log: newStringReadSeeker("log"),
|
||||||
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
expectedErr: "error readers return errors",
|
expectedErr: "error readers return errors",
|
||||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||||
},
|
},
|
||||||
@@ -244,22 +260,30 @@ func TestPutBackup(t *testing.T) {
|
|||||||
metadata: newStringReadSeeker("metadata"),
|
metadata: newStringReadSeeker("metadata"),
|
||||||
contents: new(errorReader),
|
contents: new(errorReader),
|
||||||
log: newStringReadSeeker("log"),
|
log: newStringReadSeeker("log"),
|
||||||
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
expectedErr: "error readers return errors",
|
expectedErr: "error readers return errors",
|
||||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "error on log upload is ok",
|
name: "error on log upload is ok",
|
||||||
metadata: newStringReadSeeker("foo"),
|
metadata: newStringReadSeeker("foo"),
|
||||||
contents: newStringReadSeeker("bar"),
|
contents: newStringReadSeeker("bar"),
|
||||||
log: new(errorReader),
|
log: new(errorReader),
|
||||||
expectedErr: "",
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
expectedKeys: []string{"backups/backup-1/ark-backup.json", "backups/backup-1/backup-1.tar.gz", "metadata/revision"},
|
expectedErr: "",
|
||||||
|
expectedKeys: []string{
|
||||||
|
"backups/backup-1/ark-backup.json",
|
||||||
|
"backups/backup-1/backup-1.tar.gz",
|
||||||
|
"backups/backup-1/backup-1-volumesnapshots.json.gz",
|
||||||
|
"metadata/revision",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "don't upload data when metadata is nil",
|
name: "don't upload data when metadata is nil",
|
||||||
metadata: nil,
|
metadata: nil,
|
||||||
contents: newStringReadSeeker("contents"),
|
contents: newStringReadSeeker("contents"),
|
||||||
log: newStringReadSeeker("log"),
|
log: newStringReadSeeker("log"),
|
||||||
|
snapshots: newStringReadSeeker("snapshots"),
|
||||||
expectedErr: "",
|
expectedErr: "",
|
||||||
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
expectedKeys: []string{"backups/backup-1/backup-1-logs.gz"},
|
||||||
},
|
},
|
||||||
@@ -269,7 +293,7 @@ func TestPutBackup(t *testing.T) {
|
|||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
harness := newObjectBackupStoreTestHarness("foo", tc.prefix)
|
||||||
|
|
||||||
err := harness.PutBackup("backup-1", tc.metadata, tc.contents, tc.log)
|
err := harness.PutBackup("backup-1", tc.metadata, tc.contents, tc.log, tc.snapshots)
|
||||||
|
|
||||||
arktest.AssertErrorMatches(t, tc.expectedErr, err)
|
arktest.AssertErrorMatches(t, tc.expectedErr, err)
|
||||||
assert.Len(t, harness.objectStore.Data[harness.bucket], len(tc.expectedKeys))
|
assert.Len(t, harness.objectStore.Data[harness.bucket], len(tc.expectedKeys))
|
||||||
|
|||||||
77
pkg/volume/snapshot.go
Normal file
77
pkg/volume/snapshot.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2018 the Heptio Ark 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 volume
|
||||||
|
|
||||||
|
// Snapshot stores information about a persistent volume snapshot taken as
|
||||||
|
// part of an Ark backup.
|
||||||
|
type Snapshot struct {
|
||||||
|
Spec SnapshotSpec `json:"spec"`
|
||||||
|
|
||||||
|
Status SnapshotStatus `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SnapshotSpec struct {
|
||||||
|
// BackupName is the name of the Ark backup this snapshot
|
||||||
|
// is associated with.
|
||||||
|
BackupName string `json:"backupName"`
|
||||||
|
|
||||||
|
// BackupUID is the UID of the Ark backup this snapshot
|
||||||
|
// is associated with.
|
||||||
|
BackupUID string `json:"backupUID"`
|
||||||
|
|
||||||
|
// Location is the name of the VolumeSnapshotLocation where this snapshot is stored.
|
||||||
|
Location string `json:"location"`
|
||||||
|
|
||||||
|
// ProviderVolumeID is the provider's ID for the volume.
|
||||||
|
ProviderVolumeID string `json:"providerVolumeID"`
|
||||||
|
|
||||||
|
// VolumeType is the type of the disk/volume in the cloud provider
|
||||||
|
// API.
|
||||||
|
VolumeType string `json:"volumeType"`
|
||||||
|
|
||||||
|
// VolumeAZ is the where the volume is provisioned
|
||||||
|
// in the cloud provider.
|
||||||
|
VolumeAZ string `json:"volumeAZ,omitempty"`
|
||||||
|
|
||||||
|
// VolumeIOPS is the optional value of provisioned IOPS for the
|
||||||
|
// disk/volume in the cloud provider API.
|
||||||
|
VolumeIOPS *int64 `json:"volumeIOPS,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SnapshotStatus struct {
|
||||||
|
// ProviderSnapshotID is the ID of the snapshot taken in the cloud
|
||||||
|
// provider API of this volume.
|
||||||
|
ProviderSnapshotID string `json:"providerSnapshotID,omitempty"`
|
||||||
|
|
||||||
|
// Phase is the current state of the VolumeSnapshot.
|
||||||
|
Phase SnapshotPhase `json:"phase,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SnapshotPhase is the lifecyle phase of an Ark volume snapshot.
|
||||||
|
type SnapshotPhase string
|
||||||
|
|
||||||
|
const (
|
||||||
|
// SnapshotPhaseNew means the volume snapshot has been created but not
|
||||||
|
// yet processed by the VolumeSnapshotController.
|
||||||
|
SnapshotPhaseNew SnapshotPhase = "New"
|
||||||
|
|
||||||
|
// SnapshotPhaseCompleted means the volume snapshot was successfully created and can be restored from..
|
||||||
|
SnapshotPhaseCompleted SnapshotPhase = "Completed"
|
||||||
|
|
||||||
|
// SnapshotPhaseFailed means the volume snapshot was unable to execute.
|
||||||
|
SnapshotPhaseFailed SnapshotPhase = "Failed"
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user