Convert manifests + BSL api client to kubebuilder (#2561)
* kubebuilder init - minimalist version Signed-off-by: Carlisia <carlisia@vmware.com> * Add back main.go, apparently kb needs it Signed-off-by: Carlisia <carlisia@vmware.com> * Tweak makefile to accomodate kubebuilder expectations Signed-off-by: Carlisia <carlisia@vmware.com> * Port BSL to kubebuilder api client Signed-off-by: Carlisia <carlisia@vmware.com> * s/cache/client bc client fetches from cache And other naming improvements Signed-off-by: Carlisia <carlisia@vmware.com> * So, .GetAPIReader is how we bypass the cache In this case, the cache hasn't started yet Signed-off-by: Carlisia <carlisia@vmware.com> * Oh that's what this code was for... adding back We still need to embed the CRDs as binary data in the Velero binary to access the generated CRDs at runtime. Signed-off-by: Carlisia <carlisia@vmware.com> * Tie in CRD/code generation w/ existing scripts Signed-off-by: Carlisia <carlisia@vmware.com> * Mostly result of running update-fmt, updated file formatting Signed-off-by: Carlisia <carlisia@vmware.com> * Just a copyright fix Signed-off-by: Carlisia <carlisia@vmware.com> * All the test fixes Signed-off-by: Carlisia <carlisia@vmware.com> * Add changelog + some cleanup Signed-off-by: Carlisia <carlisia@vmware.com> * Update backup manifest Signed-off-by: Carlisia <carlisia@vmware.com> * Remove unneeded auto-generated files Signed-off-by: Carlisia <carlisia@vmware.com> * Keep everything in the same (existing) package Signed-off-by: Carlisia <carlisia@vmware.com> * Fix/clean scripts, generated code, and calls Deleting the entire `generated` directory and running `make update` works. Modifying an api and running `make verify` works as expected. Signed-off-by: Carlisia <carlisia@vmware.com> * Clean up schema and client calls + code reviews Signed-off-by: Carlisia <carlisia@vmware.com> * Move all code gen to inside builder container Signed-off-by: Carlisia <carlisia@vmware.com> * Address code review Signed-off-by: Carlisia <carlisia@vmware.com> * Fix imports/aliases Signed-off-by: Carlisia <carlisia@vmware.com> * More code reviews Signed-off-by: Carlisia <carlisia@vmware.com> * Add waitforcachesync Signed-off-by: Carlisia <carlisia@vmware.com> * Have manager register ALL controllers This will allow for proper cache management. Signed-off-by: Carlisia <carlisia@vmware.com> * Status subresource is now enabled; cleanup Signed-off-by: Carlisia <carlisia@vmware.com> * More code reviews Signed-off-by: Carlisia <carlisia@vmware.com> * Clean up Signed-off-by: Carlisia <carlisia@vmware.com> * Manager registers ALL controllers for restic too Signed-off-by: Carlisia <carlisia@vmware.com> * More code reviews Signed-off-by: Carlisia <carlisia@vmware.com> * Add deprecation warning/todo Signed-off-by: Carlisia <carlisia@vmware.com> * Add documentation Signed-off-by: Carlisia <carlisia@vmware.com> * Add helpful comments Signed-off-by: Carlisia <carlisia@vmware.com> * Address code review Signed-off-by: Carlisia <carlisia@vmware.com> * More idiomatic Runnable Signed-off-by: Carlisia <carlisia@vmware.com> * Clean up imports Signed-off-by: Carlisia <carlisia@vmware.com>pull/2676/head
parent
6e86a83cf3
commit
4048c020a8
|
@ -0,0 +1,7 @@
|
|||
domain: io
|
||||
repo: github.com/vmware-tanzu/velero
|
||||
resources:
|
||||
- group: velero
|
||||
kind: BackupStorageLocation
|
||||
version: v1
|
||||
version: "2"
|
|
@ -0,0 +1 @@
|
|||
Convert manifests + BSL api client to kubebuilder
|
|
@ -0,0 +1,423 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.4
|
||||
creationTimestamp: null
|
||||
name: backups.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
names:
|
||||
kind: Backup
|
||||
listKind: BackupList
|
||||
plural: backups
|
||||
singular: backup
|
||||
preserveUnknownFields: false
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
description: Backup is a Velero resource that respresents the capture of Kubernetes
|
||||
cluster state at a point in time (API objects and associated volume state).
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: BackupSpec defines the specification for a Velero backup.
|
||||
properties:
|
||||
defaultVolumesToRestic:
|
||||
description: DefaultVolumesToRestic specifies whether restic should
|
||||
be used to take a backup of all pod volumes by default.
|
||||
type: boolean
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces contains a list of namespaces that are
|
||||
not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: ExcludedResources is a slice of resource names that are
|
||||
not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
hooks:
|
||||
description: Hooks represent custom behaviors that should be executed
|
||||
at different phases of the backup.
|
||||
properties:
|
||||
resources:
|
||||
description: Resources are hooks that should be executed when backing
|
||||
up individual instances of a resource.
|
||||
items:
|
||||
description: BackupResourceHookSpec defines one or more BackupResourceHooks
|
||||
that should be executed based on the rules defined for namespaces,
|
||||
resources, and label selector.
|
||||
properties:
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces specifies the namespaces to
|
||||
which this hook spec does not apply.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: ExcludedResources specifies the resources to
|
||||
which this hook spec does not apply.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: IncludedNamespaces specifies the namespaces to
|
||||
which this hook spec applies. If empty, it applies to all
|
||||
namespaces.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: IncludedResources specifies the resources to
|
||||
which this hook spec applies. If empty, it applies to all
|
||||
resources.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
labelSelector:
|
||||
description: LabelSelector, if specified, filters the resources
|
||||
to which this hook spec applies.
|
||||
nullable: true
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn,
|
||||
Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values.
|
||||
If the operator is In or NotIn, the values array
|
||||
must be non-empty. If the operator is Exists or
|
||||
DoesNotExist, the values array must be empty.
|
||||
This array is replaced during a strategic merge
|
||||
patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field is
|
||||
"key", the operator is "In", and the values array contains
|
||||
only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
name:
|
||||
description: Name is the name of this hook.
|
||||
type: string
|
||||
post:
|
||||
description: PostHooks is a list of BackupResourceHooks to
|
||||
execute after storing the item in the backup. These are
|
||||
executed after all "additional items" from item actions
|
||||
are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for a resource.
|
||||
properties:
|
||||
exec:
|
||||
description: Exec defines an exec hook.
|
||||
properties:
|
||||
command:
|
||||
description: Command is the command and arguments
|
||||
to execute.
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: Container is the container in the pod
|
||||
where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
behave if it encounters an error executing this
|
||||
hook.
|
||||
enum:
|
||||
- Continue
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook to complete
|
||||
before considering the execution a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
type: object
|
||||
required:
|
||||
- exec
|
||||
type: object
|
||||
type: array
|
||||
pre:
|
||||
description: PreHooks is a list of BackupResourceHooks to
|
||||
execute prior to storing the item in the backup. These are
|
||||
executed before any "additional items" from item actions
|
||||
are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for a resource.
|
||||
properties:
|
||||
exec:
|
||||
description: Exec defines an exec hook.
|
||||
properties:
|
||||
command:
|
||||
description: Command is the command and arguments
|
||||
to execute.
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: Container is the container in the pod
|
||||
where the command should be executed. If not specified,
|
||||
the pod's first container is used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
behave if it encounters an error executing this
|
||||
hook.
|
||||
enum:
|
||||
- Continue
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook to complete
|
||||
before considering the execution a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
type: object
|
||||
required:
|
||||
- exec
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
includeClusterResources:
|
||||
description: IncludeClusterResources specifies whether cluster-scoped
|
||||
resources should be included for consideration in the backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
includedNamespaces:
|
||||
description: IncludedNamespaces is a slice of namespace names to include
|
||||
objects from. If empty, all namespaces are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: IncludedResources is a slice of resource names to include
|
||||
in the backup. If empty, all resources are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
labelSelector:
|
||||
description: LabelSelector is a metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If empty or nil, all
|
||||
objects are included. Optional.
|
||||
nullable: true
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that contains
|
||||
values, a key, and an operator that relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship to a
|
||||
set of values. Valid operators are In, NotIn, Exists and
|
||||
DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the operator
|
||||
is In or NotIn, the values array must be non-empty. If the
|
||||
operator is Exists or DoesNotExist, the values array must
|
||||
be empty. This array is replaced during a strategic merge
|
||||
patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator is
|
||||
"In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
snapshotVolumes:
|
||||
description: SnapshotVolumes specifies whether to take cloud snapshots
|
||||
of any PV's referenced in the set of objects included in the Backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
storageLocation:
|
||||
description: StorageLocation is a string containing the name of a BackupStorageLocation
|
||||
where the backup should be stored.
|
||||
type: string
|
||||
ttl:
|
||||
description: TTL is a time.Duration-parseable string describing how
|
||||
long the Backup should be retained for.
|
||||
type: string
|
||||
volumeSnapshotLocations:
|
||||
description: VolumeSnapshotLocations is a list containing names of VolumeSnapshotLocations
|
||||
associated with this backup.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
status:
|
||||
description: BackupStatus captures the current status of a Velero backup.
|
||||
properties:
|
||||
completionTimestamp:
|
||||
description: CompletionTimestamp records the time a backup was completed.
|
||||
Completion time is recorded even on failed backups. Completion time
|
||||
is recorded before uploading the backup object. The server's time
|
||||
is used for CompletionTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
errors:
|
||||
description: Errors is a count of all error messages that were generated
|
||||
during execution of the backup. The actual errors are in the backup's
|
||||
log file in object storage.
|
||||
type: integer
|
||||
expiration:
|
||||
description: Expiration is when this Backup is eligible for garbage-collection.
|
||||
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:
|
||||
- New
|
||||
- FailedValidation
|
||||
- InProgress
|
||||
- Completed
|
||||
- PartiallyFailed
|
||||
- Failed
|
||||
- Deleting
|
||||
type: string
|
||||
progress:
|
||||
description: Progress contains information about the backup's execution
|
||||
progress. Note that this information is best-effort only -- if Velero
|
||||
fails to update it during a backup for any reason, it may be inaccurate/stale.
|
||||
nullable: true
|
||||
properties:
|
||||
itemsBackedUp:
|
||||
description: ItemsBackedUp is the number of items that have actually
|
||||
been written to the backup tarball so far.
|
||||
type: integer
|
||||
totalItems:
|
||||
description: TotalItems is the total number of items to be backed
|
||||
up. This number may change throughout the execution of the backup
|
||||
due to plugins that return additional related items to back up,
|
||||
the velero.io/exclude-from-backup label, and various other filters
|
||||
that happen as items are processed.
|
||||
type: integer
|
||||
type: object
|
||||
startTimestamp:
|
||||
description: StartTimestamp records the time a backup was started. Separate
|
||||
from CreationTimestamp, since that value changes on restores. The
|
||||
server's time is used for StartTimestamps
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
validationErrors:
|
||||
description: ValidationErrors is a slice of all validation errors (if
|
||||
applicable).
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
version:
|
||||
description: 'Version is the backup format major version. Deprecated:
|
||||
Please see FormatVersion'
|
||||
type: integer
|
||||
volumeSnapshotsAttempted:
|
||||
description: VolumeSnapshotsAttempted is the total number of attempted
|
||||
volume snapshots for this backup.
|
||||
type: integer
|
||||
volumeSnapshotsCompleted:
|
||||
description: VolumeSnapshotsCompleted is the total number of successfully
|
||||
completed volume snapshots for this backup.
|
||||
type: integer
|
||||
warnings:
|
||||
description: Warnings is a count of all warning messages that were generated
|
||||
during execution of the backup. The actual warnings are in the backup's
|
||||
log file in object storage.
|
||||
type: integer
|
||||
type: object
|
||||
type: object
|
||||
version: v1
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
|
@ -8,6 +8,14 @@ metadata:
|
|||
creationTimestamp: null
|
||||
name: backupstoragelocations.velero.io
|
||||
spec:
|
||||
additionalPrinterColumns:
|
||||
- JSONPath: .status.phase
|
||||
description: Backup Storage Location status such as Available/Unavailable
|
||||
name: Phase
|
||||
type: string
|
||||
- JSONPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
group: velero.io
|
||||
names:
|
||||
kind: BackupStorageLocation
|
||||
|
@ -16,10 +24,12 @@ spec:
|
|||
singular: backupstoragelocation
|
||||
preserveUnknownFields: false
|
||||
scope: Namespaced
|
||||
subresources:
|
||||
status: {}
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
description: BackupStorageLocation is a location where Velero stores backup
|
||||
objects.
|
||||
objects
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
|
@ -34,8 +44,8 @@ spec:
|
|||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: BackupStorageLocationSpec defines the specification for a Velero
|
||||
BackupStorageLocation.
|
||||
description: BackupStorageLocationSpec defines the desired state of a Velero
|
||||
BackupStorageLocation
|
||||
properties:
|
||||
accessMode:
|
||||
description: AccessMode defines the permissions for the backup storage
|
||||
|
@ -81,8 +91,7 @@ spec:
|
|||
- provider
|
||||
type: object
|
||||
status:
|
||||
description: BackupStorageLocationStatus describes the current status of
|
||||
a Velero BackupStorageLocation.
|
||||
description: BackupStorageLocationStatus defines the observed state of BackupStorageLocation
|
||||
properties:
|
||||
accessMode:
|
||||
description: "AccessMode is an unused field. \n Deprecated: there is
|
|
@ -0,0 +1,379 @@
|
|||
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1beta1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.2.4
|
||||
creationTimestamp: null
|
||||
name: schedules.velero.io
|
||||
spec:
|
||||
group: velero.io
|
||||
names:
|
||||
kind: Schedule
|
||||
listKind: ScheduleList
|
||||
plural: schedules
|
||||
singular: schedule
|
||||
preserveUnknownFields: false
|
||||
scope: Namespaced
|
||||
validation:
|
||||
openAPIV3Schema:
|
||||
description: Schedule is a Velero resource that represents a pre-scheduled or
|
||||
periodic Backup that should be run.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: 'APIVersion defines the versioned schema of this representation
|
||||
of an object. Servers should convert recognized schemas to the latest
|
||||
internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
|
||||
type: string
|
||||
kind:
|
||||
description: 'Kind is a string value representing the REST resource this
|
||||
object represents. Servers may infer this from the endpoint the client
|
||||
submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: ScheduleSpec defines the specification for a Velero schedule
|
||||
properties:
|
||||
schedule:
|
||||
description: Schedule is a Cron expression defining when to run the
|
||||
Backup.
|
||||
type: string
|
||||
template:
|
||||
description: Template is the definition of the Backup to be run on the
|
||||
provided schedule
|
||||
properties:
|
||||
defaultVolumesToRestic:
|
||||
description: DefaultVolumesToRestic specifies whether restic should
|
||||
be used to take a backup of all pod volumes by default.
|
||||
type: boolean
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces contains a list of namespaces that
|
||||
are not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: ExcludedResources is a slice of resource names that
|
||||
are not included in the backup.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
hooks:
|
||||
description: Hooks represent custom behaviors that should be executed
|
||||
at different phases of the backup.
|
||||
properties:
|
||||
resources:
|
||||
description: Resources are hooks that should be executed when
|
||||
backing up individual instances of a resource.
|
||||
items:
|
||||
description: BackupResourceHookSpec defines one or more BackupResourceHooks
|
||||
that should be executed based on the rules defined for namespaces,
|
||||
resources, and label selector.
|
||||
properties:
|
||||
excludedNamespaces:
|
||||
description: ExcludedNamespaces specifies the namespaces
|
||||
to which this hook spec does not apply.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
excludedResources:
|
||||
description: ExcludedResources specifies the resources
|
||||
to which this hook spec does not apply.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedNamespaces:
|
||||
description: IncludedNamespaces specifies the namespaces
|
||||
to which this hook spec applies. If empty, it applies
|
||||
to all namespaces.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: IncludedResources specifies the resources
|
||||
to which this hook spec applies. If empty, it applies
|
||||
to all resources.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
labelSelector:
|
||||
description: LabelSelector, if specified, filters the
|
||||
resources to which this hook spec applies.
|
||||
nullable: true
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector
|
||||
requirements. The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector
|
||||
that contains values, a key, and an operator that
|
||||
relates the key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector
|
||||
applies to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In,
|
||||
NotIn, Exists and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values.
|
||||
If the operator is In or NotIn, the values
|
||||
array must be non-empty. If the operator is
|
||||
Exists or DoesNotExist, the values array must
|
||||
be empty. This array is replaced during a
|
||||
strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs.
|
||||
A single {key,value} in the matchLabels map is equivalent
|
||||
to an element of matchExpressions, whose key field
|
||||
is "key", the operator is "In", and the values array
|
||||
contains only "value". The requirements are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
name:
|
||||
description: Name is the name of this hook.
|
||||
type: string
|
||||
post:
|
||||
description: PostHooks is a list of BackupResourceHooks
|
||||
to execute after storing the item in the backup. These
|
||||
are executed after all "additional items" from item
|
||||
actions are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for a
|
||||
resource.
|
||||
properties:
|
||||
exec:
|
||||
description: Exec defines an exec hook.
|
||||
properties:
|
||||
command:
|
||||
description: Command is the command and arguments
|
||||
to execute.
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: Container is the container in the
|
||||
pod where the command should be executed.
|
||||
If not specified, the pod's first container
|
||||
is used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
behave if it encounters an error executing
|
||||
this hook.
|
||||
enum:
|
||||
- Continue
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook to
|
||||
complete before considering the execution
|
||||
a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
type: object
|
||||
required:
|
||||
- exec
|
||||
type: object
|
||||
type: array
|
||||
pre:
|
||||
description: PreHooks is a list of BackupResourceHooks
|
||||
to execute prior to storing the item in the backup.
|
||||
These are executed before any "additional items" from
|
||||
item actions are processed.
|
||||
items:
|
||||
description: BackupResourceHook defines a hook for a
|
||||
resource.
|
||||
properties:
|
||||
exec:
|
||||
description: Exec defines an exec hook.
|
||||
properties:
|
||||
command:
|
||||
description: Command is the command and arguments
|
||||
to execute.
|
||||
items:
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
container:
|
||||
description: Container is the container in the
|
||||
pod where the command should be executed.
|
||||
If not specified, the pod's first container
|
||||
is used.
|
||||
type: string
|
||||
onError:
|
||||
description: OnError specifies how Velero should
|
||||
behave if it encounters an error executing
|
||||
this hook.
|
||||
enum:
|
||||
- Continue
|
||||
- Fail
|
||||
type: string
|
||||
timeout:
|
||||
description: Timeout defines the maximum amount
|
||||
of time Velero should wait for the hook to
|
||||
complete before considering the execution
|
||||
a failure.
|
||||
type: string
|
||||
required:
|
||||
- command
|
||||
type: object
|
||||
required:
|
||||
- exec
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
nullable: true
|
||||
type: array
|
||||
type: object
|
||||
includeClusterResources:
|
||||
description: IncludeClusterResources specifies whether cluster-scoped
|
||||
resources should be included for consideration in the backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
includedNamespaces:
|
||||
description: IncludedNamespaces is a slice of namespace names to
|
||||
include objects from. If empty, all namespaces are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
includedResources:
|
||||
description: IncludedResources is a slice of resource names to include
|
||||
in the backup. If empty, all resources are included.
|
||||
items:
|
||||
type: string
|
||||
nullable: true
|
||||
type: array
|
||||
labelSelector:
|
||||
description: LabelSelector is a metav1.LabelSelector to filter with
|
||||
when adding individual objects to the backup. If empty or nil,
|
||||
all objects are included. Optional.
|
||||
nullable: true
|
||||
properties:
|
||||
matchExpressions:
|
||||
description: matchExpressions is a list of label selector requirements.
|
||||
The requirements are ANDed.
|
||||
items:
|
||||
description: A label selector requirement is a selector that
|
||||
contains values, a key, and an operator that relates the
|
||||
key and values.
|
||||
properties:
|
||||
key:
|
||||
description: key is the label key that the selector applies
|
||||
to.
|
||||
type: string
|
||||
operator:
|
||||
description: operator represents a key's relationship
|
||||
to a set of values. Valid operators are In, NotIn, Exists
|
||||
and DoesNotExist.
|
||||
type: string
|
||||
values:
|
||||
description: values is an array of string values. If the
|
||||
operator is In or NotIn, the values array must be non-empty.
|
||||
If the operator is Exists or DoesNotExist, the values
|
||||
array must be empty. This array is replaced during a
|
||||
strategic merge patch.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- key
|
||||
- operator
|
||||
type: object
|
||||
type: array
|
||||
matchLabels:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: matchLabels is a map of {key,value} pairs. A single
|
||||
{key,value} in the matchLabels map is equivalent to an element
|
||||
of matchExpressions, whose key field is "key", the operator
|
||||
is "In", and the values array contains only "value". The requirements
|
||||
are ANDed.
|
||||
type: object
|
||||
type: object
|
||||
snapshotVolumes:
|
||||
description: SnapshotVolumes specifies whether to take cloud snapshots
|
||||
of any PV's referenced in the set of objects included in the Backup.
|
||||
nullable: true
|
||||
type: boolean
|
||||
storageLocation:
|
||||
description: StorageLocation is a string containing the name of
|
||||
a BackupStorageLocation where the backup should be stored.
|
||||
type: string
|
||||
ttl:
|
||||
description: TTL is a time.Duration-parseable string describing
|
||||
how long the Backup should be retained for.
|
||||
type: string
|
||||
volumeSnapshotLocations:
|
||||
description: VolumeSnapshotLocations is a list containing names
|
||||
of VolumeSnapshotLocations associated with this backup.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
required:
|
||||
- schedule
|
||||
- template
|
||||
type: object
|
||||
status:
|
||||
description: ScheduleStatus captures the current state of a Velero schedule
|
||||
properties:
|
||||
lastBackup:
|
||||
description: LastBackup is the last time a Backup was run for this Schedule
|
||||
schedule
|
||||
format: date-time
|
||||
nullable: true
|
||||
type: string
|
||||
phase:
|
||||
description: Phase is the current phase of the Schedule
|
||||
enum:
|
||||
- New
|
||||
- Enabled
|
||||
- FailedValidation
|
||||
type: string
|
||||
validationErrors:
|
||||
description: ValidationErrors is a slice of all validation errors (if
|
||||
applicable)
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
type: object
|
||||
type: object
|
||||
version: v1
|
||||
versions:
|
||||
- name: v1
|
||||
served: true
|
||||
storage: true
|
||||
status:
|
||||
acceptedNames:
|
||||
kind: ""
|
||||
plural: ""
|
||||
conditions: []
|
||||
storedVersions: []
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,16 @@
|
|||
apiVersion: velero.io/v1
|
||||
kind: BackupStorageLocation
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
component: velero
|
||||
name: default
|
||||
namespace: velero
|
||||
spec:
|
||||
config:
|
||||
region: minio
|
||||
s3ForcePathStyle: "true"
|
||||
s3Url: http://minio.velero.svc:9000
|
||||
objectStorage:
|
||||
bucket: velero
|
||||
provider: aws
|
3
go.mod
3
go.mod
|
@ -30,7 +30,7 @@ require (
|
|||
github.com/spf13/cobra v0.0.5
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/stretchr/testify v1.4.0
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9
|
||||
google.golang.org/grpc v1.26.0
|
||||
k8s.io/api v0.17.4
|
||||
k8s.io/apiextensions-apiserver v0.17.4
|
||||
|
@ -39,4 +39,5 @@ require (
|
|||
k8s.io/client-go v0.17.4
|
||||
k8s.io/klog v1.0.0
|
||||
k8s.io/utils v0.0.0-20191218082557-f07c713de883 // indirect
|
||||
sigs.k8s.io/controller-runtime v0.5.2
|
||||
)
|
||||
|
|
39
go.sum
39
go.sum
|
@ -58,7 +58,9 @@ github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdko
|
|||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
|
@ -103,6 +105,7 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
|||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29 h1:llBx5m8Gk0lrAaiLud2wktkX/e8haX7Ru0oVfQqtZQ4=
|
||||
github.com/docker/spdystream v0.0.0-20170912183627-bc6354cbbc29/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
|
||||
|
@ -125,7 +128,10 @@ github.com/go-ini/ini v1.28.2 h1:drmmYv7psRpoGZkPtPKKTB+ZFSnvmwCMfNj5o1nLh2Y=
|
|||
github.com/go-ini/ini v1.28.2/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/zapr v0.1.0 h1:h+WVe9j6HAA01niTJPA/kKH0i7e0rLZBCwauQFcRE54=
|
||||
github.com/go-logr/zapr v0.1.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk=
|
||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
|
||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
|
||||
|
@ -212,6 +218,8 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
|||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g=
|
||||
github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
|
||||
github.com/googleapis/gnostic v0.3.1 h1:WeAefnSUHlBb0iJKwxFDZdbfGwkd7xRNuV+IpXMJhYk=
|
||||
github.com/googleapis/gnostic v0.3.1/go.mod h1:on+2t9HRStVgn95RSsFWFz+6Q0Snyqv1awfrALZdbtU=
|
||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
|
@ -234,6 +242,7 @@ github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKe
|
|||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
|
||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
|
@ -306,9 +315,13 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
|||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.2 h1:uqH7bpe+ERSiDa34FDOF7RikN6RzXgduUF8yarlZp94=
|
||||
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.11.0 h1:JAKSXpt1YjtLA7YpPiqO9ss6sNXEsPfSGdwN0UHqzrw=
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.8.1 h1:C5Dqfs/LeauYDX0jJXIe2SWmwCbGzx9yF8C8xy3Lh34=
|
||||
github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
|
@ -383,8 +396,11 @@ go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qL
|
|||
go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
|
@ -396,6 +412,7 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -439,6 +456,8 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM=
|
||||
golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
|
@ -468,8 +487,11 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f h1:72l8qCJ1nGxMGH26QVBVIxKd/D34cfGt0OvrPtpemyY=
|
||||
golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -502,7 +524,10 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw
|
|||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1 h1:xyiBuvkD2g5n7cYzx6u2sxQvsAy4QJsZFCzGVdzOXZ0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.0.1/go.mod h1:IhYNNY4jnS53ZnfE4PAmpKtDpTCj1JFXc+3mwe7XcUU=
|
||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
|
||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
|
||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
|
||||
|
@ -533,10 +558,12 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
|||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
|
@ -552,6 +579,7 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
|
|||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
|
@ -561,23 +589,30 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
|
|||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
k8s.io/api v0.17.0/go.mod h1:npsyOePkeP0CPwyGfXDHxvypiYMJxBWAMpQxCaJ4ZxI=
|
||||
k8s.io/api v0.17.2/go.mod h1:BS9fjjLc4CMuqfSO8vgbHPKMt5+SF0ET6u/RVDihTo4=
|
||||
k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo=
|
||||
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
|
||||
k8s.io/apiextensions-apiserver v0.17.2/go.mod h1:4KdMpjkEjjDI2pPfBA15OscyNldHWdBCfsWMDWAmSTs=
|
||||
k8s.io/apiextensions-apiserver v0.17.4 h1:ZKFnw3cJrGZ/9s6y+DerTF4FL+dmK0a04A++7JkmMho=
|
||||
k8s.io/apiextensions-apiserver v0.17.4/go.mod h1:rCbbbaFS/s3Qau3/1HbPlHblrWpFivoaLYccCffvQGI=
|
||||
k8s.io/apimachinery v0.17.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.1-beta.0/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.2/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
|
||||
k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw=
|
||||
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/apiserver v0.17.2/go.mod h1:lBmw/TtQdtxvrTk0e2cgtOxHizXI+d0mmGQURIHQZlo=
|
||||
k8s.io/apiserver v0.17.4/go.mod h1:5ZDQ6Xr5MNBxyi3iUZXS84QOhZl+W7Oq2us/29c0j9I=
|
||||
k8s.io/cli-runtime v0.17.4 h1:ZIJdxpBEszZqUhydrCoiI5rLXS2J/1AF5xFok2QJ9bc=
|
||||
k8s.io/cli-runtime v0.17.4/go.mod h1:IVW4zrKKx/8gBgNNkhiUIc7nZbVVNhc1+HcQh+PiNHc=
|
||||
k8s.io/client-go v0.17.0/go.mod h1:TYgR6EUHs6k45hb6KWjVD6jFZvJV4gHDikv/It0xz+k=
|
||||
k8s.io/client-go v0.17.2/go.mod h1:QAzRgsa0C2xl4/eVpeVAZMvikCn8Nm81yqVx3Kk9XYI=
|
||||
k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8=
|
||||
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
|
||||
k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
|
||||
k8s.io/code-generator v0.17.4/go.mod h1:l8BLVwASXQZTo2xamW5mQNFCe1XPiAesVq7Y1t7PiQQ=
|
||||
k8s.io/component-base v0.17.0/go.mod h1:rKuRAokNMY2nn2A6LP/MiwpoaMRHpfRnrPaUJJj1Yoc=
|
||||
k8s.io/component-base v0.17.2/go.mod h1:zMPW3g5aH7cHJpKYQ/ZsGMcgbsA/VyhEugF3QT1awLs=
|
||||
k8s.io/component-base v0.17.4/go.mod h1:5BRqHMbbQPm2kKu35v3G+CpVq4K0RJKC7TRioF0I9lE=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
|
@ -597,6 +632,8 @@ modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03
|
|||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
|
||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
sigs.k8s.io/controller-runtime v0.5.2 h1:pyXbUfoTo+HA3jeIfr0vgi+1WtmNh0CwlcnQGLXwsSw=
|
||||
sigs.k8s.io/controller-runtime v0.5.2/go.mod h1:JZUwSMVbxDupo0lTJSSFP5pimEyxGynROImSsqIOx1A=
|
||||
sigs.k8s.io/kustomize v2.0.3+incompatible/go.mod h1:MkjgH3RdOWrievjo6c9T245dYlB5QeXV4WCbnt/PEpU=
|
||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
|
||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
|
||||
|
|
|
@ -14,8 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
// This code embeds the CRD manifests in pkg/generated/crds/manifests in
|
||||
// pkg/generated/crds/crds.go.
|
||||
// This code embeds the CRD manifests in config/crd/bases in
|
||||
// config/crd/crds/crds.go.
|
||||
|
||||
package main
|
||||
|
||||
|
@ -30,7 +30,7 @@ import (
|
|||
"text/template"
|
||||
)
|
||||
|
||||
// This is relative to pkg/generated/crds
|
||||
// This is relative to config/crd/crds
|
||||
const goHeaderFile = "../../../hack/boilerplate.go.txt"
|
||||
|
||||
const tpl = `{{.GoHeader}}
|
||||
|
@ -96,14 +96,14 @@ func main() {
|
|||
GoHeader: string(headerBytes),
|
||||
}
|
||||
|
||||
// This is relative to pkg/generated/crds
|
||||
manifests, err := ioutil.ReadDir("manifests")
|
||||
// This is relative to config/crd/crds
|
||||
manifests, err := ioutil.ReadDir("../bases")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
for _, crd := range manifests {
|
||||
file, err := os.Open("manifests/" + crd.Name())
|
||||
file, err := os.Open("../bases/" + crd.Name())
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ func main() {
|
|||
data.RawCRDs = append(data.RawCRDs, fmt.Sprintf("%q", buf.Bytes()))
|
||||
}
|
||||
|
||||
t, err := template.New("crds").Parse(tpl)
|
||||
t, err := template.New("crd").Parse(tpl)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
|
|
@ -44,9 +44,12 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \
|
|||
--output-base ../../.. \
|
||||
$@
|
||||
|
||||
# Generate manifests e.g. CRD, RBAC etc.
|
||||
controller-gen \
|
||||
crd:crdVersions=v1beta1,preserveUnknownFields=false \
|
||||
output:dir=./pkg/generated/crds/manifests \
|
||||
paths=./pkg/apis/velero/v1/...
|
||||
crd:crdVersions=v1beta1,preserveUnknownFields=false,trivialVersions=true \
|
||||
rbac:roleName=manager-role \
|
||||
paths=./pkg/apis/velero/v1/... \
|
||||
paths=./pkg/controller/... \
|
||||
output:crd:artifacts:config=config/crd/bases
|
||||
|
||||
go generate ./pkg/generated/crds
|
||||
go generate ./config/crd/crds
|
||||
|
|
|
@ -19,11 +19,11 @@ HACK_DIR=$(dirname "${BASH_SOURCE}")
|
|||
${HACK_DIR}/update-generated-crd-code.sh --verify-only
|
||||
|
||||
# ensure no changes to generated CRDs
|
||||
if ! git diff --exit-code pkg/generated/crds/crds.go >/dev/null; then
|
||||
if ! git diff --exit-code config/crd/crds/crds.go >/dev/null; then
|
||||
# revert changes to state before running CRD generation to stay consistent
|
||||
# with code-generator `--verify-only` option which discards generated changes
|
||||
git checkout pkg/generated/crds
|
||||
git checkout config/crd/bases
|
||||
|
||||
echo "CRD verification - failed! Generated CRDs are out-of-date, please run 'make update'."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// TODO(2.0) After converting all controllers to runttime-controller,
|
||||
// the functions in this file will no longer be needed and should be removed.
|
||||
package managercontroller
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/controller"
|
||||
)
|
||||
|
||||
// Runnable will turn a "regular" runnable component (such as a controller)
|
||||
// into a controller-runtime Runnable
|
||||
func Runnable(p controller.Interface, numWorkers int) manager.Runnable {
|
||||
f := func(stop <-chan struct{}) error {
|
||||
|
||||
// Create a cancel context for handling the stop signal.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// If a signal is received on the stop channel, cancel the
|
||||
// context. This will propagate the cancel into the p.Run
|
||||
// function below.
|
||||
go func() {
|
||||
select {
|
||||
case <-stop:
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}()
|
||||
|
||||
// This is a blocking call that either completes
|
||||
// or is cancellable on receiving a stop signal.
|
||||
return p.Run(ctx, numWorkers)
|
||||
}
|
||||
return manager.RunnableFunc(f)
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright 2018 the Velero contributors.
|
||||
Copyright 2017, 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -21,56 +21,7 @@ import (
|
|||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// BackupStorageLocation is a location where Velero stores backup objects.
|
||||
type BackupStorageLocation struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +optional
|
||||
Spec BackupStorageLocationSpec `json:"spec,omitempty"`
|
||||
|
||||
// +optional
|
||||
Status BackupStorageLocationStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// BackupStorageLocationList is a list of BackupStorageLocations.
|
||||
type BackupStorageLocationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// +optional
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
|
||||
Items []BackupStorageLocation `json:"items"`
|
||||
}
|
||||
|
||||
// StorageType represents the type of storage that a backup location uses.
|
||||
// ObjectStorage must be non-nil, since it is currently the only supported StorageType.
|
||||
type StorageType struct {
|
||||
ObjectStorage *ObjectStorageLocation `json:"objectStorage"`
|
||||
}
|
||||
|
||||
// ObjectStorageLocation specifies the settings necessary to connect to a provider's object storage.
|
||||
type ObjectStorageLocation struct {
|
||||
// Bucket is the bucket to use for object storage.
|
||||
Bucket string `json:"bucket"`
|
||||
|
||||
// Prefix is the path inside a bucket to use for Velero storage. Optional.
|
||||
// +optional
|
||||
Prefix string `json:"prefix,omitempty"`
|
||||
|
||||
// CACert defines a CA bundle to use when verifying TLS connections to the provider.
|
||||
// +optional
|
||||
CACert []byte `json:"caCert,omitempty"`
|
||||
}
|
||||
|
||||
// BackupStorageLocationSpec defines the specification for a Velero BackupStorageLocation.
|
||||
// BackupStorageLocationSpec defines the desired state of a Velero BackupStorageLocation
|
||||
type BackupStorageLocationSpec struct {
|
||||
// Provider is the provider of the backup storage.
|
||||
Provider string `json:"provider"`
|
||||
|
@ -91,34 +42,7 @@ type BackupStorageLocationSpec struct {
|
|||
BackupSyncPeriod *metav1.Duration `json:"backupSyncPeriod,omitempty"`
|
||||
}
|
||||
|
||||
// BackupStorageLocationPhase is the lifecyle phase of a Velero BackupStorageLocation.
|
||||
// +kubebuilder:validation:Enum=Available;Unavailable
|
||||
type BackupStorageLocationPhase string
|
||||
|
||||
const (
|
||||
// BackupStorageLocationPhaseAvailable means the location is available to read and write from.
|
||||
BackupStorageLocationPhaseAvailable BackupStorageLocationPhase = "Available"
|
||||
|
||||
// BackupStorageLocationPhaseUnavailable means the location is unavailable to read and write from.
|
||||
BackupStorageLocationPhaseUnavailable BackupStorageLocationPhase = "Unavailable"
|
||||
)
|
||||
|
||||
// BackupStorageLocationAccessMode represents the permissions for a BackupStorageLocation.
|
||||
// +kubebuilder:validation:Enum=ReadOnly;ReadWrite
|
||||
type BackupStorageLocationAccessMode string
|
||||
|
||||
const (
|
||||
// BackupStorageLocationAccessModeReadOnly represents read-only access to a BackupStorageLocation.
|
||||
BackupStorageLocationAccessModeReadOnly BackupStorageLocationAccessMode = "ReadOnly"
|
||||
|
||||
// BackupStorageLocationAccessModeReadWrite represents read and write access to a BackupStorageLocation.
|
||||
BackupStorageLocationAccessModeReadWrite BackupStorageLocationAccessMode = "ReadWrite"
|
||||
)
|
||||
|
||||
// TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus.
|
||||
// TODO(2.0): remove the LastSyncedRevision field from BackupStorageLocationStatus.
|
||||
|
||||
// BackupStorageLocationStatus describes the current status of a Velero BackupStorageLocation.
|
||||
// BackupStorageLocationStatus defines the observed state of BackupStorageLocation
|
||||
type BackupStorageLocationStatus struct {
|
||||
// Phase is the current state of the BackupStorageLocation.
|
||||
// +optional
|
||||
|
@ -145,3 +69,82 @@ type BackupStorageLocationStatus struct {
|
|||
// +optional
|
||||
AccessMode BackupStorageLocationAccessMode `json:"accessMode,omitempty"`
|
||||
}
|
||||
|
||||
// TODO(2.0) After converting all resources to use the runttime-controller client,
|
||||
// the genclient and k8s:deepcopy markers will no longer be needed and should be removed.
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:object:generate=true
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase",description="Backup Storage Location status such as Available/Unavailable"
|
||||
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
|
||||
|
||||
// BackupStorageLocation is a location where Velero stores backup objects
|
||||
type BackupStorageLocation struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
Spec BackupStorageLocationSpec `json:"spec,omitempty"`
|
||||
Status BackupStorageLocationStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// TODO(2.0) After converting all resources to use the runttime-controller client,
|
||||
// the k8s:deepcopy marker will no longer be needed and should be removed.
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// BackupStorageLocationList contains a list of BackupStorageLocation
|
||||
type BackupStorageLocationList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitempty"`
|
||||
Items []BackupStorageLocation `json:"items"`
|
||||
}
|
||||
|
||||
// StorageType represents the type of storage that a backup location uses.
|
||||
// ObjectStorage must be non-nil, since it is currently the only supported StorageType.
|
||||
type StorageType struct {
|
||||
ObjectStorage *ObjectStorageLocation `json:"objectStorage"`
|
||||
}
|
||||
|
||||
// ObjectStorageLocation specifies the settings necessary to connect to a provider's object storage.
|
||||
type ObjectStorageLocation struct {
|
||||
// Bucket is the bucket to use for object storage.
|
||||
Bucket string `json:"bucket"`
|
||||
|
||||
// Prefix is the path inside a bucket to use for Velero storage. Optional.
|
||||
// +optional
|
||||
Prefix string `json:"prefix,omitempty"`
|
||||
|
||||
// CACert defines a CA bundle to use when verifying TLS connections to the provider.
|
||||
// +optional
|
||||
CACert []byte `json:"caCert,omitempty"`
|
||||
}
|
||||
|
||||
// BackupStorageLocationPhase is the lifecycle phase of a Velero BackupStorageLocation.
|
||||
// +kubebuilder:validation:Enum=Available;Unavailable
|
||||
type BackupStorageLocationPhase string
|
||||
|
||||
const (
|
||||
// BackupStorageLocationPhaseAvailable means the location is available to read and write from.
|
||||
BackupStorageLocationPhaseAvailable BackupStorageLocationPhase = "Available"
|
||||
|
||||
// BackupStorageLocationPhaseUnavailable means the location is unavailable to read and write from.
|
||||
BackupStorageLocationPhaseUnavailable BackupStorageLocationPhase = "Unavailable"
|
||||
)
|
||||
|
||||
// BackupStorageLocationAccessMode represents the permissions for a BackupStorageLocation.
|
||||
// +kubebuilder:validation:Enum=ReadOnly;ReadWrite
|
||||
type BackupStorageLocationAccessMode string
|
||||
|
||||
const (
|
||||
// BackupStorageLocationAccessModeReadOnly represents read-only access to a BackupStorageLocation.
|
||||
BackupStorageLocationAccessModeReadOnly BackupStorageLocationAccessMode = "ReadOnly"
|
||||
|
||||
// BackupStorageLocationAccessModeReadWrite represents read and write access to a BackupStorageLocation.
|
||||
BackupStorageLocationAccessModeReadWrite BackupStorageLocationAccessMode = "ReadWrite"
|
||||
)
|
||||
|
||||
// TODO(2.0): remove the AccessMode field from BackupStorageLocationStatus.
|
||||
// TODO(2.0): remove the LastSyncedRevision field from BackupStorageLocationStatus.
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package v1 contains API Schema definitions for the velero v1 API group
|
||||
// +kubebuilder:object:generate=true
|
||||
// +groupName=velero.io
|
||||
package v1
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
SchemeGroupVersion = schema.GroupVersion{Group: "velero.io", Version: "v1"}
|
||||
|
||||
// SchemeBuilder is used to add go types to the GroupVersionKind scheme
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
|
||||
// AddToScheme adds the types in this group-version to the given scheme.
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
|
@ -22,20 +22,6 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var (
|
||||
// SchemeBuilder collects the scheme builder functions for the Velero API
|
||||
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes)
|
||||
|
||||
// AddToScheme applies the SchemeBuilder functions to a specified scheme
|
||||
AddToScheme = SchemeBuilder.AddToScheme
|
||||
)
|
||||
|
||||
// GroupName is the group name for the Velero API
|
||||
const GroupName = "velero.io"
|
||||
|
||||
// SchemeGroupVersion is the GroupVersion for the Velero API
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
|
||||
|
||||
// Resource gets a Velero GroupResource for a specified resource
|
||||
func Resource(resource string) schema.GroupResource {
|
||||
return SchemeGroupVersion.WithResource(resource).GroupResource()
|
||||
|
|
|
@ -19,13 +19,16 @@ package client
|
|||
import (
|
||||
"os"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/dynamic"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
|
||||
)
|
||||
|
||||
|
@ -42,6 +45,9 @@ type Factory interface {
|
|||
// DynamicClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster
|
||||
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
|
||||
DynamicClient() (dynamic.Interface, error)
|
||||
// KubebuilderClient returns a Kubernetes dynamic client. It uses the following priority to specify the cluster
|
||||
// configuration: --kubeconfig flag, KUBECONFIG environment variable, in-cluster configuration.
|
||||
KubebuilderClient() (kbclient.Client, error)
|
||||
// SetBasename changes the basename for an already-constructed client.
|
||||
// This is useful for generating clients that require a different user-agent string below the root `velero`
|
||||
// command, such as the server subcommand.
|
||||
|
@ -81,7 +87,7 @@ func NewFactory(baseName string, config VeleroConfig) Factory {
|
|||
// We didn't get the namespace via env var or config file, so use the default.
|
||||
// Command line flags will override when BindFlags is called.
|
||||
if f.namespace == "" {
|
||||
f.namespace = v1.DefaultNamespace
|
||||
f.namespace = velerov1api.DefaultNamespace
|
||||
}
|
||||
|
||||
f.flags.StringVar(&f.kubeconfig, "kubeconfig", "", "Path to the kubeconfig file to use to talk to the Kubernetes apiserver. If unset, try the environment variable KUBECONFIG, as well as in-cluster configuration")
|
||||
|
@ -137,6 +143,21 @@ func (f *factory) DynamicClient() (dynamic.Interface, error) {
|
|||
return dynamicClient, nil
|
||||
}
|
||||
|
||||
func (f *factory) KubebuilderClient() (kbclient.Client, error) {
|
||||
clientConfig, err := f.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
velerov1api.AddToScheme(scheme)
|
||||
kubebuilderClient, err := kbclient.New(clientConfig, kbclient.Options{
|
||||
Scheme: scheme,
|
||||
})
|
||||
|
||||
return kubebuilderClient, nil
|
||||
}
|
||||
|
||||
func (f *factory) SetBasename(name string) {
|
||||
f.baseName = name
|
||||
}
|
||||
|
|
|
@ -17,9 +17,12 @@ limitations under the License.
|
|||
package backup
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -146,13 +149,22 @@ func (o *CreateOptions) Validate(c *cobra.Command, args []string, f client.Facto
|
|||
return err
|
||||
}
|
||||
|
||||
client, err := f.KubebuilderClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure that unless FromSchedule is set, args contains a backup name
|
||||
if o.FromSchedule == "" && len(args) != 1 {
|
||||
return fmt.Errorf("a backup name is required, unless you are creating based on a schedule")
|
||||
}
|
||||
|
||||
if o.StorageLocation != "" {
|
||||
if _, err := o.client.VeleroV1().BackupStorageLocations(f.Namespace()).Get(o.StorageLocation, metav1.GetOptions{}); err != nil {
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
if err := client.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: f.Namespace(),
|
||||
Name: o.StorageLocation,
|
||||
}, location); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package backuplocation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -26,6 +27,8 @@ import (
|
|||
"github.com/spf13/pflag"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
|
@ -146,12 +149,12 @@ func (o *CreateOptions) Run(c *cobra.Command, f client.Factory) error {
|
|||
return err
|
||||
}
|
||||
|
||||
client, err := f.Client()
|
||||
client, err := f.KubebuilderClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := client.VeleroV1().BackupStorageLocations(backupStorageLocation.Namespace).Create(backupStorageLocation); err != nil {
|
||||
if err := client.Create(context.Background(), backupStorageLocation, &kbclient.CreateOptions{}); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,14 @@ limitations under the License.
|
|||
package backuplocation
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
|
||||
|
@ -36,19 +40,24 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command {
|
|||
err := output.ValidateFlags(c)
|
||||
cmd.CheckError(err)
|
||||
|
||||
veleroClient, err := f.Client()
|
||||
client, err := f.KubebuilderClient()
|
||||
cmd.CheckError(err)
|
||||
|
||||
var locations *api.BackupStorageLocationList
|
||||
locations := new(velerov1api.BackupStorageLocationList)
|
||||
if len(args) > 0 {
|
||||
locations = new(api.BackupStorageLocationList)
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
for _, name := range args {
|
||||
location, err := veleroClient.VeleroV1().BackupStorageLocations(f.Namespace()).Get(name, metav1.GetOptions{})
|
||||
err = client.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: f.Namespace(),
|
||||
Name: name,
|
||||
}, location)
|
||||
cmd.CheckError(err)
|
||||
locations.Items = append(locations.Items, *location)
|
||||
}
|
||||
} else {
|
||||
locations, err = veleroClient.VeleroV1().BackupStorageLocations(f.Namespace()).List(listOptions)
|
||||
err := client.List(context.Background(), locations, &kbclient.ListOptions{
|
||||
Namespace: f.Namespace(),
|
||||
})
|
||||
cmd.CheckError(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -20,19 +20,24 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/util/managercontroller"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
kubeinformers "k8s.io/client-go/informers"
|
||||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/buildinfo"
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
|
@ -43,6 +48,13 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
)
|
||||
|
||||
func NewServerCommand(f client.Factory) *cobra.Command {
|
||||
|
@ -86,6 +98,7 @@ type resticServer struct {
|
|||
ctx context.Context
|
||||
cancelFunc context.CancelFunc
|
||||
fileSystem filesystem.Interface
|
||||
mgr manager.Manager
|
||||
}
|
||||
|
||||
func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*resticServer, error) {
|
||||
|
@ -130,6 +143,20 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic
|
|||
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
|
||||
clientConfig, err := factory.ClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseDevMode(true)))
|
||||
velerov1api.AddToScheme(scheme)
|
||||
mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{
|
||||
Scheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &resticServer{
|
||||
kubeClient: kubeClient,
|
||||
veleroClient: veleroClient,
|
||||
|
@ -141,6 +168,7 @@ func newResticServer(logger logrus.FieldLogger, factory client.Factory) (*restic
|
|||
ctx: ctx,
|
||||
cancelFunc: cancelFunc,
|
||||
fileSystem: filesystem.NewFileSystem(),
|
||||
mgr: mgr,
|
||||
}
|
||||
|
||||
if err := s.validatePodVolumesHostPath(); err != nil {
|
||||
|
@ -155,8 +183,6 @@ func (s *resticServer) run() {
|
|||
|
||||
s.logger.Info("Starting controllers")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
backupController := controller.NewPodVolumeBackupController(
|
||||
s.logger,
|
||||
s.veleroInformerFactory.Velero().V1().PodVolumeBackups(),
|
||||
|
@ -165,14 +191,9 @@ func (s *resticServer) run() {
|
|||
s.secretInformer,
|
||||
s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(),
|
||||
s.kubeInformerFactory.Core().V1().PersistentVolumes(),
|
||||
s.veleroInformerFactory.Velero().V1().BackupStorageLocations(),
|
||||
s.mgr.GetClient(),
|
||||
os.Getenv("NODE_NAME"),
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
backupController.Run(s.ctx, 1)
|
||||
}()
|
||||
|
||||
restoreController := controller.NewPodVolumeRestoreController(
|
||||
s.logger,
|
||||
|
@ -182,26 +203,30 @@ func (s *resticServer) run() {
|
|||
s.secretInformer,
|
||||
s.kubeInformerFactory.Core().V1().PersistentVolumeClaims(),
|
||||
s.kubeInformerFactory.Core().V1().PersistentVolumes(),
|
||||
s.veleroInformerFactory.Velero().V1().BackupStorageLocations(),
|
||||
s.mgr.GetClient(),
|
||||
os.Getenv("NODE_NAME"),
|
||||
)
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
restoreController.Run(s.ctx, 1)
|
||||
}()
|
||||
|
||||
go s.veleroInformerFactory.Start(s.ctx.Done())
|
||||
go s.kubeInformerFactory.Start(s.ctx.Done())
|
||||
go s.podInformer.Run(s.ctx.Done())
|
||||
go s.secretInformer.Run(s.ctx.Done())
|
||||
|
||||
s.logger.Info("Controllers started successfully")
|
||||
// TODO(2.0): presuming all controllers and resources are converted to runtime-controller
|
||||
// by v2.0, the block from this line and including the `s.mgr.Start() will be
|
||||
// deprecated, since the manager auto-starts all the caches. Until then, we need to start the
|
||||
// cache for them manually.
|
||||
|
||||
<-s.ctx.Done()
|
||||
// Adding the controllers to the manager will register them as a (runtime-controller) runnable,
|
||||
// so the manager will ensure the cache is started and ready before all controller are started
|
||||
s.mgr.Add(managercontroller.Runnable(backupController, 1))
|
||||
s.mgr.Add(managercontroller.Runnable(restoreController, 1))
|
||||
|
||||
s.logger.Info("Waiting for all controllers to shut down gracefully")
|
||||
wg.Wait()
|
||||
s.logger.Info("Controllers starting...")
|
||||
|
||||
if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
s.logger.Fatal("Problem starting manager", err)
|
||||
}
|
||||
}
|
||||
|
||||
// validatePodVolumesHostPath validates that the pod volumes path contains a
|
||||
|
|
|
@ -25,7 +25,6 @@ import (
|
|||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -34,6 +33,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
@ -56,6 +56,7 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/flag"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/signals"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/controller"
|
||||
velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery"
|
||||
"github.com/vmware-tanzu/velero/pkg/features"
|
||||
|
@ -68,6 +69,14 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/restore"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/manager"
|
||||
|
||||
"github.com/vmware-tanzu/velero/internal/util/managercontroller"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -240,6 +249,7 @@ type server struct {
|
|||
resticManager restic.RepositoryManager
|
||||
metrics *metrics.ServerMetrics
|
||||
config serverConfig
|
||||
mgr manager.Manager
|
||||
}
|
||||
|
||||
func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*server, error) {
|
||||
|
@ -294,6 +304,16 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s
|
|||
}
|
||||
}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
velerov1api.AddToScheme(scheme)
|
||||
mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{
|
||||
Scheme: scheme,
|
||||
})
|
||||
if err != nil {
|
||||
cancelFunc()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := &server{
|
||||
namespace: f.Namespace(),
|
||||
metricsAddress: config.metricsAddress,
|
||||
|
@ -311,6 +331,7 @@ func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*s
|
|||
logLevel: logger.Level,
|
||||
pluginRegistry: pluginRegistry,
|
||||
config: config,
|
||||
mgr: mgr,
|
||||
}
|
||||
|
||||
return s, nil
|
||||
|
@ -342,7 +363,13 @@ func (s *server) run() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if _, err := s.veleroClient.VeleroV1().BackupStorageLocations(s.namespace).Get(s.config.defaultBackupLocation, metav1.GetOptions{}); err != nil {
|
||||
// Fetching from the server directly since at this point
|
||||
// the cache has not yet started
|
||||
bsl := &velerov1api.BackupStorageLocation{}
|
||||
if err := s.mgr.GetAPIReader().Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: s.namespace,
|
||||
Name: s.config.defaultBackupLocation,
|
||||
}, bsl); err != nil {
|
||||
s.logger.WithError(errors.WithStack(err)).
|
||||
Warnf("A backup storage location named %s has been specified for the server to use by default, but no corresponding backup storage location exists. Backups with a location not matching the default will need to explicitly specify an existing location", s.config.defaultBackupLocation)
|
||||
}
|
||||
|
@ -442,8 +469,12 @@ func (s *server) validateBackupStorageLocations() error {
|
|||
pluginManager := clientmgmt.NewManager(s.logger, s.logLevel, s.pluginRegistry)
|
||||
defer pluginManager.CleanupClients()
|
||||
|
||||
locations, err := s.veleroClient.VeleroV1().BackupStorageLocations(s.namespace).List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
// Fetching from the server directly since at this point
|
||||
// the cache has not yet started
|
||||
locations := &velerov1api.BackupStorageLocationList{}
|
||||
if err := s.mgr.GetAPIReader().List(context.Background(), locations, &kbclient.ListOptions{
|
||||
Namespace: s.namespace,
|
||||
}); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
|
@ -543,7 +574,7 @@ func (s *server) initRestic() error {
|
|||
secretsInformer,
|
||||
s.sharedInformerFactory.Velero().V1().ResticRepositories(),
|
||||
s.veleroClient.VeleroV1(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations(),
|
||||
s.mgr.GetClient(),
|
||||
s.kubeClient.CoreV1(),
|
||||
s.kubeClient.CoreV1(),
|
||||
s.logger,
|
||||
|
@ -588,7 +619,6 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.logger.Info("Starting controllers")
|
||||
|
||||
ctx := s.ctx
|
||||
var wg sync.WaitGroup
|
||||
|
||||
go func() {
|
||||
metricsMux := http.NewServeMux()
|
||||
|
@ -611,10 +641,9 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
backupSyncControllerRunInfo := func() controllerRunInfo {
|
||||
backupSyncContoller := controller.NewBackupSyncController(
|
||||
s.veleroClient.VeleroV1(),
|
||||
s.veleroClient.VeleroV1(),
|
||||
s.mgr.GetClient(),
|
||||
s.veleroClient.VeleroV1(),
|
||||
s.sharedInformerFactory.Velero().V1().Backups().Lister(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.config.backupSyncPeriod,
|
||||
s.namespace,
|
||||
s.csiSnapshotClient,
|
||||
|
@ -653,7 +682,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.logLevel,
|
||||
newPluginManager,
|
||||
backupTracker,
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.mgr.GetClient(),
|
||||
s.config.defaultBackupLocation,
|
||||
s.config.defaultVolumesToRestic,
|
||||
s.config.defaultBackupTTL,
|
||||
|
@ -693,7 +722,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.sharedInformerFactory.Velero().V1().Backups(),
|
||||
s.sharedInformerFactory.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
s.veleroClient.VeleroV1(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.mgr.GetClient(),
|
||||
)
|
||||
|
||||
return controllerRunInfo{
|
||||
|
@ -713,7 +742,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
backupTracker,
|
||||
s.resticManager,
|
||||
s.sharedInformerFactory.Velero().V1().PodVolumeBackups().Lister(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.mgr.GetClient(),
|
||||
s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
csiVSLister,
|
||||
csiVSCLister,
|
||||
|
@ -748,7 +777,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.veleroClient.VeleroV1(),
|
||||
restorer,
|
||||
s.sharedInformerFactory.Velero().V1().Backups().Lister(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.mgr.GetClient(),
|
||||
s.sharedInformerFactory.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
s.logger,
|
||||
s.logLevel,
|
||||
|
@ -769,7 +798,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.logger,
|
||||
s.sharedInformerFactory.Velero().V1().ResticRepositories(),
|
||||
s.veleroClient.VeleroV1(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.mgr.GetClient(),
|
||||
s.resticManager,
|
||||
s.config.defaultResticMaintenanceFrequency,
|
||||
)
|
||||
|
@ -785,7 +814,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.veleroClient.VeleroV1(),
|
||||
s.sharedInformerFactory.Velero().V1().DownloadRequests(),
|
||||
s.sharedInformerFactory.Velero().V1().Restores().Lister(),
|
||||
s.sharedInformerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
s.mgr.GetClient(),
|
||||
s.sharedInformerFactory.Velero().V1().Backups().Lister(),
|
||||
newPluginManager,
|
||||
s.logger,
|
||||
|
@ -872,23 +901,22 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string
|
|||
s.logger.WithField("informer", informer).Info("Informer cache synced")
|
||||
}
|
||||
|
||||
// now that the informer caches have all synced, we can start running the controllers
|
||||
// TODO(2.0): presuming all controllers and resources are converted to runtime-controller
|
||||
// by v2.0, the block from this line and including the `s.mgr.Start() will be
|
||||
// deprecated, since the manager auto-starts all the caches. Until then, we need to start the
|
||||
// cache for them manually.
|
||||
for i := range controllers {
|
||||
controllerRunInfo := controllers[i]
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
controllerRunInfo.controller.Run(ctx, controllerRunInfo.numWorkers)
|
||||
wg.Done()
|
||||
}()
|
||||
// Adding the controllers to the manager will register them as a (runtime-controller) runnable,
|
||||
// so the manager will ensure the cache is started and ready before all controller are started
|
||||
s.mgr.Add(managercontroller.Runnable(controllerRunInfo.controller, controllerRunInfo.numWorkers))
|
||||
}
|
||||
|
||||
s.logger.Info("Server started successfully")
|
||||
s.logger.Info("Server starting...")
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
s.logger.Info("Waiting for all controllers to shut down gracefully")
|
||||
wg.Wait()
|
||||
if err := s.mgr.Start(s.ctx.Done()); err != nil {
|
||||
s.logger.Fatal("Problem starting manager", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ var (
|
|||
{Name: "Name", Type: "string", Format: "name"},
|
||||
{Name: "Provider"},
|
||||
{Name: "Bucket/Prefix"},
|
||||
{Name: "Status"},
|
||||
{Name: "Phase"},
|
||||
{Name: "Access Mode"},
|
||||
}
|
||||
)
|
||||
|
|
|
@ -19,6 +19,7 @@ package controller
|
|||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -57,6 +58,8 @@ import (
|
|||
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
"github.com/vmware-tanzu/velero/pkg/volume"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type backupController struct {
|
||||
|
@ -65,11 +68,11 @@ type backupController struct {
|
|||
backupper pkgbackup.Backupper
|
||||
lister velerov1listers.BackupLister
|
||||
client velerov1client.BackupsGetter
|
||||
kbClient kbclient.Client
|
||||
clock clock.Clock
|
||||
backupLogLevel logrus.Level
|
||||
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager
|
||||
backupTracker BackupTracker
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
defaultBackupLocation string
|
||||
defaultVolumesToRestic bool
|
||||
defaultBackupTTL time.Duration
|
||||
|
@ -91,7 +94,7 @@ func NewBackupController(
|
|||
backupLogLevel logrus.Level,
|
||||
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager,
|
||||
backupTracker BackupTracker,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
kbClient kbclient.Client,
|
||||
defaultBackupLocation string,
|
||||
defaultVolumesToRestic bool,
|
||||
defaultBackupTTL time.Duration,
|
||||
|
@ -112,7 +115,7 @@ func NewBackupController(
|
|||
backupLogLevel: backupLogLevel,
|
||||
newPluginManager: newPluginManager,
|
||||
backupTracker: backupTracker,
|
||||
backupLocationLister: backupLocationLister,
|
||||
kbClient: kbClient,
|
||||
defaultBackupLocation: defaultBackupLocation,
|
||||
defaultVolumesToRestic: defaultVolumesToRestic,
|
||||
defaultBackupTTL: defaultBackupTTL,
|
||||
|
@ -371,7 +374,11 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
|
|||
}
|
||||
|
||||
// validate the storage location, and store the BackupStorageLocation API obj on the request
|
||||
if storageLocation, err := c.backupLocationLister.BackupStorageLocations(request.Namespace).Get(request.Spec.StorageLocation); err != nil {
|
||||
storageLocation := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: request.Namespace,
|
||||
Name: request.Spec.StorageLocation,
|
||||
}, storageLocation); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: %v", err))
|
||||
} else {
|
||||
|
@ -385,7 +392,6 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg
|
|||
fmt.Sprintf("backup can't be created because backup storage location %s is currently in read-only mode", request.StorageLocation.Name))
|
||||
}
|
||||
}
|
||||
|
||||
// validate and get the backup's VolumeSnapshotLocations, and store the
|
||||
// VolumeSnapshotLocation API objs on the request
|
||||
if locs, errs := c.validateAndGetSnapshotLocations(request.Backup); len(errs) > 0 {
|
||||
|
|
|
@ -18,8 +18,10 @@ package controller
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -32,8 +34,8 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
|
||||
|
@ -154,7 +156,7 @@ func TestProcessBackupValidationFailures(t *testing.T) {
|
|||
{
|
||||
name: "non-existent backup location fails validation",
|
||||
backup: defaultBackup().StorageLocation("nonexistent").Result(),
|
||||
expectedErrs: []string{"a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocation.velero.io \"nonexistent\" not found"},
|
||||
expectedErrs: []string{"a BackupStorageLocation CRD with the name specified in the backup spec needs to be created before this backup can be executed. Error: backupstoragelocations.velero.io \"nonexistent\" not found"},
|
||||
},
|
||||
{
|
||||
name: "backup for read-only backup location fails validation",
|
||||
|
@ -177,12 +179,19 @@ func TestProcessBackupValidationFailures(t *testing.T) {
|
|||
discoveryHelper, err := discovery.NewHelper(apiServer.DiscoveryClient, logger)
|
||||
require.NoError(t, err)
|
||||
|
||||
var fakeClient kbclient.Client
|
||||
if test.backupLocation != nil {
|
||||
fakeClient = newFakeClient(t, test.backupLocation)
|
||||
} else {
|
||||
fakeClient = newFakeClient(t)
|
||||
}
|
||||
|
||||
c := &backupController{
|
||||
genericController: newGenericController("backup-test", logger),
|
||||
discoveryHelper: discoveryHelper,
|
||||
client: clientset.VeleroV1(),
|
||||
lister: sharedInformers.Velero().V1().Backups().Lister(),
|
||||
backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
kbClient: fakeClient,
|
||||
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
defaultBackupLocation: defaultBackupLocation.Name,
|
||||
clock: &clock.RealClock{},
|
||||
|
@ -192,13 +201,6 @@ func TestProcessBackupValidationFailures(t *testing.T) {
|
|||
require.NotNil(t, test.backup)
|
||||
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup))
|
||||
|
||||
if test.backupLocation != nil {
|
||||
_, err := clientset.VeleroV1().BackupStorageLocations(test.backupLocation.Namespace).Create(test.backupLocation)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.backupLocation))
|
||||
}
|
||||
|
||||
require.NoError(t, c.processBackup(fmt.Sprintf("%s/%s", test.backup.Namespace, test.backup.Name)))
|
||||
|
||||
res, err := clientset.VeleroV1().Backups(test.backup.Namespace).Get(test.backup.Name, metav1.GetOptions{})
|
||||
|
@ -244,6 +246,7 @@ func TestBackupLocationLabel(t *testing.T) {
|
|||
clientset = fake.NewSimpleClientset(test.backup)
|
||||
sharedInformers = informers.NewSharedInformerFactory(clientset, 0)
|
||||
logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag)
|
||||
fakeClient = newFakeClient(t)
|
||||
)
|
||||
|
||||
apiServer := velerotest.NewAPIServer(t)
|
||||
|
@ -255,7 +258,7 @@ func TestBackupLocationLabel(t *testing.T) {
|
|||
discoveryHelper: discoveryHelper,
|
||||
client: clientset.VeleroV1(),
|
||||
lister: sharedInformers.Velero().V1().Backups().Lister(),
|
||||
backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
kbClient: fakeClient,
|
||||
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
defaultBackupLocation: test.backupLocation.Name,
|
||||
clock: &clock.RealClock{},
|
||||
|
@ -303,6 +306,7 @@ func TestDefaultBackupTTL(t *testing.T) {
|
|||
formatFlag := logging.FormatText
|
||||
var (
|
||||
clientset = fake.NewSimpleClientset(test.backup)
|
||||
fakeClient = newFakeClient(t)
|
||||
logger = logging.DefaultLogger(logrus.DebugLevel, formatFlag)
|
||||
sharedInformers = informers.NewSharedInformerFactory(clientset, 0)
|
||||
)
|
||||
|
@ -316,7 +320,7 @@ func TestDefaultBackupTTL(t *testing.T) {
|
|||
c := &backupController{
|
||||
genericController: newGenericController("backup-test", logger),
|
||||
discoveryHelper: discoveryHelper,
|
||||
backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
kbClient: fakeClient,
|
||||
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
defaultBackupTTL: defaultBackupTTL.Duration,
|
||||
clock: clock.NewFakeClock(now),
|
||||
|
@ -776,6 +780,14 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
backupper = new(fakeBackupper)
|
||||
)
|
||||
|
||||
var fakeClient kbclient.Client
|
||||
// add the test's backup storage location if it's different than the default
|
||||
if test.backupLocation != nil && test.backupLocation != defaultBackupLocation {
|
||||
fakeClient = newFakeClient(t, test.backupLocation)
|
||||
} else {
|
||||
fakeClient = newFakeClient(t)
|
||||
}
|
||||
|
||||
apiServer := velerotest.NewAPIServer(t)
|
||||
|
||||
apiServer.DiscoveryClient.FakedServerVersion = &version.Info{
|
||||
|
@ -798,7 +810,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
discoveryHelper: discoveryHelper,
|
||||
client: clientset.VeleroV1(),
|
||||
lister: sharedInformers.Velero().V1().Backups().Lister(),
|
||||
backupLocationLister: sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
kbClient: fakeClient,
|
||||
snapshotLocationLister: sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
defaultBackupLocation: defaultBackupLocation.Name,
|
||||
defaultVolumesToRestic: test.defaultVolumesToRestic,
|
||||
|
@ -833,19 +845,7 @@ func TestProcessBackupCompletions(t *testing.T) {
|
|||
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup))
|
||||
|
||||
// add the default backup storage location to the clientset and the informer/lister store
|
||||
_, err = clientset.VeleroV1().BackupStorageLocations(defaultBackupLocation.Namespace).Create(defaultBackupLocation)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(defaultBackupLocation))
|
||||
|
||||
// add the test's backup storage location to the clientset and the informer/lister store
|
||||
// if it's different than the default
|
||||
if test.backupLocation != nil && test.backupLocation != defaultBackupLocation {
|
||||
_, err := clientset.VeleroV1().BackupStorageLocations(test.backupLocation.Namespace).Create(test.backupLocation)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.backupLocation))
|
||||
}
|
||||
require.NoError(t, fakeClient.Create(context.Background(), defaultBackupLocation))
|
||||
|
||||
require.NoError(t, c.processBackup(fmt.Sprintf("%s/%s", test.backup.Namespace, test.backup.Name)))
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
const resticTimeout = time.Minute
|
||||
|
@ -64,7 +66,7 @@ type backupDeletionController struct {
|
|||
backupTracker BackupTracker
|
||||
resticMgr restic.RepositoryManager
|
||||
podvolumeBackupLister velerov1listers.PodVolumeBackupLister
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
kbClient client.Client
|
||||
snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister
|
||||
csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister
|
||||
csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister
|
||||
|
@ -87,7 +89,7 @@ func NewBackupDeletionController(
|
|||
backupTracker BackupTracker,
|
||||
resticMgr restic.RepositoryManager,
|
||||
podvolumeBackupLister velerov1listers.PodVolumeBackupLister,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
kbClient client.Client,
|
||||
snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister,
|
||||
csiSnapshotLister snapshotv1beta1listers.VolumeSnapshotLister,
|
||||
csiSnapshotContentLister snapshotv1beta1listers.VolumeSnapshotContentLister,
|
||||
|
@ -105,7 +107,7 @@ func NewBackupDeletionController(
|
|||
backupTracker: backupTracker,
|
||||
resticMgr: resticMgr,
|
||||
podvolumeBackupLister: podvolumeBackupLister,
|
||||
backupLocationLister: backupLocationLister,
|
||||
kbClient: kbClient,
|
||||
snapshotLocationLister: snapshotLocationLister,
|
||||
csiSnapshotLister: csiSnapshotLister,
|
||||
csiSnapshotContentLister: csiSnapshotContentLister,
|
||||
|
@ -214,15 +216,18 @@ func (c *backupDeletionController) processRequest(req *velerov1api.DeleteBackupR
|
|||
}
|
||||
|
||||
// Don't allow deleting backups in read-only storage locations
|
||||
location, err := c.backupLocationLister.BackupStorageLocations(backup.Namespace).Get(backup.Spec.StorageLocation)
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err := c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) {
|
||||
r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed
|
||||
r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("backup storage location %s not found", backup.Spec.StorageLocation))
|
||||
})
|
||||
return err
|
||||
}
|
||||
if err != nil {
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: backup.Namespace,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
}, location); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
_, err := c.patchDeleteBackupRequest(req, func(r *velerov1api.DeleteBackupRequest) {
|
||||
r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed
|
||||
r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("backup storage location %s not found", backup.Spec.StorageLocation))
|
||||
})
|
||||
return err
|
||||
}
|
||||
return errors.Wrap(err, "error getting backup storage location")
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -34,8 +35,9 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
core "k8s.io/client-go/testing"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
|
||||
|
@ -63,7 +65,7 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) {
|
|||
NewBackupTracker(),
|
||||
nil, // restic repository manager
|
||||
sharedInformers.Velero().V1().PodVolumeBackups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
nil,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
nil, // csiSnapshotLister
|
||||
nil, // csiSnapshotContentLister
|
||||
|
@ -84,21 +86,21 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) {
|
|||
req := pkgbackup.NewDeleteBackupRequest("foo", "uid")
|
||||
req.Namespace = "foo"
|
||||
req.Name = "foo-abcde"
|
||||
req.Status.Phase = velerov1.DeleteBackupRequestPhaseProcessed
|
||||
req.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed
|
||||
|
||||
err = controller.processQueueItem("foo/bar")
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Invoke processRequestFunc
|
||||
for _, phase := range []velerov1.DeleteBackupRequestPhase{"", velerov1.DeleteBackupRequestPhaseNew, velerov1.DeleteBackupRequestPhaseInProgress} {
|
||||
for _, phase := range []velerov1api.DeleteBackupRequestPhase{"", velerov1api.DeleteBackupRequestPhaseNew, velerov1api.DeleteBackupRequestPhaseInProgress} {
|
||||
t.Run(fmt.Sprintf("phase=%s", phase), func(t *testing.T) {
|
||||
req.Status.Phase = phase
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(req)
|
||||
|
||||
var errorToReturn error
|
||||
var actual *velerov1.DeleteBackupRequest
|
||||
var actual *velerov1api.DeleteBackupRequest
|
||||
var called bool
|
||||
controller.processRequestFunc = func(r *velerov1.DeleteBackupRequest) error {
|
||||
controller.processRequestFunc = func(r *velerov1api.DeleteBackupRequest) error {
|
||||
called = true
|
||||
actual = r
|
||||
return errorToReturn
|
||||
|
@ -121,20 +123,22 @@ func TestBackupDeletionControllerProcessQueueItem(t *testing.T) {
|
|||
|
||||
type backupDeletionControllerTestData struct {
|
||||
client *fake.Clientset
|
||||
fakeClient client.Client
|
||||
sharedInformers informers.SharedInformerFactory
|
||||
volumeSnapshotter *velerotest.FakeVolumeSnapshotter
|
||||
backupStore *persistencemocks.BackupStore
|
||||
controller *backupDeletionController
|
||||
req *velerov1.DeleteBackupRequest
|
||||
req *velerov1api.DeleteBackupRequest
|
||||
}
|
||||
|
||||
func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletionControllerTestData {
|
||||
func setupBackupDeletionControllerTest(t *testing.T, objects ...runtime.Object) *backupDeletionControllerTestData {
|
||||
req := pkgbackup.NewDeleteBackupRequest("foo", "uid")
|
||||
req.Namespace = "velero"
|
||||
req.Name = "foo-abcde"
|
||||
|
||||
var (
|
||||
client = fake.NewSimpleClientset(append(objects, req)...)
|
||||
fakeClient = newFakeClient(t, objects...)
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
volumeSnapshotter = &velerotest.FakeVolumeSnapshotter{SnapshotsTaken: sets.NewString()}
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
|
@ -143,6 +147,7 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio
|
|||
|
||||
data := &backupDeletionControllerTestData{
|
||||
client: client,
|
||||
fakeClient: fakeClient,
|
||||
sharedInformers: sharedInformers,
|
||||
volumeSnapshotter: volumeSnapshotter,
|
||||
backupStore: backupStore,
|
||||
|
@ -156,7 +161,7 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio
|
|||
NewBackupTracker(),
|
||||
nil, // restic repository manager
|
||||
sharedInformers.Velero().V1().PodVolumeBackups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
fakeClient,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
nil, // csiSnapshotLister
|
||||
nil, // csiSnapshotContentLister
|
||||
|
@ -168,7 +173,7 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio
|
|||
req: req,
|
||||
}
|
||||
|
||||
data.controller.newBackupStore = func(*velerov1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
data.controller.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
return backupStore, nil
|
||||
}
|
||||
|
||||
|
@ -179,8 +184,7 @@ func setupBackupDeletionControllerTest(objects ...runtime.Object) *backupDeletio
|
|||
|
||||
func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
||||
t.Run("missing spec.backupName", func(t *testing.T) {
|
||||
td := setupBackupDeletionControllerTest()
|
||||
|
||||
td := setupBackupDeletionControllerTest(t)
|
||||
td.req.Spec.BackupName = ""
|
||||
|
||||
err := td.controller.processRequest(td.req)
|
||||
|
@ -188,7 +192,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -200,7 +204,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("existing deletion requests for the backup are deleted", func(t *testing.T) {
|
||||
td := setupBackupDeletionControllerTest()
|
||||
td := setupBackupDeletionControllerTest(t)
|
||||
|
||||
// add the backup to the tracker so the execution of processRequest doesn't progress
|
||||
// past checking for an in-progress backup. this makes validation easier.
|
||||
|
@ -208,15 +212,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(td.req))
|
||||
|
||||
existing := &velerov1.DeleteBackupRequest{
|
||||
existing := &velerov1api.DeleteBackupRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: td.req.Namespace,
|
||||
Name: "bar",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: td.req.Spec.BackupName,
|
||||
velerov1api.BackupNameLabel: td.req.Spec.BackupName,
|
||||
},
|
||||
},
|
||||
Spec: velerov1.DeleteBackupRequestSpec{
|
||||
Spec: velerov1api.DeleteBackupRequestSpec{
|
||||
BackupName: td.req.Spec.BackupName,
|
||||
},
|
||||
}
|
||||
|
@ -225,15 +229,15 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, td.sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(
|
||||
&velerov1.DeleteBackupRequest{
|
||||
&velerov1api.DeleteBackupRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: td.req.Namespace,
|
||||
Name: "bar-2",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "some-other-backup",
|
||||
velerov1api.BackupNameLabel: "some-other-backup",
|
||||
},
|
||||
},
|
||||
Spec: velerov1.DeleteBackupRequestSpec{
|
||||
Spec: velerov1api.DeleteBackupRequestSpec{
|
||||
BackupName: "some-other-backup",
|
||||
},
|
||||
},
|
||||
|
@ -242,7 +246,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
assert.NoError(t, td.controller.processRequest(td.req))
|
||||
|
||||
expectedDeleteAction := core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
"bar",
|
||||
)
|
||||
|
@ -255,7 +259,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("deleting an in progress backup isn't allowed", func(t *testing.T) {
|
||||
td := setupBackupDeletionControllerTest()
|
||||
td := setupBackupDeletionControllerTest(t)
|
||||
|
||||
td.controller.backupTracker.Add(td.req.Namespace, td.req.Spec.BackupName)
|
||||
|
||||
|
@ -264,7 +268,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -276,12 +280,10 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("patching to InProgress fails", func(t *testing.T) {
|
||||
backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
location := builder.ForBackupStorageLocation("velero", "default").Result()
|
||||
|
||||
td := setupBackupDeletionControllerTest(backup)
|
||||
|
||||
td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location)
|
||||
td := setupBackupDeletionControllerTest(t, location, backup)
|
||||
|
||||
td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, nil, errors.New("bad")
|
||||
|
@ -292,12 +294,12 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
backup.Namespace,
|
||||
backup.Name,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -308,12 +310,10 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("patching backup to Deleting fails", func(t *testing.T) {
|
||||
backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
location := builder.ForBackupStorageLocation("velero", "default").Result()
|
||||
|
||||
td := setupBackupDeletionControllerTest(backup)
|
||||
|
||||
td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location)
|
||||
td := setupBackupDeletionControllerTest(t, location, backup)
|
||||
|
||||
td.client.PrependReactor("patch", "deletebackuprequests", func(action core.Action) (bool, runtime.Object, error) {
|
||||
return true, td.req, nil
|
||||
|
@ -327,19 +327,19 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
backup.Namespace,
|
||||
backup.Name,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"status":{"phase":"InProgress"}}`),
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
backup.Namespace,
|
||||
backup.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -350,19 +350,19 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("unable to find backup", func(t *testing.T) {
|
||||
td := setupBackupDeletionControllerTest()
|
||||
td := setupBackupDeletionControllerTest(t)
|
||||
|
||||
err := td.controller.processRequest(td.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedActions := []core.Action{
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -374,21 +374,21 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("unable to find backup storage location", func(t *testing.T) {
|
||||
backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
|
||||
td := setupBackupDeletionControllerTest(backup)
|
||||
td := setupBackupDeletionControllerTest(t, backup)
|
||||
|
||||
err := td.controller.processRequest(td.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedActions := []core.Action{
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -400,24 +400,22 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("backup storage location is in read-only mode", func(t *testing.T) {
|
||||
backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1.BackupStorageLocationAccessModeReadOnly).Result()
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").StorageLocation("default").Result()
|
||||
location := builder.ForBackupStorageLocation("velero", "default").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result()
|
||||
|
||||
td := setupBackupDeletionControllerTest(backup)
|
||||
|
||||
td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location)
|
||||
td := setupBackupDeletionControllerTest(t, location, backup)
|
||||
|
||||
err := td.controller.processRequest(td.req)
|
||||
require.NoError(t, err)
|
||||
|
||||
expectedActions := []core.Action{
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
|
@ -429,42 +427,43 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("full delete, no errors", func(t *testing.T) {
|
||||
backup := builder.ForBackup(velerov1.DefaultNamespace, "foo").Result()
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, "foo").Result()
|
||||
backup.UID = "uid"
|
||||
backup.Spec.StorageLocation = "primary"
|
||||
|
||||
restore1 := builder.ForRestore("velero", "restore-1").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result()
|
||||
restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1.RestorePhaseCompleted).Backup("foo").Result()
|
||||
restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1.RestorePhaseCompleted).Backup("some-other-backup").Result()
|
||||
restore1 := builder.ForRestore("velero", "restore-1").Phase(velerov1api.RestorePhaseCompleted).Backup("foo").Result()
|
||||
restore2 := builder.ForRestore("velero", "restore-2").Phase(velerov1api.RestorePhaseCompleted).Backup("foo").Result()
|
||||
restore3 := builder.ForRestore("velero", "restore-3").Phase(velerov1api.RestorePhaseCompleted).Backup("some-other-backup").Result()
|
||||
|
||||
td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3)
|
||||
td := setupBackupDeletionControllerTest(t, backup, restore1, restore2, restore3)
|
||||
|
||||
td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore1)
|
||||
td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2)
|
||||
td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3)
|
||||
|
||||
location := &velerov1.BackupStorageLocation{
|
||||
location := &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: backup.Namespace,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
},
|
||||
Spec: velerov1.BackupStorageLocationSpec{
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "objStoreProvider",
|
||||
StorageType: velerov1.StorageType{
|
||||
ObjectStorage: &velerov1.ObjectStorageLocation{
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "bucket",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.NoError(t, td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location))
|
||||
|
||||
snapshotLocation := &velerov1.VolumeSnapshotLocation{
|
||||
require.NoError(t, td.fakeClient.Create(context.Background(), location))
|
||||
|
||||
snapshotLocation := &velerov1api.VolumeSnapshotLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: backup.Namespace,
|
||||
Name: "vsl-1",
|
||||
},
|
||||
Spec: velerov1.VolumeSnapshotLocationSpec{
|
||||
Spec: velerov1api.VolumeSnapshotLocationSpec{
|
||||
Provider: "provider-1",
|
||||
},
|
||||
}
|
||||
|
@ -514,55 +513,55 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"metadata":{"labels":{"velero.io/backup-name":"foo"}},"status":{"phase":"InProgress"}}`),
|
||||
),
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`),
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"status":{"phase":"Deleting"}}`),
|
||||
),
|
||||
core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("restores"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("restores"),
|
||||
td.req.Namespace,
|
||||
"restore-1",
|
||||
),
|
||||
core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("restores"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("restores"),
|
||||
td.req.Namespace,
|
||||
"restore-2",
|
||||
),
|
||||
core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"status":{"phase":"Processed"}}`),
|
||||
),
|
||||
core.NewDeleteCollectionAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"),
|
||||
),
|
||||
|
@ -584,19 +583,19 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
backup.Spec.StorageLocation = "primary"
|
||||
|
||||
restore1 := builder.ForRestore("velero", "restore-1").
|
||||
Phase(velerov1.RestorePhaseCompleted).
|
||||
Phase(velerov1api.RestorePhaseCompleted).
|
||||
Backup("the-really-long-backup-name-that-is-much-more-than-63-characters").
|
||||
Result()
|
||||
restore2 := builder.ForRestore("velero", "restore-2").
|
||||
Phase(velerov1.RestorePhaseCompleted).
|
||||
Phase(velerov1api.RestorePhaseCompleted).
|
||||
Backup("the-really-long-backup-name-that-is-much-more-than-63-characters").
|
||||
Result()
|
||||
restore3 := builder.ForRestore("velero", "restore-3").
|
||||
Phase(velerov1.RestorePhaseCompleted).
|
||||
Phase(velerov1api.RestorePhaseCompleted).
|
||||
Backup("some-other-backup").
|
||||
Result()
|
||||
|
||||
td := setupBackupDeletionControllerTest(backup, restore1, restore2, restore3)
|
||||
td := setupBackupDeletionControllerTest(t, backup, restore1, restore2, restore3)
|
||||
td.req = pkgbackup.NewDeleteBackupRequest(backup.Name, string(backup.UID))
|
||||
td.req.Namespace = "velero"
|
||||
td.req.Name = "foo-abcde"
|
||||
|
@ -604,28 +603,28 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore2)
|
||||
td.sharedInformers.Velero().V1().Restores().Informer().GetStore().Add(restore3)
|
||||
|
||||
location := &velerov1.BackupStorageLocation{
|
||||
location := &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: backup.Namespace,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
},
|
||||
Spec: velerov1.BackupStorageLocationSpec{
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: "objStoreProvider",
|
||||
StorageType: velerov1.StorageType{
|
||||
ObjectStorage: &velerov1.ObjectStorageLocation{
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: "bucket",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
require.NoError(t, td.sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location))
|
||||
require.NoError(t, td.fakeClient.Create(context.Background(), location))
|
||||
|
||||
snapshotLocation := &velerov1.VolumeSnapshotLocation{
|
||||
snapshotLocation := &velerov1api.VolumeSnapshotLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: backup.Namespace,
|
||||
Name: "vsl-1",
|
||||
},
|
||||
Spec: velerov1.VolumeSnapshotLocationSpec{
|
||||
Spec: velerov1api.VolumeSnapshotLocationSpec{
|
||||
Provider: "provider-1",
|
||||
},
|
||||
}
|
||||
|
@ -673,55 +672,55 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"metadata":{"labels":{"velero.io/backup-name":"the-really-long-backup-name-that-is-much-more-than-63-cha6ca4bc"}},"status":{"phase":"InProgress"}}`),
|
||||
),
|
||||
core.NewGetAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"metadata":{"labels":{"velero.io/backup-uid":"uid"}}}`),
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"status":{"phase":"Deleting"}}`),
|
||||
),
|
||||
core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("restores"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("restores"),
|
||||
td.req.Namespace,
|
||||
"restore-1",
|
||||
),
|
||||
core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("restores"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("restores"),
|
||||
td.req.Namespace,
|
||||
"restore-2",
|
||||
),
|
||||
core.NewDeleteAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("backups"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("backups"),
|
||||
td.req.Namespace,
|
||||
td.req.Spec.BackupName,
|
||||
),
|
||||
core.NewPatchAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
td.req.Name,
|
||||
types.MergePatchType,
|
||||
[]byte(`{"status":{"phase":"Processed"}}`),
|
||||
),
|
||||
core.NewDeleteCollectionAction(
|
||||
velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"),
|
||||
td.req.Namespace,
|
||||
pkgbackup.NewDeleteBackupRequestListOptions(td.req.Spec.BackupName, "uid"),
|
||||
),
|
||||
|
@ -735,6 +734,7 @@ func TestBackupDeletionControllerProcessRequest(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
||||
|
||||
now := time.Date(2018, 4, 4, 12, 0, 0, 0, time.UTC)
|
||||
unexpired1 := time.Date(2018, 4, 4, 11, 0, 0, 0, time.UTC)
|
||||
unexpired2 := time.Date(2018, 4, 3, 12, 0, 1, 0, time.UTC)
|
||||
|
@ -743,7 +743,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
|
||||
tests := []struct {
|
||||
name string
|
||||
requests []*velerov1.DeleteBackupRequest
|
||||
requests []*velerov1api.DeleteBackupRequest
|
||||
expectedDeletions []string
|
||||
}{
|
||||
{
|
||||
|
@ -751,14 +751,14 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "older than max age, phase = '', don't delete",
|
||||
requests: []*velerov1.DeleteBackupRequest{
|
||||
requests: []*velerov1api.DeleteBackupRequest{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
CreationTimestamp: metav1.Time{Time: expired1},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: "",
|
||||
},
|
||||
},
|
||||
|
@ -766,45 +766,45 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "older than max age, phase = New, don't delete",
|
||||
requests: []*velerov1.DeleteBackupRequest{
|
||||
requests: []*velerov1api.DeleteBackupRequest{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
CreationTimestamp: metav1.Time{Time: expired1},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Phase: velerov1.DeleteBackupRequestPhaseNew,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseNew,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "older than max age, phase = InProcess, don't delete",
|
||||
requests: []*velerov1.DeleteBackupRequest{
|
||||
requests: []*velerov1api.DeleteBackupRequest{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "name",
|
||||
CreationTimestamp: metav1.Time{Time: expired1},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Phase: velerov1.DeleteBackupRequestPhaseInProgress,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseInProgress,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "some expired, some not",
|
||||
requests: []*velerov1.DeleteBackupRequest{
|
||||
requests: []*velerov1api.DeleteBackupRequest{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ns",
|
||||
Name: "unexpired-1",
|
||||
CreationTimestamp: metav1.Time{Time: unexpired1},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Phase: velerov1.DeleteBackupRequestPhaseProcessed,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseProcessed,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -813,8 +813,8 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
Name: "expired-1",
|
||||
CreationTimestamp: metav1.Time{Time: expired1},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Phase: velerov1.DeleteBackupRequestPhaseProcessed,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseProcessed,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -823,8 +823,8 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
Name: "unexpired-2",
|
||||
CreationTimestamp: metav1.Time{Time: unexpired2},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Phase: velerov1.DeleteBackupRequestPhaseProcessed,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseProcessed,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -833,8 +833,8 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
Name: "expired-2",
|
||||
CreationTimestamp: metav1.Time{Time: expired2},
|
||||
},
|
||||
Status: velerov1.DeleteBackupRequestStatus{
|
||||
Phase: velerov1.DeleteBackupRequestPhaseProcessed,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseProcessed,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -845,6 +845,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
client := fake.NewSimpleClientset()
|
||||
fakeClient := newFakeClient(t)
|
||||
sharedInformers := informers.NewSharedInformerFactory(client, 0)
|
||||
|
||||
controller := NewBackupDeletionController(
|
||||
|
@ -857,7 +858,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
NewBackupTracker(),
|
||||
nil,
|
||||
sharedInformers.Velero().V1().PodVolumeBackups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
fakeClient,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
nil, // csiSnapshotLister
|
||||
nil, // csiSnapshotContentLister
|
||||
|
@ -878,7 +879,7 @@ func TestBackupDeletionControllerDeleteExpiredRequests(t *testing.T) {
|
|||
|
||||
expectedActions := []core.Action{}
|
||||
for _, name := range test.expectedDeletions {
|
||||
expectedActions = append(expectedActions, core.NewDeleteAction(velerov1.SchemeGroupVersion.WithResource("deletebackuprequests"), "ns", name))
|
||||
expectedActions = append(expectedActions, core.NewDeleteAction(velerov1api.SchemeGroupVersion.WithResource("deletebackuprequests"), "ns", name))
|
||||
}
|
||||
|
||||
velerotest.CompareActions(t, expectedActions, client.Actions())
|
||||
|
@ -981,7 +982,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "vs1",
|
||||
Namespace: "ns1",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup1",
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
|
@ -1003,7 +1004,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "vs2",
|
||||
Namespace: "ns1",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup1",
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
|
@ -1025,7 +1026,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "vs1",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup1",
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
|
@ -1047,7 +1048,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "vs2",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup1",
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
|
@ -1061,7 +1062,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "ns1NilStatusVS",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup2",
|
||||
velerov1api.BackupNameLabel: "backup2",
|
||||
},
|
||||
},
|
||||
Status: nil,
|
||||
|
@ -1073,7 +1074,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "ns1NilBoundVSCVS",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup3",
|
||||
velerov1api.BackupNameLabel: "backup3",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
|
@ -1088,7 +1089,7 @@ func TestDeleteCSIVolumeSnapshots(t *testing.T) {
|
|||
Name: "ns1NonExistentVSCVS",
|
||||
Namespace: "ns2",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup3",
|
||||
velerov1api.BackupNameLabel: "backup3",
|
||||
},
|
||||
},
|
||||
Status: &snapshotv1beta1api.VolumeSnapshotStatus{
|
||||
|
@ -1151,7 +1152,7 @@ func TestDeleteCSIVolumeSnapshotContents(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "retainVSC",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup1",
|
||||
velerov1api.BackupNameLabel: "backup1",
|
||||
},
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
|
@ -1162,7 +1163,7 @@ func TestDeleteCSIVolumeSnapshotContents(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "deleteVSC",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup2",
|
||||
velerov1api.BackupNameLabel: "backup2",
|
||||
},
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{
|
||||
|
@ -1174,7 +1175,7 @@ func TestDeleteCSIVolumeSnapshotContents(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "nothingVSC",
|
||||
Labels: map[string]string{
|
||||
velerov1.BackupNameLabel: "backup3",
|
||||
velerov1api.BackupNameLabel: "backup3",
|
||||
},
|
||||
},
|
||||
Spec: snapshotv1beta1api.VolumeSnapshotContentSpec{},
|
||||
|
|
|
@ -17,15 +17,15 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
snapshotterClientSet "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
kuberrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
|
@ -36,31 +36,31 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/label"
|
||||
"github.com/vmware-tanzu/velero/pkg/persistence"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type backupSyncController struct {
|
||||
*genericController
|
||||
|
||||
backupClient velerov1client.BackupsGetter
|
||||
backupLocationClient velerov1client.BackupStorageLocationsGetter
|
||||
podVolumeBackupClient velerov1client.PodVolumeBackupsGetter
|
||||
backupLister velerov1listers.BackupLister
|
||||
csiSnapshotClient *snapshotterClientSet.Clientset
|
||||
kubeClient kubernetes.Interface
|
||||
backupStorageLocationLister velerov1listers.BackupStorageLocationLister
|
||||
namespace string
|
||||
defaultBackupLocation string
|
||||
defaultBackupSyncPeriod time.Duration
|
||||
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager
|
||||
newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error)
|
||||
backupClient velerov1client.BackupsGetter
|
||||
kbClient client.Client
|
||||
podVolumeBackupClient velerov1client.PodVolumeBackupsGetter
|
||||
backupLister velerov1listers.BackupLister
|
||||
csiSnapshotClient *snapshotterClientSet.Clientset
|
||||
kubeClient kubernetes.Interface
|
||||
namespace string
|
||||
defaultBackupLocation string
|
||||
defaultBackupSyncPeriod time.Duration
|
||||
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager
|
||||
newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error)
|
||||
}
|
||||
|
||||
func NewBackupSyncController(
|
||||
backupClient velerov1client.BackupsGetter,
|
||||
backupLocationClient velerov1client.BackupStorageLocationsGetter,
|
||||
kbClient client.Client,
|
||||
podVolumeBackupClient velerov1client.PodVolumeBackupsGetter,
|
||||
backupLister velerov1listers.BackupLister,
|
||||
backupStorageLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
syncPeriod time.Duration,
|
||||
namespace string,
|
||||
csiSnapshotClient *snapshotterClientSet.Clientset,
|
||||
|
@ -75,17 +75,16 @@ func NewBackupSyncController(
|
|||
logger.Infof("Backup sync period is %v", syncPeriod)
|
||||
|
||||
c := &backupSyncController{
|
||||
genericController: newGenericController("backup-sync", logger),
|
||||
backupClient: backupClient,
|
||||
backupLocationClient: backupLocationClient,
|
||||
podVolumeBackupClient: podVolumeBackupClient,
|
||||
namespace: namespace,
|
||||
defaultBackupLocation: defaultBackupLocation,
|
||||
defaultBackupSyncPeriod: syncPeriod,
|
||||
backupLister: backupLister,
|
||||
backupStorageLocationLister: backupStorageLocationLister,
|
||||
csiSnapshotClient: csiSnapshotClient,
|
||||
kubeClient: kubeClient,
|
||||
genericController: newGenericController("backup-sync", logger),
|
||||
backupClient: backupClient,
|
||||
kbClient: kbClient,
|
||||
podVolumeBackupClient: podVolumeBackupClient,
|
||||
namespace: namespace,
|
||||
defaultBackupLocation: defaultBackupLocation,
|
||||
defaultBackupSyncPeriod: syncPeriod,
|
||||
backupLister: backupLister,
|
||||
csiSnapshotClient: csiSnapshotClient,
|
||||
kubeClient: kubeClient,
|
||||
|
||||
// use variables to refer to these functions so they can be
|
||||
// replaced with fakes for testing.
|
||||
|
@ -101,35 +100,38 @@ func NewBackupSyncController(
|
|||
|
||||
// orderedBackupLocations returns a new slice with the default backup location first (if it exists),
|
||||
// followed by the rest of the locations in no particular order.
|
||||
func orderedBackupLocations(locations []*velerov1api.BackupStorageLocation, defaultLocationName string) []*velerov1api.BackupStorageLocation {
|
||||
var result []*velerov1api.BackupStorageLocation
|
||||
func orderedBackupLocations(locationList *velerov1api.BackupStorageLocationList, defaultLocationName string) []velerov1api.BackupStorageLocation {
|
||||
var result []velerov1api.BackupStorageLocation
|
||||
|
||||
for i := range locations {
|
||||
if locations[i].Name == defaultLocationName {
|
||||
for i := range locationList.Items {
|
||||
if locationList.Items[i].Name == defaultLocationName {
|
||||
// put the default location first
|
||||
result = append(result, locations[i])
|
||||
result = append(result, locationList.Items[i])
|
||||
// append everything before the default
|
||||
result = append(result, locations[:i]...)
|
||||
result = append(result, locationList.Items[:i]...)
|
||||
// append everything after the default
|
||||
result = append(result, locations[i+1:]...)
|
||||
result = append(result, locationList.Items[i+1:]...)
|
||||
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
return locations
|
||||
return locationList.Items
|
||||
}
|
||||
|
||||
func (c *backupSyncController) run() {
|
||||
c.logger.Debug("Checking for existing backup storage locations to sync into cluster")
|
||||
|
||||
locations, err := c.backupStorageLocationLister.BackupStorageLocations(c.namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
locationList := &velerov1api.BackupStorageLocationList{}
|
||||
if err := c.kbClient.List(context.Background(), locationList, &client.ListOptions{
|
||||
Namespace: c.namespace,
|
||||
}); err != nil {
|
||||
c.logger.WithError(errors.WithStack(err)).Error("Error getting backup storage locations from lister")
|
||||
return
|
||||
}
|
||||
|
||||
// sync the default location first, if it exists
|
||||
locations = orderedBackupLocations(locations, c.defaultBackupLocation)
|
||||
locations := orderedBackupLocations(locationList, c.defaultBackupLocation)
|
||||
|
||||
pluginManager := c.newPluginManager(c.logger)
|
||||
defer pluginManager.CleanupClients()
|
||||
|
@ -162,7 +164,7 @@ func (c *backupSyncController) run() {
|
|||
|
||||
log.Debug("Checking backup location for backups to sync into cluster")
|
||||
|
||||
backupStore, err := c.newBackupStore(location, pluginManager, log)
|
||||
backupStore, err := c.newBackupStore(&location, pluginManager, log)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Error getting backup store for this location")
|
||||
continue
|
||||
|
@ -304,23 +306,9 @@ func (c *backupSyncController) run() {
|
|||
c.deleteOrphanedBackups(location.Name, backupStoreBackups, log)
|
||||
|
||||
// update the location's last-synced time field
|
||||
patch := map[string]interface{}{
|
||||
"status": map[string]interface{}{
|
||||
"lastSyncedTime": time.Now().UTC(),
|
||||
},
|
||||
}
|
||||
|
||||
patchBytes, err := json.Marshal(patch)
|
||||
if err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error marshaling last-synced patch to JSON")
|
||||
continue
|
||||
}
|
||||
|
||||
if _, err = c.backupLocationClient.BackupStorageLocations(c.namespace).Patch(
|
||||
location.Name,
|
||||
types.MergePatchType,
|
||||
patchBytes,
|
||||
); err != nil {
|
||||
statusPatch := client.MergeFrom(location.DeepCopyObject())
|
||||
location.Status.LastSyncedTime = &metav1.Time{Time: time.Now().UTC()}
|
||||
if err := c.kbClient.Status().Patch(context.Background(), &location, statusPatch); err != nil {
|
||||
log.WithError(errors.WithStack(err)).Error("Error patching backup location's last-synced time")
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -332,6 +333,7 @@ func TestBackupSyncControllerRun(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
fakeClient = newFakeClient(t)
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
pluginManager = &pluginmocks.Manager{}
|
||||
backupStores = make(map[string]*persistencemocks.BackupStore)
|
||||
|
@ -339,10 +341,9 @@ func TestBackupSyncControllerRun(t *testing.T) {
|
|||
|
||||
c := NewBackupSyncController(
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
fakeClient,
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
time.Duration(0),
|
||||
test.namespace,
|
||||
nil, // csiSnapshotClient
|
||||
|
@ -360,7 +361,7 @@ func TestBackupSyncControllerRun(t *testing.T) {
|
|||
pluginManager.On("CleanupClients").Return(nil)
|
||||
|
||||
for _, location := range test.locations {
|
||||
require.NoError(t, sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(location))
|
||||
require.NoError(t, fakeClient.Create(context.Background(), location))
|
||||
backupStores[location.Name] = &persistencemocks.BackupStore{}
|
||||
}
|
||||
|
||||
|
@ -559,15 +560,15 @@ func TestDeleteOrphanedBackups(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
fakeClient = newFakeClient(t)
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
)
|
||||
|
||||
c := NewBackupSyncController(
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
fakeClient,
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
time.Duration(0),
|
||||
test.namespace,
|
||||
nil, // csiSnapshotClient
|
||||
|
@ -652,15 +653,15 @@ func TestStorageLabelsInDeleteOrphanedBackups(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
fakeClient = newFakeClient(t)
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
)
|
||||
|
||||
c := NewBackupSyncController(
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
fakeClient,
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
time.Duration(0),
|
||||
test.namespace,
|
||||
nil, // csiSnapshotClient
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
|
@ -29,8 +30,9 @@ import (
|
|||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
|
||||
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
|
||||
|
@ -46,10 +48,10 @@ type downloadRequestController struct {
|
|||
downloadRequestLister velerov1listers.DownloadRequestLister
|
||||
restoreLister velerov1listers.RestoreLister
|
||||
clock clock.Clock
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
kbClient client.Client
|
||||
backupLister velerov1listers.BackupLister
|
||||
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager
|
||||
newBackupStore func(*v1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error)
|
||||
newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error)
|
||||
}
|
||||
|
||||
// NewDownloadRequestController creates a new DownloadRequestController.
|
||||
|
@ -57,7 +59,7 @@ func NewDownloadRequestController(
|
|||
downloadRequestClient velerov1client.DownloadRequestsGetter,
|
||||
downloadRequestInformer velerov1informers.DownloadRequestInformer,
|
||||
restoreLister velerov1listers.RestoreLister,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
kbClient client.Client,
|
||||
backupLister velerov1listers.BackupLister,
|
||||
newPluginManager func(logrus.FieldLogger) clientmgmt.Manager,
|
||||
logger logrus.FieldLogger,
|
||||
|
@ -67,7 +69,7 @@ func NewDownloadRequestController(
|
|||
downloadRequestClient: downloadRequestClient,
|
||||
downloadRequestLister: downloadRequestInformer.Lister(),
|
||||
restoreLister: restoreLister,
|
||||
backupLocationLister: backupLocationLister,
|
||||
kbClient: kbClient,
|
||||
backupLister: backupLister,
|
||||
|
||||
// use variables to refer to these functions so they can be
|
||||
|
@ -85,7 +87,7 @@ func NewDownloadRequestController(
|
|||
AddFunc: func(obj interface{}) {
|
||||
key, err := cache.MetaNamespaceKeyFunc(obj)
|
||||
if err != nil {
|
||||
downloadRequest := obj.(*v1.DownloadRequest)
|
||||
downloadRequest := obj.(*velerov1api.DownloadRequest)
|
||||
c.logger.WithError(errors.WithStack(err)).
|
||||
WithField("downloadRequest", downloadRequest.Name).
|
||||
Error("Error creating queue key, item not added to queue")
|
||||
|
@ -121,9 +123,9 @@ func (c *downloadRequestController) processDownloadRequest(key string) error {
|
|||
}
|
||||
|
||||
switch downloadRequest.Status.Phase {
|
||||
case "", v1.DownloadRequestPhaseNew:
|
||||
case "", velerov1api.DownloadRequestPhaseNew:
|
||||
return c.generatePreSignedURL(downloadRequest, log)
|
||||
case v1.DownloadRequestPhaseProcessed:
|
||||
case velerov1api.DownloadRequestPhaseProcessed:
|
||||
return c.deleteIfExpired(downloadRequest)
|
||||
}
|
||||
|
||||
|
@ -134,7 +136,7 @@ const signedURLTTL = 10 * time.Minute
|
|||
|
||||
// generatePreSignedURL generates a pre-signed URL for downloadRequest, changes the phase to
|
||||
// Processed, and persists the changes to storage.
|
||||
func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.DownloadRequest, log logrus.FieldLogger) error {
|
||||
func (c *downloadRequestController) generatePreSignedURL(downloadRequest *velerov1api.DownloadRequest, log logrus.FieldLogger) error {
|
||||
update := downloadRequest.DeepCopy()
|
||||
|
||||
var (
|
||||
|
@ -143,7 +145,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow
|
|||
)
|
||||
|
||||
switch downloadRequest.Spec.Target.Kind {
|
||||
case v1.DownloadTargetKindRestoreLog, v1.DownloadTargetKindRestoreResults:
|
||||
case velerov1api.DownloadTargetKindRestoreLog, velerov1api.DownloadTargetKindRestoreResults:
|
||||
restore, err := c.restoreLister.Restores(downloadRequest.Namespace).Get(downloadRequest.Spec.Target.Name)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error getting Restore")
|
||||
|
@ -159,8 +161,11 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow
|
|||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
backupLocation, err := c.backupLocationLister.BackupStorageLocations(backup.Namespace).Get(backup.Spec.StorageLocation)
|
||||
if err != nil {
|
||||
backupLocation := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: backup.Namespace,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
}, backupLocation); err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
|
@ -176,7 +181,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow
|
|||
return err
|
||||
}
|
||||
|
||||
update.Status.Phase = v1.DownloadRequestPhaseProcessed
|
||||
update.Status.Phase = velerov1api.DownloadRequestPhaseProcessed
|
||||
update.Status.Expiration = &metav1.Time{Time: c.clock.Now().Add(persistence.DownloadURLTTL)}
|
||||
|
||||
_, err = patchDownloadRequest(downloadRequest, update, c.downloadRequestClient)
|
||||
|
@ -184,7 +189,7 @@ func (c *downloadRequestController) generatePreSignedURL(downloadRequest *v1.Dow
|
|||
}
|
||||
|
||||
// deleteIfExpired deletes downloadRequest if it has expired.
|
||||
func (c *downloadRequestController) deleteIfExpired(downloadRequest *v1.DownloadRequest) error {
|
||||
func (c *downloadRequestController) deleteIfExpired(downloadRequest *velerov1api.DownloadRequest) error {
|
||||
log := c.logger.WithField("key", kube.NamespaceAndName(downloadRequest))
|
||||
log.Info("checking for expiration of DownloadRequest")
|
||||
if downloadRequest.Status.Expiration.Time.After(c.clock.Now()) {
|
||||
|
@ -216,7 +221,7 @@ func (c *downloadRequestController) resync() {
|
|||
}
|
||||
}
|
||||
|
||||
func patchDownloadRequest(original, updated *v1.DownloadRequest, client velerov1client.DownloadRequestsGetter) (*v1.DownloadRequest, error) {
|
||||
func patchDownloadRequest(original, updated *velerov1api.DownloadRequest, client velerov1client.DownloadRequestsGetter) (*velerov1api.DownloadRequest, error) {
|
||||
origBytes, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error marshalling original download request")
|
||||
|
|
|
@ -27,7 +27,9 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
|
||||
informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
|
||||
|
@ -48,7 +50,7 @@ type downloadRequestTestHarness struct {
|
|||
controller *downloadRequestController
|
||||
}
|
||||
|
||||
func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness {
|
||||
func newDownloadRequestTestHarness(t *testing.T, fakeClient client.Client) *downloadRequestTestHarness {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
informerFactory = informers.NewSharedInformerFactory(client, 0)
|
||||
|
@ -58,7 +60,7 @@ func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness {
|
|||
client.VeleroV1(),
|
||||
informerFactory.Velero().V1().DownloadRequests(),
|
||||
informerFactory.Velero().V1().Restores().Lister(),
|
||||
informerFactory.Velero().V1().BackupStorageLocations().Lister(),
|
||||
fakeClient,
|
||||
informerFactory.Velero().V1().Backups().Lister(),
|
||||
func(logrus.FieldLogger) clientmgmt.Manager { return pluginManager },
|
||||
velerotest.NewLogger(),
|
||||
|
@ -69,7 +71,7 @@ func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness {
|
|||
require.NoError(t, err)
|
||||
controller.clock = clock.NewFakeClock(clockTime)
|
||||
|
||||
controller.newBackupStore = func(*v1.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
controller.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
return backupStore, nil
|
||||
}
|
||||
|
||||
|
@ -84,34 +86,34 @@ func newDownloadRequestTestHarness(t *testing.T) *downloadRequestTestHarness {
|
|||
}
|
||||
}
|
||||
|
||||
func newDownloadRequest(phase v1.DownloadRequestPhase, targetKind v1.DownloadTargetKind, targetName string) *v1.DownloadRequest {
|
||||
return &v1.DownloadRequest{
|
||||
func newDownloadRequest(phase velerov1api.DownloadRequestPhase, targetKind velerov1api.DownloadTargetKind, targetName string) *velerov1api.DownloadRequest {
|
||||
return &velerov1api.DownloadRequest{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a-download-request",
|
||||
Namespace: v1.DefaultNamespace,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
},
|
||||
Spec: v1.DownloadRequestSpec{
|
||||
Target: v1.DownloadTarget{
|
||||
Spec: velerov1api.DownloadRequestSpec{
|
||||
Target: velerov1api.DownloadTarget{
|
||||
Kind: targetKind,
|
||||
Name: targetName,
|
||||
},
|
||||
},
|
||||
Status: v1.DownloadRequestStatus{
|
||||
Status: velerov1api.DownloadRequestStatus{
|
||||
Phase: phase,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newBackupLocation(name, provider, bucket string) *v1.BackupStorageLocation {
|
||||
return &v1.BackupStorageLocation{
|
||||
func newBackupLocation(name, provider, bucket string) *velerov1api.BackupStorageLocation {
|
||||
return &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: v1.DefaultNamespace,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
},
|
||||
Spec: v1.BackupStorageLocationSpec{
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: provider,
|
||||
StorageType: v1.StorageType{
|
||||
ObjectStorage: &v1.ObjectStorageLocation{
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: bucket,
|
||||
},
|
||||
},
|
||||
|
@ -120,17 +122,18 @@ func newBackupLocation(name, provider, bucket string) *v1.BackupStorageLocation
|
|||
}
|
||||
|
||||
func TestProcessDownloadRequest(t *testing.T) {
|
||||
defaultBackup := func() *v1.Backup {
|
||||
return builder.ForBackup(v1.DefaultNamespace, "a-backup").StorageLocation("a-location").Result()
|
||||
|
||||
defaultBackup := func() *velerov1api.Backup {
|
||||
return builder.ForBackup(velerov1api.DefaultNamespace, "a-backup").StorageLocation("a-location").Result()
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key string
|
||||
downloadRequest *v1.DownloadRequest
|
||||
backup *v1.Backup
|
||||
restore *v1.Restore
|
||||
backupLocation *v1.BackupStorageLocation
|
||||
downloadRequest *velerov1api.DownloadRequest
|
||||
backup *velerov1api.Backup
|
||||
restore *velerov1api.Restore
|
||||
backupLocation *velerov1api.BackupStorageLocation
|
||||
expired bool
|
||||
expectedErr string
|
||||
expectGetsURL bool
|
||||
|
@ -149,94 +152,94 @@ func TestProcessDownloadRequest(t *testing.T) {
|
|||
},
|
||||
{
|
||||
name: "backup contents request for nonexistent backup returns an error",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"),
|
||||
backup: builder.ForBackup(v1.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"),
|
||||
backup: builder.ForBackup(velerov1api.DefaultNamespace, "non-matching-backup").StorageLocation("a-location").Result(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectedErr: "backup.velero.io \"a-backup\" not found",
|
||||
},
|
||||
{
|
||||
name: "restore log request for nonexistent restore returns an error",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(v1.DefaultNamespace, "non-matching-restore").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "non-matching-restore").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectedErr: "error getting Restore: restore.velero.io \"a-backup-20170912150214\" not found",
|
||||
},
|
||||
{
|
||||
name: "backup contents request for backup with nonexistent location returns an error",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("non-matching-location", "a-provider", "a-bucket"),
|
||||
expectedErr: "backupstoragelocation.velero.io \"a-location\" not found",
|
||||
expectedErr: "backupstoragelocations.velero.io \"a-location\" not found",
|
||||
},
|
||||
{
|
||||
name: "backup contents request with phase '' gets a url",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupContents, "a-backup"),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupContents, "a-backup"),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "backup contents request with phase 'New' gets a url",
|
||||
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupContents, "a-backup"),
|
||||
downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupContents, "a-backup"),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "backup log request with phase '' gets a url",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindBackupLog, "a-backup"),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindBackupLog, "a-backup"),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "backup log request with phase 'New' gets a url",
|
||||
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindBackupLog, "a-backup"),
|
||||
downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindBackupLog, "a-backup"),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "restore log request with phase '' gets a url",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "restore log request with phase 'New' gets a url",
|
||||
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreLog, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "restore results request with phase '' gets a url",
|
||||
downloadRequest: newDownloadRequest("", v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
downloadRequest: newDownloadRequest("", velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "restore results request with phase 'New' gets a url",
|
||||
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseNew, v1.DownloadTargetKindRestoreResults, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(v1.DefaultNamespace, "a-backup-20170912150214").Phase(v1.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseNew, velerov1api.DownloadTargetKindRestoreResults, "a-backup-20170912150214"),
|
||||
restore: builder.ForRestore(velerov1api.DefaultNamespace, "a-backup-20170912150214").Phase(velerov1api.RestorePhaseCompleted).Backup("a-backup").Result(),
|
||||
backup: defaultBackup(),
|
||||
backupLocation: newBackupLocation("a-location", "a-provider", "a-bucket"),
|
||||
expectGetsURL: true,
|
||||
},
|
||||
{
|
||||
name: "request with phase 'Processed' is not deleted if not expired",
|
||||
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"),
|
||||
downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"),
|
||||
backup: defaultBackup(),
|
||||
},
|
||||
{
|
||||
name: "request with phase 'Processed' is deleted if expired",
|
||||
downloadRequest: newDownloadRequest(v1.DownloadRequestPhaseProcessed, v1.DownloadTargetKindBackupLog, "a-backup-20170912150214"),
|
||||
downloadRequest: newDownloadRequest(velerov1api.DownloadRequestPhaseProcessed, velerov1api.DownloadTargetKindBackupLog, "a-backup-20170912150214"),
|
||||
backup: defaultBackup(),
|
||||
expired: true,
|
||||
},
|
||||
|
@ -244,13 +247,20 @@ func TestProcessDownloadRequest(t *testing.T) {
|
|||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
harness := newDownloadRequestTestHarness(t)
|
||||
var fakeClient client.Client
|
||||
if tc.backupLocation != nil {
|
||||
fakeClient = newFakeClient(t, tc.backupLocation)
|
||||
} else {
|
||||
fakeClient = newFakeClient(t)
|
||||
}
|
||||
|
||||
harness := newDownloadRequestTestHarness(t, fakeClient)
|
||||
|
||||
// set up test case data
|
||||
|
||||
// Set .status.expiration properly for processed requests. Since "expired" is relative to the controller's
|
||||
// clock time, it's easier to do this here than as part of the test case definitions.
|
||||
if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == v1.DownloadRequestPhaseProcessed {
|
||||
if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == velerov1api.DownloadRequestPhaseProcessed {
|
||||
if tc.expired {
|
||||
tc.downloadRequest.Status.Expiration = &metav1.Time{Time: harness.controller.clock.Now().Add(-1 * time.Minute)}
|
||||
} else {
|
||||
|
@ -273,10 +283,6 @@ func TestProcessDownloadRequest(t *testing.T) {
|
|||
require.NoError(t, harness.informerFactory.Velero().V1().Backups().Informer().GetStore().Add(tc.backup))
|
||||
}
|
||||
|
||||
if tc.backupLocation != nil {
|
||||
require.NoError(t, harness.informerFactory.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(tc.backupLocation))
|
||||
}
|
||||
|
||||
if tc.expectGetsURL {
|
||||
harness.backupStore.On("GetDownloadURL", tc.downloadRequest.Spec.Target).Return("a-url", nil)
|
||||
}
|
||||
|
@ -300,12 +306,12 @@ func TestProcessDownloadRequest(t *testing.T) {
|
|||
output, err := harness.client.VeleroV1().DownloadRequests(tc.downloadRequest.Namespace).Get(tc.downloadRequest.Name, metav1.GetOptions{})
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, string(v1.DownloadRequestPhaseProcessed), string(output.Status.Phase))
|
||||
assert.Equal(t, string(velerov1api.DownloadRequestPhaseProcessed), string(output.Status.Phase))
|
||||
assert.Equal(t, "a-url", output.Status.DownloadURL)
|
||||
assert.True(t, velerotest.TimesAreEqual(harness.controller.clock.Now().Add(signedURLTTL), output.Status.Expiration.Time), "expiration does not match")
|
||||
}
|
||||
|
||||
if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == v1.DownloadRequestPhaseProcessed {
|
||||
if tc.downloadRequest != nil && tc.downloadRequest.Status.Phase == velerov1api.DownloadRequestPhaseProcessed {
|
||||
res, err := harness.client.VeleroV1().DownloadRequests(tc.downloadRequest.Namespace).Get(tc.downloadRequest.Name, metav1.GetOptions{})
|
||||
|
||||
if tc.expired {
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
@ -26,6 +27,8 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
pkgbackup "github.com/vmware-tanzu/velero/pkg/backup"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
|
@ -45,7 +48,7 @@ type gcController struct {
|
|||
backupLister velerov1listers.BackupLister
|
||||
deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister
|
||||
deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
kbClient client.Client
|
||||
|
||||
clock clock.Clock
|
||||
}
|
||||
|
@ -56,7 +59,7 @@ func NewGCController(
|
|||
backupInformer velerov1informers.BackupInformer,
|
||||
deleteBackupRequestLister velerov1listers.DeleteBackupRequestLister,
|
||||
deleteBackupRequestClient velerov1client.DeleteBackupRequestsGetter,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
kbClient client.Client,
|
||||
) Interface {
|
||||
c := &gcController{
|
||||
genericController: newGenericController("gc-controller", logger),
|
||||
|
@ -64,7 +67,7 @@ func NewGCController(
|
|||
backupLister: backupInformer.Lister(),
|
||||
deleteBackupRequestLister: deleteBackupRequestLister,
|
||||
deleteBackupRequestClient: deleteBackupRequestClient,
|
||||
backupLocationLister: backupLocationLister,
|
||||
kbClient: kbClient,
|
||||
}
|
||||
|
||||
c.syncHandler = c.processQueueItem
|
||||
|
@ -130,11 +133,14 @@ func (c *gcController) processQueueItem(key string) error {
|
|||
|
||||
log.Info("Backup has expired")
|
||||
|
||||
loc, err := c.backupLocationLister.BackupStorageLocations(ns).Get(backup.Spec.StorageLocation)
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Warnf("Backup cannot be garbage-collected because backup storage location %s does not exist", backup.Spec.StorageLocation)
|
||||
}
|
||||
if err != nil {
|
||||
loc := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: ns,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
}, loc); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
log.Warnf("Backup cannot be garbage-collected because backup storage location %s does not exist", backup.Spec.StorageLocation)
|
||||
}
|
||||
return errors.Wrap(err, "error getting backup storage location")
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,9 @@ import (
|
|||
"k8s.io/apimachinery/pkg/watch"
|
||||
core "k8s.io/client-go/testing"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
|
||||
informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
|
||||
|
@ -50,7 +52,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) {
|
|||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
nil,
|
||||
).(*gcController)
|
||||
)
|
||||
|
||||
|
@ -64,7 +66,7 @@ func TestGCControllerEnqueueAllBackups(t *testing.T) {
|
|||
var expected []string
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
backup := builder.ForBackup(api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result()
|
||||
backup := builder.ForBackup(velerov1api.DefaultNamespace, fmt.Sprintf("backup-%d", i)).Result()
|
||||
sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(backup)
|
||||
expected = append(expected, kube.NamespaceAndName(backup))
|
||||
}
|
||||
|
@ -111,7 +113,7 @@ func TestGCControllerHasUpdateFunc(t *testing.T) {
|
|||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
nil,
|
||||
).(*gcController)
|
||||
|
||||
keys := make(chan string)
|
||||
|
@ -148,14 +150,15 @@ func TestGCControllerHasUpdateFunc(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGCControllerProcessQueueItem(t *testing.T) {
|
||||
|
||||
fakeClock := clock.NewFakeClock(time.Now())
|
||||
defaultBackupLocation := builder.ForBackupStorageLocation("velero", "default").Result()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
backup *api.Backup
|
||||
deleteBackupRequests []*api.DeleteBackupRequest
|
||||
backupLocation *api.BackupStorageLocation
|
||||
backup *velerov1api.Backup
|
||||
deleteBackupRequests []*velerov1api.DeleteBackupRequest
|
||||
backupLocation *velerov1api.BackupStorageLocation
|
||||
expectDeletion bool
|
||||
createDeleteBackupRequestError bool
|
||||
expectError bool
|
||||
|
@ -172,13 +175,13 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "expired backup in read-only storage location is not deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-only").Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(api.BackupStorageLocationAccessModeReadOnly).Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "read-only").AccessMode(velerov1api.BackupStorageLocationAccessModeReadOnly).Result(),
|
||||
expectDeletion: false,
|
||||
},
|
||||
{
|
||||
name: "expired backup in read-write storage location is deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Minute)).StorageLocation("read-write").Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(api.BackupStorageLocationAccessModeReadWrite).Result(),
|
||||
backupLocation: builder.ForBackupStorageLocation("velero", "read-write").AccessMode(velerov1api.BackupStorageLocationAccessModeReadWrite).Result(),
|
||||
expectDeletion: true,
|
||||
},
|
||||
{
|
||||
|
@ -191,18 +194,18 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
|||
name: "expired backup with a pending deletion request is not deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
deleteBackupRequests: []*api.DeleteBackupRequest{
|
||||
deleteBackupRequests: []*velerov1api.DeleteBackupRequest{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: api.DefaultNamespace,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
api.BackupNameLabel: "backup-1",
|
||||
api.BackupUIDLabel: "",
|
||||
velerov1api.BackupNameLabel: "backup-1",
|
||||
velerov1api.BackupUIDLabel: "",
|
||||
},
|
||||
},
|
||||
Status: api.DeleteBackupRequestStatus{
|
||||
Phase: api.DeleteBackupRequestPhaseInProgress,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseInProgress,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -212,18 +215,18 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
|||
name: "expired backup with only processed deletion requests is deleted",
|
||||
backup: defaultBackup().Expiration(fakeClock.Now().Add(-time.Second)).StorageLocation("default").Result(),
|
||||
backupLocation: defaultBackupLocation,
|
||||
deleteBackupRequests: []*api.DeleteBackupRequest{
|
||||
deleteBackupRequests: []*velerov1api.DeleteBackupRequest{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: api.DefaultNamespace,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "foo",
|
||||
Labels: map[string]string{
|
||||
api.BackupNameLabel: "backup-1",
|
||||
api.BackupUIDLabel: "",
|
||||
velerov1api.BackupNameLabel: "backup-1",
|
||||
velerov1api.BackupUIDLabel: "",
|
||||
},
|
||||
},
|
||||
Status: api.DeleteBackupRequestStatus{
|
||||
Phase: api.DeleteBackupRequestPhaseProcessed,
|
||||
Status: velerov1api.DeleteBackupRequestStatus{
|
||||
Phase: velerov1api.DeleteBackupRequestPhaseProcessed,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -246,12 +249,19 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
|||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
)
|
||||
|
||||
var fakeClient kbclient.Client
|
||||
if test.backupLocation != nil {
|
||||
fakeClient = newFakeClient(t, test.backupLocation)
|
||||
} else {
|
||||
fakeClient = newFakeClient(t)
|
||||
}
|
||||
|
||||
controller := NewGCController(
|
||||
velerotest.NewLogger(),
|
||||
sharedInformers.Velero().V1().Backups(),
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Lister(),
|
||||
client.VeleroV1(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
fakeClient,
|
||||
).(*gcController)
|
||||
controller.clock = fakeClock
|
||||
|
||||
|
@ -261,10 +271,6 @@ func TestGCControllerProcessQueueItem(t *testing.T) {
|
|||
sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup)
|
||||
}
|
||||
|
||||
if test.backupLocation != nil {
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.backupLocation)
|
||||
}
|
||||
|
||||
for _, dbr := range test.deleteBackupRequests {
|
||||
sharedInformers.Velero().V1().DeleteBackupRequests().Informer().GetStore().Add(dbr)
|
||||
}
|
||||
|
|
|
@ -42,6 +42,8 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/filesystem"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type podVolumeBackupController struct {
|
||||
|
@ -53,7 +55,7 @@ type podVolumeBackupController struct {
|
|||
podLister corev1listers.PodLister
|
||||
pvcLister corev1listers.PersistentVolumeClaimLister
|
||||
pvLister corev1listers.PersistentVolumeLister
|
||||
backupLocationLister listers.BackupStorageLocationLister
|
||||
kbClient client.Client
|
||||
nodeName string
|
||||
|
||||
processBackupFunc func(*velerov1api.PodVolumeBackup) error
|
||||
|
@ -70,7 +72,7 @@ func NewPodVolumeBackupController(
|
|||
secretInformer cache.SharedIndexInformer,
|
||||
pvcInformer corev1informers.PersistentVolumeClaimInformer,
|
||||
pvInformer corev1informers.PersistentVolumeInformer,
|
||||
backupLocationInformer informers.BackupStorageLocationInformer,
|
||||
kbClient client.Client,
|
||||
nodeName string,
|
||||
) Interface {
|
||||
c := &podVolumeBackupController{
|
||||
|
@ -81,7 +83,7 @@ func NewPodVolumeBackupController(
|
|||
secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()),
|
||||
pvcLister: pvcInformer.Lister(),
|
||||
pvLister: pvInformer.Lister(),
|
||||
backupLocationLister: backupLocationInformer.Lister(),
|
||||
kbClient: kbClient,
|
||||
nodeName: nodeName,
|
||||
|
||||
fileSystem: filesystem.NewFileSystem(),
|
||||
|
@ -95,7 +97,6 @@ func NewPodVolumeBackupController(
|
|||
podInformer.HasSynced,
|
||||
secretInformer.HasSynced,
|
||||
pvcInformer.Informer().HasSynced,
|
||||
backupLocationInformer.Informer().HasSynced,
|
||||
)
|
||||
c.processBackupFunc = c.processBackup
|
||||
|
||||
|
@ -228,10 +229,11 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack
|
|||
)
|
||||
|
||||
// if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic
|
||||
caCert, err := restic.GetCACert(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
caCert, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Error getting caCert")
|
||||
}
|
||||
|
||||
var caCertFile string
|
||||
if caCert != nil {
|
||||
caCertFile, err = restic.TempCACertFile(caCert, req.Spec.BackupStorageLocation, c.fileSystem)
|
||||
|
@ -247,12 +249,12 @@ func (c *podVolumeBackupController) processBackup(req *velerov1api.PodVolumeBack
|
|||
// set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations)
|
||||
var env []string
|
||||
if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") {
|
||||
if env, err = restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil {
|
||||
if env, err = restic.AzureCmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation); err != nil {
|
||||
return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log)
|
||||
}
|
||||
resticCmd.Env = env
|
||||
} else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") {
|
||||
if env, err = restic.S3CmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation); err != nil {
|
||||
if env, err = restic.S3CmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation); err != nil {
|
||||
return c.fail(req, errors.Wrap(err, "error setting restic cmd env").Error(), log)
|
||||
}
|
||||
resticCmd.Env = env
|
||||
|
|
|
@ -36,6 +36,8 @@ import (
|
|||
corev1informers "k8s.io/client-go/informers/core/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
k8scache "sigs.k8s.io/controller-runtime/pkg/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
|
@ -56,7 +58,8 @@ type podVolumeRestoreController struct {
|
|||
secretLister corev1listers.SecretLister
|
||||
pvcLister corev1listers.PersistentVolumeClaimLister
|
||||
pvLister corev1listers.PersistentVolumeLister
|
||||
backupLocationLister listers.BackupStorageLocationLister
|
||||
backupLocationInformer k8scache.Informer
|
||||
kbClient client.Client
|
||||
nodeName string
|
||||
|
||||
processRestoreFunc func(*velerov1api.PodVolumeRestore) error
|
||||
|
@ -73,7 +76,7 @@ func NewPodVolumeRestoreController(
|
|||
secretInformer cache.SharedIndexInformer,
|
||||
pvcInformer corev1informers.PersistentVolumeClaimInformer,
|
||||
pvInformer corev1informers.PersistentVolumeInformer,
|
||||
backupLocationInformer informers.BackupStorageLocationInformer,
|
||||
kbClient client.Client,
|
||||
nodeName string,
|
||||
) Interface {
|
||||
c := &podVolumeRestoreController{
|
||||
|
@ -84,7 +87,7 @@ func NewPodVolumeRestoreController(
|
|||
secretLister: corev1listers.NewSecretLister(secretInformer.GetIndexer()),
|
||||
pvcLister: pvcInformer.Lister(),
|
||||
pvLister: pvInformer.Lister(),
|
||||
backupLocationLister: backupLocationInformer.Lister(),
|
||||
kbClient: kbClient,
|
||||
nodeName: nodeName,
|
||||
|
||||
fileSystem: filesystem.NewFileSystem(),
|
||||
|
@ -98,7 +101,6 @@ func NewPodVolumeRestoreController(
|
|||
podInformer.HasSynced,
|
||||
secretInformer.HasSynced,
|
||||
pvcInformer.Informer().HasSynced,
|
||||
backupLocationInformer.Informer().HasSynced,
|
||||
)
|
||||
c.processRestoreFunc = c.processRestore
|
||||
|
||||
|
@ -294,10 +296,11 @@ func (c *podVolumeRestoreController) processRestore(req *velerov1api.PodVolumeRe
|
|||
defer os.Remove(credsFile)
|
||||
|
||||
// if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic
|
||||
caCert, err := restic.GetCACert(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
caCert, err := restic.GetCACert(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Error getting caCert")
|
||||
}
|
||||
|
||||
var caCertFile string
|
||||
if caCert != nil {
|
||||
caCertFile, err = restic.TempCACertFile(caCert, req.Spec.BackupStorageLocation, c.fileSystem)
|
||||
|
@ -347,13 +350,13 @@ func (c *podVolumeRestoreController) restorePodVolume(req *velerov1api.PodVolume
|
|||
// Running restic command might need additional provider specific environment variables. Based on the provider, we
|
||||
// set resticCmd.Env appropriately (currently for Azure and S3 based backuplocations)
|
||||
if strings.HasPrefix(req.Spec.RepoIdentifier, "azure") {
|
||||
env, err := restic.AzureCmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
env, err := restic.AzureCmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
if err != nil {
|
||||
return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log)
|
||||
}
|
||||
resticCmd.Env = env
|
||||
} else if strings.HasPrefix(req.Spec.RepoIdentifier, "s3") {
|
||||
env, err := restic.S3CmdEnv(c.backupLocationLister, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
env, err := restic.S3CmdEnv(c.kbClient, req.Namespace, req.Spec.BackupStorageLocation)
|
||||
if err != nil {
|
||||
return c.failRestore(req, errors.Wrap(err, "error setting restic cmd env").Error(), log)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -31,11 +32,13 @@ import (
|
|||
"k8s.io/apimachinery/pkg/util/clock"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1"
|
||||
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/restic"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
type resticRepositoryController struct {
|
||||
|
@ -43,7 +46,7 @@ type resticRepositoryController struct {
|
|||
|
||||
resticRepositoryClient velerov1client.ResticRepositoriesGetter
|
||||
resticRepositoryLister velerov1listers.ResticRepositoryLister
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
kbClient client.Client
|
||||
repositoryManager restic.RepositoryManager
|
||||
defaultMaintenanceFrequency time.Duration
|
||||
|
||||
|
@ -55,7 +58,7 @@ func NewResticRepositoryController(
|
|||
logger logrus.FieldLogger,
|
||||
resticRepositoryInformer velerov1informers.ResticRepositoryInformer,
|
||||
resticRepositoryClient velerov1client.ResticRepositoriesGetter,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
kbClient client.Client,
|
||||
repositoryManager restic.RepositoryManager,
|
||||
defaultMaintenanceFrequency time.Duration,
|
||||
) Interface {
|
||||
|
@ -63,7 +66,7 @@ func NewResticRepositoryController(
|
|||
genericController: newGenericController("restic-repository", logger),
|
||||
resticRepositoryClient: resticRepositoryClient,
|
||||
resticRepositoryLister: resticRepositoryInformer.Lister(),
|
||||
backupLocationLister: backupLocationLister,
|
||||
kbClient: kbClient,
|
||||
repositoryManager: repositoryManager,
|
||||
defaultMaintenanceFrequency: defaultMaintenanceFrequency,
|
||||
|
||||
|
@ -129,7 +132,7 @@ func (c *resticRepositoryController) processQueueItem(key string) error {
|
|||
// Don't mutate the shared cache
|
||||
reqCopy := req.DeepCopy()
|
||||
|
||||
if req.Status.Phase == "" || req.Status.Phase == v1.ResticRepositoryPhaseNew {
|
||||
if req.Status.Phase == "" || req.Status.Phase == velerov1api.ResticRepositoryPhaseNew {
|
||||
return c.initializeRepo(reqCopy, log)
|
||||
}
|
||||
|
||||
|
@ -142,29 +145,32 @@ func (c *resticRepositoryController) processQueueItem(key string) error {
|
|||
}
|
||||
|
||||
switch req.Status.Phase {
|
||||
case v1.ResticRepositoryPhaseReady:
|
||||
case velerov1api.ResticRepositoryPhaseReady:
|
||||
return c.runMaintenanceIfDue(reqCopy, log)
|
||||
case v1.ResticRepositoryPhaseNotReady:
|
||||
case velerov1api.ResticRepositoryPhaseNotReady:
|
||||
return c.checkNotReadyRepo(reqCopy, log)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, log logrus.FieldLogger) error {
|
||||
func (c *resticRepositoryController) initializeRepo(req *velerov1api.ResticRepository, log logrus.FieldLogger) error {
|
||||
log.Info("Initializing restic repository")
|
||||
|
||||
// confirm the repo's BackupStorageLocation is valid
|
||||
loc, err := c.backupLocationLister.BackupStorageLocations(req.Namespace).Get(req.Spec.BackupStorageLocation)
|
||||
if err != nil {
|
||||
loc := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: req.Namespace,
|
||||
Name: req.Spec.BackupStorageLocation,
|
||||
}, loc); err != nil {
|
||||
return c.patchResticRepository(req, repoNotReady(err.Error()))
|
||||
}
|
||||
|
||||
repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace)
|
||||
if err != nil {
|
||||
return c.patchResticRepository(req, func(r *v1.ResticRepository) {
|
||||
return c.patchResticRepository(req, func(r *velerov1api.ResticRepository) {
|
||||
r.Status.Message = err.Error()
|
||||
r.Status.Phase = v1.ResticRepositoryPhaseNotReady
|
||||
r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady
|
||||
|
||||
if r.Spec.MaintenanceFrequency.Duration <= 0 {
|
||||
r.Spec.MaintenanceFrequency = metav1.Duration{Duration: c.defaultMaintenanceFrequency}
|
||||
|
@ -173,7 +179,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo
|
|||
}
|
||||
|
||||
// defaulting - if the patch fails, return an error so the item is returned to the queue
|
||||
if err := c.patchResticRepository(req, func(r *v1.ResticRepository) {
|
||||
if err := c.patchResticRepository(req, func(r *velerov1api.ResticRepository) {
|
||||
r.Spec.ResticIdentifier = repoIdentifier
|
||||
|
||||
if r.Spec.MaintenanceFrequency.Duration <= 0 {
|
||||
|
@ -187,8 +193,8 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo
|
|||
return c.patchResticRepository(req, repoNotReady(err.Error()))
|
||||
}
|
||||
|
||||
return c.patchResticRepository(req, func(req *v1.ResticRepository) {
|
||||
req.Status.Phase = v1.ResticRepositoryPhaseReady
|
||||
return c.patchResticRepository(req, func(req *velerov1api.ResticRepository) {
|
||||
req.Status.Phase = velerov1api.ResticRepositoryPhaseReady
|
||||
req.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()}
|
||||
})
|
||||
}
|
||||
|
@ -196,7 +202,7 @@ func (c *resticRepositoryController) initializeRepo(req *v1.ResticRepository, lo
|
|||
// ensureRepo checks to see if a repository exists, and attempts to initialize it if
|
||||
// it does not exist. An error is returned if the repository can't be connected to
|
||||
// or initialized.
|
||||
func ensureRepo(repo *v1.ResticRepository, repoManager restic.RepositoryManager) error {
|
||||
func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.RepositoryManager) error {
|
||||
if err := repoManager.ConnectToRepo(repo); err != nil {
|
||||
// If the repository has not yet been initialized, the error message will always include
|
||||
// the following string. This is the only scenario where we should try to initialize it.
|
||||
|
@ -212,7 +218,7 @@ func ensureRepo(repo *v1.ResticRepository, repoManager restic.RepositoryManager)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepository, log logrus.FieldLogger) error {
|
||||
func (c *resticRepositoryController) runMaintenanceIfDue(req *velerov1api.ResticRepository, log logrus.FieldLogger) error {
|
||||
log.Debug("resticRepositoryController.runMaintenanceIfDue")
|
||||
|
||||
now := c.clock.Now()
|
||||
|
@ -229,23 +235,23 @@ func (c *resticRepositoryController) runMaintenanceIfDue(req *v1.ResticRepositor
|
|||
log.Debug("Pruning repo")
|
||||
if err := c.repositoryManager.PruneRepo(req); err != nil {
|
||||
log.WithError(err).Warn("error pruning repository")
|
||||
if patchErr := c.patchResticRepository(req, func(r *v1.ResticRepository) {
|
||||
if patchErr := c.patchResticRepository(req, func(r *velerov1api.ResticRepository) {
|
||||
r.Status.Message = err.Error()
|
||||
}); patchErr != nil {
|
||||
return patchErr
|
||||
}
|
||||
}
|
||||
|
||||
return c.patchResticRepository(req, func(req *v1.ResticRepository) {
|
||||
return c.patchResticRepository(req, func(req *velerov1api.ResticRepository) {
|
||||
req.Status.LastMaintenanceTime = &metav1.Time{Time: now}
|
||||
})
|
||||
}
|
||||
|
||||
func dueForMaintenance(req *v1.ResticRepository, now time.Time) bool {
|
||||
func dueForMaintenance(req *velerov1api.ResticRepository, now time.Time) bool {
|
||||
return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now)
|
||||
}
|
||||
|
||||
func (c *resticRepositoryController) checkNotReadyRepo(req *v1.ResticRepository, log logrus.FieldLogger) error {
|
||||
func (c *resticRepositoryController) checkNotReadyRepo(req *velerov1api.ResticRepository, log logrus.FieldLogger) error {
|
||||
// no identifier: can't possibly be ready, so just return
|
||||
if req.Spec.ResticIdentifier == "" {
|
||||
return nil
|
||||
|
@ -262,16 +268,16 @@ func (c *resticRepositoryController) checkNotReadyRepo(req *v1.ResticRepository,
|
|||
return c.patchResticRepository(req, repoReady())
|
||||
}
|
||||
|
||||
func repoNotReady(msg string) func(*v1.ResticRepository) {
|
||||
return func(r *v1.ResticRepository) {
|
||||
r.Status.Phase = v1.ResticRepositoryPhaseNotReady
|
||||
func repoNotReady(msg string) func(*velerov1api.ResticRepository) {
|
||||
return func(r *velerov1api.ResticRepository) {
|
||||
r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady
|
||||
r.Status.Message = msg
|
||||
}
|
||||
}
|
||||
|
||||
func repoReady() func(*v1.ResticRepository) {
|
||||
return func(r *v1.ResticRepository) {
|
||||
r.Status.Phase = v1.ResticRepositoryPhaseReady
|
||||
func repoReady() func(*velerov1api.ResticRepository) {
|
||||
return func(r *velerov1api.ResticRepository) {
|
||||
r.Status.Phase = velerov1api.ResticRepositoryPhaseReady
|
||||
r.Status.Message = ""
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +285,7 @@ func repoReady() func(*v1.ResticRepository) {
|
|||
// patchResticRepository mutates req with the provided mutate function, and patches it
|
||||
// through the Kube API. After executing this function, req will be updated with both
|
||||
// the mutation and the results of the Patch() API call.
|
||||
func (c *resticRepositoryController) patchResticRepository(req *v1.ResticRepository, mutate func(*v1.ResticRepository)) error {
|
||||
func (c *resticRepositoryController) patchResticRepository(req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error {
|
||||
// Record original json
|
||||
oldData, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
|
@ -305,7 +311,7 @@ func (c *resticRepositoryController) patchResticRepository(req *v1.ResticReposit
|
|||
}
|
||||
|
||||
// patch, and if successful, update req
|
||||
var patched *v1.ResticRepository
|
||||
var patched *velerov1api.ResticRepository
|
||||
if patched, err = c.resticRepositoryClient.ResticRepositories(req.Namespace).Patch(req.Name, types.MergePatchType, patchBytes); err != nil {
|
||||
return errors.Wrap(err, "error patching ResticRepository")
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package controller
|
|||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -48,6 +49,8 @@ import (
|
|||
"github.com/vmware-tanzu/velero/pkg/util/collections"
|
||||
kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/logging"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// nonRestorableResources is a blacklist for the restoration process. Any resources
|
||||
|
@ -80,7 +83,7 @@ type restoreController struct {
|
|||
restorer pkgrestore.Restorer
|
||||
backupLister velerov1listers.BackupLister
|
||||
restoreLister velerov1listers.RestoreLister
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
kbClient client.Client
|
||||
snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister
|
||||
restoreLogLevel logrus.Level
|
||||
defaultBackupLocation string
|
||||
|
@ -88,7 +91,7 @@ type restoreController struct {
|
|||
logFormat logging.Format
|
||||
|
||||
newPluginManager func(logger logrus.FieldLogger) clientmgmt.Manager
|
||||
newBackupStore func(*api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error)
|
||||
newBackupStore func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error)
|
||||
}
|
||||
|
||||
func NewRestoreController(
|
||||
|
@ -98,7 +101,7 @@ func NewRestoreController(
|
|||
podVolumeBackupClient velerov1client.PodVolumeBackupsGetter,
|
||||
restorer pkgrestore.Restorer,
|
||||
backupLister velerov1listers.BackupLister,
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister,
|
||||
kbClient client.Client,
|
||||
snapshotLocationLister velerov1listers.VolumeSnapshotLocationLister,
|
||||
logger logrus.FieldLogger,
|
||||
restoreLogLevel logrus.Level,
|
||||
|
@ -115,7 +118,7 @@ func NewRestoreController(
|
|||
restorer: restorer,
|
||||
backupLister: backupLister,
|
||||
restoreLister: restoreInformer.Lister(),
|
||||
backupLocationLister: backupLocationLister,
|
||||
kbClient: kbClient,
|
||||
snapshotLocationLister: snapshotLocationLister,
|
||||
restoreLogLevel: restoreLogLevel,
|
||||
defaultBackupLocation: defaultBackupLocation,
|
||||
|
@ -274,7 +277,7 @@ func (c *restoreController) processRestore(restore *api.Restore) error {
|
|||
|
||||
type backupInfo struct {
|
||||
backup *api.Backup
|
||||
location *api.BackupStorageLocation
|
||||
location *velerov1api.BackupStorageLocation
|
||||
backupStore persistence.BackupStore
|
||||
}
|
||||
|
||||
|
@ -396,8 +399,11 @@ func (c *restoreController) fetchBackupInfo(backupName string, pluginManager cli
|
|||
return backupInfo{}, err
|
||||
}
|
||||
|
||||
location, err := c.backupLocationLister.BackupStorageLocations(c.namespace).Get(backup.Spec.StorageLocation)
|
||||
if err != nil {
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
if err := c.kbClient.Get(context.Background(), client.ObjectKey{
|
||||
Namespace: c.namespace,
|
||||
Name: backup.Spec.StorageLocation,
|
||||
}, location); err != nil {
|
||||
return backupInfo{}, errors.WithStack(err)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ package controller
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
@ -34,7 +35,7 @@ import (
|
|||
core "k8s.io/client-go/testing"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/fake"
|
||||
informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions"
|
||||
|
@ -52,29 +53,30 @@ import (
|
|||
)
|
||||
|
||||
func TestFetchBackupInfo(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
backupName string
|
||||
informerLocations []*api.BackupStorageLocation
|
||||
informerBackups []*api.Backup
|
||||
backupStoreBackup *api.Backup
|
||||
informerLocations []*velerov1api.BackupStorageLocation
|
||||
informerBackups []*velerov1api.Backup
|
||||
backupStoreBackup *velerov1api.Backup
|
||||
backupStoreError error
|
||||
expectedRes *api.Backup
|
||||
expectedRes *velerov1api.Backup
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "lister has backup",
|
||||
backupName: "backup-1",
|
||||
informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()},
|
||||
informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()},
|
||||
informerLocations: []*velerov1api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()},
|
||||
informerBackups: []*velerov1api.Backup{defaultBackup().StorageLocation("default").Result()},
|
||||
expectedRes: defaultBackup().StorageLocation("default").Result(),
|
||||
},
|
||||
{
|
||||
name: "lister does not have a backup, but backupSvc does",
|
||||
backupName: "backup-1",
|
||||
backupStoreBackup: defaultBackup().StorageLocation("default").Result(),
|
||||
informerLocations: []*api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()},
|
||||
informerBackups: []*api.Backup{defaultBackup().StorageLocation("default").Result()},
|
||||
informerLocations: []*velerov1api.BackupStorageLocation{builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()},
|
||||
informerBackups: []*velerov1api.Backup{defaultBackup().StorageLocation("default").Result()},
|
||||
expectedRes: defaultBackup().StorageLocation("default").Result(),
|
||||
},
|
||||
{
|
||||
|
@ -91,6 +93,7 @@ func TestFetchBackupInfo(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
fakeClient = newFakeClient(t)
|
||||
restorer = &fakeRestorer{}
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
logger = velerotest.NewLogger()
|
||||
|
@ -102,13 +105,13 @@ func TestFetchBackupInfo(t *testing.T) {
|
|||
defer backupStore.AssertExpectations(t)
|
||||
|
||||
c := NewRestoreController(
|
||||
api.DefaultNamespace,
|
||||
velerov1api.DefaultNamespace,
|
||||
sharedInformers.Velero().V1().Restores(),
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
restorer,
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
fakeClient,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
logger,
|
||||
logrus.InfoLevel,
|
||||
|
@ -118,13 +121,13 @@ func TestFetchBackupInfo(t *testing.T) {
|
|||
formatFlag,
|
||||
).(*restoreController)
|
||||
|
||||
c.newBackupStore = func(*api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
c.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
return backupStore, nil
|
||||
}
|
||||
|
||||
if test.backupStoreError == nil {
|
||||
for _, itm := range test.informerLocations {
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(itm)
|
||||
require.NoError(t, fakeClient.Create(context.Background(), itm))
|
||||
}
|
||||
|
||||
for _, itm := range test.informerBackups {
|
||||
|
@ -157,7 +160,7 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
|||
tests := []struct {
|
||||
name string
|
||||
restoreKey string
|
||||
restore *api.Restore
|
||||
restore *velerov1api.Restore
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
|
@ -172,17 +175,17 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
|||
{
|
||||
name: "restore with phase InProgress does not get processed",
|
||||
restoreKey: "foo/bar",
|
||||
restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseInProgress).Result(),
|
||||
restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseInProgress).Result(),
|
||||
},
|
||||
{
|
||||
name: "restore with phase Completed does not get processed",
|
||||
restoreKey: "foo/bar",
|
||||
restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseCompleted).Result(),
|
||||
restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseCompleted).Result(),
|
||||
},
|
||||
{
|
||||
name: "restore with phase FailedValidation does not get processed",
|
||||
restoreKey: "foo/bar",
|
||||
restore: builder.ForRestore("foo", "bar").Phase(api.RestorePhaseFailedValidation).Result(),
|
||||
restore: builder.ForRestore("foo", "bar").Phase(velerov1api.RestorePhaseFailedValidation).Result(),
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -198,13 +201,13 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
|||
)
|
||||
|
||||
c := NewRestoreController(
|
||||
api.DefaultNamespace,
|
||||
velerov1api.DefaultNamespace,
|
||||
sharedInformers.Velero().V1().Restores(),
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
restorer,
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
nil,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
logger,
|
||||
logrus.InfoLevel,
|
||||
|
@ -226,20 +229,21 @@ func TestProcessQueueItemSkips(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestProcessQueueItem(t *testing.T) {
|
||||
|
||||
defaultStorageLocation := builder.ForBackupStorageLocation("velero", "default").Provider("myCloud").Bucket("bucket").Result()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
restoreKey string
|
||||
location *api.BackupStorageLocation
|
||||
restore *api.Restore
|
||||
backup *api.Backup
|
||||
location *velerov1api.BackupStorageLocation
|
||||
restore *velerov1api.Restore
|
||||
backup *velerov1api.Backup
|
||||
restorerError error
|
||||
expectedErr bool
|
||||
expectedPhase string
|
||||
expectedValidationErrors []string
|
||||
expectedRestoreErrors int
|
||||
expectedRestorerCall *api.Restore
|
||||
expectedRestorerCall *velerov1api.Restore
|
||||
backupStoreGetBackupMetadataErr error
|
||||
backupStoreGetBackupContentsErr error
|
||||
putRestoreLogErr error
|
||||
|
@ -248,80 +252,80 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "restore with both namespace in both includedNamespaces and excludedNamespaces fails validation",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "another-1", "*", velerov1api.RestorePhaseNew).ExcludedNamespaces("another-1").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{"Invalid included/excluded namespace lists: excludes list cannot contain an item in the includes list: another-1"},
|
||||
},
|
||||
{
|
||||
name: "restore with resource in both includedResources and excludedResources fails validation",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", api.RestorePhaseNew).ExcludedResources("a-resource").Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "*", "a-resource", velerov1api.RestorePhaseNew).ExcludedResources("a-resource").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: a-resource"},
|
||||
},
|
||||
{
|
||||
name: "new restore with empty backup and schedule names fails validation",
|
||||
restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "", "ns-1", "", velerov1api.RestorePhaseNew).Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"},
|
||||
},
|
||||
{
|
||||
name: "new restore with backup and schedule names provided fails validation",
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Schedule("sched-1").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{"Either a backup or schedule must be specified as a source for the restore, but not both"},
|
||||
},
|
||||
{
|
||||
name: "valid restore with schedule name gets executed",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "", "ns-1", "", api.RestorePhaseNew).Schedule("sched-1").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "sched-1")).Phase(api.BackupPhaseCompleted).Result(),
|
||||
restore: NewRestore("foo", "bar", "", "ns-1", "", velerov1api.RestorePhaseNew).Schedule("sched-1").Result(),
|
||||
backup: defaultBackup().StorageLocation("default").ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "sched-1")).Phase(velerov1api.BackupPhaseCompleted).Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseInProgress),
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Schedule("sched-1").Result(),
|
||||
expectedPhase: string(velerov1api.RestorePhaseInProgress),
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Schedule("sched-1").Result(),
|
||||
},
|
||||
{
|
||||
name: "restore with non-existent backup name fails",
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "*", velerov1api.RestorePhaseNew).Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{"Error retrieving backup: backup.velero.io \"backup-1\" not found"},
|
||||
backupStoreGetBackupMetadataErr: errors.New("no backup here"),
|
||||
},
|
||||
{
|
||||
name: "restorer throwing an error causes the restore to fail",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
restorerError: errors.New("blarg"),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseInProgress),
|
||||
expectedFinalPhase: string(api.RestorePhasePartiallyFailed),
|
||||
expectedPhase: string(velerov1api.RestorePhaseInProgress),
|
||||
expectedFinalPhase: string(velerov1api.RestorePhasePartiallyFailed),
|
||||
expectedRestoreErrors: 1,
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(),
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Result(),
|
||||
},
|
||||
{
|
||||
name: "valid restore gets executed",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseInProgress),
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", api.RestorePhaseInProgress).Result(),
|
||||
expectedPhase: string(velerov1api.RestorePhaseInProgress),
|
||||
expectedRestorerCall: NewRestore("foo", "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseInProgress).Result(),
|
||||
},
|
||||
{
|
||||
name: "restoration of nodes is not supported",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "nodes", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{
|
||||
"nodes are non-restorable resources",
|
||||
"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: nodes",
|
||||
|
@ -330,10 +334,10 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "restoration of events is not supported",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{
|
||||
"events are non-restorable resources",
|
||||
"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: events",
|
||||
|
@ -342,10 +346,10 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "restoration of events.events.k8s.io is not supported",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "events.events.k8s.io", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{
|
||||
"events.events.k8s.io are non-restorable resources",
|
||||
"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: events.events.k8s.io",
|
||||
|
@ -354,10 +358,10 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "restoration of backups.velero.io is not supported",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "backups.velero.io", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{
|
||||
"backups.velero.io are non-restorable resources",
|
||||
"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: backups.velero.io",
|
||||
|
@ -366,10 +370,10 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "restoration of restores.velero.io is not supported",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", api.RestorePhaseNew).Result(),
|
||||
restore: NewRestore("foo", "bar", "backup-1", "ns-1", "restores.velero.io", velerov1api.RestorePhaseNew).Result(),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
expectedErr: false,
|
||||
expectedPhase: string(api.RestorePhaseFailedValidation),
|
||||
expectedPhase: string(velerov1api.RestorePhaseFailedValidation),
|
||||
expectedValidationErrors: []string{
|
||||
"restores.velero.io are non-restorable resources",
|
||||
"Invalid included/excluded resource lists: excludes list cannot contain an item in the includes list: restores.velero.io",
|
||||
|
@ -378,9 +382,9 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
{
|
||||
name: "backup download error results in failed restore",
|
||||
location: defaultStorageLocation,
|
||||
restore: NewRestore(api.DefaultNamespace, "bar", "backup-1", "ns-1", "", api.RestorePhaseNew).Result(),
|
||||
expectedPhase: string(api.RestorePhaseInProgress),
|
||||
expectedFinalPhase: string(api.RestorePhaseFailed),
|
||||
restore: NewRestore(velerov1api.DefaultNamespace, "bar", "backup-1", "ns-1", "", velerov1api.RestorePhaseNew).Result(),
|
||||
expectedPhase: string(velerov1api.RestorePhaseInProgress),
|
||||
expectedFinalPhase: string(velerov1api.RestorePhaseFailed),
|
||||
backupStoreGetBackupContentsErr: errors.New("Couldn't download backup"),
|
||||
backup: defaultBackup().StorageLocation("default").Result(),
|
||||
},
|
||||
|
@ -392,6 +396,7 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
t.Run(test.name, func(t *testing.T) {
|
||||
var (
|
||||
client = fake.NewSimpleClientset()
|
||||
fakeClient = newFakeClient(t)
|
||||
restorer = &fakeRestorer{}
|
||||
sharedInformers = informers.NewSharedInformerFactory(client, 0)
|
||||
logger = velerotest.NewLogger()
|
||||
|
@ -403,13 +408,13 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
defer backupStore.AssertExpectations(t)
|
||||
|
||||
c := NewRestoreController(
|
||||
api.DefaultNamespace,
|
||||
velerov1api.DefaultNamespace,
|
||||
sharedInformers.Velero().V1().Restores(),
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
restorer,
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
fakeClient,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
logger,
|
||||
logrus.InfoLevel,
|
||||
|
@ -419,12 +424,12 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
formatFlag,
|
||||
).(*restoreController)
|
||||
|
||||
c.newBackupStore = func(*api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
c.newBackupStore = func(*velerov1api.BackupStorageLocation, persistence.ObjectStoreGetter, logrus.FieldLogger) (persistence.BackupStore, error) {
|
||||
return backupStore, nil
|
||||
}
|
||||
|
||||
if test.location != nil {
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Informer().GetStore().Add(test.location)
|
||||
require.NoError(t, fakeClient.Create(context.Background(), test.location))
|
||||
}
|
||||
if test.backup != nil {
|
||||
sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(test.backup)
|
||||
|
@ -462,7 +467,7 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
// these are the fields that we expect to be set by
|
||||
// the controller
|
||||
|
||||
res.Status.Phase = api.RestorePhase(phase)
|
||||
res.Status.Phase = velerov1api.RestorePhase(phase)
|
||||
|
||||
backupName, found, err := unstructured.NestedString(patchMap, "spec", "backupName")
|
||||
if found {
|
||||
|
@ -546,9 +551,9 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
}
|
||||
|
||||
type StatusPatch struct {
|
||||
Phase api.RestorePhase `json:"phase"`
|
||||
ValidationErrors []string `json:"validationErrors"`
|
||||
Errors int `json:"errors"`
|
||||
Phase velerov1api.RestorePhase `json:"phase"`
|
||||
ValidationErrors []string `json:"validationErrors"`
|
||||
Errors int `json:"errors"`
|
||||
}
|
||||
|
||||
type Patch struct {
|
||||
|
@ -568,7 +573,7 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
|
||||
expected := Patch{
|
||||
Status: StatusPatch{
|
||||
Phase: api.RestorePhase(test.expectedPhase),
|
||||
Phase: velerov1api.RestorePhase(test.expectedPhase),
|
||||
ValidationErrors: test.expectedValidationErrors,
|
||||
},
|
||||
}
|
||||
|
@ -593,7 +598,7 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
|
||||
expected = Patch{
|
||||
Status: StatusPatch{
|
||||
Phase: api.RestorePhaseCompleted,
|
||||
Phase: velerov1api.RestorePhaseCompleted,
|
||||
Errors: test.expectedRestoreErrors,
|
||||
},
|
||||
}
|
||||
|
@ -601,7 +606,7 @@ func TestProcessQueueItem(t *testing.T) {
|
|||
if test.expectedFinalPhase != "" {
|
||||
expected = Patch{
|
||||
Status: StatusPatch{
|
||||
Phase: api.RestorePhase(test.expectedFinalPhase),
|
||||
Phase: velerov1api.RestorePhase(test.expectedFinalPhase),
|
||||
Errors: test.expectedRestoreErrors,
|
||||
},
|
||||
}
|
||||
|
@ -628,13 +633,13 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
)
|
||||
|
||||
c := NewRestoreController(
|
||||
api.DefaultNamespace,
|
||||
velerov1api.DefaultNamespace,
|
||||
sharedInformers.Velero().V1().Restores(),
|
||||
client.VeleroV1(),
|
||||
client.VeleroV1(),
|
||||
nil,
|
||||
sharedInformers.Velero().V1().Backups().Lister(),
|
||||
sharedInformers.Velero().V1().BackupStorageLocations().Lister(),
|
||||
nil,
|
||||
sharedInformers.Velero().V1().VolumeSnapshotLocations().Lister(),
|
||||
logger,
|
||||
logrus.DebugLevel,
|
||||
|
@ -644,12 +649,12 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
formatFlag,
|
||||
).(*restoreController)
|
||||
|
||||
restore := &api.Restore{
|
||||
restore := &velerov1api.Restore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: api.DefaultNamespace,
|
||||
Namespace: velerov1api.DefaultNamespace,
|
||||
Name: "restore-1",
|
||||
},
|
||||
Spec: api.RestoreSpec{
|
||||
Spec: velerov1api.RestoreSpec{
|
||||
ScheduleName: "schedule-1",
|
||||
},
|
||||
}
|
||||
|
@ -657,8 +662,8 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
// no backups created from the schedule: fail validation
|
||||
require.NoError(t, sharedInformers.Velero().V1().Backups().Informer().GetStore().Add(
|
||||
defaultBackup().
|
||||
ObjectMeta(builder.WithLabels(api.ScheduleNameLabel, "non-matching-schedule")).
|
||||
Phase(api.BackupPhaseCompleted).
|
||||
ObjectMeta(builder.WithLabels(velerov1api.ScheduleNameLabel, "non-matching-schedule")).
|
||||
Phase(velerov1api.BackupPhaseCompleted).
|
||||
Result(),
|
||||
))
|
||||
|
||||
|
@ -671,9 +676,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
defaultBackup().
|
||||
ObjectMeta(
|
||||
builder.WithName("backup-2"),
|
||||
builder.WithLabels(api.ScheduleNameLabel, "schedule-1"),
|
||||
builder.WithLabels(velerov1api.ScheduleNameLabel, "schedule-1"),
|
||||
).
|
||||
Phase(api.BackupPhaseInProgress).
|
||||
Phase(velerov1api.BackupPhaseInProgress).
|
||||
Result(),
|
||||
))
|
||||
|
||||
|
@ -688,9 +693,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
defaultBackup().
|
||||
ObjectMeta(
|
||||
builder.WithName("foo"),
|
||||
builder.WithLabels(api.ScheduleNameLabel, "schedule-1"),
|
||||
builder.WithLabels(velerov1api.ScheduleNameLabel, "schedule-1"),
|
||||
).
|
||||
Phase(api.BackupPhaseCompleted).
|
||||
Phase(velerov1api.BackupPhaseCompleted).
|
||||
StartTimestamp(now).
|
||||
Result(),
|
||||
))
|
||||
|
@ -698,9 +703,9 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
defaultBackup().
|
||||
ObjectMeta(
|
||||
builder.WithName("foo"),
|
||||
builder.WithLabels(api.ScheduleNameLabel, "schedule-1"),
|
||||
builder.WithLabels(velerov1api.ScheduleNameLabel, "schedule-1"),
|
||||
).
|
||||
Phase(api.BackupPhaseCompleted).
|
||||
Phase(velerov1api.BackupPhaseCompleted).
|
||||
StartTimestamp(now.Add(time.Second)).
|
||||
Result(),
|
||||
))
|
||||
|
@ -711,7 +716,7 @@ func TestvalidateAndCompleteWhenScheduleNameSpecified(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBackupXorScheduleProvided(t *testing.T) {
|
||||
r := &api.Restore{}
|
||||
r := &velerov1api.Restore{}
|
||||
assert.False(t, backupXorScheduleProvided(r))
|
||||
|
||||
r.Spec.BackupName = "backup-1"
|
||||
|
@ -728,12 +733,12 @@ func TestBackupXorScheduleProvided(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMostRecentCompletedBackup(t *testing.T) {
|
||||
backups := []*api.Backup{
|
||||
backups := []*velerov1api.Backup{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "a",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: "",
|
||||
},
|
||||
},
|
||||
|
@ -741,32 +746,32 @@ func TestMostRecentCompletedBackup(t *testing.T) {
|
|||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "b",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Phase: api.BackupPhaseNew,
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseNew,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "c",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Phase: api.BackupPhaseInProgress,
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseInProgress,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "d",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Phase: api.BackupPhaseFailedValidation,
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailedValidation,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "e",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Phase: api.BackupPhaseFailed,
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseFailed,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -775,22 +780,22 @@ func TestMostRecentCompletedBackup(t *testing.T) {
|
|||
|
||||
now := time.Now()
|
||||
|
||||
backups = append(backups, &api.Backup{
|
||||
backups = append(backups, &velerov1api.Backup{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Phase: api.BackupPhaseCompleted,
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
StartTimestamp: &metav1.Time{Time: now},
|
||||
},
|
||||
})
|
||||
|
||||
expected := &api.Backup{
|
||||
expected := &velerov1api.Backup{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "bar",
|
||||
},
|
||||
Status: api.BackupStatus{
|
||||
Phase: api.BackupPhaseCompleted,
|
||||
Status: velerov1api.BackupStatus{
|
||||
Phase: velerov1api.BackupPhaseCompleted,
|
||||
StartTimestamp: &metav1.Time{Time: now.Add(time.Second)},
|
||||
},
|
||||
}
|
||||
|
@ -799,7 +804,7 @@ func TestMostRecentCompletedBackup(t *testing.T) {
|
|||
assert.Equal(t, expected, mostRecentCompletedBackup(backups))
|
||||
}
|
||||
|
||||
func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.RestorePhase) *builder.RestoreBuilder {
|
||||
func NewRestore(ns, name, backup, includeNS, includeResource string, phase velerov1api.RestorePhase) *builder.RestoreBuilder {
|
||||
restore := builder.ForRestore(ns, name).Phase(phase).Backup(backup)
|
||||
|
||||
if includeNS != "" {
|
||||
|
@ -817,7 +822,7 @@ func NewRestore(ns, name, backup, includeNS, includeResource string, phase api.R
|
|||
|
||||
type fakeRestorer struct {
|
||||
mock.Mock
|
||||
calledWithArg api.Restore
|
||||
calledWithArg velerov1api.Restore
|
||||
}
|
||||
|
||||
func (r *fakeRestorer) Restore(
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright 2020 the Velero contributors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
func newFakeClient(t *testing.T, initObjs ...runtime.Object) client.Client {
|
||||
err := velerov1api.AddToScheme(scheme.Scheme)
|
||||
require.NoError(t, err)
|
||||
return fake.NewFakeClientWithScheme(scheme.Scheme, initObjs...)
|
||||
}
|
|
@ -19,6 +19,8 @@ package install
|
|||
import (
|
||||
"time"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -26,9 +28,8 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
|
||||
v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/config/crd/crds"
|
||||
"github.com/vmware-tanzu/velero/pkg/buildinfo"
|
||||
"github.com/vmware-tanzu/velero/pkg/generated/crds"
|
||||
)
|
||||
|
||||
// Use "latest" if the build process didn't supply a version
|
||||
|
@ -137,17 +138,17 @@ func Namespace(namespace string) *corev1.Namespace {
|
|||
}
|
||||
}
|
||||
|
||||
func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *v1.BackupStorageLocation {
|
||||
return &v1.BackupStorageLocation{
|
||||
func BackupStorageLocation(namespace, provider, bucket, prefix string, config map[string]string, caCert []byte) *velerov1api.BackupStorageLocation {
|
||||
return &velerov1api.BackupStorageLocation{
|
||||
ObjectMeta: objectMeta(namespace, "default"),
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "BackupStorageLocation",
|
||||
APIVersion: v1.SchemeGroupVersion.String(),
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
},
|
||||
Spec: v1.BackupStorageLocationSpec{
|
||||
Spec: velerov1api.BackupStorageLocationSpec{
|
||||
Provider: provider,
|
||||
StorageType: v1.StorageType{
|
||||
ObjectStorage: &v1.ObjectStorageLocation{
|
||||
StorageType: velerov1api.StorageType{
|
||||
ObjectStorage: &velerov1api.ObjectStorageLocation{
|
||||
Bucket: bucket,
|
||||
Prefix: prefix,
|
||||
CACert: caCert,
|
||||
|
@ -158,14 +159,14 @@ func BackupStorageLocation(namespace, provider, bucket, prefix string, config ma
|
|||
}
|
||||
}
|
||||
|
||||
func VolumeSnapshotLocation(namespace, provider string, config map[string]string) *v1.VolumeSnapshotLocation {
|
||||
return &v1.VolumeSnapshotLocation{
|
||||
func VolumeSnapshotLocation(namespace, provider string, config map[string]string) *velerov1api.VolumeSnapshotLocation {
|
||||
return &velerov1api.VolumeSnapshotLocation{
|
||||
ObjectMeta: objectMeta(namespace, "default"),
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "VolumeSnapshotLocation",
|
||||
APIVersion: v1.SchemeGroupVersion.String(),
|
||||
APIVersion: velerov1api.SchemeGroupVersion.String(),
|
||||
},
|
||||
Spec: v1.VolumeSnapshotLocationSpec{
|
||||
Spec: velerov1api.VolumeSnapshotLocationSpec{
|
||||
Provider: provider,
|
||||
Config: config,
|
||||
},
|
||||
|
|
|
@ -17,11 +17,14 @@ limitations under the License.
|
|||
package restic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
@ -284,17 +287,20 @@ func TempCACertFile(caCert []byte, bsl string, fs filesystem.Interface) (string,
|
|||
return name, nil
|
||||
}
|
||||
|
||||
func GetCACert(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, bsl string) ([]byte, error) {
|
||||
location, err := backupLocationLister.BackupStorageLocations(namespace).Get(bsl)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error getting backup storage location")
|
||||
func GetCACert(client kbclient.Client, namespace, backupLocation string) ([]byte, error) {
|
||||
location := &velerov1api.BackupStorageLocation{}
|
||||
if err := client.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: namespace,
|
||||
Name: backupLocation,
|
||||
}, location); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if location.Spec.ObjectStorage != nil {
|
||||
return location.Spec.ObjectStorage.CACert, nil
|
||||
if location.Spec.ObjectStorage == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return location.Spec.ObjectStorage.CACert, nil
|
||||
}
|
||||
|
||||
// NewPodVolumeRestoreListOptions creates a ListOptions with a label selector configured to
|
||||
|
@ -309,10 +315,13 @@ func NewPodVolumeRestoreListOptions(name string) metav1.ListOptions {
|
|||
// should be used when running a restic command for an Azure backend. This list is
|
||||
// the current environment, plus the Azure-specific variables restic needs, namely
|
||||
// a storage account name and key.
|
||||
func AzureCmdEnv(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, backupLocation string) ([]string, error) {
|
||||
loc, err := backupLocationLister.BackupStorageLocations(namespace).Get(backupLocation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error getting backup storage location")
|
||||
func AzureCmdEnv(client kbclient.Client, namespace, backupLocation string) ([]string, error) {
|
||||
loc := &velerov1api.BackupStorageLocation{}
|
||||
if err := client.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: namespace,
|
||||
Name: backupLocation,
|
||||
}, loc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
azureVars, err := getAzureResticEnvVars(loc.Spec.Config)
|
||||
|
@ -332,10 +341,13 @@ func AzureCmdEnv(backupLocationLister velerov1listers.BackupStorageLocationListe
|
|||
// should be used when running a restic command for an S3 backend. This list is
|
||||
// the current environment, plus the AWS-specific variables restic needs, namely
|
||||
// a credential profile.
|
||||
func S3CmdEnv(backupLocationLister velerov1listers.BackupStorageLocationLister, namespace, backupLocation string) ([]string, error) {
|
||||
loc, err := backupLocationLister.BackupStorageLocations(namespace).Get(backupLocation)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error getting backup storage location")
|
||||
func S3CmdEnv(client kbclient.Client, namespace, backupLocation string) ([]string, error) {
|
||||
loc := &velerov1api.BackupStorageLocation{}
|
||||
if err := client.Get(context.Background(), kbclient.ObjectKey{
|
||||
Namespace: namespace,
|
||||
Name: backupLocation,
|
||||
}, loc); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
awsVars, err := getS3ResticEnvVars(loc.Spec.Config)
|
||||
|
|
|
@ -17,18 +17,22 @@ limitations under the License.
|
|||
package restic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
k8sfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
|
@ -381,10 +385,8 @@ func TestTempCredentialsFile(t *testing.T) {
|
|||
|
||||
func TestTempCACertFile(t *testing.T) {
|
||||
var (
|
||||
bslInformer = cache.NewSharedIndexInformer(nil, new(velerov1api.BackupStorageLocation), 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
|
||||
bslLister = velerov1listers.NewBackupStorageLocationLister(bslInformer.GetIndexer())
|
||||
fs = velerotest.NewFakeFileSystem()
|
||||
bsl = &velerov1api.BackupStorageLocation{
|
||||
fs = velerotest.NewFakeFileSystem()
|
||||
bsl = &velerov1api.BackupStorageLocation{
|
||||
TypeMeta: metav1.TypeMeta{},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "velero",
|
||||
|
@ -398,15 +400,11 @@ func TestTempCACertFile(t *testing.T) {
|
|||
}
|
||||
)
|
||||
|
||||
// bsl not in lister: expect an error
|
||||
caCert, err := GetCACert(bslLister, "velero", "default")
|
||||
assert.Error(t, err)
|
||||
fakeClient := newFakeClient(t)
|
||||
fakeClient.Create(context.Background(), bsl)
|
||||
|
||||
// now add bsl to lister
|
||||
require.NoError(t, bslInformer.GetStore().Add(bsl))
|
||||
|
||||
// bsl in lister: expect temp file to be created with cacert value
|
||||
caCert, err = GetCACert(bslLister, "velero", "default")
|
||||
// expect temp file to be created with cacert value
|
||||
caCert, err := GetCACert(fakeClient, bsl.Namespace, bsl.Name)
|
||||
require.NoError(t, err)
|
||||
|
||||
fileName, err := TempCACertFile(caCert, "default", fs)
|
||||
|
@ -521,3 +519,9 @@ func TestGetPodVolumesUsingRestic(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func newFakeClient(t *testing.T, initObjs ...runtime.Object) client.Client {
|
||||
err := velerov1api.AddToScheme(scheme.Scheme)
|
||||
require.NoError(t, err)
|
||||
return k8sfake.NewFakeClientWithScheme(scheme.Scheme, initObjs...)
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import (
|
|||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
|
||||
kbclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
|
@ -79,20 +81,19 @@ type RestorerFactory interface {
|
|||
}
|
||||
|
||||
type repositoryManager struct {
|
||||
namespace string
|
||||
veleroClient clientset.Interface
|
||||
secretsLister corev1listers.SecretLister
|
||||
repoLister velerov1listers.ResticRepositoryLister
|
||||
repoInformerSynced cache.InformerSynced
|
||||
backupLocationLister velerov1listers.BackupStorageLocationLister
|
||||
backupLocationInformerSynced cache.InformerSynced
|
||||
log logrus.FieldLogger
|
||||
repoLocker *repoLocker
|
||||
repoEnsurer *repositoryEnsurer
|
||||
fileSystem filesystem.Interface
|
||||
ctx context.Context
|
||||
pvcClient corev1client.PersistentVolumeClaimsGetter
|
||||
pvClient corev1client.PersistentVolumesGetter
|
||||
namespace string
|
||||
veleroClient clientset.Interface
|
||||
secretsLister corev1listers.SecretLister
|
||||
repoLister velerov1listers.ResticRepositoryLister
|
||||
repoInformerSynced cache.InformerSynced
|
||||
kbClient kbclient.Client
|
||||
log logrus.FieldLogger
|
||||
repoLocker *repoLocker
|
||||
repoEnsurer *repositoryEnsurer
|
||||
fileSystem filesystem.Interface
|
||||
ctx context.Context
|
||||
pvcClient corev1client.PersistentVolumeClaimsGetter
|
||||
pvClient corev1client.PersistentVolumesGetter
|
||||
}
|
||||
|
||||
// NewRepositoryManager constructs a RepositoryManager.
|
||||
|
@ -103,23 +104,22 @@ func NewRepositoryManager(
|
|||
secretsInformer cache.SharedIndexInformer,
|
||||
repoInformer velerov1informers.ResticRepositoryInformer,
|
||||
repoClient velerov1client.ResticRepositoriesGetter,
|
||||
backupLocationInformer velerov1informers.BackupStorageLocationInformer,
|
||||
kbClient kbclient.Client,
|
||||
pvcClient corev1client.PersistentVolumeClaimsGetter,
|
||||
pvClient corev1client.PersistentVolumesGetter,
|
||||
log logrus.FieldLogger,
|
||||
) (RepositoryManager, error) {
|
||||
rm := &repositoryManager{
|
||||
namespace: namespace,
|
||||
veleroClient: veleroClient,
|
||||
secretsLister: corev1listers.NewSecretLister(secretsInformer.GetIndexer()),
|
||||
repoLister: repoInformer.Lister(),
|
||||
repoInformerSynced: repoInformer.Informer().HasSynced,
|
||||
backupLocationLister: backupLocationInformer.Lister(),
|
||||
backupLocationInformerSynced: backupLocationInformer.Informer().HasSynced,
|
||||
pvcClient: pvcClient,
|
||||
pvClient: pvClient,
|
||||
log: log,
|
||||
ctx: ctx,
|
||||
namespace: namespace,
|
||||
veleroClient: veleroClient,
|
||||
secretsLister: corev1listers.NewSecretLister(secretsInformer.GetIndexer()),
|
||||
repoLister: repoInformer.Lister(),
|
||||
repoInformerSynced: repoInformer.Informer().HasSynced,
|
||||
kbClient: kbClient,
|
||||
pvcClient: pvcClient,
|
||||
pvClient: pvClient,
|
||||
log: log,
|
||||
ctx: ctx,
|
||||
|
||||
repoLocker: newRepoLocker(),
|
||||
repoEnsurer: newRepositoryEnsurer(repoInformer, repoClient, log),
|
||||
|
@ -245,10 +245,11 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error {
|
|||
cmd.PasswordFile = file
|
||||
|
||||
// if there's a caCert on the ObjectStorage, write it to disk so that it can be passed to restic
|
||||
caCert, err := GetCACert(rm.backupLocationLister, rm.namespace, backupLocation)
|
||||
caCert, err := GetCACert(rm.kbClient, rm.namespace, backupLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var caCertFile string
|
||||
if caCert != nil {
|
||||
caCertFile, err = TempCACertFile(caCert, backupLocation, rm.fileSystem)
|
||||
|
@ -261,21 +262,13 @@ func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error {
|
|||
cmd.CACertFile = caCertFile
|
||||
|
||||
if strings.HasPrefix(cmd.RepoIdentifier, "azure") {
|
||||
if !cache.WaitForCacheSync(rm.ctx.Done(), rm.backupLocationInformerSynced) {
|
||||
return errors.New("timed out waiting for cache to sync")
|
||||
}
|
||||
|
||||
env, err := AzureCmdEnv(rm.backupLocationLister, rm.namespace, backupLocation)
|
||||
env, err := AzureCmdEnv(rm.kbClient, rm.namespace, backupLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cmd.Env = env
|
||||
} else if strings.HasPrefix(cmd.RepoIdentifier, "s3") {
|
||||
if !cache.WaitForCacheSync(rm.ctx.Done(), rm.backupLocationInformerSynced) {
|
||||
return errors.New("timed out waiting for cache to sync")
|
||||
}
|
||||
|
||||
env, err := S3CmdEnv(rm.backupLocationLister, rm.namespace, backupLocation)
|
||||
env, err := S3CmdEnv(rm.kbClient, rm.namespace, backupLocation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -53,8 +53,8 @@ Example:
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
|
||||
velerov1api ""github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
)
|
||||
|
||||
|
|
|
@ -43,8 +43,7 @@ Example:
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
|
||||
velerov1api ""github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
)
|
||||
|
||||
|
|
|
@ -43,8 +43,8 @@ Example:
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
|
||||
velerov1api ""github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
)
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@ Example:
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
corev1listers "k8s.io/client-go/listers/core/v1"
|
||||
|
||||
velerov1api ""github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1"
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue