From 9219e588d9f1b8a385d15dc963af5b3f3625ccbe Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Fri, 5 Apr 2024 13:53:15 -0400 Subject: [PATCH 01/69] Add design for velero backup performance improvements Signed-off-by: Scott Seago --- design/backup-performance-improvements.md | 358 ++++++++++++++++++++++ 1 file changed, 358 insertions(+) create mode 100644 design/backup-performance-improvements.md diff --git a/design/backup-performance-improvements.md b/design/backup-performance-improvements.md new file mode 100644 index 000000000..41d08e4fd --- /dev/null +++ b/design/backup-performance-improvements.md @@ -0,0 +1,358 @@ +# Velero Backup performance Improvements and VolumeGroupSnapshot enablement + +There are two different goals here, linked by a single primary missing feature in the Velero backup workflow. +The first goal is to enhance backup performance by allowing the primary backup controller to run in multiple threads, enabling Velero to back up multiple items at the same time for a given backup. +The second goal is to enable Velero to eventually support VolumeGroupSnapshots. +For both of these goals, Velero needs a way to determine which items should be backed up together. + +This design proposal will include two development phases: +- Phase 1 will refactor the backup workflow to identify blocks of items that should be backed up together, and then coordinate backup hooks among items in the block. +- Phase 2 will add multiple multiple worker threads for backing up item blocks, so instead of backing up each block as it identified, the velero backup workflow will instead add the block to a channel and one of the workers will pick it up. +- Actual support for VolumeGroupSnapshots is out-of-scope here and will be handled in a future design proposal, but the item block refactor introduced in Phase 1 is a primary building block for this future proposal. + +## Background +Currently, during backup processing, the main Velero backup controller runs in a single thread, completely finishing the primary backup processing for one resource before moving on to the next one. +We can improve the overall backup performance by backing up multiple items for a backup at the same time, but before we can do this we must first identify resources that need to be backed up together. +As part of this initial refactoring, once these "Item Blocks" are identified, an additional change will be to move pod hook processing up to the ItemBlock level. +If there are multiple pods in the ItemBlock, pre-hooks for all pods will be run before backing up the items, followed by post-hooks for all pods. +This change to hook processing is another prerequisite for future VolumeGroupSnapshot support, since supporting this will require backing up the pods and volumes together for any volumes which belong to the same group. +Once we are backing up items by block, the next step will be to create multiple worker threads to process and back up ItemBlocks, so that we can back up multiple ItemBlocks at the same time. + +In looking at the different kinds of large backups that Velero must deal with, two obvious scenarios come to mind: +1. Backups with a relatively small number of large volumes +2. Backups with a large number of relatively small volumes. + +In case 1, the majority of the time spent on the backup is in the asynchronous phases -- CSI snapshot creation actions after the snaphandle exists, and DataUpload processing. In that case, parallel item processing will likely have a minimal impact on overall backup completion time. + +In case 2, the majority of time spent on the backup will likely be during the synchronous actions. Especially as regards CSI snapshot creation, the waiting for the VSC snaphandle to exist will result in significant passage of time with thousands of volumes. This is the sort of use case which will benefit the most from parallel item processing. + +## Goals +- Identify groups of items to back up together (ItemBlocks). +- Manage backup hooks at the ItemBlock level rather than per-item. +- Using worker threads, back up ItemBlocks at the same time. + +## Non Goals +- Support VolumeGroupSnapshots: this is a future feature, although certain prerequisites for this enhancement are included in this proposal. +- Process multiple backups in parallel: this is a future feature, although certain prerequisites for this enhancement are included in this proposal. +- Refactoring plugin infrastructure to avoid RPC calls for internal plugins. + +## High-Level Design + +### Phase 1: ItemBlock processing +- A new BIA method, `GetAdditionalItems`, will be needed for pre-processing ItemBlocks (this will require a new BIAv3 API). +- When processing the list of items returned from the item collector, instead of simply calling `BackupItem` on each in turn, we will use the `GetAdditionalItems` BIAv3 API call to determine other items to include with the current item in an ItemBlock. Repeat recursively on each item returned. +- Don't include an item in more than one ItemBlock -- if the next item from the item collector is already in a block, skip it. +- Once ItemBlock is determined, call new func `BackupItemBlock` instead of `BackupItem`. +- New func `BackupItemBlock` will call pre hooks for any pods in the block, then back up the items in the block (`BackupItem` will no longer run hooks directly), then call post hooks for any pods in the block. + +### Phase 2: Process ItemBlocks for a single backup in multiple threads +- Concurrent `BackupItemBlock` operations will be executed by worker threads invoked by the backup controller, which will communicate with the backup controller operation via a shared channel. +- The ItemBlock processing loop implemented in Phase 1 will be modified to send each newly-created ItemBlock to the shared channel rather than calling `BackupItemBlock` inline. +- Users will be able to configure the number of workers available for concurrent `BackupItemBlock` operations. +- Access to the BackedUpItems map must be synchronized + +## Detailed Design + +### Phase 1: ItemBlock processing + +#### BackupItemAction plugin changes + +In order for Velero to identify groups of items to back up together in an ItemBlock, we need a way to identify items which need to be backed up along with the current item. While the current `Execute` BackupItemAction method does return a list of additional items which are required by the current item, we need to know this *before* we start the item backup. To support this, we need a new API method, `GetAdditionalItems` which Velero will call on each item as it processes it for an ItemBlock. The expectation is that this method will return the same items as currently returned as additional items by the current `Execute` method, with the exception that items which are not created until calling `Execute` should not be returned here, as they don't exist yet. + +#### Proto changes (compiled into golang by protoc) + +The BackupItemAction service gets one new rpc method: +``` +service BackupItemAction { + rpc GetAdditionalItems(BackupItemActionGetAdditionalItemsRequest) returns (BackupItemActionGetAdditionalItemsResponse); +} +``` + +To support this new rpc method, we define new request/response message types: +``` +message BackupItemActionAdditionalItemsRequest { + string plugin = 1; + bytes item = 2; + bytes backup = 3; +} + +message BackupItemActionAdditionalItemsResponse { + repeated generated.ResourceIdentifier additionalItems = 1; +} +``` + +A new PluginKind, `BackupItemActionV3`, will be created, and the backup process will be modified to use this plugin kind. Unlike with the V1->V2 transition, however, we will not provide a V3 adapter for BIAv2, because there is no reliable way to know what items to return from the new method. Instead, Velero will only invoke the new `GetAdditionalItems` method if *all* registered plugins are V3. If there are any V2 plugins registered, then we will continue to use the BIAv2 API, and process item blocks with only one item. If there are both V2 and V3 plugins, we will adapt (i.e. downgrade) V3 plugins to V2 rather than vice versa. In order for Velero to support item block processing out of the box, all existing V1 and V2 plugins in velero core and supported plugins will need to be converted to V3 as part of implementation. If we are adapting to V2 plugins, then velero will *not* call the new V3 plugin API method, and every item returned from the Item Collector will be in its own ItemBlock of one. + +The compatibility mode fallback to using BIAv2 is necessary, because without complete information about inter-item dependencies provided by the new BIAv3 method, we run the risk of breaking these dependencies when backing up items in parallel. To give a specific example, if the Pod BIA was not upgraded to implement the new method, then we would have no way of knowing when generating ItemBlocks that Pods must be backed up with the PVCs they mount. As a result, for a backup with a single pod and single pvc, the pod might end up in itemBlock1, and its pvc in itemBlock2. Now the two item blocks get sent to separate workers to get backed up. Since pod and related PVC are being backed up separately, it's possible that the PVC backup operation will begin before the pod backup hook is started. On the other hand, it's also possible that the pod post hook will run before the PVC is backed up. If we recognize that we have a v1/v2 plugin, then we can only run a single worker goroutine, which will force the incomplete ItemBlocks to run sequentially, which will resolve this difficulty. + +### Changes to processing item list from the Item Collector + +#### New structs ItemBlock and ItemBlockItem +``` +type ItemBlock struct { + log logrus.FieldLogger + itemBackupper *itemBackupper + Items []ItemBlockItem +} + +type ItemBlockItem struct { + gr schema.GroupResource + item *unstructured.Unstructured + preferredGVR schema.GroupVersionResource +} +``` + +#### Current workflow +In the `BackupWithResolvers` func, the current Velero implementation iterates over the list of items for backup returned by the Item Collector. For each item, Velero loads the item from the file created by the Item Collector, we call `backupItem`, update the GR map if successful, remove the (temporary) file containing item metadata, and update progress for the backup. + +#### Modifications to the loop over ItemCollector results +The `kubernetesResource` struct used by the item collector will be modified to add an `orderedResource` bool which will be set true for all of the resources moved to the beginning of the list as a result of being ordered resources. + +The current workflow within each iteration of the ItemCollector.items loop will replaced with the following: +- (note that some of the below should be pulled out into a helper func to facilitate recursive call to it for items returned from `GetAdditonalItems`.) +- Before loop iteration, create a new `itemsInBlock` map of type map[velero.ResourceIdentifier]bool which represents the set of items already included in a block. +- If `item` is already in `itemsInBlock`, continue. This one has already been processed. +- Add `item` to `itemsInBlock`. +- Load item from ItemCollector file. Close/remove file after loading (on error return or not, possibly with similar anonymous func to current impl) +- Get matching BIA plugins for item, call `GetAdditionalItems` for each. For each item returned, get full item content from ItemCollector (if present in item list, pulling from file, removing file when done) or from cluster (if not present in item list), add item to the current block, add item to `itemsInBlock` map, and then recursively apply current step to each (i.e. call BIA method, add to block, etc.) +- Once full ItemBlock list is generated, call `backupItemBlock(block ItemBlock) +- Add `backupItemBlock` return values to `backedUpGroupResources` map + +Note that if there are BIAv2 plugins present, we will downgrade all v3 plugins to v2, meaning that we will not have a `GetAdditionalItems` func to call. In this case, we will make the following change to the ItemCollector iteration workflow described above: +- At the point where we call `GetAdditionalItems` on the item's registered plugins, we will not call this. Instead, we will treat the item as if it had returned no additional items. The ItemBlock will only have one entry. + +#### New func `backupItemBlock` + +Method signature for new func `backupItemBlock` is as follows: +``` +func backupItemBlock(block ItemBlock) []schema.GroupResource +``` +The return value is a slice of GRs for resources which were backed up. Velero tracks these to determine which CRDs need to be included in the backup. Note that we need to make sure we include in this not only those resources that were backed up directly, but also those backed up indirectly via additional items BIA execute returns. + +In order to handle backup hooks, this func will first take the input item list (`block.items`) and get a list of included pods, filtered to include only those not yet backed up (using `block.itemBackupper.backupRequest.BackedUpItems`). Iterate over this list and execute pre hooks (pulled out of `itemBackupper.backupItemInternal`) for each item. +Now iterate over the full list (`block.items`) and call `backupItem` for each. After the first, the later items should already have been backed up, but calling a second time is harmless, since the first thing Velero does is check the `BackedUpItems` map, exiting if item is already backed up). We still need this call in case there's a plugin which returns something in `GetAdditionalItems` but forgets to return it in the `Execute` additional items return value. If we don't do this, we could end up missing items. + +After backing up the items in the block, we now execute post hooks using the same filtered item list we used for pre hooks, again taking the logic from `itemBackupper.backupItemInternal`). + +#### `itemBackupper.backupItemInternal` cleanup + +After implementing backup hooks in `backupItemBlock`, hook processing should be removed from `itemBackupper.backupItemInternal`. + +### Phase 2: Process ItemBlocks for a single backup in multiple threads + +#### New input field for number of ItemBlock workers + +The velero installer and server CLIs will get a new input field `itemBlockWorkerCount`, which will be passed along to the `backupReconciler`. +The `backupReconciler` struct will also have this new field added. + +#### Worker pool for item block processing + +A new type, `ItemBlockWorker` will be added which will manage a pool of worker goroutines which will process item blocks, a shared input channel for passing blocks to workers, and a WaitGroup to shut down cleanly when the reconciler exits. +``` +type ItemBlockWorkerPool struct { + itemBlockChannel chan ItemBlockInput + wg *sync.WaitGroup + logger logrus.FieldLogger +} + +type ItemBlockInput struct { + itemBlock ItemBlock + returnChan chan ItemBlockReturn +} + +type ItemBlockReturn struct { + itemBlock ItemBlock + resources []schema.GroupResource + err error +} + +func (*p ItemBlockWorkerPool) getInputChannel() chan ItemBlockInput +func RunItemBlockWorkers(context context.Context, workers int) +func processItemBlocksWorker(context context.Context, itemBlockChannel chan ItemBlockInput, logger logrus.FieldLogger, wg *sync.WaitGroup) +``` + +The worker pool will be started by calling `RunItemBlockWorkers` in `backupReconciler.SetupWithManager`, passing in the worker count and reconciler context. +`SetupWithManager` will also add the input channel to the `itemBackupper` so that it will be available during backup processing. +The func `RunItemBlockWorkers` will create the `ItemBlockWorkerPool` with a shared buffered input channel (fixed buffer size) and start `workers` gororoutines which will each call `processItemBlocksWorker`. +The `processItemBlocksWorker` func (run by the worker goroutines) will read from `itemBlockChannel`, call `BackupItemBlock` on the retrieved `ItemBlock`, and then send the return value to the retrieved `returnChan`, and then process the next block. + +#### Modify ItemBlock processing loop to send ItemBlocks to the worker pool rather than backing them up directly + +The ItemBlock processing loop implemented in Phase 1 will be modified to send each newly-created ItemBlock to the shared channel rather than calling `BackupItemBlock` inline, using a WaitGroup to manage in-process items. A separate goroutine will be created to process returns for this backup. After completion of the ItemBlock processing loop, velero will use the WaitGroup to wait for all ItemBlock processing to complete before moving forward. + +A simplified example of what this response goroutine might look like: +``` + // omitting cancel handling, context, etc + ret := make(chan ItemBlockReturn) + wg := &sync.WaitGroup{} + // Handle returns + go func() { + for { + select { + case response := <-ret: // process each BackupItemBlock response + func() { + defer wg.Done() + responses = append(responses, response) + }() + case <-ctx.Done(): + return + } + } + }() + // Simplified illustration, looping over and assumed already-determined ItemBlock list + for _, itemBlock := range itemBlocks { + wg.Add(1) + inputChan <- ItemBlockInput{itemBlock: itemBlock, returnChan: ret} + } + done := make(chan struct{}) + go func() { + defer close(done) + wg.Wait() + }() + // Wait for all the ItemBlocks to be processed + select { + case <-done: + logger.Info("done processing ItemBlocks") + } + // responses from BackupItemBlock calls are in responses +``` + +The ItemBlock processing loop described above will be split into two separate iterations. For the first iteration, velero will only process those items at the beginning of the loop identified as `orderedResources` -- when the groups generated from these resources are passed to the worker channel, velero will wait for the response before moving on to the next ItemBlock. This is to ensure that the ordered resources are processed in the required order. Once the last ordered resource is processed, the remaining ItemBlocks will be processed and sent to the worker channel without waiting for a response, in order to allow these ItemBlocks to be processed in parallel. + +#### Synchronize access to the BackedUpItems map + +Velero uses a map of BackedUpItems to track which items have already been backed up. This prevents velero from attempting to back up an item more than once, as well as guarding against creating infinite loops due to circular dependencies in the additional items returns. Since velero will now be accessing this map from the parallel goroutines, access to the map must be synchronized with mutexes. + +#### V3 vs V1/2 BackupItemAction plugins registered + +Full item block functionality is only possible if all registered BIA plugins implement the v3 interface. In phase 1 work, if any v1 or v2 plugins are registered, then v3 plugins are adapted to v2 and we treat every item returned from the collector as an ItemBlock of one. Because we are not able to track inter-item dependencies in this operation mode, we will also need to ensure that only one worker is active in processing these ItemBlocks of size 1 -- since we're not tracking dependencies, we must process items in the order returned by the collector to ensure that PVCs are backed up with their Pods, etc. Therefore, the worker count will be 1, regardless of configuration, in this scenario. A warning should be logged when starting the worker pool when this happens. + +## Alternatives considered + +### Per-backup worker pool + +The current design makes use of a permanent worker pool, started at backup controller startup time. With this design, when we follow on with running multiple backups in parallel, the same set of workers will take ItemBlock inputs from more than one backup. Another approach that was initially considered was a temporary worker pool, created while processing a backup, and deleted upon backup completion. + +#### User-visible API differences between the two approaches + +The main user-visible difference here is in the configuration API. For the permanent worker approach, the worker count represents the total worker count for all backups. The concurrent backup count represents the number of backups running at the same time. At any given time, though, the maximum number of worker threads backing up items concurrently is equal to the worker count. If worker count is 15 and the concurrent backup count is 3, then there will be, at most, 15 items being processed at the same time, split among up to three running backups. + +For the per-backup worker approach, the worker count represents the worker count for each backup. The concurrent backup count, as before, represents the number of backups running at the same time. If worker count is 15 and the concurrent backup count is 3, then there will be, at most, 45 items being processed at the same time, up to 15 for each of up to three running backups. +#### Comparison of the two approaches + +- Permanent worker pool advantages: + - This is the more commonly-followed Kubernetes pattern. It's generally better to follow standard practices, unless there are genuine reasons for the use case to go in a different way. + - It's easier for users to understand the maximum number of concurrent items processed, which will have performance impact and impact on the resource requirements for the Velero pod. Users will not have to multiply the config numbers in their heads when working out how many total workers are present. + - It will give us more flexibility for future enhancements around concurrent backups. One possible use case: backup priority. Maybe a user wants scheduled backups to have a lower priority than user-generated backups, since a user is sitting there waiting for completion -- a shared worker pool could react to the priority by taking ItemBlocks for the higher priority backup first, which would allow a large lower-priority backup's items to be preempted by a higher-priority backup's items without needing to explicitly stop the main controller flow for that backup. +- Per-backup worker pool advantages: + - Lower memory consumption than permanent worker pool, but the total memory used by a worker blocked on input will be pretty low, so if we're talking only 10-20 workers, the impact will be minimal. + +## Compatibility + +Because V1 and V2 BIA plugins do not provide the new `GetAdditionalItems` call and there is no reasonable default value, they cannot be adapted to V3. +An empty list of items would result in nothing added to the current ItemBlock, which would miss required additional items returned by `Execute`, which could result in invalid backup data if associated items end up backed up in parallel with each other. +The ability to back up items in parallel (and, eventually, the ability to make use of VolumeGroupSnapshots) depends on *every* registered BIA plugin being V3 or later. +Any V1/V2 plugins registered will result in equivalent performance to current Velero -- a single worker goroutine, and ItemBlocks of exactly one item. + +A specific example to illustrate why the v2 fallback is required: if the Pod BIA was not upgraded to implement the new method, then we would have no way of knowing when generating ItemBlocks that Pods must be backed up with the PVCs they mount. As a result, for a backup with a single pod and single pvc, the pod might end up in itemBlock1, and its pvc in itemBlock2. Now the two item blocks get sent to separate workers to get backed up. Since pod and related PVC are being backed up separately, it's possible that the PVC backup operation will begin before the pod backup hook is started. On the other hand, it's also possible that the pod post hook will run before the PVC is backed up. If we recognize that we have a v1/v2 plugin, then we can only run a single worker goroutine, which will force the incomplete ItemBlocks to run sequentially, which will resolve this difficulty. + +In order to ensure that the new functionality works out of the box for anyone using only supported Velero plugins, phase 1 implementation must include upgrading all supported BIA plugins to V3. +Since the CSI plugin is moving back into the main Velero repo starting with Velero 1.14, all of this should be internal to the main velero repo, as the supported storage plugins do not implement BackupItemActions. + +### Example upgrade to BIAv3 + +Included below is an example of what might be required to upgrade a v1 plugin which returns additional items to BIAv3. +The code is taken from the internal velero `pod_action.go` which identifies the items required for a given pod. + +Basically, the required changes are as follows: +- (for v1 plugins) Implement `Name()`. This returns a string. The content isn't particularly important, since Velero doesn't actually make an RPC call for this method, but it must be defined on the type in order to implement the interface. +- (for v1 plugins) Implement `Progress(`). Since the plugin doesn't have any asynchronous operations (or it would already be a v2 plugin), this should just return the error `biav2.AsyncOperationsNotSupportedError()`. +- (for v1 plugins) Implement `Cancel(`). Since the plugin doesn't have any asynchronous operations (or it would already be a v2 plugin), this should just return nil. +- (for v1 or v2 plugins) Implement `GetAdditionalItems()` If the additionalItems return value from `Execute()` is nil (or only returns items newly-created in Execute()), it should return nil. Otherwise, it should return the same items as `Execute()` minus any items that don't exist yet. In the example below, this was done by putting the additional item list generation code into `GetAdditionalItems()` and refactoring `Execute()` to call the new func to get the list. For plugins which return a combination of already-existing and newly-created items, `GetAdditionalItems()` should generate the already-existing list, and `Execute()` should append the newly-created items to the list. +- When registering the BIA, replace `RegisterBackupItemAction` (for v1 plugins) or `RegisterBackupItemActionV2` (for v2 plugins) with `RegisterBackupItemActionV3` + +```diff --git a/pkg/backup/actions/pod_action.go b/pkg/backup/actions/pod_action.go +index ce6b1ade8..5625dcb5b 100644 +--- a/pkg/backup/actions/pod_action.go ++++ b/pkg/backup/actions/pod_action.go +@@ -25,6 +25,7 @@ import ( + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" ++ biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" + ) + + // PodAction implements ItemAction. +@@ -47,13 +48,19 @@ func (a *PodAction) AppliesTo() (velero.ResourceSelector, error) { + // Execute scans the pod's spec.volumes for persistentVolumeClaim volumes and returns a + // ResourceIdentifier list containing references to all of the persistentVolumeClaim volumes used by + // the pod. This ensures that when a pod is backed up, all referenced PVCs are backed up too. +-func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { ++func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, string, []velero.ResourceIdentifier, error) { + a.log.Info("Executing podAction") + defer a.log.Info("Done executing podAction") + ++ additionalItems, err := a.GetAdditionalItems(item, backup) ++ return item, additionalItems, "", nil, err ++} ++ ++// v3 API call, return nil if no additional items for this resource ++func (a *PodAction) GetAdditionalItems(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { + pod := new(corev1api.Pod) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil { +- return nil, nil, errors.WithStack(err) ++ return nil, errors.WithStack(err) + } + + var additionalItems []velero.ResourceIdentifier +@@ -67,7 +74,7 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti + + if len(pod.Spec.Volumes) == 0 { + a.log.Info("pod has no volumes") +- return item, additionalItems, nil ++ return additionalItems, nil + } + + for _, volume := range pod.Spec.Volumes { +@@ -82,5 +89,20 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti + } + } + +- return item, additionalItems, nil ++ return additionalItems, nil ++} ++ ++// v2 API call ++func (a *PodAction) Name() string { ++ return "PodAction" ++} ++ ++// v2 API call, just return this error for BIAs without async operations ++func (a *PodAction) Progress(operationID string, backup *velerov1api.Backup) (velero.OperationProgress, error) { ++ return velero.OperationProgress{}, biav2.AsyncOperationsNotSupportedError() ++} ++ ++// v2 API call, just return nil for BIAs without async operations ++func (a *PodAction) Cancel(operationID string, backup *velerov1api.Backup) error { ++ return nil + } +diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go +index c375c5437..7717443dc 100644 +--- a/pkg/cmd/server/plugin/plugin.go ++++ b/pkg/cmd/server/plugin/plugin.go +@@ -42,7 +42,7 @@ func NewCommand(f client.Factory) *cobra.Command { + Run: func(c *cobra.Command, args []string) { + pluginServer = pluginServer. + RegisterBackupItemAction("velero.io/pv", newPVBackupItemAction). +- RegisterBackupItemAction("velero.io/pod", newPodBackupItemAction). ++ RegisterBackupItemActionV3("velero.io/pod", newPodBackupItemAction). + RegisterBackupItemAction("velero.io/service-account", newServiceAccountBackupItemAction(f)). + RegisterRestoreItemAction("velero.io/job", newJobRestoreItemAction). + RegisterRestoreItemAction("velero.io/pod", newPodRestoreItemAction). +``` + + +## Implementation +Phase 1 and Phase 2 could be implemented within the same Velero release cycle, but they need not be. +Phase 1 is expected to be implemented in Velero 1.15. +Phase 2 could either be in 1.15 as well, or in a later release, depending on the release timing and resource availability. From 7873ced0f1cec957adbc9583db6829b52dea91e7 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Wed, 24 Apr 2024 17:30:41 -0400 Subject: [PATCH 02/69] updated design to remove biav3 requirement for everything, added alternatives Signed-off-by: Scott Seago --- design/backup-performance-improvements.md | 35 +++++++++++------------ 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/design/backup-performance-improvements.md b/design/backup-performance-improvements.md index 41d08e4fd..0257d5801 100644 --- a/design/backup-performance-improvements.md +++ b/design/backup-performance-improvements.md @@ -7,7 +7,7 @@ For both of these goals, Velero needs a way to determine which items should be b This design proposal will include two development phases: - Phase 1 will refactor the backup workflow to identify blocks of items that should be backed up together, and then coordinate backup hooks among items in the block. -- Phase 2 will add multiple multiple worker threads for backing up item blocks, so instead of backing up each block as it identified, the velero backup workflow will instead add the block to a channel and one of the workers will pick it up. +- Phase 2 will add multiple worker threads for backing up item blocks, so instead of backing up each block as it identified, the velero backup workflow will instead add the block to a channel and one of the workers will pick it up. - Actual support for VolumeGroupSnapshots is out-of-scope here and will be handled in a future design proposal, but the item block refactor introduced in Phase 1 is a primary building block for this future proposal. ## Background @@ -81,9 +81,9 @@ message BackupItemActionAdditionalItemsResponse { } ``` -A new PluginKind, `BackupItemActionV3`, will be created, and the backup process will be modified to use this plugin kind. Unlike with the V1->V2 transition, however, we will not provide a V3 adapter for BIAv2, because there is no reliable way to know what items to return from the new method. Instead, Velero will only invoke the new `GetAdditionalItems` method if *all* registered plugins are V3. If there are any V2 plugins registered, then we will continue to use the BIAv2 API, and process item blocks with only one item. If there are both V2 and V3 plugins, we will adapt (i.e. downgrade) V3 plugins to V2 rather than vice versa. In order for Velero to support item block processing out of the box, all existing V1 and V2 plugins in velero core and supported plugins will need to be converted to V3 as part of implementation. If we are adapting to V2 plugins, then velero will *not* call the new V3 plugin API method, and every item returned from the Item Collector will be in its own ItemBlock of one. +A new PluginKind, `BackupItemActionV3`, will be created, and the backup process will be modified to use this plugin kind. Existing v1/v2 plugins will be adapted to v3 with an empty `GetAdditionalItems` method, meaning that those plugins will not add anything to the ItemBlock for the item being backed up. -The compatibility mode fallback to using BIAv2 is necessary, because without complete information about inter-item dependencies provided by the new BIAv3 method, we run the risk of breaking these dependencies when backing up items in parallel. To give a specific example, if the Pod BIA was not upgraded to implement the new method, then we would have no way of knowing when generating ItemBlocks that Pods must be backed up with the PVCs they mount. As a result, for a backup with a single pod and single pvc, the pod might end up in itemBlock1, and its pvc in itemBlock2. Now the two item blocks get sent to separate workers to get backed up. Since pod and related PVC are being backed up separately, it's possible that the PVC backup operation will begin before the pod backup hook is started. On the other hand, it's also possible that the pod post hook will run before the PVC is backed up. If we recognize that we have a v1/v2 plugin, then we can only run a single worker goroutine, which will force the incomplete ItemBlocks to run sequentially, which will resolve this difficulty. +Any BIA plugins which return additional items from `Execute()` that need to be backed up at the same time or sequentially in the same worker thread as the current items need to be upgraded to v3. This mainly applies to plugins that operate on pods which reference resources which must be backed up along with the pod and are potentially affected by pod hooks or for plugins which connect multiple pods whose volumes should be backed up at the same time. ### Changes to processing item list from the Item Collector @@ -118,8 +118,6 @@ The current workflow within each iteration of the ItemCollector.items loop will - Once full ItemBlock list is generated, call `backupItemBlock(block ItemBlock) - Add `backupItemBlock` return values to `backedUpGroupResources` map -Note that if there are BIAv2 plugins present, we will downgrade all v3 plugins to v2, meaning that we will not have a `GetAdditionalItems` func to call. In this case, we will make the following change to the ItemCollector iteration workflow described above: -- At the point where we call `GetAdditionalItems` on the item's registered plugins, we will not call this. Instead, we will treat the item as if it had returned no additional items. The ItemBlock will only have one entry. #### New func `backupItemBlock` @@ -223,12 +221,21 @@ The ItemBlock processing loop described above will be split into two separate it Velero uses a map of BackedUpItems to track which items have already been backed up. This prevents velero from attempting to back up an item more than once, as well as guarding against creating infinite loops due to circular dependencies in the additional items returns. Since velero will now be accessing this map from the parallel goroutines, access to the map must be synchronized with mutexes. -#### V3 vs V1/2 BackupItemAction plugins registered - -Full item block functionality is only possible if all registered BIA plugins implement the v3 interface. In phase 1 work, if any v1 or v2 plugins are registered, then v3 plugins are adapted to v2 and we treat every item returned from the collector as an ItemBlock of one. Because we are not able to track inter-item dependencies in this operation mode, we will also need to ensure that only one worker is active in processing these ItemBlocks of size 1 -- since we're not tracking dependencies, we must process items in the order returned by the collector to ensure that PVCs are backed up with their Pods, etc. Therefore, the worker count will be 1, regardless of configuration, in this scenario. A warning should be logged when starting the worker pool when this happens. - ## Alternatives considered +### New ItemBlockAction plugin type + +Instead of adding a new `GetAdditionalItems` method to BackupItemAction, another possibility would be to leave BIA alone and create a new plugin type, ItemBlockAction. +Rather than adding the `GetAdditionalItems` method to the existing BIA plugin type, velero would introduce a new ItemBlockAction plugin type which provides the single `GetAdditionalItems` method, described above as a new BIAv3 method. +For existing BIA implementations which return additional items in `Execute()`, instead of adding the new v3 method (and, if necessary, the missing v2 methods) to the existing BIA, they would create a new ItemBlockAction plugin to provide this method. + +The change to the backup workflow would be relatively simple: resolve registered ItemBlockAction (IBA) plugins as well as BIA plugins, determine for both plugin types which apply to the current resource type, and when calling the new API method, iterate over IBA plugins rather than BIA plugins. + +The change to the "BackupItemAction plugin changes" section would be to create a new plugin type rather than enhancing the existing plugin type. The API function needed in this new plugin type would be identical to the one described as a new BIAv3 method. The other work here would be to create the infrastructure/scaffolding to define, register, etc. the new plugin type. + +Overall, the decision of extending BIA vs. creating a new plugin type won't change much in the design here. +The new plugin type will require more initial work, but ongoing maintenance should be similar between the options. + ### Per-backup worker pool The current design makes use of a permanent worker pool, started at backup controller startup time. With this design, when we follow on with running multiple backups in parallel, the same set of workers will take ItemBlock inputs from more than one backup. Another approach that was initially considered was a temporary worker pool, created while processing a backup, and deleted upon backup completion. @@ -249,16 +256,6 @@ For the per-backup worker approach, the worker count represents the worker count ## Compatibility -Because V1 and V2 BIA plugins do not provide the new `GetAdditionalItems` call and there is no reasonable default value, they cannot be adapted to V3. -An empty list of items would result in nothing added to the current ItemBlock, which would miss required additional items returned by `Execute`, which could result in invalid backup data if associated items end up backed up in parallel with each other. -The ability to back up items in parallel (and, eventually, the ability to make use of VolumeGroupSnapshots) depends on *every* registered BIA plugin being V3 or later. -Any V1/V2 plugins registered will result in equivalent performance to current Velero -- a single worker goroutine, and ItemBlocks of exactly one item. - -A specific example to illustrate why the v2 fallback is required: if the Pod BIA was not upgraded to implement the new method, then we would have no way of knowing when generating ItemBlocks that Pods must be backed up with the PVCs they mount. As a result, for a backup with a single pod and single pvc, the pod might end up in itemBlock1, and its pvc in itemBlock2. Now the two item blocks get sent to separate workers to get backed up. Since pod and related PVC are being backed up separately, it's possible that the PVC backup operation will begin before the pod backup hook is started. On the other hand, it's also possible that the pod post hook will run before the PVC is backed up. If we recognize that we have a v1/v2 plugin, then we can only run a single worker goroutine, which will force the incomplete ItemBlocks to run sequentially, which will resolve this difficulty. - -In order to ensure that the new functionality works out of the box for anyone using only supported Velero plugins, phase 1 implementation must include upgrading all supported BIA plugins to V3. -Since the CSI plugin is moving back into the main Velero repo starting with Velero 1.14, all of this should be internal to the main velero repo, as the supported storage plugins do not implement BackupItemActions. - ### Example upgrade to BIAv3 Included below is an example of what might be required to upgrade a v1 plugin which returns additional items to BIAv3. From 3c2d77f4cfe2319217c4823a791f768fdd7bd25d Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Wed, 8 May 2024 17:24:40 -0400 Subject: [PATCH 03/69] replaced BIAv3 with new ItemBlockAction plugin type Signed-off-by: Scott Seago --- design/backup-performance-improvements.md | 246 +++++++++++----------- 1 file changed, 124 insertions(+), 122 deletions(-) diff --git a/design/backup-performance-improvements.md b/design/backup-performance-improvements.md index 0257d5801..ebd075bee 100644 --- a/design/backup-performance-improvements.md +++ b/design/backup-performance-improvements.md @@ -6,13 +6,14 @@ The second goal is to enable Velero to eventually support VolumeGroupSnapshots. For both of these goals, Velero needs a way to determine which items should be backed up together. This design proposal will include two development phases: -- Phase 1 will refactor the backup workflow to identify blocks of items that should be backed up together, and then coordinate backup hooks among items in the block. +- Phase 1 will refactor the backup workflow to identify blocks of related items that should be backed up together, and then coordinate backup hooks among items in the block. - Phase 2 will add multiple worker threads for backing up item blocks, so instead of backing up each block as it identified, the velero backup workflow will instead add the block to a channel and one of the workers will pick it up. - Actual support for VolumeGroupSnapshots is out-of-scope here and will be handled in a future design proposal, but the item block refactor introduced in Phase 1 is a primary building block for this future proposal. ## Background Currently, during backup processing, the main Velero backup controller runs in a single thread, completely finishing the primary backup processing for one resource before moving on to the next one. We can improve the overall backup performance by backing up multiple items for a backup at the same time, but before we can do this we must first identify resources that need to be backed up together. +Generally speaking, resources that need to be backed up together are resources with interdependencies -- pods with their PVCs, PVCs with their PVs, groups of pods that form a single application, CRs, pods, and other resources that belong to the same operator, etc. As part of this initial refactoring, once these "Item Blocks" are identified, an additional change will be to move pod hook processing up to the ItemBlock level. If there are multiple pods in the ItemBlock, pre-hooks for all pods will be run before backing up the items, followed by post-hooks for all pods. This change to hook processing is another prerequisite for future VolumeGroupSnapshot support, since supporting this will require backing up the pods and volumes together for any volumes which belong to the same group. @@ -27,7 +28,7 @@ In case 1, the majority of the time spent on the backup is in the asynchronous p In case 2, the majority of time spent on the backup will likely be during the synchronous actions. Especially as regards CSI snapshot creation, the waiting for the VSC snaphandle to exist will result in significant passage of time with thousands of volumes. This is the sort of use case which will benefit the most from parallel item processing. ## Goals -- Identify groups of items to back up together (ItemBlocks). +- Identify groups of related items to back up together (ItemBlocks). - Manage backup hooks at the ItemBlock level rather than per-item. - Using worker threads, back up ItemBlocks at the same time. @@ -38,12 +39,25 @@ In case 2, the majority of time spent on the backup will likely be during the sy ## High-Level Design +### ItemBlock concept + +The updated design is based on a new struct/type called `ItemBlock`. +Essentially, an `ItemBlock` is a group of items that must be backed up together in order to guarantee backup integrity. +When we eventually split item backup across multiple worker threads, `ItemBlocks` will be kept together as the basic unit of backup. +To facilitate this, a new plugin type, `ItemBlockAction` will allow relationships between items to be identified by velero -- any resources that must be backed up with other resources will need IBA plugins defined for them. +Examples of `ItemBlocks` include: +1. A pod, its mounted PVCs, and the bound PVs for those PVCs. +2. A VolumeGroup (related PVCs and PVs) along with any pods mounting these volumes. +3. For a ReadWriteMany PVC, the PVC, its bound PV, and all pods mounting this PVC. + ### Phase 1: ItemBlock processing -- A new BIA method, `GetAdditionalItems`, will be needed for pre-processing ItemBlocks (this will require a new BIAv3 API). -- When processing the list of items returned from the item collector, instead of simply calling `BackupItem` on each in turn, we will use the `GetAdditionalItems` BIAv3 API call to determine other items to include with the current item in an ItemBlock. Repeat recursively on each item returned. +- A new plugin type, `ItemBlockAction`, will be created +- `ItemBlockAction` will contain the API method `GetRelatedItems`, which will be needed for determining which items to group together into `ItemBlocks`. +- When processing the list of items returned from the item collector, instead of simply calling `BackupItem` on each in turn, we will use the `GetRelatedItems` API call to determine other items to include with the current item in an ItemBlock. Repeat recursively on each item returned. - Don't include an item in more than one ItemBlock -- if the next item from the item collector is already in a block, skip it. - Once ItemBlock is determined, call new func `BackupItemBlock` instead of `BackupItem`. - New func `BackupItemBlock` will call pre hooks for any pods in the block, then back up the items in the block (`BackupItem` will no longer run hooks directly), then call post hooks for any pods in the block. +- The finalize phase will not be affected by the ItemBlock design, since this is just updating resources after async operations are completed on the items and there is no need to run these updates in parallel. ### Phase 2: Process ItemBlocks for a single backup in multiple threads - Concurrent `BackupItemBlock` operations will be executed by worker threads invoked by the backup controller, which will communicate with the backup controller operation via a shared channel. @@ -55,42 +69,50 @@ In case 2, the majority of time spent on the backup will likely be during the sy ### Phase 1: ItemBlock processing -#### BackupItemAction plugin changes +#### New ItemBlockAction plugin type -In order for Velero to identify groups of items to back up together in an ItemBlock, we need a way to identify items which need to be backed up along with the current item. While the current `Execute` BackupItemAction method does return a list of additional items which are required by the current item, we need to know this *before* we start the item backup. To support this, we need a new API method, `GetAdditionalItems` which Velero will call on each item as it processes it for an ItemBlock. The expectation is that this method will return the same items as currently returned as additional items by the current `Execute` method, with the exception that items which are not created until calling `Execute` should not be returned here, as they don't exist yet. +In order for Velero to identify groups of items to back up together in an ItemBlock, we need a way to identify items which need to be backed up along with the current item. While the current `Execute` BackupItemAction method does return a list of additional items which are required by the current item, we need to know this *before* we start the item backup. To support this, we need a new plugin type, `ItemBlockAction` (IBA) with an API method, `GetRelatedItems` which Velero will call on each item as it processes. The expectation is that the registered IBA plugins will return the same items as returned as additional items by the BIA `Execute` method, with the exception that items which are not created until calling `Execute` should not be returned here, as they don't exist yet. -#### Proto changes (compiled into golang by protoc) +#### Proto definition (compiled into golang by protoc) -The BackupItemAction service gets one new rpc method: +The ItemBlockAction plugin type is defined as follows: ``` -service BackupItemAction { - rpc GetAdditionalItems(BackupItemActionGetAdditionalItemsRequest) returns (BackupItemActionGetAdditionalItemsResponse); +service ItemBlockAction { + rpc AppliesTo(ItemBlockActionAppliesToRequest) returns (ItemBlockActionAppliesToResponse); + rpc GetRelatedItems(ItemBlockActionGetRelatedItemsRequest) returns (ItemBlockActionGetRelatedItemsResponse); } -``` -To support this new rpc method, we define new request/response message types: -``` -message BackupItemActionAdditionalItemsRequest { +message ItemBlockActionAppliesToRequest { + string plugin = 1; +} + +message ItemBlockActionAppliesToResponse { + ResourceSelector ResourceSelector = 1; +} + +message ItemBlockActionRelatedItemsRequest { string plugin = 1; bytes item = 2; bytes backup = 3; } -message BackupItemActionAdditionalItemsResponse { - repeated generated.ResourceIdentifier additionalItems = 1; +message ItemBlockActionRelatedItemsResponse { + repeated generated.ResourceIdentifier relatedItems = 1; } ``` -A new PluginKind, `BackupItemActionV3`, will be created, and the backup process will be modified to use this plugin kind. Existing v1/v2 plugins will be adapted to v3 with an empty `GetAdditionalItems` method, meaning that those plugins will not add anything to the ItemBlock for the item being backed up. +A new PluginKind, `ItemBlockActionV1`, will be created, and the backup process will be modified to use this plugin kind. -Any BIA plugins which return additional items from `Execute()` that need to be backed up at the same time or sequentially in the same worker thread as the current items need to be upgraded to v3. This mainly applies to plugins that operate on pods which reference resources which must be backed up along with the pod and are potentially affected by pod hooks or for plugins which connect multiple pods whose volumes should be backed up at the same time. +For any BIA plugins which return additional items from `Execute()` that need to be backed up at the same time or sequentially in the same worker thread as the current items should add a new IBA plugin to return these same items (minus any which won't exist before BIA `Execute()` is called). +This mainly applies to plugins that operate on pods which reference resources which must be backed up along with the pod and are potentially affected by pod hooks or for plugins which connect multiple pods whose volumes should be backed up at the same time. ### Changes to processing item list from the Item Collector #### New structs ItemBlock and ItemBlockItem -``` +```go type ItemBlock struct { log logrus.FieldLogger + // This is a reference to the shared itemBackupper for the backup itemBackupper *itemBackupper Items []ItemBlockItem } @@ -107,14 +129,16 @@ In the `BackupWithResolvers` func, the current Velero implementation iterates ov #### Modifications to the loop over ItemCollector results The `kubernetesResource` struct used by the item collector will be modified to add an `orderedResource` bool which will be set true for all of the resources moved to the beginning of the list as a result of being ordered resources. +While the item collector already puts ordered resources first, there is no indication in the list which of these initial items are from the ordered resources list and which are the remaining (unordered) items. +Velero needs to know which resources are ordered because when we process them later, these initial resources must be processed sequentially, one at a time, before processing the remaining resources in a parallel manner. The current workflow within each iteration of the ItemCollector.items loop will replaced with the following: -- (note that some of the below should be pulled out into a helper func to facilitate recursive call to it for items returned from `GetAdditonalItems`.) +- (note that some of the below should be pulled out into a helper func to facilitate recursive call to it for items returned from `GetRelatedItems`.) - Before loop iteration, create a new `itemsInBlock` map of type map[velero.ResourceIdentifier]bool which represents the set of items already included in a block. - If `item` is already in `itemsInBlock`, continue. This one has already been processed. - Add `item` to `itemsInBlock`. - Load item from ItemCollector file. Close/remove file after loading (on error return or not, possibly with similar anonymous func to current impl) -- Get matching BIA plugins for item, call `GetAdditionalItems` for each. For each item returned, get full item content from ItemCollector (if present in item list, pulling from file, removing file when done) or from cluster (if not present in item list), add item to the current block, add item to `itemsInBlock` map, and then recursively apply current step to each (i.e. call BIA method, add to block, etc.) +- Get matching IBA plugins for item, call `GetRelatedItems` for each. For each item returned, get full item content from ItemCollector (if present in item list, pulling from file, removing file when done) or from cluster (if not present in item list), add item to the current block, add item to `itemsInBlock` map, and then recursively apply current step to each (i.e. call IBA method, add to block, etc.) - Once full ItemBlock list is generated, call `backupItemBlock(block ItemBlock) - Add `backupItemBlock` return values to `backedUpGroupResources` map @@ -122,7 +146,7 @@ The current workflow within each iteration of the ItemCollector.items loop will #### New func `backupItemBlock` Method signature for new func `backupItemBlock` is as follows: -``` +```go func backupItemBlock(block ItemBlock) []schema.GroupResource ``` The return value is a slice of GRs for resources which were backed up. Velero tracks these to determine which CRDs need to be included in the backup. Note that we need to make sure we include in this not only those resources that were backed up directly, but also those backed up indirectly via additional items BIA execute returns. @@ -146,7 +170,7 @@ The `backupReconciler` struct will also have this new field added. #### Worker pool for item block processing A new type, `ItemBlockWorker` will be added which will manage a pool of worker goroutines which will process item blocks, a shared input channel for passing blocks to workers, and a WaitGroup to shut down cleanly when the reconciler exits. -``` +```go type ItemBlockWorkerPool struct { itemBlockChannel chan ItemBlockInput wg *sync.WaitGroup @@ -179,7 +203,7 @@ The `processItemBlocksWorker` func (run by the worker goroutines) will read from The ItemBlock processing loop implemented in Phase 1 will be modified to send each newly-created ItemBlock to the shared channel rather than calling `BackupItemBlock` inline, using a WaitGroup to manage in-process items. A separate goroutine will be created to process returns for this backup. After completion of the ItemBlock processing loop, velero will use the WaitGroup to wait for all ItemBlock processing to complete before moving forward. A simplified example of what this response goroutine might look like: -``` +```go // omitting cancel handling, context, etc ret := make(chan ItemBlockReturn) wg := &sync.WaitGroup{} @@ -215,26 +239,26 @@ A simplified example of what this response goroutine might look like: // responses from BackupItemBlock calls are in responses ``` -The ItemBlock processing loop described above will be split into two separate iterations. For the first iteration, velero will only process those items at the beginning of the loop identified as `orderedResources` -- when the groups generated from these resources are passed to the worker channel, velero will wait for the response before moving on to the next ItemBlock. This is to ensure that the ordered resources are processed in the required order. Once the last ordered resource is processed, the remaining ItemBlocks will be processed and sent to the worker channel without waiting for a response, in order to allow these ItemBlocks to be processed in parallel. +When processing the responses, the main thing is to set `backedUpGroupResources[item.groupResource]=true` for each GR returned, which will give the same result as the current implementation calling items one-by-one and setting that field as needed. + +The ItemBlock processing loop described above will be split into two separate iterations. For the first iteration, velero will only process those items at the beginning of the loop identified as `orderedResources` -- when the groups generated from these resources are passed to the worker channel, velero will wait for the response before moving on to the next ItemBlock. +This is to ensure that the ordered resources are processed in the required order. Once the last ordered resource is processed, the remaining ItemBlocks will be processed and sent to the worker channel without waiting for a response, in order to allow these ItemBlocks to be processed in parallel. +The reason we must execute `ItemBlocks` with ordered resources first (and one at a time) is that this is a list of resources identified by the user as resources which must be backed up first, and in a particular order. #### Synchronize access to the BackedUpItems map Velero uses a map of BackedUpItems to track which items have already been backed up. This prevents velero from attempting to back up an item more than once, as well as guarding against creating infinite loops due to circular dependencies in the additional items returns. Since velero will now be accessing this map from the parallel goroutines, access to the map must be synchronized with mutexes. +### Backup Finalize phase + +The finalize phase will not be affected by the ItemBlock design, since this is just updating resources after async operations are completed on the items and there is no need to run these updates in parallel. + ## Alternatives considered -### New ItemBlockAction plugin type +### BackpuItemAction v3 API -Instead of adding a new `GetAdditionalItems` method to BackupItemAction, another possibility would be to leave BIA alone and create a new plugin type, ItemBlockAction. -Rather than adding the `GetAdditionalItems` method to the existing BIA plugin type, velero would introduce a new ItemBlockAction plugin type which provides the single `GetAdditionalItems` method, described above as a new BIAv3 method. -For existing BIA implementations which return additional items in `Execute()`, instead of adding the new v3 method (and, if necessary, the missing v2 methods) to the existing BIA, they would create a new ItemBlockAction plugin to provide this method. - -The change to the backup workflow would be relatively simple: resolve registered ItemBlockAction (IBA) plugins as well as BIA plugins, determine for both plugin types which apply to the current resource type, and when calling the new API method, iterate over IBA plugins rather than BIA plugins. - -The change to the "BackupItemAction plugin changes" section would be to create a new plugin type rather than enhancing the existing plugin type. The API function needed in this new plugin type would be identical to the one described as a new BIAv3 method. The other work here would be to create the infrastructure/scaffolding to define, register, etc. the new plugin type. - -Overall, the decision of extending BIA vs. creating a new plugin type won't change much in the design here. -The new plugin type will require more initial work, but ongoing maintenance should be similar between the options. +Instead of adding a new `ItemBlockAction` plugin type, we could add a `GetAdditionalItems` method to BackupItemAction. +This was rejected because the new plugin type provides a cleaner interface, and keeps the function of grouping related items separate from the function of modifying item content for the backup. ### Per-backup worker pool @@ -256,96 +280,74 @@ For the per-backup worker approach, the worker count represents the worker count ## Compatibility -### Example upgrade to BIAv3 +### Example IBA implementation for BIA plugins which return additional items -Included below is an example of what might be required to upgrade a v1 plugin which returns additional items to BIAv3. +Included below is an example of what might be required for a BIA plugin which returns additional items. The code is taken from the internal velero `pod_action.go` which identifies the items required for a given pod. -Basically, the required changes are as follows: -- (for v1 plugins) Implement `Name()`. This returns a string. The content isn't particularly important, since Velero doesn't actually make an RPC call for this method, but it must be defined on the type in order to implement the interface. -- (for v1 plugins) Implement `Progress(`). Since the plugin doesn't have any asynchronous operations (or it would already be a v2 plugin), this should just return the error `biav2.AsyncOperationsNotSupportedError()`. -- (for v1 plugins) Implement `Cancel(`). Since the plugin doesn't have any asynchronous operations (or it would already be a v2 plugin), this should just return nil. -- (for v1 or v2 plugins) Implement `GetAdditionalItems()` If the additionalItems return value from `Execute()` is nil (or only returns items newly-created in Execute()), it should return nil. Otherwise, it should return the same items as `Execute()` minus any items that don't exist yet. In the example below, this was done by putting the additional item list generation code into `GetAdditionalItems()` and refactoring `Execute()` to call the new func to get the list. For plugins which return a combination of already-existing and newly-created items, `GetAdditionalItems()` should generate the already-existing list, and `Execute()` should append the newly-created items to the list. -- When registering the BIA, replace `RegisterBackupItemAction` (for v1 plugins) or `RegisterBackupItemActionV2` (for v2 plugins) with `RegisterBackupItemActionV3` +In this particular case, the only function of pod_action is to return additional items, so we can really just convert this plugin to an IBA plugin. If there were other actions, such as modifying the pod content on backup, then we would still need the pod action, and the related items vs. content manipulation functions would need to be separated. + +```go +// PodAction implements ItemBlockAction. +type PodAction struct { + log logrus.FieldLogger +} + +// NewPodAction creates a new ItemAction for pods. +func NewPodAction(logger logrus.FieldLogger) *PodAction { + return &PodAction{log: logger} +} + +// AppliesTo returns a ResourceSelector that applies only to pods. +func (a *PodAction) AppliesTo() (velero.ResourceSelector, error) { + return velero.ResourceSelector{ + IncludedResources: []string{"pods"}, + }, nil +} + +// GetRelatedItems scans the pod's spec.volumes for persistentVolumeClaim volumes and returns a +// ResourceIdentifier list containing references to all of the persistentVolumeClaim volumes used by +// the pod. This ensures that when a pod is backed up, all referenced PVCs are backed up too. +func (a *PodAction) GetRelatedItems(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { + pod := new(corev1api.Pod) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil { + return nil, errors.WithStack(err) + } + + var relatedItems []velero.ResourceIdentifier + if pod.Spec.PriorityClassName != "" { + a.log.Infof("Adding priorityclass %s to relatedItems", pod.Spec.PriorityClassName) + relatedItems = append(relatedItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.PriorityClasses, + Name: pod.Spec.PriorityClassName, + }) + } + + if len(pod.Spec.Volumes) == 0 { + a.log.Info("pod has no volumes") + return relatedItems, nil + } + + for _, volume := range pod.Spec.Volumes { + if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName != "" { + a.log.Infof("Adding pvc %s to relatedItems", volume.PersistentVolumeClaim.ClaimName) + + relatedItems = append(relatedItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.PersistentVolumeClaims, + Namespace: pod.Namespace, + Name: volume.PersistentVolumeClaim.ClaimName, + }) + } + } + + return relatedItems, nil +} + +// API call +func (a *PodAction) Name() string { + return "PodAction" +} -```diff --git a/pkg/backup/actions/pod_action.go b/pkg/backup/actions/pod_action.go -index ce6b1ade8..5625dcb5b 100644 ---- a/pkg/backup/actions/pod_action.go -+++ b/pkg/backup/actions/pod_action.go -@@ -25,6 +25,7 @@ import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/kuberesource" - "github.com/vmware-tanzu/velero/pkg/plugin/velero" -+ biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" - ) - - // PodAction implements ItemAction. -@@ -47,13 +48,19 @@ func (a *PodAction) AppliesTo() (velero.ResourceSelector, error) { - // Execute scans the pod's spec.volumes for persistentVolumeClaim volumes and returns a - // ResourceIdentifier list containing references to all of the persistentVolumeClaim volumes used by - // the pod. This ensures that when a pod is backed up, all referenced PVCs are backed up too. --func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { -+func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, string, []velero.ResourceIdentifier, error) { - a.log.Info("Executing podAction") - defer a.log.Info("Done executing podAction") - -+ additionalItems, err := a.GetAdditionalItems(item, backup) -+ return item, additionalItems, "", nil, err -+} -+ -+// v3 API call, return nil if no additional items for this resource -+func (a *PodAction) GetAdditionalItems(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { - pod := new(corev1api.Pod) - if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil { -- return nil, nil, errors.WithStack(err) -+ return nil, errors.WithStack(err) - } - - var additionalItems []velero.ResourceIdentifier -@@ -67,7 +74,7 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti - - if len(pod.Spec.Volumes) == 0 { - a.log.Info("pod has no volumes") -- return item, additionalItems, nil -+ return additionalItems, nil - } - - for _, volume := range pod.Spec.Volumes { -@@ -82,5 +89,20 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti - } - } - -- return item, additionalItems, nil -+ return additionalItems, nil -+} -+ -+// v2 API call -+func (a *PodAction) Name() string { -+ return "PodAction" -+} -+ -+// v2 API call, just return this error for BIAs without async operations -+func (a *PodAction) Progress(operationID string, backup *velerov1api.Backup) (velero.OperationProgress, error) { -+ return velero.OperationProgress{}, biav2.AsyncOperationsNotSupportedError() -+} -+ -+// v2 API call, just return nil for BIAs without async operations -+func (a *PodAction) Cancel(operationID string, backup *velerov1api.Backup) error { -+ return nil - } -diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go -index c375c5437..7717443dc 100644 ---- a/pkg/cmd/server/plugin/plugin.go -+++ b/pkg/cmd/server/plugin/plugin.go -@@ -42,7 +42,7 @@ func NewCommand(f client.Factory) *cobra.Command { - Run: func(c *cobra.Command, args []string) { - pluginServer = pluginServer. - RegisterBackupItemAction("velero.io/pv", newPVBackupItemAction). -- RegisterBackupItemAction("velero.io/pod", newPodBackupItemAction). -+ RegisterBackupItemActionV3("velero.io/pod", newPodBackupItemAction). - RegisterBackupItemAction("velero.io/service-account", newServiceAccountBackupItemAction(f)). - RegisterRestoreItemAction("velero.io/job", newJobRestoreItemAction). - RegisterRestoreItemAction("velero.io/pod", newPodRestoreItemAction). ``` From 0288ab7611ffafeac40418cef2f0a728a7f27512 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 27 Jun 2024 12:49:32 -0400 Subject: [PATCH 04/69] add Restore improvements to non-goals Signed-off-by: Scott Seago --- design/backup-performance-improvements.md | 1 + 1 file changed, 1 insertion(+) diff --git a/design/backup-performance-improvements.md b/design/backup-performance-improvements.md index ebd075bee..2d90a7189 100644 --- a/design/backup-performance-improvements.md +++ b/design/backup-performance-improvements.md @@ -36,6 +36,7 @@ In case 2, the majority of time spent on the backup will likely be during the sy - Support VolumeGroupSnapshots: this is a future feature, although certain prerequisites for this enhancement are included in this proposal. - Process multiple backups in parallel: this is a future feature, although certain prerequisites for this enhancement are included in this proposal. - Refactoring plugin infrastructure to avoid RPC calls for internal plugins. +- Restore performance improvements: this is potentially a future feature ## High-Level Design From 3fa8f6c72d208fa20246c6e6ed39cd617981567b Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 1 Jul 2024 19:04:31 +0800 Subject: [PATCH 05/69] issue 7620: design for backup repo configurations Signed-off-by: Lyndon-Li --- design/backup-repo-config.md | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 design/backup-repo-config.md diff --git a/design/backup-repo-config.md b/design/backup-repo-config.md new file mode 100644 index 000000000..6b483be40 --- /dev/null +++ b/design/backup-repo-config.md @@ -0,0 +1,55 @@ +# Backup Repository Configuration Design + +## Glossary & Abbreviation + +**Backup Storage**: The storage to store the backup data. Check [Unified Repository design][1] for details. +**Backup Repository**: Backup repository is layered between BR data movers and Backup Storage to provide BR related features that is introduced in [Unified Repository design][1]. + +## Background + +According to the [Unified Repository design][1] Velero uses selectable backup respositories for various backup/restore methods, i.e., fs-backup, volume snapshot data movement, etc. To achieve the best performance, backup respositories may need to be configured according to the running environments. +For example, if there are sufficient CPU and memory resources in the environment, users may enable compression feature provided by the backup repository, so as to achieve the best backup throughput. +As another example, if the local disk space is not sufficent, users may want to constraint the backup repository's cache size, so as to prevent from running out of the disk space. +Therefore, it is worthy to allow users to configure some essential prameters of the backup repsoitories, and the configuration may vary from backup respositories. + +## Goals + +- Create a mechnism for users to specify configurations for backup repositories + +## Non-Goals + +## Solution + +### BackupRepository CRD + +After a backup repository is initialized, a BackupRepository CR is created to represent the instance of the backup repository. The BackupRepository's spec is a core parameter used by Uinified Repo when interactive with the backup repsoitory. Therefore, we can add the configurations into the BackupRepository CR. +The configurations may be different varying from backup repositories, therefore, we will not define each of the configurations explictly. Instead, we add a map in the BackupRepository's spec to take any configuration to be set to the backup repository. +During various operations to the backup repository, the Unified Repo modules will retrieve from the map for the specific configuration that is required at that time. So even though it is specified, a configuration may not be visite/hornored if the operations don't require it for the specific backup repository, this won't bring any issue. + +Below is the new BackupRepository's spec after adding the configuration map: + +### BackupRepository configMap + +The BackupRepository CR is not created explicitly but a Velero CLI, but created as part of the backup/restore/maintenance operation if the CR doesn't exist. As a result, users don't have any way to specify the configurations before the BackupRepository CR is created. +Therefore, we create a BackupRepository configMap as a template of the configurations to be applied to the backup repository CR created during the backup/restore/maintenance operation. For an existing BackupRepository CR, the configMap is never visited, if users want to modify the configuration value, they should directly edit the BackupRepository CR. +The BackupRepository configMap is created by users. If the configMap is not there, nothing is specified to the backup repository CR, so the Unified Repo modules use the hard-coded values to configre the backup repository. +The BackupRepository configMap is backup repository type specific, that is, for each type of backup repository, there is one configMap. Therefore, a ```backup-repository-config``` label should be applied to the configMap with the value of the repository's type. During the backup repository creation, the configMap is searched by the label. + +### Configurations + +With the above mechanisms, all kinds of configurations could be added. Here list the configurations supported at present: +```cache-limit```: specify the size limit for the local data cache. The more data is cached locally, the less data is donwloaded from the backup storage, so the better performance may be achieved. You can specify any size that is smaller than the free space so that the disk space won't run out. This parameter is for each repository connection, that is, users could change it before connecting to the repository. +```compression-algo```: specify the compression algorithm to be used for a backup repsotiory. Most of the backup repositories support the data compression feature, but they may support different compression algorithms or performe differently to compression algorithms. This parameter is used when intializing the backup repository, once the backup repository is initialized, the compression algorithm won't be changed, so this parameter is ignored. + +Below is an example of the BackupRepository configMap with the supported configurations: +```go +``` + +To create the configMap, users need to save something like the above sample to a json file and then run below command: +``` +kubectl create cm node-agent-config -n velero --from-file= +``` + + + +[1]: Implemented/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md \ No newline at end of file From bd2008c893e15ec5e6f4f2f231073ef475dfcd02 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Wed, 26 Jun 2024 23:55:13 -0400 Subject: [PATCH 06/69] Make pkg/install/Deployment podTemplateOptions bool functions accept bool param Signed-off-by: Tiger Kaovilai --- pkg/install/deployment.go | 20 ++++++++++---------- pkg/install/deployment_test.go | 4 ++-- pkg/install/resources.go | 10 +++++----- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pkg/install/deployment.go b/pkg/install/deployment.go index a8c2a131a..9ac46ece8 100644 --- a/pkg/install/deployment.go +++ b/pkg/install/deployment.go @@ -95,9 +95,9 @@ func WithSecret(secretPresent bool) podTemplateOption { } } -func WithRestoreOnly() podTemplateOption { +func WithRestoreOnly(b bool) podTemplateOption { return func(c *podTemplateConfig) { - c.restoreOnly = true + c.restoreOnly = b } } @@ -143,21 +143,21 @@ func WithUploaderType(t string) podTemplateOption { } } -func WithDefaultVolumesToFsBackup() podTemplateOption { +func WithDefaultVolumesToFsBackup(b bool) podTemplateOption { return func(c *podTemplateConfig) { - c.defaultVolumesToFsBackup = true + c.defaultVolumesToFsBackup = b } } -func WithDefaultSnapshotMoveData() podTemplateOption { +func WithDefaultSnapshotMoveData(b bool) podTemplateOption { return func(c *podTemplateConfig) { - c.defaultSnapshotMoveData = true + c.defaultSnapshotMoveData = b } } -func WithDisableInformerCache() podTemplateOption { +func WithDisableInformerCache(b bool) podTemplateOption { return func(c *podTemplateConfig) { - c.disableInformerCache = true + c.disableInformerCache = b } } @@ -167,9 +167,9 @@ func WithServiceAccountName(sa string) podTemplateOption { } } -func WithPrivilegedNodeAgent() podTemplateOption { +func WithPrivilegedNodeAgent(b bool) podTemplateOption { return func(c *podTemplateConfig) { - c.privilegedNodeAgent = true + c.privilegedNodeAgent = b } } diff --git a/pkg/install/deployment_test.go b/pkg/install/deployment_test.go index 04d301c01..426a53df6 100644 --- a/pkg/install/deployment_test.go +++ b/pkg/install/deployment_test.go @@ -31,7 +31,7 @@ func TestDeployment(t *testing.T) { assert.Equal(t, "velero", deploy.ObjectMeta.Namespace) - deploy = Deployment("velero", WithRestoreOnly()) + deploy = Deployment("velero", WithRestoreOnly(true)) assert.Equal(t, "--restore-only", deploy.Spec.Template.Spec.Containers[0].Args[1]) deploy = Deployment("velero", WithEnvFromSecretKey("my-var", "my-secret", "my-key")) @@ -67,7 +67,7 @@ func TestDeployment(t *testing.T) { deploy = Deployment("velero", WithServiceAccountName("test-sa")) assert.Equal(t, "test-sa", deploy.Spec.Template.Spec.ServiceAccountName) - deploy = Deployment("velero", WithDisableInformerCache()) + deploy = Deployment("velero", WithDisableInformerCache(true)) assert.Len(t, deploy.Spec.Template.Spec.Containers[0].Args, 2) assert.Equal(t, "--disable-informer-cache=true", deploy.Spec.Template.Spec.Containers[0].Args[1]) diff --git a/pkg/install/resources.go b/pkg/install/resources.go index 171fc2ece..752bc025b 100644 --- a/pkg/install/resources.go +++ b/pkg/install/resources.go @@ -358,7 +358,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { } if o.RestoreOnly { - deployOpts = append(deployOpts, WithRestoreOnly()) + deployOpts = append(deployOpts, WithRestoreOnly(true)) } if len(o.Plugins) > 0 { @@ -366,15 +366,15 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { } if o.DefaultVolumesToFsBackup { - deployOpts = append(deployOpts, WithDefaultVolumesToFsBackup()) + deployOpts = append(deployOpts, WithDefaultVolumesToFsBackup(true)) } if o.DefaultSnapshotMoveData { - deployOpts = append(deployOpts, WithDefaultSnapshotMoveData()) + deployOpts = append(deployOpts, WithDefaultSnapshotMoveData(true)) } if o.DisableInformerCache { - deployOpts = append(deployOpts, WithDisableInformerCache()) + deployOpts = append(deployOpts, WithDisableInformerCache(true)) } deploy := Deployment(o.Namespace, deployOpts...) @@ -396,7 +396,7 @@ func AllResources(o *VeleroOptions) *unstructured.UnstructuredList { dsOpts = append(dsOpts, WithFeatures(o.Features)) } if o.PrivilegedNodeAgent { - dsOpts = append(dsOpts, WithPrivilegedNodeAgent()) + dsOpts = append(dsOpts, WithPrivilegedNodeAgent(true)) } ds := DaemonSet(o.Namespace, dsOpts...) if err := appendUnstructured(resources, ds); err != nil { From 49097744ee1f2686ba5660efcf42462c03e7f690 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 6 Jun 2024 18:27:51 +0800 Subject: [PATCH 07/69] new exposer for data mover ms Signed-off-by: Lyndon-Li --- changelogs/unreleased/7988-Lyndon-Li | 1 + pkg/cmd/cli/datamover/backup.go | 92 ++++++ pkg/cmd/cli/datamover/data_mover.go | 67 +++++ pkg/cmd/cli/datamover/data_mover_test.go | 131 +++++++++ pkg/cmd/cli/datamover/restore.go | 92 ++++++ pkg/cmd/velero/velero.go | 2 + .../data_download_controller_test.go | 12 +- pkg/controller/data_upload_controller_test.go | 12 +- pkg/exposer/csi_snapshot.go | 71 +++-- pkg/exposer/csi_snapshot_test.go | 12 +- pkg/exposer/generic_restore.go | 71 +++-- pkg/exposer/generic_restore_test.go | 13 +- pkg/exposer/image.go | 26 +- pkg/exposer/image_test.go | 271 ++++++++++++++++++ pkg/exposer/types.go | 5 +- pkg/util/kube/pvc_pv.go | 9 +- pkg/util/kube/pvc_pv_test.go | 62 ++++ 17 files changed, 904 insertions(+), 45 deletions(-) create mode 100644 changelogs/unreleased/7988-Lyndon-Li create mode 100644 pkg/cmd/cli/datamover/backup.go create mode 100644 pkg/cmd/cli/datamover/data_mover.go create mode 100644 pkg/cmd/cli/datamover/data_mover_test.go create mode 100644 pkg/cmd/cli/datamover/restore.go create mode 100644 pkg/exposer/image_test.go diff --git a/changelogs/unreleased/7988-Lyndon-Li b/changelogs/unreleased/7988-Lyndon-Li new file mode 100644 index 000000000..3630890b6 --- /dev/null +++ b/changelogs/unreleased/7988-Lyndon-Li @@ -0,0 +1 @@ +New data path for data mover ms according to design #7574 \ No newline at end of file diff --git a/pkg/cmd/cli/datamover/backup.go b/pkg/cmd/cli/datamover/backup.go new file mode 100644 index 000000000..aabcd0acc --- /dev/null +++ b/pkg/cmd/cli/datamover/backup.go @@ -0,0 +1,92 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "fmt" + "strings" + "time" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/vmware-tanzu/velero/pkg/buildinfo" + "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/util/logging" +) + +type dataMoverBackupConfig struct { + volumePath string + volumeMode string + duName string + resourceTimeout time.Duration +} + +func NewBackupCommand(f client.Factory) *cobra.Command { + config := dataMoverBackupConfig{} + + logLevelFlag := logging.LogLevelFlag(logrus.InfoLevel) + formatFlag := logging.NewFormatFlag() + + command := &cobra.Command{ + Use: "backup", + Short: "Run the velero data-mover backup", + Long: "Run the velero data-mover backup", + Hidden: true, + Run: func(c *cobra.Command, args []string) { + logLevel := logLevelFlag.Parse() + logrus.Infof("Setting log-level to %s", strings.ToUpper(logLevel.String())) + + logger := logging.DefaultLogger(logLevel, formatFlag.Parse()) + logger.Infof("Starting Velero data-mover backup %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) + + f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) + s := newdataMoverBackup(logger, config) + + s.run() + }, + } + + command.Flags().Var(logLevelFlag, "log-level", fmt.Sprintf("The level at which to log. Valid values are %s.", strings.Join(logLevelFlag.AllowedValues(), ", "))) + command.Flags().Var(formatFlag, "log-format", fmt.Sprintf("The format for log output. Valid values are %s.", strings.Join(formatFlag.AllowedValues(), ", "))) + command.Flags().StringVar(&config.volumePath, "volume-path", config.volumePath, "The full path of the volume to be backed up") + command.Flags().StringVar(&config.volumeMode, "volume-mode", config.volumeMode, "The mode of the volume to be backed up") + command.Flags().StringVar(&config.duName, "data-upload", config.duName, "The data upload name") + command.Flags().DurationVar(&config.resourceTimeout, "resource-timeout", config.resourceTimeout, "How long to wait for resource processes which are not covered by other specific timeout parameters.") + + _ = command.MarkFlagRequired("volume-path") + _ = command.MarkFlagRequired("volume-mode") + _ = command.MarkFlagRequired("data-upload") + _ = command.MarkFlagRequired("resource-timeout") + + return command +} + +type dataMoverBackup struct { + logger logrus.FieldLogger + config dataMoverBackupConfig +} + +func newdataMoverBackup(logger logrus.FieldLogger, config dataMoverBackupConfig) *dataMoverBackup { + s := &dataMoverBackup{ + logger: logger, + config: config, + } + + return s +} + +func (s *dataMoverBackup) run() { + time.Sleep(time.Duration(1<<63 - 1)) +} diff --git a/pkg/cmd/cli/datamover/data_mover.go b/pkg/cmd/cli/datamover/data_mover.go new file mode 100644 index 000000000..c4400a344 --- /dev/null +++ b/pkg/cmd/cli/datamover/data_mover.go @@ -0,0 +1,67 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "fmt" + "os" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/vmware-tanzu/velero/pkg/client" +) + +func NewCommand(f client.Factory) *cobra.Command { + command := &cobra.Command{ + Use: "data-mover", + Short: "Run the velero data-mover", + Long: "Run the velero data-mover", + Hidden: true, + } + + command.AddCommand( + NewBackupCommand(f), + NewRestoreCommand(f), + ) + + return command +} + +var funcExit = os.Exit +var funcCreateFile = os.Create + +func exitWithMessage(logger logrus.FieldLogger, succeed bool, message string, a ...any) { + exitCode := 0 + if !succeed { + exitCode = 1 + } + + toWrite := fmt.Sprintf(message, a...) + + podFile, err := funcCreateFile("/dev/termination-log") + if err != nil { + logger.WithError(err).Error("Failed to create termination log file") + exitCode = 1 + } else { + if _, err := podFile.WriteString(toWrite); err != nil { + logger.WithError(err).Error("Failed to write error to termination log file") + exitCode = 1 + } + + podFile.Close() + } + + funcExit(exitCode) +} diff --git a/pkg/cmd/cli/datamover/data_mover_test.go b/pkg/cmd/cli/datamover/data_mover_test.go new file mode 100644 index 000000000..5442fd9a0 --- /dev/null +++ b/pkg/cmd/cli/datamover/data_mover_test.go @@ -0,0 +1,131 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +type exitWithMessageMock struct { + createErr error + writeFail bool + filePath string + exitCode int +} + +func (em *exitWithMessageMock) Exit(code int) { + em.exitCode = code +} + +func (em *exitWithMessageMock) CreateFile(name string) (*os.File, error) { + if em.createErr != nil { + return nil, em.createErr + } + + if em.writeFail { + return os.OpenFile(em.filePath, os.O_CREATE|os.O_RDONLY, 0500) + } else { + return os.Create(em.filePath) + } +} + +func TestExitWithMessage(t *testing.T) { + tests := []struct { + name string + message string + succeed bool + args []interface{} + createErr error + writeFail bool + expectedExitCode int + expectedMessage string + }{ + { + name: "create pod file failed", + createErr: errors.New("fake-create-file-error"), + succeed: true, + expectedExitCode: 1, + }, + { + name: "write pod file failed", + writeFail: true, + succeed: true, + expectedExitCode: 1, + }, + { + name: "not succeed", + message: "fake-message-1, arg-1 %s, arg-2 %v, arg-3 %v", + args: []interface{}{ + "arg-1-1", + 10, + false, + }, + expectedExitCode: 1, + expectedMessage: fmt.Sprintf("fake-message-1, arg-1 %s, arg-2 %v, arg-3 %v", "arg-1-1", 10, false), + }, + { + name: "not succeed", + message: "fake-message-2, arg-1 %s, arg-2 %v, arg-3 %v", + args: []interface{}{ + "arg-1-2", + 20, + true, + }, + succeed: true, + expectedMessage: fmt.Sprintf("fake-message-2, arg-1 %s, arg-2 %v, arg-3 %v", "arg-1-2", 20, true), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + podFile := filepath.Join(os.TempDir(), uuid.NewString()) + + em := exitWithMessageMock{ + createErr: test.createErr, + writeFail: test.writeFail, + filePath: podFile, + } + + funcExit = em.Exit + funcCreateFile = em.CreateFile + + exitWithMessage(velerotest.NewLogger(), test.succeed, test.message, test.args...) + + assert.Equal(t, test.expectedExitCode, em.exitCode) + + if test.createErr == nil && !test.writeFail { + reader, err := os.Open(podFile) + require.NoError(t, err) + + message, err := io.ReadAll(reader) + require.NoError(t, err) + + reader.Close() + + assert.Equal(t, string(message), test.expectedMessage) + } + }) + } +} diff --git a/pkg/cmd/cli/datamover/restore.go b/pkg/cmd/cli/datamover/restore.go new file mode 100644 index 000000000..ddd44729f --- /dev/null +++ b/pkg/cmd/cli/datamover/restore.go @@ -0,0 +1,92 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "fmt" + "strings" + "time" + + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + + "github.com/vmware-tanzu/velero/pkg/buildinfo" + "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/util/logging" +) + +type dataMoverRestoreConfig struct { + volumePath string + volumeMode string + ddName string + resourceTimeout time.Duration +} + +func NewRestoreCommand(f client.Factory) *cobra.Command { + logLevelFlag := logging.LogLevelFlag(logrus.InfoLevel) + formatFlag := logging.NewFormatFlag() + + config := dataMoverRestoreConfig{} + + command := &cobra.Command{ + Use: "restore", + Short: "Run the velero data-mover restore", + Long: "Run the velero data-mover restore", + Hidden: true, + Run: func(c *cobra.Command, args []string) { + logLevel := logLevelFlag.Parse() + logrus.Infof("Setting log-level to %s", strings.ToUpper(logLevel.String())) + + logger := logging.DefaultLogger(logLevel, formatFlag.Parse()) + logger.Infof("Starting Velero data-mover restore %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) + + f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) + s := newdataMoverRestore(logger, config) + + s.run() + }, + } + + command.Flags().Var(logLevelFlag, "log-level", fmt.Sprintf("The level at which to log. Valid values are %s.", strings.Join(logLevelFlag.AllowedValues(), ", "))) + command.Flags().Var(formatFlag, "log-format", fmt.Sprintf("The format for log output. Valid values are %s.", strings.Join(formatFlag.AllowedValues(), ", "))) + command.Flags().StringVar(&config.volumePath, "volume-path", config.volumePath, "The full path of the volume to be restored") + command.Flags().StringVar(&config.volumeMode, "volume-mode", config.volumeMode, "The mode of the volume to be restored") + command.Flags().StringVar(&config.ddName, "data-download", config.ddName, "The data download name") + command.Flags().DurationVar(&config.resourceTimeout, "resource-timeout", config.resourceTimeout, "How long to wait for resource processes which are not covered by other specific timeout parameters.") + + _ = command.MarkFlagRequired("volume-path") + _ = command.MarkFlagRequired("volume-mode") + _ = command.MarkFlagRequired("data-download") + _ = command.MarkFlagRequired("resource-timeout") + + return command +} + +type dataMoverRestore struct { + logger logrus.FieldLogger + config dataMoverRestoreConfig +} + +func newdataMoverRestore(logger logrus.FieldLogger, config dataMoverRestoreConfig) *dataMoverRestore { + s := &dataMoverRestore{ + logger: logger, + config: config, + } + + return s +} + +func (s *dataMoverRestore) run() { + time.Sleep(time.Duration(1<<63 - 1)) +} diff --git a/pkg/cmd/velero/velero.go b/pkg/cmd/velero/velero.go index 972f5bb73..eab96d62f 100644 --- a/pkg/cmd/velero/velero.go +++ b/pkg/cmd/velero/velero.go @@ -51,6 +51,7 @@ import ( veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/features" + "github.com/vmware-tanzu/velero/pkg/cmd/cli/datamover" "github.com/vmware-tanzu/velero/pkg/cmd/cli/nodeagent" ) @@ -124,6 +125,7 @@ operations can also be performed as 'velero backup get' and 'velero schedule cre snapshotlocation.NewCommand(f), debug.NewCommand(f), repomantenance.NewCommand(f), + datamover.NewCommand(f), ) // init and add the klog flags diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index a36730b64..2c40370fa 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -162,7 +162,17 @@ func TestDataDownloadReconcile(t *testing.T) { Kind: "DaemonSet", APIVersion: appsv1.SchemeGroupVersion.String(), }, - Spec: appsv1.DaemonSetSpec{}, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fake-image", + }, + }, + }, + }, + }, } tests := []struct { diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index 13c0b20f7..371aa3869 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -170,7 +170,17 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci Kind: "DaemonSet", APIVersion: appsv1.SchemeGroupVersion.String(), }, - Spec: appsv1.DaemonSetSpec{}, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fake-image", + }, + }, + }, + }, + }, } dataPathMgr := datapath.NewManager(1) diff --git a/pkg/exposer/csi_snapshot.go b/pkg/exposer/csi_snapshot.go index 7a37b794c..5ced6d509 100644 --- a/pkg/exposer/csi_snapshot.go +++ b/pkg/exposer/csi_snapshot.go @@ -18,6 +18,7 @@ package exposer import ( "context" + "fmt" "time" snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1" @@ -174,7 +175,7 @@ func (e *csiSnapshotExposer) Expose(ctx context.Context, ownerObject corev1.Obje } }() - backupPod, err := e.createBackupPod(ctx, ownerObject, backupPVC, csiExposeParam.HostingPodLabels, csiExposeParam.Affinity) + backupPod, err := e.createBackupPod(ctx, ownerObject, backupPVC, csiExposeParam.OperationTimeout, csiExposeParam.HostingPodLabels, csiExposeParam.Affinity) if err != nil { return errors.Wrap(err, "error to create backup pod") } @@ -195,6 +196,8 @@ func (e *csiSnapshotExposer) GetExposed(ctx context.Context, ownerObject corev1. backupPodName := ownerObject.Name backupPVCName := ownerObject.Name + + containerName := string(ownerObject.UID) volumeName := string(ownerObject.UID) curLog := e.log.WithFields(logrus.Fields{ @@ -237,7 +240,11 @@ func (e *csiSnapshotExposer) GetExposed(ctx context.Context, ownerObject corev1. curLog.WithField("pod", pod.Name).Infof("Backup volume is found in pod at index %v", i) - return &ExposeResult{ByPod: ExposeByPod{HostingPod: pod, VolumeName: volumeName}}, nil + return &ExposeResult{ByPod: ExposeByPod{ + HostingPod: pod, + HostingContainer: containerName, + VolumeName: volumeName, + }}, nil } func (e *csiSnapshotExposer) PeekExposed(ctx context.Context, ownerObject corev1.ObjectReference) error { @@ -391,12 +398,12 @@ func (e *csiSnapshotExposer) createBackupPVC(ctx context.Context, ownerObject co return created, err } -func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject corev1.ObjectReference, backupPVC *corev1.PersistentVolumeClaim, +func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject corev1.ObjectReference, backupPVC *corev1.PersistentVolumeClaim, operationTimeout time.Duration, label map[string]string, affinity *nodeagent.LoadAffinity) (*corev1.Pod, error) { podName := ownerObject.Name - volumeName := string(ownerObject.UID) containerName := string(ownerObject.UID) + volumeName := string(ownerObject.UID) podInfo, err := getInheritedPodInfo(ctx, e.kubeClient, ownerObject.Namespace) if err != nil { @@ -404,14 +411,41 @@ func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject co } var gracePeriod int64 = 0 - volumeMounts, volumeDevices := kube.MakePodPVCAttachment(volumeName, backupPVC.Spec.VolumeMode) + volumeMounts, volumeDevices, volumePath := kube.MakePodPVCAttachment(volumeName, backupPVC.Spec.VolumeMode) + volumeMounts = append(volumeMounts, podInfo.volumeMounts...) + + volumes := []corev1.Volume{{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: backupPVC.Name, + }, + }, + }} + volumes = append(volumes, podInfo.volumes...) if label == nil { label = make(map[string]string) } - label[podGroupLabel] = podGroupSnapshot + volumeMode := corev1.PersistentVolumeFilesystem + if backupPVC.Spec.VolumeMode != nil { + volumeMode = *backupPVC.Spec.VolumeMode + } + + args := []string{ + fmt.Sprintf("--volume-path=%s", volumePath), + fmt.Sprintf("--volume-mode=%s", volumeMode), + fmt.Sprintf("--data-upload=%s", ownerObject.Name), + fmt.Sprintf("--resource-timeout=%s", operationTimeout.String()), + } + + args = append(args, podInfo.logFormatArgs...) + args = append(args, podInfo.logLevelArgs...) + + userID := int64(0) + pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ Name: podName, @@ -446,21 +480,24 @@ func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject co Name: containerName, Image: podInfo.image, ImagePullPolicy: corev1.PullNever, - Command: []string{"/velero-helper", "pause"}, - VolumeMounts: volumeMounts, - VolumeDevices: volumeDevices, + Command: []string{ + "/velero", + "data-mover", + "backup", + }, + Args: args, + VolumeMounts: volumeMounts, + VolumeDevices: volumeDevices, + Env: podInfo.env, }, }, ServiceAccountName: podInfo.serviceAccount, TerminationGracePeriodSeconds: &gracePeriod, - Volumes: []corev1.Volume{{ - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: backupPVC.Name, - }, - }, - }}, + Volumes: volumes, + RestartPolicy: corev1.RestartPolicyNever, + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: &userID, + }, }, } diff --git a/pkg/exposer/csi_snapshot_test.go b/pkg/exposer/csi_snapshot_test.go index 4bed98cd1..29dd60cc7 100644 --- a/pkg/exposer/csi_snapshot_test.go +++ b/pkg/exposer/csi_snapshot_test.go @@ -138,7 +138,17 @@ func TestExpose(t *testing.T) { Kind: "DaemonSet", APIVersion: appsv1.SchemeGroupVersion.String(), }, - Spec: appsv1.DaemonSetSpec{}, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "node-agent", + }, + }, + }, + }, + }, } tests := []struct { diff --git a/pkg/exposer/generic_restore.go b/pkg/exposer/generic_restore.go index a1ebb7245..245fdfa14 100644 --- a/pkg/exposer/generic_restore.go +++ b/pkg/exposer/generic_restore.go @@ -87,7 +87,7 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1.O return errors.Errorf("Target PVC %s/%s has already been bound, abort", sourceNamespace, targetPVCName) } - restorePod, err := e.createRestorePod(ctx, ownerObject, targetPVC, hostingPodLabels, selectedNode) + restorePod, err := e.createRestorePod(ctx, ownerObject, targetPVC, timeout, hostingPodLabels, selectedNode) if err != nil { return errors.Wrapf(err, "error to create restore pod") } @@ -119,6 +119,8 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1.O func (e *genericRestoreExposer) GetExposed(ctx context.Context, ownerObject corev1.ObjectReference, nodeClient client.Client, nodeName string, timeout time.Duration) (*ExposeResult, error) { restorePodName := ownerObject.Name restorePVCName := ownerObject.Name + + containerName := string(ownerObject.UID) volumeName := string(ownerObject.UID) curLog := e.log.WithFields(logrus.Fields{ @@ -162,7 +164,11 @@ func (e *genericRestoreExposer) GetExposed(ctx context.Context, ownerObject core curLog.WithField("pod", pod.Name).Infof("Restore volume is found in pod at index %v", i) - return &ExposeResult{ByPod: ExposeByPod{HostingPod: pod, VolumeName: volumeName}}, nil + return &ExposeResult{ByPod: ExposeByPod{ + HostingPod: pod, + HostingContainer: containerName, + VolumeName: volumeName, + }}, nil } func (e *genericRestoreExposer) PeekExposed(ctx context.Context, ownerObject corev1.ObjectReference) error { @@ -291,12 +297,12 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co } func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObject corev1.ObjectReference, targetPVC *corev1.PersistentVolumeClaim, - label map[string]string, selectedNode string) (*corev1.Pod, error) { + operationTimeout time.Duration, label map[string]string, selectedNode string) (*corev1.Pod, error) { restorePodName := ownerObject.Name restorePVCName := ownerObject.Name - volumeName := string(ownerObject.UID) containerName := string(ownerObject.UID) + volumeName := string(ownerObject.UID) podInfo, err := getInheritedPodInfo(ctx, e.kubeClient, ownerObject.Namespace) if err != nil { @@ -304,7 +310,35 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec } var gracePeriod int64 = 0 - volumeMounts, volumeDevices := kube.MakePodPVCAttachment(volumeName, targetPVC.Spec.VolumeMode) + volumeMounts, volumeDevices, volumePath := kube.MakePodPVCAttachment(volumeName, targetPVC.Spec.VolumeMode) + volumeMounts = append(volumeMounts, podInfo.volumeMounts...) + + volumes := []corev1.Volume{{ + Name: volumeName, + VolumeSource: corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: restorePVCName, + }, + }, + }} + volumes = append(volumes, podInfo.volumes...) + + volumeMode := corev1.PersistentVolumeFilesystem + if targetPVC.Spec.VolumeMode != nil { + volumeMode = *targetPVC.Spec.VolumeMode + } + + args := []string{ + fmt.Sprintf("--volume-path=%s", volumePath), + fmt.Sprintf("--volume-mode=%s", volumeMode), + fmt.Sprintf("--data-download=%s", ownerObject.Name), + fmt.Sprintf("--resource-timeout=%s", operationTimeout.String()), + } + + args = append(args, podInfo.logFormatArgs...) + args = append(args, podInfo.logLevelArgs...) + + userID := int64(0) pod := &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -327,22 +361,25 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec Name: containerName, Image: podInfo.image, ImagePullPolicy: corev1.PullNever, - Command: []string{"/velero-helper", "pause"}, - VolumeMounts: volumeMounts, - VolumeDevices: volumeDevices, + Command: []string{ + "/velero", + "data-mover", + "restore", + }, + Args: args, + VolumeMounts: volumeMounts, + VolumeDevices: volumeDevices, + Env: podInfo.env, }, }, ServiceAccountName: podInfo.serviceAccount, TerminationGracePeriodSeconds: &gracePeriod, - Volumes: []corev1.Volume{{ - Name: volumeName, - VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: restorePVCName, - }, - }, - }}, - NodeName: selectedNode, + Volumes: volumes, + NodeName: selectedNode, + RestartPolicy: corev1.RestartPolicyNever, + SecurityContext: &corev1.PodSecurityContext{ + RunAsUser: &userID, + }, }, } diff --git a/pkg/exposer/generic_restore_test.go b/pkg/exposer/generic_restore_test.go index 7721effc1..5e492ccdf 100644 --- a/pkg/exposer/generic_restore_test.go +++ b/pkg/exposer/generic_restore_test.go @@ -31,6 +31,7 @@ import ( velerotest "github.com/vmware-tanzu/velero/pkg/test" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" corev1api "k8s.io/api/core/v1" clientTesting "k8s.io/client-go/testing" ) @@ -74,7 +75,17 @@ func TestRestoreExpose(t *testing.T) { Kind: "DaemonSet", APIVersion: appsv1.SchemeGroupVersion.String(), }, - Spec: appsv1.DaemonSetSpec{}, + Spec: appsv1.DaemonSetSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Image: "fake-image", + }, + }, + }, + }, + }, } tests := []struct { diff --git a/pkg/exposer/image.go b/pkg/exposer/image.go index c29a59447..8091e12bd 100644 --- a/pkg/exposer/image.go +++ b/pkg/exposer/image.go @@ -18,8 +18,10 @@ package exposer import ( "context" + "strings" "github.com/pkg/errors" + v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" "github.com/vmware-tanzu/velero/pkg/nodeagent" @@ -28,6 +30,11 @@ import ( type inheritedPodInfo struct { image string serviceAccount string + env []v1.EnvVar + volumeMounts []v1.VolumeMount + volumes []v1.Volume + logLevelArgs []string + logFormatArgs []string } func getInheritedPodInfo(ctx context.Context, client kubernetes.Interface, veleroNamespace string) (inheritedPodInfo, error) { @@ -39,11 +46,28 @@ func getInheritedPodInfo(ctx context.Context, client kubernetes.Interface, veler } if len(podSpec.Containers) != 1 { - return podInfo, errors.Wrap(err, "unexpected pod template from node-agent") + return podInfo, errors.New("unexpected pod template from node-agent") } podInfo.image = podSpec.Containers[0].Image podInfo.serviceAccount = podSpec.ServiceAccountName + podInfo.env = podSpec.Containers[0].Env + podInfo.volumeMounts = podSpec.Containers[0].VolumeMounts + podInfo.volumes = podSpec.Volumes + + args := podSpec.Containers[0].Args + for i, arg := range args { + if arg == "--log-format" { + podInfo.logFormatArgs = append(podInfo.logFormatArgs, args[i:i+2]...) + } else if strings.HasPrefix(arg, "--log-format") { + podInfo.logFormatArgs = append(podInfo.logFormatArgs, arg) + } else if arg == "--log-level" { + podInfo.logLevelArgs = append(podInfo.logLevelArgs, args[i:i+2]...) + } else if strings.HasPrefix(arg, "--log-level") { + podInfo.logLevelArgs = append(podInfo.logLevelArgs, arg) + } + } + return podInfo, nil } diff --git a/pkg/exposer/image_test.go b/pkg/exposer/image_test.go new file mode 100644 index 000000000..b9aaf51eb --- /dev/null +++ b/pkg/exposer/image_test.go @@ -0,0 +1,271 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package exposer + +import ( + "context" + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + + appsv1 "k8s.io/api/apps/v1" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/fake" +) + +func TestGetInheritedPodInfo(t *testing.T) { + daemonSet := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-ns", + Name: "node-agent", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSet", + }, + } + + daemonSetWithNoLog := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-ns", + Name: "node-agent", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSet", + }, + Spec: appsv1.DaemonSetSpec{ + Template: v1.PodTemplateSpec{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "container-1", + Image: "image-1", + Env: []v1.EnvVar{ + { + Name: "env-1", + Value: "value-1", + }, + { + Name: "env-2", + Value: "value-2", + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + ServiceAccountName: "sa-1", + }, + }, + }, + } + + daemonSetWithLog := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-ns", + Name: "node-agent", + }, + TypeMeta: metav1.TypeMeta{ + Kind: "DaemonSet", + }, + Spec: appsv1.DaemonSetSpec{ + Template: v1.PodTemplateSpec{ + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "container-1", + Image: "image-1", + Env: []v1.EnvVar{ + { + Name: "env-1", + Value: "value-1", + }, + { + Name: "env-2", + Value: "value-2", + }, + }, + VolumeMounts: []v1.VolumeMount{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + Args: []string{ + "--log-format=json", + "--log-level", + "debug", + }, + Command: []string{ + "command-1", + }, + }, + }, + Volumes: []v1.Volume{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + ServiceAccountName: "sa-1", + }, + }, + }, + } + + scheme := runtime.NewScheme() + appsv1.AddToScheme(scheme) + + tests := []struct { + name string + namespace string + client kubernetes.Interface + kubeClientObj []runtime.Object + result inheritedPodInfo + expectErr string + }{ + { + name: "ds is not found", + namespace: "fake-ns", + expectErr: "error to get node-agent pod template: error to get node-agent daemonset: daemonsets.apps \"node-agent\" not found", + }, + { + name: "ds pod container number is invalidate", + namespace: "fake-ns", + kubeClientObj: []runtime.Object{ + daemonSet, + }, + expectErr: "unexpected pod template from node-agent", + }, + { + name: "no log info", + namespace: "fake-ns", + kubeClientObj: []runtime.Object{ + daemonSetWithNoLog, + }, + result: inheritedPodInfo{ + image: "image-1", + serviceAccount: "sa-1", + env: []v1.EnvVar{ + { + Name: "env-1", + Value: "value-1", + }, + { + Name: "env-2", + Value: "value-2", + }, + }, + volumeMounts: []v1.VolumeMount{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + volumes: []v1.Volume{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + }, + }, + { + name: "with log info", + namespace: "fake-ns", + kubeClientObj: []runtime.Object{ + daemonSetWithLog, + }, + result: inheritedPodInfo{ + image: "image-1", + serviceAccount: "sa-1", + env: []v1.EnvVar{ + { + Name: "env-1", + Value: "value-1", + }, + { + Name: "env-2", + Value: "value-2", + }, + }, + volumeMounts: []v1.VolumeMount{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + volumes: []v1.Volume{ + { + Name: "volume-1", + }, + { + Name: "volume-2", + }, + }, + logFormatArgs: []string{ + "--log-format=json", + }, + logLevelArgs: []string{ + "--log-level", + "debug", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fakeKubeClient := fake.NewSimpleClientset(test.kubeClientObj...) + info, err := getInheritedPodInfo(context.Background(), fakeKubeClient, test.namespace) + + if test.expectErr == "" { + assert.NoError(t, err) + assert.True(t, reflect.DeepEqual(info, test.result)) + } else { + assert.EqualError(t, err, test.expectErr) + } + }) + } +} diff --git a/pkg/exposer/types.go b/pkg/exposer/types.go index f87620e8f..d4d8c8730 100644 --- a/pkg/exposer/types.go +++ b/pkg/exposer/types.go @@ -35,6 +35,7 @@ type ExposeResult struct { // ExposeByPod defines the result for the expose method that a hosting pod is created type ExposeByPod struct { - HostingPod *corev1.Pod - VolumeName string + HostingPod *corev1.Pod + HostingContainer string + VolumeName string } diff --git a/pkg/util/kube/pvc_pv.go b/pkg/util/kube/pvc_pv.go index 12daa0e3e..cdb132d2f 100644 --- a/pkg/util/kube/pvc_pv.go +++ b/pkg/util/kube/pvc_pv.go @@ -340,23 +340,24 @@ func IsPVCBound(pvc *corev1api.PersistentVolumeClaim) bool { } // MakePodPVCAttachment returns the volume mounts and devices for a pod needed to attach a PVC -func MakePodPVCAttachment(volumeName string, volumeMode *corev1api.PersistentVolumeMode) ([]corev1api.VolumeMount, []corev1api.VolumeDevice) { +func MakePodPVCAttachment(volumeName string, volumeMode *corev1api.PersistentVolumeMode) ([]corev1api.VolumeMount, []corev1api.VolumeDevice, string) { var volumeMounts []corev1api.VolumeMount = nil var volumeDevices []corev1api.VolumeDevice = nil + volumePath := "/" + volumeName if volumeMode != nil && *volumeMode == corev1api.PersistentVolumeBlock { volumeDevices = []corev1api.VolumeDevice{{ Name: volumeName, - DevicePath: "/" + volumeName, + DevicePath: volumePath, }} } else { volumeMounts = []corev1api.VolumeMount{{ Name: volumeName, - MountPath: "/" + volumeName, + MountPath: volumePath, }} } - return volumeMounts, volumeDevices + return volumeMounts, volumeDevices, volumePath } func GetPVForPVC( diff --git a/pkg/util/kube/pvc_pv_test.go b/pkg/util/kube/pvc_pv_test.go index 113c28e23..acc790666 100644 --- a/pkg/util/kube/pvc_pv_test.go +++ b/pkg/util/kube/pvc_pv_test.go @@ -1378,3 +1378,65 @@ func TestGetPVCForPodVolume(t *testing.T) { }) } } + +func TestMakePodPVCAttachment(t *testing.T) { + testCases := []struct { + name string + volumeName string + volumeMode corev1api.PersistentVolumeMode + expectedVolumeMount []corev1api.VolumeMount + expectedVolumeDevice []corev1api.VolumeDevice + expectedVolumePath string + }{ + { + name: "no volume mode specified", + volumeName: "volume-1", + expectedVolumeMount: []corev1api.VolumeMount{ + { + Name: "volume-1", + MountPath: "/volume-1", + }, + }, + expectedVolumePath: "/volume-1", + }, + { + name: "fs mode specified", + volumeName: "volume-2", + volumeMode: corev1api.PersistentVolumeFilesystem, + expectedVolumeMount: []corev1api.VolumeMount{ + { + Name: "volume-2", + MountPath: "/volume-2", + }, + }, + expectedVolumePath: "/volume-2", + }, + { + name: "block volume mode specified", + volumeName: "volume-3", + volumeMode: corev1api.PersistentVolumeBlock, + expectedVolumeDevice: []corev1api.VolumeDevice{ + { + Name: "volume-3", + DevicePath: "/volume-3", + }, + }, + expectedVolumePath: "/volume-3", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var volMode *v1.PersistentVolumeMode + if tc.volumeMode != "" { + volMode = &tc.volumeMode + } + + mount, device, path := MakePodPVCAttachment(tc.volumeName, volMode) + + assert.Equal(t, tc.expectedVolumeMount, mount) + assert.Equal(t, tc.expectedVolumeDevice, device) + assert.Equal(t, tc.expectedVolumePath, path) + }) + } +} From c8baaa9b1122878c52be7b4166c9109fd0d72443 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 18 Jul 2024 16:43:16 +0200 Subject: [PATCH 08/69] testifylint: enable more rules (#8024) Signed-off-by: Matthieu MOREL --- .golangci.yaml | 5 ----- internal/credentials/file_store_test.go | 2 +- internal/resourcepolicies/volume_resources_test.go | 4 ++-- internal/volume/volumes_information_test.go | 12 ++++++------ pkg/backup/actions/csi/pvc_action_test.go | 4 ++-- pkg/backup/item_collector_test.go | 4 ++-- pkg/buildinfo/buildinfo_test.go | 2 +- pkg/client/client_test.go | 2 +- pkg/cmd/cli/backup/create_test.go | 4 ++-- pkg/cmd/cli/backup/logs_test.go | 2 +- pkg/cmd/cli/nodeagent/server_test.go | 5 ++--- pkg/controller/backup_controller_test.go | 6 +++--- pkg/controller/backup_repository_controller_test.go | 8 ++++---- pkg/controller/data_download_controller_test.go | 2 +- pkg/controller/data_upload_controller_test.go | 4 ++-- pkg/discovery/helper_test.go | 5 +++-- pkg/exposer/csi_snapshot_test.go | 2 +- pkg/install/resources_test.go | 12 ++++++------ pkg/plugin/clientmgmt/manager_test.go | 2 +- pkg/plugin/clientmgmt/process/client_builder_test.go | 2 +- pkg/restore/actions/change_image_name_action_test.go | 2 +- pkg/restore/actions/change_pvc_node_selector_test.go | 3 +-- pkg/restore/actions/csi/pvc_action_test.go | 2 +- pkg/restore/restore_test.go | 2 +- pkg/uploader/kopia/shim_test.go | 5 +++-- pkg/uploader/kopia/snapshot_test.go | 7 ++++--- pkg/uploader/provider/kopia_test.go | 3 ++- pkg/uploader/provider/provider_test.go | 3 ++- pkg/util/kube/utils_test.go | 2 +- pkg/util/logging/dual_mode_logger_test.go | 5 ++--- test/pkg/client/client_test.go | 2 +- 31 files changed, 61 insertions(+), 64 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 880e30737..4c414b843 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -238,14 +238,9 @@ linters-settings: testifylint: # TODO: enable them all disable: - - error-is-as - - expected-actual - go-require - float-compare - require-error - - suite-dont-use-pkg - - suite-extra-assert-call - - suite-thelper enable-all: true testpackage: # regexp pattern to skip files diff --git a/internal/credentials/file_store_test.go b/internal/credentials/file_store_test.go index 9244007b6..fcb5c5957 100644 --- a/internal/credentials/file_store_test.go +++ b/internal/credentials/file_store_test.go @@ -83,7 +83,7 @@ func TestNamespacedFileStore(t *testing.T) { require.NoError(t, err) } - require.Equal(t, path, tc.expectedPath) + require.Equal(t, tc.expectedPath, path) contents, err := fs.ReadFile(path) require.NoError(t, err) diff --git a/internal/resourcepolicies/volume_resources_test.go b/internal/resourcepolicies/volume_resources_test.go index 4ff7a6c9c..4d5d7a743 100644 --- a/internal/resourcepolicies/volume_resources_test.go +++ b/internal/resourcepolicies/volume_resources_test.go @@ -52,8 +52,8 @@ func TestParseCapacity(t *testing.T) { t.Run(test.input, func(t *testing.T) { actual, actualErr := parseCapacity(test.input) if test.expected != emptyCapacity { - assert.Equal(t, test.expected.lower.Cmp(actual.lower), 0) - assert.Equal(t, test.expected.upper.Cmp(actual.upper), 0) + assert.Equal(t, 0, test.expected.lower.Cmp(actual.lower)) + assert.Equal(t, 0, test.expected.upper.Cmp(actual.upper)) } assert.Equal(t, test.expectedErr, actualErr) }) diff --git a/internal/volume/volumes_information_test.go b/internal/volume/volumes_information_test.go index 4aefad7e6..b3eb10714 100644 --- a/internal/volume/volumes_information_test.go +++ b/internal/volume/volumes_information_test.go @@ -1023,28 +1023,28 @@ func TestRestoreVolumeInfoTrackNativeSnapshot(t *testing.T) { restore := builder.ForRestore("velero", "testRestore").Result() tracker := NewRestoreVolInfoTracker(restore, logrus.New(), fakeCilent) tracker.TrackNativeSnapshot("testPV", "snap-001", "ebs", "us-west-1", 10000) - assert.Equal(t, *tracker.pvNativeSnapshotMap["testPV"], NativeSnapshotInfo{ + assert.Equal(t, NativeSnapshotInfo{ SnapshotHandle: "snap-001", VolumeType: "ebs", VolumeAZ: "us-west-1", IOPS: "10000", - }) + }, *tracker.pvNativeSnapshotMap["testPV"]) tracker.TrackNativeSnapshot("testPV", "snap-002", "ebs", "us-west-2", 15000) - assert.Equal(t, *tracker.pvNativeSnapshotMap["testPV"], NativeSnapshotInfo{ + assert.Equal(t, NativeSnapshotInfo{ SnapshotHandle: "snap-002", VolumeType: "ebs", VolumeAZ: "us-west-2", IOPS: "15000", - }) + }, *tracker.pvNativeSnapshotMap["testPV"]) tracker.RenamePVForNativeSnapshot("testPV", "newPV") _, ok := tracker.pvNativeSnapshotMap["testPV"] assert.False(t, ok) - assert.Equal(t, *tracker.pvNativeSnapshotMap["newPV"], NativeSnapshotInfo{ + assert.Equal(t, NativeSnapshotInfo{ SnapshotHandle: "snap-002", VolumeType: "ebs", VolumeAZ: "us-west-2", IOPS: "15000", - }) + }, *tracker.pvNativeSnapshotMap["newPV"]) } func TestRestoreVolumeInfoResult(t *testing.T) { diff --git a/pkg/backup/actions/csi/pvc_action_test.go b/pkg/backup/actions/csi/pvc_action_test.go index 790dde6e7..59da6145f 100644 --- a/pkg/backup/actions/csi/pvc_action_test.go +++ b/pkg/backup/actions/csi/pvc_action_test.go @@ -203,7 +203,7 @@ func TestExecute(t *testing.T) { resultUnstructed, _, _, _, err := pvcBIA.Execute(&unstructured.Unstructured{Object: pvcMap}, tc.backup) if tc.expectedErr != nil { - require.Equal(t, err, tc.expectedErr) + require.EqualError(t, err, tc.expectedErr.Error()) } else { require.NoError(t, err) } @@ -367,7 +367,7 @@ func TestCancel(t *testing.T) { err = pvcBIA.Cancel(tc.operationID, tc.backup) if tc.expectedErr != nil { - require.Equal(t, err, tc.expectedErr) + require.EqualError(t, err, tc.expectedErr.Error()) } du := new(velerov2alpha1.DataUpload) diff --git a/pkg/backup/item_collector_test.go b/pkg/backup/item_collector_test.go index 562e6a2c3..f24b42486 100644 --- a/pkg/backup/item_collector_test.go +++ b/pkg/backup/item_collector_test.go @@ -74,7 +74,7 @@ func TestSortOrderedResource(t *testing.T) { {namespace: "ns1", name: "pod1"}, } sortedResources := sortResourcesByOrder(log, podResources, order) - assert.Equal(t, sortedResources, expectedResources) + assert.Equal(t, expectedResources, sortedResources) // Test cluster resources pvResources := []*kubernetesResource{ @@ -87,7 +87,7 @@ func TestSortOrderedResource(t *testing.T) { {name: "pv1"}, } sortedPvResources := sortResourcesByOrder(log, pvResources, pvOrder) - assert.Equal(t, sortedPvResources, expectedPvResources) + assert.Equal(t, expectedPvResources, sortedPvResources) } func TestFilterNamespaces(t *testing.T) { diff --git a/pkg/buildinfo/buildinfo_test.go b/pkg/buildinfo/buildinfo_test.go index be12bdcab..a2d454719 100644 --- a/pkg/buildinfo/buildinfo_test.go +++ b/pkg/buildinfo/buildinfo_test.go @@ -46,7 +46,7 @@ func TestFormattedGitSHA(t *testing.T) { t.Run(test.name, func(t *testing.T) { GitSHA = test.sha GitTreeState = test.state - assert.Equal(t, FormattedGitSHA(), test.expected) + assert.Equal(t, test.expected, FormattedGitSHA()) }) } } diff --git a/pkg/client/client_test.go b/pkg/client/client_test.go index e54e72b61..b336bdd43 100644 --- a/pkg/client/client_test.go +++ b/pkg/client/client_test.go @@ -45,7 +45,7 @@ func TestBuildUserAgent(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { resp := buildUserAgent(test.command, test.version, test.gitSha, test.os, test.arch) - assert.Equal(t, resp, test.expected) + assert.Equal(t, test.expected, resp) }) } } diff --git a/pkg/cmd/cli/backup/create_test.go b/pkg/cmd/cli/backup/create_test.go index 83e509879..a099c3adb 100644 --- a/pkg/cmd/cli/backup/create_test.go +++ b/pkg/cmd/cli/backup/create_test.go @@ -138,7 +138,7 @@ func TestCreateOptions_OrderedResources(t *testing.T) { "pods": "ns1/p1,ns1/p2", "persistentvolumeclaims": "ns2/pvc1,ns2/pvc2", } - assert.Equal(t, orderedResources, expectedResources) + assert.Equal(t, expectedResources, orderedResources) orderedResources, err = ParseOrderedResources("pods= ns1/p1,ns1/p2 ; persistentvolumes=pv1,pv2") assert.NoError(t, err) @@ -147,7 +147,7 @@ func TestCreateOptions_OrderedResources(t *testing.T) { "pods": "ns1/p1,ns1/p2", "persistentvolumes": "pv1,pv2", } - assert.Equal(t, orderedResources, expectedMixedResources) + assert.Equal(t, expectedMixedResources, orderedResources) } func TestCreateCommand(t *testing.T) { diff --git a/pkg/cmd/cli/backup/logs_test.go b/pkg/cmd/cli/backup/logs_test.go index 973c1e463..2d6ee828e 100644 --- a/pkg/cmd/cli/backup/logs_test.go +++ b/pkg/cmd/cli/backup/logs_test.go @@ -79,7 +79,7 @@ func TestNewLogsCommand(t *testing.T) { err = l.Run(c, f) require.Error(t, err) - require.Contains(t, err.Error(), fmt.Sprintf("logs for backup \"%s\" are not available until it's finished processing", backupName)) + require.ErrorContains(t, err, fmt.Sprintf("logs for backup \"%s\" are not available until it's finished processing", backupName)) }) t.Run("Backup not exist test", func(t *testing.T) { diff --git a/pkg/cmd/cli/nodeagent/server_test.go b/pkg/cmd/cli/nodeagent/server_test.go index 187cc6dc0..fd2c6de94 100644 --- a/pkg/cmd/cli/nodeagent/server_test.go +++ b/pkg/cmd/cli/nodeagent/server_test.go @@ -20,7 +20,6 @@ import ( "fmt" "os" "path/filepath" - "strings" "testing" "github.com/pkg/errors" @@ -166,7 +165,7 @@ func Test_getDataPathConfigs(t *testing.T) { if test.expectLog == "" { assert.Equal(t, "", logBuffer) } else { - assert.True(t, strings.Contains(logBuffer, test.expectLog)) + assert.Contains(t, logBuffer, test.expectLog) } }) } @@ -384,7 +383,7 @@ func Test_getDataPathConcurrentNum(t *testing.T) { if test.expectLog == "" { assert.Equal(t, "", logBuffer) } else { - assert.True(t, strings.Contains(logBuffer, test.expectLog)) + assert.Contains(t, logBuffer, test.expectLog) } }) } diff --git a/pkg/controller/backup_controller_test.go b/pkg/controller/backup_controller_test.go index ca439611d..c175c7041 100644 --- a/pkg/controller/backup_controller_test.go +++ b/pkg/controller/backup_controller_test.go @@ -139,7 +139,7 @@ func TestProcessBackupNonProcessedItems(t *testing.T) { require.NoError(t, c.kbClient.Create(context.Background(), test.backup)) } actualResult, err := c.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.backup.Namespace, Name: test.backup.Name}}) - assert.Equal(t, actualResult, ctrl.Result{}) + assert.Equal(t, ctrl.Result{}, actualResult) assert.NoError(t, err) // Any backup that would actually proceed to validation will cause a segfault because this @@ -229,7 +229,7 @@ func TestProcessBackupValidationFailures(t *testing.T) { require.NoError(t, c.kbClient.Create(context.Background(), test.backup)) actualResult, err := c.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.backup.Namespace, Name: test.backup.Name}}) - assert.Equal(t, actualResult, ctrl.Result{}) + assert.Equal(t, ctrl.Result{}, actualResult) assert.NoError(t, err) res := &velerov1api.Backup{} err = c.kbClient.Get(context.Background(), kbclient.ObjectKey{Namespace: test.backup.Namespace, Name: test.backup.Name}, res) @@ -1377,7 +1377,7 @@ func TestProcessBackupCompletions(t *testing.T) { } actualResult, err := c.Reconcile(ctx, ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.backup.Namespace, Name: test.backup.Name}}) - assert.Equal(t, actualResult, ctrl.Result{}) + assert.Equal(t, ctrl.Result{}, actualResult) assert.NoError(t, err) // Disable CSI feature to not impact other test cases. diff --git a/pkg/controller/backup_repository_controller_test.go b/pkg/controller/backup_repository_controller_test.go index 4c8f5fedc..519f3cb02 100644 --- a/pkg/controller/backup_repository_controller_test.go +++ b/pkg/controller/backup_repository_controller_test.go @@ -66,10 +66,10 @@ func TestPatchBackupRepository(t *testing.T) { assert.NoError(t, err) err = reconciler.patchBackupRepository(context.Background(), rr, repoReady()) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) err = reconciler.patchBackupRepository(context.Background(), rr, repoNotReady("not ready")) assert.NoError(t, err) - assert.NotEqual(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) + assert.NotEqual(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) } func TestCheckNotReadyRepo(t *testing.T) { @@ -94,7 +94,7 @@ func TestCheckNotReadyRepo(t *testing.T) { assert.NoError(t, err) _, err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) assert.Equal(t, "s3:test.amazonaws.com/bucket/restic/volume-ns-1", rr.Spec.ResticIdentifier) } @@ -135,7 +135,7 @@ func TestInitializeRepo(t *testing.T) { assert.NoError(t, err) err = reconciler.initializeRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) + assert.Equal(t, velerov1api.BackupRepositoryPhaseReady, rr.Status.Phase) } func TestBackupRepoReconcile(t *testing.T) { diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index 932cb1014..4ee6e96e2 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -446,7 +446,7 @@ func TestDataDownloadReconcile(t *testing.T) { }) if test.expectedStatusMsg != "" { - assert.Contains(t, err.Error(), test.expectedStatusMsg) + require.ErrorContains(t, err, test.expectedStatusMsg) } else { require.NoError(t, err) } diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index b8e4c88a2..4ac454cbf 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -561,11 +561,11 @@ func TestReconcile(t *testing.T) { }, }) - assert.Equal(t, actualResult, test.expectedRequeue) + assert.Equal(t, test.expectedRequeue, actualResult) if test.expectedErrMsg == "" { require.NoError(t, err) } else { - assert.Contains(t, err.Error(), test.expectedErrMsg) + require.ErrorContains(t, err, test.expectedErrMsg) } du := velerov2alpha1api.DataUpload{} diff --git a/pkg/discovery/helper_test.go b/pkg/discovery/helper_test.go index 1619203b6..37a16b536 100644 --- a/pkg/discovery/helper_test.go +++ b/pkg/discovery/helper_test.go @@ -23,6 +23,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/version" @@ -311,7 +312,7 @@ func TestHelper_ResourceFor(t *testing.T) { if tc.err == "" { assert.NoError(t, err) } else { - assert.Contains(t, err.Error(), tc.err) + require.ErrorContains(t, err, tc.err) } assert.Equal(t, *tc.expectedGVR, gvr) assert.Equal(t, *tc.expectedAPIResource, apiResource) @@ -416,7 +417,7 @@ func TestHelper_KindFor(t *testing.T) { if tc.err == "" { assert.NoError(t, err) } else { - assert.Contains(t, err.Error(), tc.err) + require.ErrorContains(t, err, tc.err) } assert.Equal(t, *tc.expectedGVR, gvr) assert.Equal(t, *tc.expectedAPIResource, apiResource) diff --git a/pkg/exposer/csi_snapshot_test.go b/pkg/exposer/csi_snapshot_test.go index 643fd70fc..5591237d7 100644 --- a/pkg/exposer/csi_snapshot_test.go +++ b/pkg/exposer/csi_snapshot_test.go @@ -427,7 +427,7 @@ func TestExpose(t *testing.T) { assert.Equal(t, expectedVS.Annotations, vsObject.Annotations) assert.Equal(t, *expectedVS.Spec.VolumeSnapshotClassName, *vsObject.Spec.VolumeSnapshotClassName) - assert.Equal(t, *expectedVS.Spec.Source.VolumeSnapshotContentName, expectedVSC.Name) + assert.Equal(t, expectedVSC.Name, *expectedVS.Spec.Source.VolumeSnapshotContentName) assert.Equal(t, expectedVSC.Annotations, vscObj.Annotations) assert.Equal(t, expectedVSC.Spec.DeletionPolicy, vscObj.Spec.DeletionPolicy) diff --git a/pkg/install/resources_test.go b/pkg/install/resources_test.go index 2722c26df..58bbac58c 100644 --- a/pkg/install/resources_test.go +++ b/pkg/install/resources_test.go @@ -45,12 +45,12 @@ func TestResources(t *testing.T) { // For k8s version v1.25 and later, need to add the following labels to make // velero installation namespace has privileged version to work with // PSA(Pod Security Admission) and PSS(Pod Security Standards). - assert.Equal(t, ns.Labels["pod-security.kubernetes.io/enforce"], "privileged") - assert.Equal(t, ns.Labels["pod-security.kubernetes.io/enforce-version"], "latest") - assert.Equal(t, ns.Labels["pod-security.kubernetes.io/audit"], "privileged") - assert.Equal(t, ns.Labels["pod-security.kubernetes.io/audit-version"], "latest") - assert.Equal(t, ns.Labels["pod-security.kubernetes.io/warn"], "privileged") - assert.Equal(t, ns.Labels["pod-security.kubernetes.io/warn-version"], "latest") + assert.Equal(t, "privileged", ns.Labels["pod-security.kubernetes.io/enforce"]) + assert.Equal(t, "latest", ns.Labels["pod-security.kubernetes.io/enforce-version"]) + assert.Equal(t, "privileged", ns.Labels["pod-security.kubernetes.io/audit"]) + assert.Equal(t, "latest", ns.Labels["pod-security.kubernetes.io/audit-version"]) + assert.Equal(t, "privileged", ns.Labels["pod-security.kubernetes.io/warn"]) + assert.Equal(t, "latest", ns.Labels["pod-security.kubernetes.io/warn-version"]) crb := ClusterRoleBinding(DefaultVeleroNamespace) // The CRB is a cluster-scoped resource diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index 1e8c14926..816fef6c0 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -813,7 +813,7 @@ func TestSanitizeName(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { sanitizedName := sanitizeName(tc.pluginName) - assert.Equal(t, sanitizedName, tc.expectedName) + assert.Equal(t, tc.expectedName, sanitizedName) }) } } diff --git a/pkg/plugin/clientmgmt/process/client_builder_test.go b/pkg/plugin/clientmgmt/process/client_builder_test.go index a8bf3ad46..cff06d93f 100644 --- a/pkg/plugin/clientmgmt/process/client_builder_test.go +++ b/pkg/plugin/clientmgmt/process/client_builder_test.go @@ -37,7 +37,7 @@ func TestNewClientBuilder(t *testing.T) { logger := test.NewLogger() logLevel := logrus.InfoLevel cb := newClientBuilder("velero", logger, logLevel) - assert.Equal(t, cb.commandName, "velero") + assert.Equal(t, "velero", cb.commandName) assert.Equal(t, []string{"--log-level", "info"}, cb.commandArgs) assert.Equal(t, newLogrusAdapter(logger, logLevel), cb.pluginLogger) diff --git a/pkg/restore/actions/change_image_name_action_test.go b/pkg/restore/actions/change_image_name_action_test.go index 78898613a..134fc154c 100644 --- a/pkg/restore/actions/change_image_name_action_test.go +++ b/pkg/restore/actions/change_image_name_action_test.go @@ -173,7 +173,7 @@ func TestChangeImageRepositoryActionExecute(t *testing.T) { pod := new(corev1.Pod) err = runtime.DefaultUnstructuredConverter.FromUnstructured(res.UpdatedItem.UnstructuredContent(), pod) require.NoError(t, err) - assert.Equal(t, pod.Spec.Containers[0].Image, tc.want) + assert.Equal(t, tc.want, pod.Spec.Containers[0].Image) } }) } diff --git a/pkg/restore/actions/change_pvc_node_selector_test.go b/pkg/restore/actions/change_pvc_node_selector_test.go index 83d9267a8..eee2bca09 100644 --- a/pkg/restore/actions/change_pvc_node_selector_test.go +++ b/pkg/restore/actions/change_pvc_node_selector_test.go @@ -20,7 +20,6 @@ import ( "bytes" "context" "fmt" - "strings" "testing" "github.com/sirupsen/logrus" @@ -168,7 +167,7 @@ func TestChangePVCNodeSelectorActionExecute(t *testing.T) { // Make sure mapped selected-node exists. logOutput := buf.String() - assert.False(t, strings.Contains(logOutput, "Selected-node's mapped node doesn't exist")) + assert.NotContains(t, logOutput, "Selected-node's mapped node doesn't exist") // validate for both error and non-error cases switch { diff --git a/pkg/restore/actions/csi/pvc_action_test.go b/pkg/restore/actions/csi/pvc_action_test.go index 095bd8dbe..53e1908f2 100644 --- a/pkg/restore/actions/csi/pvc_action_test.go +++ b/pkg/restore/actions/csi/pvc_action_test.go @@ -250,7 +250,7 @@ func TestResetPVCSpec(t *testing.T) { assert.Emptyf(t, tc.pvc.Spec.VolumeName, "expected change to Spec.VolumeName missing, Want: \"\"; Got: %s", tc.pvc.Spec.VolumeName) assert.Equalf(t, *tc.pvc.Spec.VolumeMode, *before.Spec.VolumeMode, "expected change to Spec.VolumeName missing, Want: \"\"; Got: %s", tc.pvc.Spec.VolumeName) assert.NotNil(t, tc.pvc.Spec.DataSource, "expected change to Spec.DataSource missing") - assert.Equalf(t, tc.pvc.Spec.DataSource.Kind, "VolumeSnapshot", "expected change to Spec.DataSource.Kind missing, Want: VolumeSnapshot, Got: %s", tc.pvc.Spec.DataSource.Kind) + assert.Equalf(t, "VolumeSnapshot", tc.pvc.Spec.DataSource.Kind, "expected change to Spec.DataSource.Kind missing, Want: VolumeSnapshot, Got: %s", tc.pvc.Spec.DataSource.Kind) assert.Equalf(t, tc.pvc.Spec.DataSource.Name, tc.vsName, "expected change to Spec.DataSource.Name missing, Want: %s, Got: %s", tc.vsName, tc.pvc.Spec.DataSource.Name) }) } diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 8bf24d845..4aa0d6d6a 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -3678,7 +3678,7 @@ func assertNonEmptyResults(t *testing.T, typeMsg string, res ...Result) { total += len(r.Namespaces) total += len(r.Velero) } - assert.Greater(t, total, 0, "Expected at least one "+typeMsg) + assert.Positive(t, total, "Expected at least one "+typeMsg) } type harness struct { diff --git a/pkg/uploader/kopia/shim_test.go b/pkg/uploader/kopia/shim_test.go index 8aaefa0c2..c3fe1ad42 100644 --- a/pkg/uploader/kopia/shim_test.go +++ b/pkg/uploader/kopia/shim_test.go @@ -28,6 +28,7 @@ import ( "github.com/kopia/kopia/repo/object" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo/mocks" @@ -109,7 +110,7 @@ func TestOpenObject(t *testing.T) { ctx := context.Background() reader, err := NewShimRepo(tc.backupRepo).OpenObject(ctx, object.ID{}) if tc.isOpenObjectError { - assert.Contains(t, err.Error(), "failed to open object") + require.ErrorContains(t, err, "failed to open object") } else if tc.isReaderNil { assert.Nil(t, reader) } else { @@ -151,7 +152,7 @@ func TestFindManifests(t *testing.T) { ctx := context.Background() _, err := NewShimRepo(tc.backupRepo).FindManifests(ctx, map[string]string{}) if tc.isGetManifestError { - assert.Contains(t, err.Error(), "failed") + require.ErrorContains(t, err, "failed") } else { assert.NoError(t, err) } diff --git a/pkg/uploader/kopia/snapshot_test.go b/pkg/uploader/kopia/snapshot_test.go index 544d66120..de621ce85 100644 --- a/pkg/uploader/kopia/snapshot_test.go +++ b/pkg/uploader/kopia/snapshot_test.go @@ -34,6 +34,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" repomocks "github.com/vmware-tanzu/velero/pkg/repository/mocks" "github.com/vmware-tanzu/velero/pkg/uploader" @@ -560,7 +561,7 @@ func TestFindPreviousSnapshotManifest(t *testing.T) { // Check if the returned error matches the expected error if tc.expectedError != nil { - assert.Contains(t, err.Error(), tc.expectedError.Error()) + require.ErrorContains(t, err, tc.expectedError.Error()) } else { assert.NoError(t, err) } @@ -653,7 +654,7 @@ func TestBackup(t *testing.T) { } // Check if the returned error matches the expected error if tc.expectedError != nil { - assert.Contains(t, err.Error(), tc.expectedError.Error()) + require.ErrorContains(t, err, tc.expectedError.Error()) } else { assert.NoError(t, err) } @@ -792,7 +793,7 @@ func TestRestore(t *testing.T) { // Check if the returned error matches the expected error if tc.expectedError != nil { - assert.Contains(t, err.Error(), tc.expectedError.Error()) + require.ErrorContains(t, err, tc.expectedError.Error()) } else { assert.NoError(t, err) } diff --git a/pkg/uploader/provider/kopia_test.go b/pkg/uploader/provider/kopia_test.go index 146c76ef2..7a7d5d271 100644 --- a/pkg/uploader/provider/kopia_test.go +++ b/pkg/uploader/provider/kopia_test.go @@ -28,6 +28,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -380,7 +381,7 @@ func TestNewKopiaUploaderProvider(t *testing.T) { // Assertions if tc.expectedError != "" { - assert.Contains(t, err.Error(), tc.expectedError) + require.ErrorContains(t, err, tc.expectedError) } else { assert.NoError(t, err) } diff --git a/pkg/uploader/provider/provider_test.go b/pkg/uploader/provider/provider_test.go index 641d9cc6b..492a58a68 100644 --- a/pkg/uploader/provider/provider_test.go +++ b/pkg/uploader/provider/provider_test.go @@ -22,6 +22,7 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -90,7 +91,7 @@ func TestNewUploaderProvider(t *testing.T) { if testCase.ExpectedError == "" { assert.NoError(t, err) } else { - assert.Contains(t, err.Error(), testCase.ExpectedError) + require.ErrorContains(t, err, testCase.ExpectedError) } }) } diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index b1a1351c2..6feb9bd94 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -479,7 +479,7 @@ func TestSinglePathMatch(t *testing.T) { _, err := SinglePathMatch("./*/subpath", fakeFS, logrus.StandardLogger()) assert.Error(t, err) - assert.Contains(t, err.Error(), "expected one matching path") + require.ErrorContains(t, err, "expected one matching path") } func TestAddAnnotations(t *testing.T) { diff --git a/pkg/util/logging/dual_mode_logger_test.go b/pkg/util/logging/dual_mode_logger_test.go index 26bd252e1..c22d19dca 100644 --- a/pkg/util/logging/dual_mode_logger_test.go +++ b/pkg/util/logging/dual_mode_logger_test.go @@ -20,7 +20,6 @@ import ( "compress/gzip" "io" "os" - "strings" "testing" "github.com/sirupsen/logrus" @@ -49,8 +48,8 @@ func TestDualModeLogger(t *testing.T) { logStr, err := readLogString(logFile) require.NoError(t, err) - assert.True(t, strings.Contains(logStr, logMsgExpect)) - assert.False(t, strings.Contains(logStr, logMsgUnexpect)) + assert.Contains(t, logStr, logMsgExpect) + assert.NotContains(t, logStr, logMsgUnexpect) logger.Dispose(velerotest.NewLogger()) diff --git a/test/pkg/client/client_test.go b/test/pkg/client/client_test.go index df8df51e4..49c0fa030 100644 --- a/test/pkg/client/client_test.go +++ b/test/pkg/client/client_test.go @@ -45,7 +45,7 @@ func TestBuildUserAgent(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { resp := buildUserAgent(test.command, test.version, test.gitSha, test.os, test.arch) - assert.Equal(t, resp, test.expected) + assert.Equal(t, test.expected, resp) }) } } From c8a0c345dce489b550fa5dfcf2f6289e7a5f98d9 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Thu, 18 Jul 2024 15:57:11 -0400 Subject: [PATCH 09/69] Avoid wrapping failed PVB status with empty message. Also change "get" to "found" as requested in issue #7857. Signed-off-by: Matthew Arnold --- pkg/cmd/cli/nodeagent/server.go | 2 +- pkg/controller/pod_volume_backup_controller.go | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 2748569f3..16543794d 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -412,7 +412,7 @@ func (s *nodeAgentServer) markInProgressPVBsFailed(client ctrlclient.Client) { } if err := controller.UpdatePVBStatusToFailed(s.ctx, client, &pvbs.Items[i], - fmt.Errorf("get a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, velerov1api.PodVolumeBackupPhaseFailed), + fmt.Errorf("found a podvolumebackup with status %q during the server starting, mark it as %q", velerov1api.PodVolumeBackupPhaseInProgress, velerov1api.PodVolumeBackupPhaseFailed), "", time.Now(), s.logger); err != nil { s.logger.WithError(errors.WithStack(err)).Errorf("failed to patch podvolumebackup %q", pvb.GetName()) continue diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index e1a00008c..e626df2b3 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -19,6 +19,7 @@ package controller import ( "context" "fmt" + "strings" "time" "github.com/pkg/errors" @@ -373,7 +374,11 @@ func UpdatePVBStatusToFailed(ctx context.Context, c client.Client, pvb *velerov1 if dataPathError, ok := errOut.(datapath.DataPathError); ok { pvb.Status.SnapshotID = dataPathError.GetSnapshotID() } - pvb.Status.Message = errors.WithMessage(errOut, msg).Error() + if len(strings.TrimSpace(msg)) == 0 { + pvb.Status.Message = errOut.Error() + } else { + pvb.Status.Message = errors.WithMessage(errOut, msg).Error() + } err := c.Patch(ctx, pvb, client.MergeFrom(original)) if err != nil { log.WithError(err).Error("error updating PodVolumeBackup status") From f8e697d1e8f3bbcbdfbdac24693ebcd53d5b57b9 Mon Sep 17 00:00:00 2001 From: Matthew Arnold Date: Thu, 18 Jul 2024 16:16:08 -0400 Subject: [PATCH 10/69] Add changelog file. Signed-off-by: Matthew Arnold --- changelogs/unreleased/8028-mrnold | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/8028-mrnold diff --git a/changelogs/unreleased/8028-mrnold b/changelogs/unreleased/8028-mrnold new file mode 100644 index 000000000..ce801be5a --- /dev/null +++ b/changelogs/unreleased/8028-mrnold @@ -0,0 +1 @@ +Avoid wrapping failed PVB status with empty message. From 0d2f3db6961ddc7023ff8370d19588e9d57e513c Mon Sep 17 00:00:00 2001 From: lyndon-li <98304688+Lyndon-Li@users.noreply.github.com> Date: Fri, 19 Jul 2024 22:18:15 +0800 Subject: [PATCH 11/69] add the design for backup PVC configurations (#7982) Signed-off-by: Lyndon-Li --- changelogs/unreleased/7982-Lyndon-Li | 1 + design/backup-pvc-config.md | 94 ++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 changelogs/unreleased/7982-Lyndon-Li create mode 100644 design/backup-pvc-config.md diff --git a/changelogs/unreleased/7982-Lyndon-Li b/changelogs/unreleased/7982-Lyndon-Li new file mode 100644 index 000000000..e0066a0db --- /dev/null +++ b/changelogs/unreleased/7982-Lyndon-Li @@ -0,0 +1 @@ +For issue #7700 and #7747, add the design for backup PVC configurations \ No newline at end of file diff --git a/design/backup-pvc-config.md b/design/backup-pvc-config.md new file mode 100644 index 000000000..7f37b1505 --- /dev/null +++ b/design/backup-pvc-config.md @@ -0,0 +1,94 @@ +# Backup PVC Configuration Design + +## Glossary & Abbreviation + +**Velero Generic Data Path (VGDP)**: VGDP is the collective modules that is introduced in [Unified Repository design][1]. Velero uses these modules to finish data transfer for various purposes (i.e., PodVolume backup/restore, Volume Snapshot Data Movement). VGDP modules include uploaders and the backup repository. + +**Exposer**: Exposer is a module that is introduced in [Volume Snapshot Data Movement Design][2]. Velero uses this module to expose the volume snapshots to Velero node-agent pods or node-agent associated pods so as to complete the data movement from the snapshots. + +**backupPVC**: The intermediate PVC created by the exposer for VGDP to access data from, see [Volume Snapshot Data Movement Design][2] for more details. + +**backupPod**: The pod consumes the backupPVC so that VGDP could access data from the backupPVC, see [Volume Snapshot Data Movement Design][2] for more details. + +**sourcePVC**: The PVC to be backed up, see [Volume Snapshot Data Movement Design][2] for more details. + +## Background + +As elaberated in [Volume Snapshot Data Movement Design][2], a backupPVC may be created by the Exposer and the VGDP reads data from the backupPVC. +In some scenarios, users may need to configure some advanced settings of the backupPVC so that the data movement could work in best performance in their environments. Specifically: +- For some storage providers, when creating a read-only volume from a snapshot, it is very fast; whereas, if a writable volume is created from the snapshot, they need to clone the entire disk data, which is time consuming. If the backupPVC's `accessModes` is set as `ReadOnlyMany`, the volume driver is able to tell the storage to create a read-only volume, which may dramatically shorten the snapshot expose time. On the other hand, `ReadOnlyMany` is not supported by all volumes. Therefore, users should be allowed to configure the `accessModes` for the backupPVC. +- Some storage providers create one or more replicas when creating a volume, the number of replicas is defined in the storage class. However, it doesn't make any sense to keep replicas when an intermediate volume used by the backup. Therefore, users should be allowed to configure another storage class specifically used by the backupPVC. + +## Goals + +- Create a mechanism for users to specify various configurations for backupPVC + +## Non-Goals + +## Solution + +We will use the ```node-agent-config``` configMap to host the backupPVC configurations. +This configMap is not created by Velero, users should create it manually on demand. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only. +Node-agent server checks these configurations at startup time and use it to initiate the related Exposer modules. Therefore, users could edit this configMap any time, but in order to make the changes effective, node-agent server needs to be restarted. +Inside ```node-agent-config``` configMap we will add one new kind of configuration as the data in the configMap, the name is ```backupPVC```. +Users may want to set different backupPVC configurations for different volumes, therefore, we define the configurations as a map and allow users to specific configurations by storage class. Specifically, the key of the map element is the storage class name used by the sourcePVC and the value is the set of configurations for the backupPVC created for the sourcePVC. + +The data structure for ```node-agent-config``` is as below: +```go +type Configs struct { + // LoadConcurrency is the config for data path load concurrency per node. + LoadConcurrency *LoadConcurrency `json:"loadConcurrency,omitempty"` + + // LoadAffinity is the config for data path load affinity. + LoadAffinity []*LoadAffinity `json:"loadAffinity,omitempty"` + + // BackupPVC is the config for backupPVC of snapshot data movement. + BackupPVC map[string]BackupPVC `json:"backupPVC,omitempty"` +} + +type BackupPVC struct { + // StorageClass is the name of storage class to be used by the backupPVC. + StorageClass string `json:"storageClass,omitempty"` + + // ReadOnly sets the backupPVC's access mode as read only. + ReadOnly bool `json:"readOnly,omitempty"` +} +``` + +### Sample +A sample of the ```node-agent-config``` configMap is as below: +```json +{ + "backupPVC": { + "storage-class-1": { + "storageClass": "snapshot-storage-class", + "readOnly": true + }, + "storage-class-2": { + "storageClass": "snapshot-storage-class" + }, + "storage-class-3": { + "readOnly": true + } + } +} +``` + +To create the configMap, users need to save something like the above sample to a json file and then run below command: +``` +kubectl create cm node-agent-config -n velero --from-file= +``` + +### Implementation +The `backupPVC` is passed to the exposer and the exposer sets the related specification and create the backupPVC. +If `backupPVC.storageClass` doesn't exist or set as empty, the sourcePVC's storage class will be used. +If `backupPVC.readOnly` is set to true, `ReadOnlyMany` will be the only value set to the backupPVC's `accessModes`, otherwise, `ReadWriteOnce` is used. + +Once `backupPVC.storageClass` is set, users must make sure that the specified storage class exists in the cluster and can be used the the backupPVC, otherwise, the corresponding DataUpload CR will stay in `Accepted` phase until the prepare timeout (by default 30min). +Once `backupPVC.readOnly` is set to true, users must make sure that the storage supports to create a `ReadOnlyMany` PVC from a snapshot, otherwise, the corresponding DataUpload CR will stay in `Accepted` phase until the prepare timeout (by default 30min). + +Once above problems happen, the DataUpload CR is cancelled after prepare timeout and the backupPVC and backupPod will be deleted, so there is no way to tell the cause is one of the above problems or others. +To help the troubleshooting, we can add some diagnostic mechanism to discover the status of the backupPod before deleting it as a result of the prepare timeout. + +[1]: Implemented/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +[2]: volume-snapshot-data-movement/volume-snapshot-data-movement.md \ No newline at end of file From fd6c74715a73e6bd656f3094ea350b0b7aef1c68 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Tue, 16 Jul 2024 14:17:47 -0700 Subject: [PATCH 12/69] Expose PVPatchMaximumDuration timeout for custom configuration Signed-off-by: Shubham Pampattiwar remove debug log Signed-off-by: Shubham Pampattiwar use resource timeout server arg Signed-off-by: Shubham Pampattiwar add changelog Signed-off-by: Shubham Pampattiwar remove hardcoded PVPatchMaximumtimeout const usagDe Signed-off-by: Shubham Pampattiwar --- changelogs/unreleased/8021-shubham-pampattiwar | 1 + pkg/cmd/server/server.go | 1 + pkg/controller/restore_finalizer_controller.go | 12 +++++++----- pkg/controller/restore_finalizer_controller_test.go | 2 ++ 4 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/8021-shubham-pampattiwar diff --git a/changelogs/unreleased/8021-shubham-pampattiwar b/changelogs/unreleased/8021-shubham-pampattiwar new file mode 100644 index 000000000..40d4a95a7 --- /dev/null +++ b/changelogs/unreleased/8021-shubham-pampattiwar @@ -0,0 +1 @@ +Make PVPatchMaximumDuration timeout configurable \ No newline at end of file diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 692f4d96e..d8938ca56 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -1022,6 +1022,7 @@ func (s *server) runControllers(defaultVolumeSnapshotLocations map[string]string s.metrics, s.crClient, multiHookTracker, + s.config.resourceTimeout, ).SetupWithManager(s.mgr); err != nil { s.logger.Fatal(err, "unable to create controller", "controller", controller.RestoreFinalizer) } diff --git a/pkg/controller/restore_finalizer_controller.go b/pkg/controller/restore_finalizer_controller.go index 856de13e6..a75ea199a 100644 --- a/pkg/controller/restore_finalizer_controller.go +++ b/pkg/controller/restore_finalizer_controller.go @@ -45,10 +45,6 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/results" ) -const ( - PVPatchMaximumDuration = 10 * time.Minute -) - type restoreFinalizerReconciler struct { client.Client namespace string @@ -59,6 +55,7 @@ type restoreFinalizerReconciler struct { clock clock.WithTickerAndDelayedExecution crClient client.Client multiHookTracker *hook.MultiHookTracker + resourceTimeout time.Duration } func NewRestoreFinalizerReconciler( @@ -70,6 +67,7 @@ func NewRestoreFinalizerReconciler( metrics *metrics.ServerMetrics, crClient client.Client, multiHookTracker *hook.MultiHookTracker, + resourceTimeout time.Duration, ) *restoreFinalizerReconciler { return &restoreFinalizerReconciler{ Client: client, @@ -81,6 +79,7 @@ func NewRestoreFinalizerReconciler( clock: &clock.RealClock{}, crClient: crClient, multiHookTracker: multiHookTracker, + resourceTimeout: resourceTimeout, } } @@ -163,6 +162,7 @@ func (r *restoreFinalizerReconciler) Reconcile(ctx context.Context, req ctrl.Req volumeInfo: volumeInfo, restoredPVCList: restoredPVCList, multiHookTracker: r.multiHookTracker, + resourceTimeout: r.resourceTimeout, } warnings, errs := finalizerCtx.execute() @@ -246,6 +246,7 @@ type finalizerContext struct { volumeInfo []*volume.BackupVolumeInfo restoredPVCList map[string]struct{} multiHookTracker *hook.MultiHookTracker + resourceTimeout time.Duration } func (ctx *finalizerContext) execute() (results.Result, results.Result) { //nolint:unparam //temporarily ignore the lint report: result 0 is always nil (unparam) @@ -268,6 +269,7 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result var pvWaitGroup sync.WaitGroup var resultLock sync.Mutex + maxConcurrency := 3 semaphore := make(chan struct{}, maxConcurrency) @@ -294,7 +296,7 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result log := ctx.logger.WithField("PVC", volInfo.PVCName).WithField("PVCNamespace", restoredNamespace) log.Debug("patching dynamic PV is in progress") - err := wait.PollUntilContextTimeout(context.Background(), 10*time.Second, PVPatchMaximumDuration, true, func(context.Context) (bool, error) { + err := wait.PollUntilContextTimeout(context.Background(), 10*time.Second, ctx.resourceTimeout, true, func(context.Context) (bool, error) { // wait for PVC to be bound pvc := &v1.PersistentVolumeClaim{} err := ctx.crClient.Get(context.Background(), client.ObjectKey{Name: volInfo.PVCName, Namespace: restoredNamespace}, pvc) diff --git a/pkg/controller/restore_finalizer_controller_test.go b/pkg/controller/restore_finalizer_controller_test.go index 9ca565849..32b28765c 100644 --- a/pkg/controller/restore_finalizer_controller_test.go +++ b/pkg/controller/restore_finalizer_controller_test.go @@ -138,6 +138,7 @@ func TestRestoreFinalizerReconcile(t *testing.T) { metrics.NewServerMetrics(), fakeClient, hook.NewMultiHookTracker(), + 10*time.Minute, ) r.clock = testclocks.NewFakeClock(now) @@ -200,6 +201,7 @@ func TestUpdateResult(t *testing.T) { metrics.NewServerMetrics(), fakeClient, hook.NewMultiHookTracker(), + 10*time.Minute, ) restore := builder.ForRestore(velerov1api.DefaultNamespace, "restore-1").Result() res := map[string]results.Result{"warnings": {}, "errors": {}} From dc4b95e7de7e70dcd4cda4a7a95808cea6986646 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 22 Jul 2024 10:35:25 +0800 Subject: [PATCH 13/69] correct data mover ms design PR number Signed-off-by: Lyndon-Li --- changelogs/unreleased/7955-Lyndon-Li | 2 +- changelogs/unreleased/7988-Lyndon-Li | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/changelogs/unreleased/7955-Lyndon-Li b/changelogs/unreleased/7955-Lyndon-Li index 3630890b6..ee67bb55d 100644 --- a/changelogs/unreleased/7955-Lyndon-Li +++ b/changelogs/unreleased/7955-Lyndon-Li @@ -1 +1 @@ -New data path for data mover ms according to design #7574 \ No newline at end of file +New data path for data mover ms according to design #7576 \ No newline at end of file diff --git a/changelogs/unreleased/7988-Lyndon-Li b/changelogs/unreleased/7988-Lyndon-Li index 3630890b6..ee67bb55d 100644 --- a/changelogs/unreleased/7988-Lyndon-Li +++ b/changelogs/unreleased/7988-Lyndon-Li @@ -1 +1 @@ -New data path for data mover ms according to design #7574 \ No newline at end of file +New data path for data mover ms according to design #7576 \ No newline at end of file From a1d6d1d6980db927dfd27547487c80960c21c3fa Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 22 Jul 2024 10:47:05 +0800 Subject: [PATCH 14/69] fix UT linter error Signed-off-by: Lyndon-Li --- pkg/cmd/cli/datamover/data_mover_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/cli/datamover/data_mover_test.go b/pkg/cmd/cli/datamover/data_mover_test.go index 5442fd9a0..51d9376d3 100644 --- a/pkg/cmd/cli/datamover/data_mover_test.go +++ b/pkg/cmd/cli/datamover/data_mover_test.go @@ -124,7 +124,7 @@ func TestExitWithMessage(t *testing.T) { reader.Close() - assert.Equal(t, string(message), test.expectedMessage) + assert.Equal(t, test.expectedMessage, string(message)) } }) } From afca7dd6fe6d25583df7bd80466c456c46bdc7ca Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Mon, 22 Jul 2024 15:58:00 +0800 Subject: [PATCH 15/69] Replace RunSpecsWithDefaultAndCustomReporters with RunSpecs. Signed-off-by: Xun Jiang --- test/Makefile | 4 ++-- test/e2e/e2e_suite_test.go | 4 +--- test/perf/e2e_suite_test.go | 4 +--- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/Makefile b/test/Makefile index 956054a97..fbc0e08e3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -166,7 +166,7 @@ run-e2e: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plugin provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v $(FOCUS_STR) $(SKIP_STR) ./e2e -- $(COMMON_ARGS) \ + @$(GINKGO) run -v $(FOCUS_STR) $(SKIP_STR) --junit-report report.xml ./e2e -- $(COMMON_ARGS) \ -upgrade-from-velero-cli=$(UPGRADE_FROM_VELERO_CLI) \ -upgrade-from-velero-version=$(UPGRADE_FROM_VELERO_VERSION) \ -migrate-from-velero-cli=$(MIGRATE_FROM_VELERO_CLI) \ @@ -200,7 +200,7 @@ run-perf: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plugin provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) -v $(FOCUS_STR) $(SKIP_STR) ./perf -- $(COMMON_ARGS) \ + @$(GINKGO) run -v $(FOCUS_STR) $(SKIP_STR) --junit-report report.xml ./perf -- $(COMMON_ARGS) \ -nfs-server-path=$(NFS_SERVER_PATH) \ -test-case-describe=$(TEST_CASE_DESCRIBE) \ -backup-for-restore=$(BACKUP_FOR_RESTORE) \ diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index ec981e0b6..45b093b13 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -26,7 +26,6 @@ import ( "time" . "github.com/onsi/ginkgo/v2" - "github.com/onsi/ginkgo/v2/reporters" . "github.com/onsi/gomega" "github.com/vmware-tanzu/velero/pkg/cmd/cli/install" @@ -245,8 +244,7 @@ func TestE2e(t *testing.T) { } RegisterFailHandler(Fail) - junitReporter := reporters.NewJUnitReporter("report.xml") - RunSpecsWithDefaultAndCustomReporters(t, "E2e Suite", []Reporter{junitReporter}) + RunSpecs(t, "E2e Suite") } var _ = BeforeSuite(func() { diff --git a/test/perf/e2e_suite_test.go b/test/perf/e2e_suite_test.go index cc7777db9..a2aa85d63 100644 --- a/test/perf/e2e_suite_test.go +++ b/test/perf/e2e_suite_test.go @@ -24,7 +24,6 @@ import ( "time" . "github.com/onsi/ginkgo/v2" - "github.com/onsi/ginkgo/v2/reporters" . "github.com/onsi/gomega" "github.com/pkg/errors" @@ -114,8 +113,7 @@ func TestE2e(t *testing.T) { } RegisterFailHandler(Fail) - junitReporter := reporters.NewJUnitReporter("report.xml") - RunSpecsWithDefaultAndCustomReporters(t, "E2e Suite", []Reporter{junitReporter}) + RunSpecs(t, "E2e Suite") } var _ = BeforeSuite(func() { From c01c679076d5d5af4cd34e9e4e5302ef8c5c84af Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 2 Jul 2024 14:14:43 +0800 Subject: [PATCH 16/69] issue 7620: add design for backup repository configurations Signed-off-by: Lyndon-Li --- changelogs/unreleased/7963-Lyndon-Li | 1 + design/backup-repo-config.md | 137 +++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 18 deletions(-) create mode 100644 changelogs/unreleased/7963-Lyndon-Li diff --git a/changelogs/unreleased/7963-Lyndon-Li b/changelogs/unreleased/7963-Lyndon-Li new file mode 100644 index 000000000..9491eb409 --- /dev/null +++ b/changelogs/unreleased/7963-Lyndon-Li @@ -0,0 +1 @@ +Add design for backup repository configurations for issue #7620, #7301 \ No newline at end of file diff --git a/design/backup-repo-config.md b/design/backup-repo-config.md index 6b483be40..9e4b168c8 100644 --- a/design/backup-repo-config.md +++ b/design/backup-repo-config.md @@ -7,14 +7,14 @@ ## Background -According to the [Unified Repository design][1] Velero uses selectable backup respositories for various backup/restore methods, i.e., fs-backup, volume snapshot data movement, etc. To achieve the best performance, backup respositories may need to be configured according to the running environments. +According to the [Unified Repository design][1] Velero uses selectable backup repositories for various backup/restore methods, i.e., fs-backup, volume snapshot data movement, etc. To achieve the best performance, backup repositories may need to be configured according to the running environments. For example, if there are sufficient CPU and memory resources in the environment, users may enable compression feature provided by the backup repository, so as to achieve the best backup throughput. -As another example, if the local disk space is not sufficent, users may want to constraint the backup repository's cache size, so as to prevent from running out of the disk space. -Therefore, it is worthy to allow users to configure some essential prameters of the backup repsoitories, and the configuration may vary from backup respositories. +As another example, if the local disk space is not sufficient, users may want to constraint the backup repository's cache size, so as to prevent the repository from running out of the disk space. +Therefore, it is worthy to allow users to configure some essential parameters of the backup repsoitories, and the configuration may vary from backup repositories. ## Goals -- Create a mechnism for users to specify configurations for backup repositories +- Create a mechanism for users to specify configurations for backup repositories ## Non-Goals @@ -22,33 +22,134 @@ Therefore, it is worthy to allow users to configure some essential prameters of ### BackupRepository CRD -After a backup repository is initialized, a BackupRepository CR is created to represent the instance of the backup repository. The BackupRepository's spec is a core parameter used by Uinified Repo when interactive with the backup repsoitory. Therefore, we can add the configurations into the BackupRepository CR. -The configurations may be different varying from backup repositories, therefore, we will not define each of the configurations explictly. Instead, we add a map in the BackupRepository's spec to take any configuration to be set to the backup repository. -During various operations to the backup repository, the Unified Repo modules will retrieve from the map for the specific configuration that is required at that time. So even though it is specified, a configuration may not be visite/hornored if the operations don't require it for the specific backup repository, this won't bring any issue. +After a backup repository is initialized, a BackupRepository CR is created to represent the instance of the backup repository. The BackupRepository's spec is a core parameter used by Unified Repo modules when interactive with the backup repsoitory. Therefore, we can add the configurations into the BackupRepository CR called ```repositoryConfig```. +The configurations may be different varying from backup repositories, therefore, we will not define each of the configurations explicitly. Instead, we add a map in the BackupRepository's spec to take any configuration to be set to the backup repository. + +During various operations to the backup repository, the Unified Repo modules will retrieve from the map for the specific configuration that is required at that time. So even though it is specified, a configuration may not be visited/hornored if the operations don't require it for the specific backup repository, this won't bring any issue. When and how a configuration is hornored is decided by the configuration itself and should be clarified in the configuration's specification. Below is the new BackupRepository's spec after adding the configuration map: +```yaml + spec: + description: BackupRepositorySpec is the specification for a BackupRepository. + properties: + backupStorageLocation: + description: |- + BackupStorageLocation is the name of the BackupStorageLocation + that should contain this repository. + type: string + maintenanceFrequency: + description: MaintenanceFrequency is how often maintenance should + be run. + type: string + repositoryConfig: + additionalProperties: + type: string + description: RepositoryConfig contains configurations for the specific + repository. + type: object + repositoryType: + description: RepositoryType indicates the type of the backend repository + enum: + - kopia + - restic + - "" + type: string + resticIdentifier: + description: |- + ResticIdentifier is the full restic-compatible string for identifying + this repository. + type: string + volumeNamespace: + description: |- + VolumeNamespace is the namespace this backup repository contains + pod volume backups for. + type: string + required: + - backupStorageLocation + - maintenanceFrequency + - resticIdentifier + - volumeNamespace + type: object +``` ### BackupRepository configMap -The BackupRepository CR is not created explicitly but a Velero CLI, but created as part of the backup/restore/maintenance operation if the CR doesn't exist. As a result, users don't have any way to specify the configurations before the BackupRepository CR is created. -Therefore, we create a BackupRepository configMap as a template of the configurations to be applied to the backup repository CR created during the backup/restore/maintenance operation. For an existing BackupRepository CR, the configMap is never visited, if users want to modify the configuration value, they should directly edit the BackupRepository CR. -The BackupRepository configMap is created by users. If the configMap is not there, nothing is specified to the backup repository CR, so the Unified Repo modules use the hard-coded values to configre the backup repository. -The BackupRepository configMap is backup repository type specific, that is, for each type of backup repository, there is one configMap. Therefore, a ```backup-repository-config``` label should be applied to the configMap with the value of the repository's type. During the backup repository creation, the configMap is searched by the label. +The BackupRepository CR is not created explicitly by a Velero CLI, but created as part of the backup/restore/maintenance operation if the CR doesn't exist. As a result, users don't have any way to specify the configurations before the BackupRepository CR is created. +Therefore, a BackupRepository configMap is introduced as a template of the configurations to be applied to the backup repository CR. +When the backup repository CR is created by the BackupRepository controller, the configurations in the configMap are copied to the ```repositoryConfig``` field. +For an existing BackupRepository CR, the configMap is never visited, if users want to modify the configuration value, they should directly edit the BackupRepository CR. + +The BackupRepository configMap is created by users in velero installation namespace. The configMap name must be specified in the velero server parameter ```--backup-repository-config```, otherwise, it won't effect. +If the configMap name is specified but the configMap doesn't exist by the time of a backup repository is created, the configMap name is ignored. +For any reason, if the configMap doesn't effect, nothing is specified to the backup repository CR, so the Unified Repo modules use the hard-coded values to configure the backup repository. + +The BackupRepository configMap supports backup repository type specific configurations, even though users can only specify one configMap. +So in the configMap struct, multiple entries are supported, indexed by the backup repository type. During the backup repository creation, the configMap is searched by the repository type. + +Below are the struct for the configMap: +``` golang +type RepoConfig struct { + CacheLimitMB int `json:"cacheLimitMB,omitempty"` + EnableCompression int `json:"enableCompression,omitempty"` +} + +type RepoConfigs struct { + Configs map[string]RepoConfig `json:"configs"` +} +``` ### Configurations -With the above mechanisms, all kinds of configurations could be added. Here list the configurations supported at present: -```cache-limit```: specify the size limit for the local data cache. The more data is cached locally, the less data is donwloaded from the backup storage, so the better performance may be achieved. You can specify any size that is smaller than the free space so that the disk space won't run out. This parameter is for each repository connection, that is, users could change it before connecting to the repository. -```compression-algo```: specify the compression algorithm to be used for a backup repsotiory. Most of the backup repositories support the data compression feature, but they may support different compression algorithms or performe differently to compression algorithms. This parameter is used when intializing the backup repository, once the backup repository is initialized, the compression algorithm won't be changed, so this parameter is ignored. +With the above mechanisms, any kind of configuration could be added. Here list the configurations defined at present: +```cacheLimitMB```: specifies the size limit(in MB) for the local data cache. The more data is cached locally, the less data may be downloaded from the backup storage, so the better performance may be achieved. Practically, users can specify any size that is smaller than the free space so that the disk space won't run out. This parameter is for each repository connection, that is, users could change it before connecting to the repository. If a backup repository doesn't use local cache, this parameter will be ignored. For Kopia repository, this parameter is supported. +```enableCompression```: specifies to enable/disable compression for a backup repsotiory. Most of the backup repositories support the data compression feature, if it is not supported by a backup repository, this parameter is ignored. Most of the backup repositories support to dynamically enable/disable compression, so this parameter is defined to be used whenever creating a write connection to the backup repository, if the dynamically changing is not supported, this parameter will be hornored only when initializing the backup repository. For Kopia repository, this parameter is supported and can be dynamically modified. -Below is an example of the BackupRepository configMap with the supported configurations: -```go +### Sample +Below is an example of the BackupRepository configMap with the configurations: +json format: +```json +{ + "configs": { + "repo-type-1": { + "cacheLimitMB": 2048, + "enableCompression": true + }, + "repo-type-2": { + "cacheLimitMB": 1024, + "enableCompression": false + } + } +} +``` +yaml format: +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: + namespace: velero +data: + configs: | + { + "repo-type-1": { + "cacheLimitMB": 2048, + "enableCompression": true + }, + "repo-type-2": { + "cacheLimitMB": 1024, + "enableCompression": false + } + } ``` -To create the configMap, users need to save something like the above sample to a json file and then run below command: +To create the configMap, users need to save something like the above sample to a file and then run below commands: ``` -kubectl create cm node-agent-config -n velero --from-file= +kubectl create cm -n velero --from-file= +``` +Or ``` +kubectl apply -f +``` From d72f8576562b07a553ad330a69b4ce5331f29297 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 16 Jul 2024 10:56:31 +0800 Subject: [PATCH 17/69] Add repository maintenance job configuration design. Signed-off-by: Xun Jiang --- design/Implemented/node-agent-affinity.md | 4 +- design/repo_maintenance_job_config.md | 261 ++++++++++++++++++++++ 2 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 design/repo_maintenance_job_config.md diff --git a/design/Implemented/node-agent-affinity.md b/design/Implemented/node-agent-affinity.md index 8a2911b10..d95312c7a 100644 --- a/design/Implemented/node-agent-affinity.md +++ b/design/Implemented/node-agent-affinity.md @@ -26,8 +26,8 @@ Therefore, in order to improve the compatibility, it is worthy to configure the ## Non-Goals - It is also beneficial to support VGDP instances affinity for PodVolume backup/restore, however, it is not possible since VGDP instances for PodVolume backup/restore should always run in the node where the source/target pods are created. -- It is also beneficial to support VGDP instances affinity for data movement restores, however, it is not possible in some cases. For example, when the `volumeBindingMode` in the storageclass is `WaitForFirstConsumer`, the restore volume must be mounted in the node where the target pod is scheduled, so the VGDP instance must run in the same node. On the other hand, considering the fact that restores may not frequently and centrally run, we will not support data movement restores. -- As elaberated in the [Volume Snapshot Data Movement Design][2], the Exposer may take different ways to expose snapshots, i.e., through backup pods (this is the only way supported at present). The implementation section below only considers this approach currently, if a new expose method is introduced in future, the definition of the affinity configurations and behaviors should still work, but we may need a new implementation. +- It is also beneficial to support VGDP instances affinity for data movement restores, however, it is not possible in some cases. For example, when the `volumeBindingMode` in the StorageClass is `WaitForFirstConsumer`, the restore volume must be mounted in the node where the target pod is scheduled, so the VGDP instance must run in the same node. On the other hand, considering the fact that restores may not frequently and centrally run, we will not support data movement restores. +- As elaborated in the [Volume Snapshot Data Movement Design][2], the Exposer may take different ways to expose snapshots, i.e., through backup pods (this is the only way supported at present). The implementation section below only considers this approach currently, if a new expose method is introduced in future, the definition of the affinity configurations and behaviors should still work, but we may need a new implementation. ## Solution diff --git a/design/repo_maintenance_job_config.md b/design/repo_maintenance_job_config.md new file mode 100644 index 000000000..ad055795d --- /dev/null +++ b/design/repo_maintenance_job_config.md @@ -0,0 +1,261 @@ +# Repository maintenance job configuration design + +## Abstract +Add this design to make the repository maintenance job can read configuration from a dedicate ConfigMap and make the Job's necessary parts configurable, e.g. `PodSpec.Affinity` and `PodSpec.resources`. + +## Background +Repository maintenance is split from the Velero server to a k8s Job in v1.14 by design [repository maintenance job](Implemented/repository-maintenance.md). +The repository maintenance Job configuration was read from the Velero server CLI parameter, and it inherits the most of Velero server's Deployment's PodSpec to fill un-configured fields. + +This design introduces a new way to let the user to customize the repository maintenance behavior instead of inheriting from the Velero server Deployment or reading from `velero server` CLI parameters. +The configurations added in this design including the resource limitations, node selection. +It's possible new configurations are introduced in future releases based on this design. + +For the node selection, the repository maintenance Job also inherits from the Velero server deployment before, but the Job may last for a while and cost noneligible resources, especially memory. +The users have the need to choose which k8s node to run the maintenance Job. +This design reuses the data structure introduced by design [node-agent affinity configuration](Implemented/node-agent-affinity.md) to make the repository maintenance job can choose which node running on. + +## Goals +- Unify the repository maintenance Job configuration at one place. +- Let user can choose repository maintenance Job running on which nodes. +- Replace the existing `velero server` parameters `--maintenance-job-cpu-request`, `--maintenance-job-mem-request`, `--maintenance-job-cpu-limit` and `--maintenance-job-mem-limit` by the proposal ConfigMap. + +## Non Goals +- There was an [issue](https://github.com/vmware-tanzu/velero/issues/7911) to require the whole Job's PodSpec should be configurable. That's not in the scope of this design. +- Please notice this new configuration is dedicated for the repository maintenance. Repository itself configuration is not covered. + + +## Compatibility +v1.14 uses the `velero server` CLI's parameter to pass the repository maintenance job configuration. +In v1.15, those parameters are removed, including `--maintenance-job-cpu-request`, `--maintenance-job-mem-request`, `--maintenance-job-cpu-limit` and `--maintenance-job-mem-limit`. +Instead, the parameters are read from the ConfigMap specified by `velero server` CLI parameter `--repo-maintenance-job-config` introduced by this design. + +## Design +This design introduces a new ConfigMap specified by `velero server` CLI parameter `--repo-maintenance-job-config` as the source of the repository maintenance job configuration. The specified ConfigMap is read from the namespace where Velero is installed. +If the ConfigMap doesn't exist, the internal default values are used. + +Example of using the parameter `--repo-maintenance-job-config`: +``` +velero server \ + ... + --repo-maintenance-job-config repo-job-config + ... +``` + +**Notice** +* Velero doesn't own this ConfigMap. If the user wants to customize the repository maintenance job, the user needs to create this ConfigMap. +* Velero reads this ConfigMap content at starting a new repository maintenance job, so the ConfigMap change will not take affect until the next created job. + +### Structure +The data structure for ```repo-maintenance-job-config``` is as below: +```go +type MaintenanceConfigMap map[string]Configs + +type Configs struct { + // LoadAffinity is the config for data path load affinity. + LoadAffinity []*LoadAffinity `json:"loadAffinity,omitempty"` + + // Resources is the config for the CPU and memory resources setting. + Resource Resources `json:"resources,omitempty"` +} + +type LoadAffinity struct { + // NodeSelector specifies the label selector to match nodes + NodeSelector metav1.LabelSelector `json:"nodeSelector"` +} + +type Resources struct { + // The repository maintenance job CPU request setting + CPURequest string `json:"cpuRequest,omitempty"` + + // The repository maintenance job memory request setting + MemRequest string `json:"memRequest,omitempty"` + + // The repository maintenance job CPU limit setting + CPULimit string `json:"cpuLimit,omitempty"` + + // The repository maintenance job memory limit setting + MemLimit string `json:"memLimit,omitempty"` +} +``` + +The ConfigMap content is a map. +If there is a key value as `global` in the map, the key's value is applied to all BackupRepositories maintenance jobs that don't their own specific configuration in the ConfigMap. +The other keys in the map is the combination of three elements of a BackupRepository: +* The namespace in which BackupRepository backs up volume data +* The BackupRepository referenced BackupStorageLocation's name +* The BackupRepository's type. Possible values are `kopia` and `restic` +If there is a key match with BackupRepository, the key's value is applied to the BackupRepository's maintenance jobs. +By this way, it's possible to let user configure before the BackupRepository is created. +This is especially convenient for administrator configuring during the Velero installation. +For example, the following BackupRepository's key should be `test-default-kopia` +``` yaml +- apiVersion: velero.io/v1 + kind: BackupRepository + metadata: + generateName: test-default-kopia- + labels: + velero.io/repository-type: kopia + velero.io/storage-location: default + velero.io/volume-namespace: test + name: test-default-kopia-kgt6n + namespace: velero + spec: + backupStorageLocation: default + maintenanceFrequency: 1h0m0s + repositoryType: kopia + resticIdentifier: gs:jxun:/restic/test + volumeNamespace: test +``` + +The `LoadAffinity` structure is reused from design [node-agent affinity configuration](Implemented/node-agent-affinity.md). +It's possible that the users want to choose nodes that match condition A or condition B to run the job. +For example, the user want to let the nodes is in a specified machine type or the nodes locate in the us-central1-x zones to run the job. +This can be done by adding multiple entries in the `LoadAffinity` array. + +### Affinity Example +A sample of the ```repo-maintenance-job-config``` ConfigMap is as below: +``` bash +cat < repo-maintenance-job-config.json +{ + "global": { + resources: { + "cpuRequest": "100m", + "cpuLimit": "200m", + "memRequest": "100Mi", + "memLimit": "200Mi" + }, + "loadAffinity": [ + { + "nodeSelector": { + "matchExpressions": [ + { + "key": "cloud.google.com/machine-family", + "operator": "In", + "values": [ + "e2" + ] + } + ] + } + }, + { + "nodeSelector": { + "matchExpressions": [ + { + "key": "topology.kubernetes.io/zone", + "operator": "In", + "values": [ + "us-central1-a", + "us-central1-b", + "us-central1-c" + ] + } + ] + } + } + ] + } +} +EOF +``` +This sample showcases two affinity configurations: +- matchLabels: maintenance job runs on nodes with label key `cloud.google.com/machine-family` and value `e2`. +- matchLabels: maintenance job runs on nodes located in `us-central1-a`, `us-central1-b` and `us-central1-c`. +The nodes matching one of the two conditions are selected. + +To create the configMap, users need to save something like the above sample to a json file and then run below command: +``` +kubectl create cm repo-maintenance-job-config -n velero --from-file=repo-maintenance-job-config.json +``` + +### Value assigning rules +If the Velero BackupRepositoryController cannot find the introduced ConfigMap, the following default values are used for repository maintenance job: +``` go +config := Configs { + // LoadAffinity is the config for data path load affinity. + LoadAffinity: nil, + + // Resources is the config for the CPU and memory resources setting. + Resources: Resources{ + // The repository maintenance job CPU request setting + CPURequest: "0m", + + // The repository maintenance job memory request setting + MemRequest: "0Mi", + + // The repository maintenance job CPU limit setting + CPULimit: "0m", + + // The repository maintenance job memory limit setting + MemLimit: "0Mi", + }, +} +``` + +If the Velero BackupRepositoryController finds the introduced ConfigMap with only `global` element, the `global` value is used. + +If the Velero BackupRepositoryController finds the introduced ConfigMap with only element matches the BackupRepository, the matched element value is used. + + +If the Velero BackupRepositoryController finds the introduced ConfigMap with both `global` element and element matches the BackupRepository, the matched element defined values overwrite the `global` value, and the `global` value is still used for matched element undefined values. + +For example, the ConfigMap content has two elements. +``` json +{ + "global": { + "resources": { + "cpuRequest": "100m", + "cpuLimit": "200m", + "memRequest": "100Mi", + "memLimit": "200Mi" + } + }, + "ns1-default-kopia": { + "resources": { + "memRequest": "400Mi", + "memLimit": "800Mi" + } + } +} +``` +The config value used for BackupRepository backing up volume data in namespace `ns1`, referencing BSL `default`, and the type is `Kopia`: +``` go +config := Configs { + // LoadAffinity is the config for data path load affinity. + LoadAffinity: nil, + + // The repository maintenance job CPU request setting + CPURequest: "100m", + + // The repository maintenance job memory request setting + MemRequest: "400Mi", + + // The repository maintenance job CPU limit setting + CPULimit: "200m", + + // The repository maintenance job memory limit setting + MemLimit: "800Mi", +} +``` + + +### Implementation +During the Velero repository controller starts to maintain a repository, it will call the repository manager's `PruneRepo` function to build the maintenance Job. +The ConfigMap specified by `velero server` CLI parameter `--repo-maintenance-job-config` is get to reinitialize the repository `MaintenanceConfig` setting. + +``` go + config, err := GetConfigs(context.Background(), namespace, crClient) + if err == nil { + if len(config.LoadAffinity) > 0 { + mgr.maintenanceCfg.Affinity = toSystemAffinity((*nodeagent.LoadAffinity)(config.LoadAffinity[0])) + } + ...... + } else { + log.Info("Cannot find the repo-maintenance-job-config ConfigMap: %s", err.Error()) + } +``` + +## Alternatives Considered +An other option is creating each ConfigMap for a BackupRepository. +This is not ideal for scenario that has a lot of BackupRepositories in the cluster. \ No newline at end of file From e862b976a4a9775237d7e45c06701474dc09b78f Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 23 Jul 2024 14:39:01 +0800 Subject: [PATCH 18/69] Use labels instead of regex to filter E2E test cases. Signed-off-by: Xun Jiang --- .github/workflows/e2e-test-kind.yaml | 35 ++-- .github/workflows/pr-codespell.yml | 2 +- test/Makefile | 135 ++++++++-------- test/e2e/README.md | 78 ++++----- .../api-group/enable_api_group_versions.go | 2 +- test/e2e/e2e_suite_test.go | 153 ++++++++++++------ test/perf/e2e_suite_test.go | 9 +- 7 files changed, 240 insertions(+), 174 deletions(-) diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index 57d499823..9a379738a 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -67,16 +67,12 @@ jobs: - 1.27.10 - 1.28.6 - 1.29.1 - focus: - # tests to focus on, use `|` to concatenate multiple regexes to run on the same job - # ordered according to e2e_suite_test.go order - - Basic\]\[ClusterResource - - ResourceFiltering - - ResourceModifier|Backups|PrivilegesMgmt\]\[SSR - - Schedule\]\[OrderedResources - - NamespaceMapping\]\[Single\]\[Restic|NamespaceMapping\]\[Multiple\]\[Restic - - Basic\]\[Nodeport - - Basic\]\[StorageClass + labels: + # labels are used to filter running E2E cases + - Basic && (ClusterResource || NodePort || StorageClass) + - ResourceFiltering && !Restic + - ResourceModifier || (Backups && BackupsSync) || PrivilegesMgmt || OrderedResources + - (NamespaceMapping && Single && Restic) || (NamespaceMapping && Multiple && Restic) fail-fast: false steps: - name: Set up Go @@ -128,13 +124,18 @@ jobs: curl -LO https://dl.k8s.io/release/v${{ matrix.k8s }}/bin/linux/amd64/kubectl sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl - GOPATH=~/go CLOUD_PROVIDER=kind \ - OBJECT_STORE_PROVIDER=aws BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ - CREDS_FILE=/tmp/credential BSL_BUCKET=bucket \ - ADDITIONAL_OBJECT_STORE_PROVIDER=aws ADDITIONAL_BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ - ADDITIONAL_CREDS_FILE=/tmp/credential ADDITIONAL_BSL_BUCKET=additional-bucket \ - GINKGO_FOCUS='${{ matrix.focus }}' VELERO_IMAGE=velero:pr-test \ - GINKGO_SKIP='SKIP_KIND|pv-backup|Restic|Snapshot|LongTime' \ + GOPATH=~/go \ + CLOUD_PROVIDER=kind \ + OBJECT_STORE_PROVIDER=aws \ + BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ + CREDS_FILE=/tmp/credential \ + BSL_BUCKET=bucket \ + ADDITIONAL_OBJECT_STORE_PROVIDER=aws \ + ADDITIONAL_BSL_CONFIG=region=minio,s3ForcePathStyle="true",s3Url=http://$(hostname -i):9000 \ + ADDITIONAL_CREDS_FILE=/tmp/credential \ + ADDITIONAL_BSL_BUCKET=additional-bucket \ + VELERO_IMAGE=velero:pr-test \ + GINKGO_LABELS="${{ matrix.labels }}" \ make -C test/ run-e2e timeout-minutes: 30 - name: Upload debug bundle diff --git a/.github/workflows/pr-codespell.yml b/.github/workflows/pr-codespell.yml index ca733318c..0d3138e40 100644 --- a/.github/workflows/pr-codespell.yml +++ b/.github/workflows/pr-codespell.yml @@ -15,7 +15,7 @@ jobs: with: # ignore the config/.../crd.go file as it's generated binary data that is edited elswhere. skip: .git,*.png,*.jpg,*.woff,*.ttf,*.gif,*.ico,./config/crd/v1beta1/crds/crds.go,./config/crd/v1/crds/crds.go,./config/crd/v2alpha1/crds/crds.go,./go.sum,./LICENSE - ignore_words_list: iam,aks,ist,bridget,ue,shouldnot,atleast,notin,sme + ignore_words_list: iam,aks,ist,bridget,ue,shouldnot,atleast,notin,sme,optin check_filenames: true check_hidden: true diff --git a/test/Makefile b/test/Makefile index fbc0e08e3..bdf87183b 100644 --- a/test/Makefile +++ b/test/Makefile @@ -47,10 +47,9 @@ TOOLS_BIN_DIR := $(TOOLS_DIR)/$(BIN_DIR) GINKGO := $(GOBIN)/ginkgo KUSTOMIZE := $(TOOLS_BIN_DIR)/kustomize OUTPUT_DIR := _output/$(GOOS)/$(GOARCH)/bin -GINKGO_FOCUS ?= -GINKGO_SKIP ?= -SKIP_STR := $(foreach var, $(subst ., ,$(GINKGO_SKIP)),-skip "$(var)") -FOCUS_STR := $(foreach var, $(subst ., ,$(GINKGO_FOCUS)),-focus "$(var)") +# Please reference to this document for Ginkgo label spec format. +# https://onsi.github.io/ginkgo/#spec-labels +GINKGO_LABELS ?= VELERO_CLI ?=$$(pwd)/../_output/bin/$(GOOS)/$(GOARCH)/velero VELERO_IMAGE ?= velero/velero:main PLUGINS ?= @@ -129,26 +128,26 @@ VELERO_POD_CPU_REQUEST ?= 2 VELERO_POD_MEM_REQUEST ?= 2Gi POD_VOLUME_OPERATION_TIMEOUT ?= 6h -COMMON_ARGS := -velerocli=$(VELERO_CLI) \ - -velero-image=$(VELERO_IMAGE) \ - -plugins=$(PLUGINS) \ - -velero-version=$(VERSION) \ - -restore-helper-image=$(RESTORE_HELPER_IMAGE) \ - -velero-namespace=$(VELERO_NAMESPACE) \ - -credentials-file=$(CREDS_FILE) \ - -bucket=$(BSL_BUCKET) \ - -prefix=$(BSL_PREFIX) \ - -bsl-config=$(BSL_CONFIG) \ - -vsl-config=$(VSL_CONFIG) \ - -cloud-provider=$(CLOUD_PROVIDER) \ - -object-store-provider="$(OBJECT_STORE_PROVIDER)" \ - -features=$(FEATURES) \ - -install-velero=$(INSTALL_VELERO) \ - -registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) \ - -debug-e2e-test=$(DEBUG_E2E_TEST) \ - -velero-server-debug-mode=$(VELERO_SERVER_DEBUG_MODE) \ - -uploader-type=$(UPLOADER_TYPE) \ - -debug-velero-pod-restart=$(DEBUG_VELERO_POD_RESTART) +COMMON_ARGS := --velerocli=$(VELERO_CLI) \ + --velero-image=$(VELERO_IMAGE) \ + --plugins=$(PLUGINS) \ + --velero-version=$(VERSION) \ + --restore-helper-image=$(RESTORE_HELPER_IMAGE) \ + --velero-namespace=$(VELERO_NAMESPACE) \ + --credentials-file=$(CREDS_FILE) \ + --bucket=$(BSL_BUCKET) \ + --prefix=$(BSL_PREFIX) \ + --bsl-config=$(BSL_CONFIG) \ + --vsl-config=$(VSL_CONFIG) \ + --cloud-provider=$(CLOUD_PROVIDER) \ + --object-store-provider="$(OBJECT_STORE_PROVIDER)" \ + --features=$(FEATURES) \ + --install-velero=$(INSTALL_VELERO) \ + --registry-credential-file=$(REGISTRY_CREDENTIAL_FILE) \ + --debug-e2e-test=$(DEBUG_E2E_TEST) \ + --velero-server-debug-mode=$(VELERO_SERVER_DEBUG_MODE) \ + --uploader-type=$(UPLOADER_TYPE) \ + --debug-velero-pod-restart=$(DEBUG_VELERO_POD_RESTART) # Make sure ginkgo is in $GOBIN .PHONY:ginkgo @@ -166,31 +165,36 @@ run-e2e: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plugin provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) run -v $(FOCUS_STR) $(SKIP_STR) --junit-report report.xml ./e2e -- $(COMMON_ARGS) \ - -upgrade-from-velero-cli=$(UPGRADE_FROM_VELERO_CLI) \ - -upgrade-from-velero-version=$(UPGRADE_FROM_VELERO_VERSION) \ - -migrate-from-velero-cli=$(MIGRATE_FROM_VELERO_CLI) \ - -migrate-from-velero-version=$(MIGRATE_FROM_VELERO_VERSION) \ - -additional-bsl-plugins=$(ADDITIONAL_BSL_PLUGINS) \ - -additional-bsl-object-store-provider="$(ADDITIONAL_OBJECT_STORE_PROVIDER)" \ - -additional-bsl-credentials-file=$(ADDITIONAL_CREDS_FILE) \ - -additional-bsl-bucket=$(ADDITIONAL_BSL_BUCKET) \ - -additional-bsl-prefix=$(ADDITIONAL_BSL_PREFIX) \ - -additional-bsl-config=$(ADDITIONAL_BSL_CONFIG) \ - -default-cluster-context=$(DEFAULT_CLUSTER) \ - -standby-cluster-context=$(STANDBY_CLUSTER) \ - -snapshot-move-data=$(SNAPSHOT_MOVE_DATA) \ - -data-mover-plugin=$(DATA_MOVER_PLUGIN) \ - -standby-cluster-cloud-provider=$(STANDBY_CLUSTER_CLOUD_PROVIDER) \ - -standby-cluster-plugins=$(STANDBY_CLUSTER_PLUGINS) \ - -standby-cluster-object-store-provider=$(STANDBY_CLUSTER_OBJECT_STORE_PROVIDER) \ - -default-cluster-name=$(DEFAULT_CLUSTER_NAME) \ - -standby-cluster-name=$(STANDBY_CLUSTER_NAME) \ - -eks-policy-arn=$(EKS_POLICY_ARN) \ - -default-cls-service-account-name=$(DEFAULT_CLS_SERVICE_ACCOUNT_NAME) \ - -standby-cls-service-account-name=$(STANDBY_CLS_SERVICE_ACCOUNT_NAME) - -kibishii-directory=$(KIBISHII_DIRECTORY) \ - -disable-informer-cache=$(DISABLE_INFORMER_CACHE) + @$(GINKGO) run \ + -v \ + --junit-report report.xml \ + --label-filter="$(GINKGO_LABELS)" \ + ./e2e \ + -- $(COMMON_ARGS) \ + --upgrade-from-velero-cli=$(UPGRADE_FROM_VELERO_CLI) \ + --upgrade-from-velero-version=$(UPGRADE_FROM_VELERO_VERSION) \ + --migrate-from-velero-cli=$(MIGRATE_FROM_VELERO_CLI) \ + --migrate-from-velero-version=$(MIGRATE_FROM_VELERO_VERSION) \ + --additional-bsl-plugins=$(ADDITIONAL_BSL_PLUGINS) \ + --additional-bsl-object-store-provider="$(ADDITIONAL_OBJECT_STORE_PROVIDER)" \ + --additional-bsl-credentials-file=$(ADDITIONAL_CREDS_FILE) \ + --additional-bsl-bucket=$(ADDITIONAL_BSL_BUCKET) \ + --additional-bsl-prefix=$(ADDITIONAL_BSL_PREFIX) \ + --additional-bsl-config=$(ADDITIONAL_BSL_CONFIG) \ + --default-cluster-context=$(DEFAULT_CLUSTER) \ + --standby-cluster-context=$(STANDBY_CLUSTER) \ + --snapshot-move-data=$(SNAPSHOT_MOVE_DATA) \ + --data-mover-plugin=$(DATA_MOVER_PLUGIN) \ + --standby-cluster-cloud-provider=$(STANDBY_CLUSTER_CLOUD_PROVIDER) \ + --standby-cluster-plugins=$(STANDBY_CLUSTER_PLUGINS) \ + --standby-cluster-object-store-provider=$(STANDBY_CLUSTER_OBJECT_STORE_PROVIDER) \ + --default-cluster-name=$(DEFAULT_CLUSTER_NAME) \ + --standby-cluster-name=$(STANDBY_CLUSTER_NAME) \ + --eks-policy-arn=$(EKS_POLICY_ARN) \ + --default-cls-service-account-name=$(DEFAULT_CLS_SERVICE_ACCOUNT_NAME) \ + --standby-cls-service-account-name=$(STANDBY_CLS_SERVICE_ACCOUNT_NAME) + --kibishii-directory=$(KIBISHII_DIRECTORY) \ + --disable-informer-cache=$(DISABLE_INFORMER_CACHE) .PHONY: run-perf run-perf: ginkgo @@ -200,20 +204,25 @@ run-perf: ginkgo (echo "Bucket to store the backups from E2E tests is required, please re-run with BSL_BUCKET="; exit 1 ) @[ "${CLOUD_PROVIDER}" ] && echo "Using cloud provider ${CLOUD_PROVIDER}" || \ (echo "Cloud provider for target cloud/plugin provider is required, please rerun with CLOUD_PROVIDER="; exit 1) - @$(GINKGO) run -v $(FOCUS_STR) $(SKIP_STR) --junit-report report.xml ./perf -- $(COMMON_ARGS) \ - -nfs-server-path=$(NFS_SERVER_PATH) \ - -test-case-describe=$(TEST_CASE_DESCRIBE) \ - -backup-for-restore=$(BACKUP_FOR_RESTORE) \ - -delete-cluster-resource=$(Delete_Cluster_Resource) \ - -node-agent-pod-cpu-limit=$(NODE_AGENT_POD_CPU_LIMIT) \ - -node-agent-pod-mem-limit=$(NODE_AGENT_POD_MEM_LIMIT) \ - -node-agent-pod-cpu-request=$(NODE_AGENT_POD_CPU_REQUEST) \ - -node-agent-pod-mem-request=$(NODE_AGENT_POD_MEM_REQUEST) \ - -velero-pod-cpu-limit=$(VELERO_POD_CPU_LIMIT) \ - -velero-pod-mem-limit=$(VELERO_POD_MEM_LIMIT) \ - -velero-pod-cpu-request=$(VELERO_POD_CPU_REQUEST) \ - -velero-pod-mem-request=$(VELERO_POD_MEM_REQUEST) \ - -pod-volume-operation-timeout=$(POD_VOLUME_OPERATION_TIMEOUT) + @$(GINKGO) run \ + -v \ + --junit-report report.xml \ + --label-filter="$(GINKGO_LABELS)" \ + ./perf \ + -- $(COMMON_ARGS) \ + --nfs-server-path=$(NFS_SERVER_PATH) \ + --test-case-describe=$(TEST_CASE_DESCRIBE) \ + --backup-for-restore=$(BACKUP_FOR_RESTORE) \ + --delete-cluster-resource=$(Delete_Cluster_Resource) \ + --node-agent-pod-cpu-limit=$(NODE_AGENT_POD_CPU_LIMIT) \ + --node-agent-pod-mem-limit=$(NODE_AGENT_POD_MEM_LIMIT) \ + --node-agent-pod-cpu-request=$(NODE_AGENT_POD_CPU_REQUEST) \ + --node-agent-pod-mem-request=$(NODE_AGENT_POD_MEM_REQUEST) \ + --velero-pod-cpu-limit=$(VELERO_POD_CPU_LIMIT) \ + --velero-pod-mem-limit=$(VELERO_POD_MEM_LIMIT) \ + --velero-pod-cpu-request=$(VELERO_POD_CPU_REQUEST) \ + --velero-pod-mem-request=$(VELERO_POD_MEM_REQUEST) \ + --pod-volume-operation-timeout=$(POD_VOLUME_OPERATION_TIMEOUT) build: ginkgo mkdir -p $(OUTPUT_DIR) diff --git a/test/e2e/README.md b/test/e2e/README.md index b1d825999..4da989f8e 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -157,9 +157,9 @@ Basic examples: BSL_CONFIG="resourceGroup=$AZURE_BACKUP_RESOURCE_GROUP,storageAccount=$AZURE_STORAGE_ACCOUNT_ID,subscriptionId=$AZURE_BACKUP_SUBSCRIPTION_ID" BSL_BUCKET= CREDS_FILE=/path/to/azure-creds CLOUD_PROVIDER=azure make test-e2e ``` Please refer to `velero-plugin-for-microsoft-azure` documentation for instruction to [set up permissions for Velero](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#set-permissions-for-velero) and to [set up azure storage account and blob container](https://github.com/vmware-tanzu/velero-plugin-for-microsoft-azure#setup-azure-storage-account-and-blob-container) -1. Run Ginko-focused Restore Multi-API Groups tests using Minio as the backup storage location: +1. Run Multi-API group and version tests using MinIO as the backup storage location: ```bash - BSL_CONFIG="region=minio,s3ForcePathStyle=\"true\",s3Url=:9000" BSL_PREFIX= BSL_BUCKET= CREDS_FILE= CLOUD_PROVIDER=kind OBJECT_STORE_PROVIDER=aws VELERO_NAMESPACE="velero" GINKGO_FOCUS="API group versions" make test-e2e + BSL_CONFIG="region=minio,s3ForcePathStyle=\"true\",s3Url=:9000" BSL_PREFIX= BSL_BUCKET= CREDS_FILE= CLOUD_PROVIDER=kind OBJECT_STORE_PROVIDER=aws VELERO_NAMESPACE="velero" GINKGO_LABELS="APIGroup && APIVersion" make test-e2e ``` 1. Run Velero tests in a kind cluster with AWS (or Minio) as the storage provider and use Microsoft Azure as the storage provider for an additional Backup Storage Location: ```bash @@ -208,60 +208,66 @@ Migration examples: ``` -1. Datamover tests: +1. Data mover tests: - The example shows all essential `make` variables for a Datamover test which is migrate from a AKS cluster to a EKS cluster. + The example shows all essential `make` variables for a data mover test which is migrate from a AKS cluster to a EKS cluster. Note: STANDBY_CLUSTER_CLOUD_PROVIDER and STANDBY_CLUSTER_OBJECT_STORE_PROVIDER is essential here, it is for identify plugins to be installed on target cluster, since DEFAULT cluster's provider is different from STANDBY cluster, plugins are different as well. ```bash + CLOUD_PROVIDER=azure \ + DEFAULT_CLUSTER= \ + STANDBY_CLUSTER= \ + FEATURES=EnableCSI \ + OBJECT_STORE_PROVIDER=aws \ + CREDS_FILE= \ + BSL_CONFIG=region= \ + BSL_BUCKET= \ + BSL_PREFIX= \ + VSL_CONFIG=region= \ + SNAPSHOT_MOVE_DATA=true \ + STANDBY_CLUSTER_CLOUD_PROVIDER=aws \ + STANDBY_CLUSTER_OBJECT_STORE_PROVIDER=aws \ + GINKGO_LABELS="Migration" \ make test-e2e - CLOUD_PROVIDER=azure \ - DEFAULT_CLUSTER= \ - STANDBY_CLUSTER= \ - FEATURES=EnableCSI \ - OBJECT_STORE_PROVIDER=aws \ - CREDS_FILE= \ - BSL_CONFIG=region= \ - BSL_BUCKET= \ - BSL_PREFIX= \ - VSL_CONFIG=region= \ - SNAPSHOT_MOVE_DATA=true \ - STANDBY_CLUSTER_CLOUD_PROVIDER=aws \ - STANDBY_CLUSTER_OBJECT_STORE_PROVIDER=aws \ - GINKGO_FOCUS=Migration ``` ## Filtering tests -Velero E2E tests uses [Ginkgo](https://onsi.github.io/ginkgo/) testing framework which allows a subset of the tests to be run using the [`-focus` and `-skip`](https://onsi.github.io/ginkgo/#focused-specs) flags to ginkgo. +In release-1.15, Velero bumps the [Ginkgo](https://onsi.github.io/ginkgo/) version to [v2](https://onsi.github.io/ginkgo/MIGRATING_TO_V2). +Velero E2E start to use [labels](https://onsi.github.io/ginkgo/#spec-labels) to filter cases instead of [`-focus` and `-skip`](https://onsi.github.io/ginkgo/#focused-specs) parameters. -For filtering tests, using `make` variables `GINKGO_FOCUS` and `GINKGO_SKIP` : -1. `GINKGO_FOCUS`: Dot-separated list of labels to be included for Ginkgo description-based filtering. Optional. The `-focus` flag is passed to ginkgo using the `GINKGO_FOCUS` `make` variable. This can be used to focus on specific tests. -1. `GINKGO_SKIP`: Dot-separated list of labels to be excluded for Ginkgo description-based filtering.Optional. The `-skip ` flag is passed to ginkgo using the `GINKGO_SKIP` `make` variable. This can be used to skip specific tests. +Both `make run-e2e` and `make run-perf` CLI support using parameter `GINKGO_LABELS` to filter test cases. + +`GINKGO_LABELS` is interpreted into `ginkgo run` CLI's parameter [`--label-filter`](https://onsi.github.io/ginkgo/#spec-labels). - -`GINKGO_FOCUS`/`GINKGO_SKIP` can be interpreted into multiple `-focus`/`-skip ` describe in [Description-Based Filtering](https://onsi.github.io/ginkgo/#description-based-filtering:~:text=Description%2DBased%20Filtering) by dot-separated format for test execution management please refer to examples below.: - - -For example, E2E tests can be run with specific cases to be included and/or excluded using the commands below: +### Examples +E2E tests can be run with specific cases to be included and/or excluded using the commands below: 1. Run Velero tests with specific cases to be included: ```bash + GINKGO_LABELS="Basic && Restic" \ + CLOUD_PROVIDER=aws \ + BSL_BUCKET=example-bucket \ + CREDS_FILE=/path/to/aws-creds \ make test-e2e \ - GINKGO_FOCUS =Basic\][\Restic \ - CLOUD_PROVIDER=aws BSL_BUCKET= BSL_PREFIX= CREDS_FILE=/path/to/aws-creds ``` - In this example, only case `[Basic][Restic]` is included. + In this example, only case have both `Basic` and `Restic` labels are included. 1. Run Velero tests with specific cases to be excluded: ```bash - make test-e2e \ - GINKGO_SKIP=Scale.Schedule.TTL.Upgrade\]\[Restic.Migration\][\Restic \ - CLOUD_PROVIDER=aws BSL_BUCKET= BSL_PREFIX= CREDS_FILE=/path/to/aws-creds + GINKGO_LABELS="!Scale || !Schedule || !TTL || !(Upgrade && Restic) || !(Migration && Restic)" \ + CLOUD_PROVIDER=aws \ + BSL_BUCKET=example-bucket \ + CREDS_FILE=/path/to/aws-creds \ + make test-e2e ``` - In this example, case `Scale`, `Schedule`, `TTL`, `[Upgrade][Restic]` and `[Migration][Restic]` will be skipped. - - + In this example, cases are labelled as + * `Scale` + * `Schedule` + * `TTL` + * `Upgrade` and `Restic` + * `Migration` and `Restic` + will be skipped. ## Full Tests execution diff --git a/test/e2e/basic/api-group/enable_api_group_versions.go b/test/e2e/basic/api-group/enable_api_group_versions.go index 4fc17ed97..7f67dbf6e 100644 --- a/test/e2e/basic/api-group/enable_api_group_versions.go +++ b/test/e2e/basic/api-group/enable_api_group_versions.go @@ -53,7 +53,7 @@ type apiGropuVersionsTest struct { want map[string]map[string]string } -func APIGropuVersionsTest() { +func APIGroupVersionsTest() { var ( group string err error diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 45b093b13..0d22510bc 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -112,79 +112,126 @@ func init() { // caused by no expected snapshot found. If we use retain as reclaim policy, then this label can be ignored, all test // cases can be executed as expected successful result. -var _ = Describe("[APIGroup][APIVersion][SKIP_KIND] Velero tests with various CRD API group versions", APIGropuVersionsTest) -var _ = Describe("[APIGroup][APIExtensions][SKIP_KIND] CRD of apiextentions v1beta1 should be B/R successfully from cluster(k8s version < 1.22) to cluster(k8s version >= 1.22)", APIExtensionsVersionsTest) +var _ = Describe("Velero tests with various CRD API group versions", + Label("APIGroup", "APIVersion", "SKIP_KIND"), APIGroupVersionsTest) +var _ = Describe("CRD of apiextentions v1beta1 should be B/R successfully from cluster(k8s version < 1.22) to cluster(k8s version >= 1.22)", + Label("APIGroup", "APIExtensions", "SKIP_KIND"), APIExtensionsVersionsTest) -// Test backup and restore of Kibishi using restic -var _ = Describe("[Basic][Restic] Velero tests on cluster using the plugin provider for object storage and Restic for volume backups", BackupRestoreWithRestic) +// Test backup and restore of Kibishii using restic +var _ = Describe("Velero tests on cluster using the plugin provider for object storage and Restic for volume backups", + Label("Basic", "Restic"), BackupRestoreWithRestic) -var _ = Describe("[Basic][Snapshot][SkipVanillaZfs] Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupRestoreWithSnapshots) +var _ = Describe("Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", + Label("Basic", "Snapshot", "SkipVanillaZfs"), BackupRestoreWithSnapshots) -var _ = Describe("[Basic][Snapshot][RetainPV] Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupRestoreRetainedPVWithSnapshots) +var _ = Describe("Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", + Label("Basic", "Snapshot", "RetainPV"), BackupRestoreRetainedPVWithSnapshots) -var _ = Describe("[Basic][Restic][RetainPV] Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupRestoreRetainedPVWithRestic) +var _ = Describe("Velero tests on cluster using the plugin provider for object storage and snapshots for volume backups", + Label("Basic", "Restic", "RetainPV"), BackupRestoreRetainedPVWithRestic) -var _ = Describe("[Basic][ClusterResource] Backup/restore of cluster resources", ResourcesCheckTest) +var _ = Describe("Backup/restore of cluster resources", + Label("Basic", "ClusterResource"), ResourcesCheckTest) -var _ = Describe("[Scale][LongTime] Backup/restore of 2500 namespaces", MultiNSBackupRestore) +var _ = Describe("Service NodePort reservation during restore is configurable", + Label("Basic", "NodePort"), NodePortTest) -// Upgrade test by Kibishi using restic -var _ = Describe("[Upgrade][Restic] Velero upgrade tests on cluster using the plugin provider for object storage and Restic for volume backups", BackupUpgradeRestoreWithRestic) -var _ = Describe("[Upgrade][Snapshot][SkipVanillaZfs] Velero upgrade tests on cluster using the plugin provider for object storage and snapshots for volume backups", BackupUpgradeRestoreWithSnapshots) +var _ = Describe("Storage class of persistent volumes and persistent volume claims can be changed during restores", + Label("Basic", "StorageClass"), StorageClasssChangingTest) + +var _ = Describe("Node selectors of persistent volume claims can be changed during restores", + Label("Basic", "SelectedNode", "SKIP_KIND"), PVCSelectedNodeChangingTest) + +var _ = Describe("Backup/restore of 2500 namespaces", + Label("Scale", "LongTime"), MultiNSBackupRestore) + +// Upgrade test by Kibishii using Restic +var _ = Describe("Velero upgrade tests on cluster using the plugin provider for object storage and Restic for volume backups", + Label("Upgrade", "Restic"), BackupUpgradeRestoreWithRestic) +var _ = Describe("Velero upgrade tests on cluster using the plugin provider for object storage and snapshots for volume backups", + Label("Upgrade", "Snapshot", "SkipVanillaZfs"), BackupUpgradeRestoreWithSnapshots) // test filter objects by namespace, type, or labels when backup or restore. -var _ = Describe("[ResourceFiltering][ExcludeFromBackup] Resources with the label velero.io/exclude-from-backup=true are not included in backup", ExcludeFromBackupTest) -var _ = Describe("[ResourceFiltering][ExcludeNamespaces][Backup] Velero test on exclude namespace from the cluster backup", BackupWithExcludeNamespaces) -var _ = Describe("[ResourceFiltering][ExcludeNamespaces][Restore] Velero test on exclude namespace from the cluster restore", RestoreWithExcludeNamespaces) -var _ = Describe("[ResourceFiltering][ExcludeResources][Backup] Velero test on exclude resources from the cluster backup", BackupWithExcludeResources) -var _ = Describe("[ResourceFiltering][ExcludeResources][Restore] Velero test on exclude resources from the cluster restore", RestoreWithExcludeResources) -var _ = Describe("[ResourceFiltering][IncludeNamespaces][Backup] Velero test on include namespace from the cluster backup", BackupWithIncludeNamespaces) -var _ = Describe("[ResourceFiltering][IncludeNamespaces][Restore] Velero test on include namespace from the cluster restore", RestoreWithIncludeNamespaces) -var _ = Describe("[ResourceFiltering][IncludeResources][Backup] Velero test on include resources from the cluster backup", BackupWithIncludeResources) -var _ = Describe("[ResourceFiltering][IncludeResources][Restore] Velero test on include resources from the cluster restore", RestoreWithIncludeResources) -var _ = Describe("[ResourceFiltering][LabelSelector] Velero test on backup include resources matching the label selector", BackupWithLabelSelector) -var _ = Describe("[ResourceFiltering][ResourcePolicies][Restic] Velero test on skip backup of volume by resource policies", ResourcePoliciesTest) +var _ = Describe("Resources with the label velero.io/exclude-from-backup=true are not included in backup", + Label("ResourceFiltering", "ExcludeFromBackup"), ExcludeFromBackupTest) +var _ = Describe("Velero test on exclude namespace from the cluster backup", + Label("ResourceFiltering", "ExcludeNamespaces", "Backup"), BackupWithExcludeNamespaces) +var _ = Describe("Velero test on exclude namespace from the cluster restore", + Label("ResourceFiltering", "ExcludeNamespaces", "Restore"), RestoreWithExcludeNamespaces) +var _ = Describe("Velero test on exclude resources from the cluster backup", + Label("ResourceFiltering", "ExcludeResources", "Backup"), BackupWithExcludeResources) +var _ = Describe("Velero test on exclude resources from the cluster restore", + Label("ResourceFiltering", "ExcludeResources", "Restore"), RestoreWithExcludeResources) +var _ = Describe("Velero test on include namespace from the cluster backup", + Label("ResourceFiltering", "IncludeNamespaces", "Backup"), BackupWithIncludeNamespaces) +var _ = Describe("Velero test on include namespace from the cluster restore", + Label("ResourceFiltering", "IncludeNamespaces", "Restore"), RestoreWithIncludeNamespaces) +var _ = Describe("Velero test on include resources from the cluster backup", + Label("ResourceFiltering", "IncludeResources", "Backup"), BackupWithIncludeResources) +var _ = Describe("Velero test on include resources from the cluster restore", + Label("ResourceFiltering", "IncludeResources", "Restore"), RestoreWithIncludeResources) +var _ = Describe("Velero test on backup include resources matching the label selector", + Label("ResourceFiltering", "LabelSelector"), BackupWithLabelSelector) +var _ = Describe("Velero test on skip backup of volume by resource policies", + Label("ResourceFiltering", "ResourcePolicies", "Restic"), ResourcePoliciesTest) // backup VolumeInfo test -var _ = Describe("[BackupVolumeInfo][SkippedVolume]", SkippedVolumeInfoTest) -var _ = Describe("[BackupVolumeInfo][FilesystemUpload]", FilesystemUploadVolumeInfoTest) -var _ = Describe("[BackupVolumeInfo][CSIDataMover]", CSIDataMoverVolumeInfoTest) -var _ = Describe("[BackupVolumeInfo][CSISnapshot]", CSISnapshotVolumeInfoTest) -var _ = Describe("[BackupVolumeInfo][NativeSnapshot]", NativeSnapshotVolumeInfoTest) +var _ = Describe("", Label("BackupVolumeInfo", "SkippedVolume"), SkippedVolumeInfoTest) +var _ = Describe("", Label("BackupVolumeInfo", "FilesystemUpload"), FilesystemUploadVolumeInfoTest) +var _ = Describe("", Label("BackupVolumeInfo", "CSIDataMover"), CSIDataMoverVolumeInfoTest) +var _ = Describe("", Label("BackupVolumeInfo", "CSISnapshot"), CSISnapshotVolumeInfoTest) +var _ = Describe("", Label("BackupVolumeInfo", "NativeSnapshot"), NativeSnapshotVolumeInfoTest) -var _ = Describe("[ResourceModifier][Restore] Velero test on resource modifiers from the cluster restore", ResourceModifiersTest) +var _ = Describe("Velero test on resource modifiers from the cluster restore", + Label("ResourceModifier", "Restore"), ResourceModifiersTest) -var _ = Describe("[Backups][Deletion][Restic] Velero tests of Restic backup deletion", BackupDeletionWithRestic) -var _ = Describe("[Backups][Deletion][Snapshot][SkipVanillaZfs] Velero tests of snapshot backup deletion", BackupDeletionWithSnapshots) -var _ = Describe("[Backups][TTL][LongTime][Snapshot][SkipVanillaZfs] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", TTLTest) -var _ = Describe("[Backups][BackupsSync] Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", BackupsSyncTest) +var _ = Describe("Velero tests of Restic backup deletion", + Label("Backups", "Deletion", "Restic"), BackupDeletionWithRestic) +var _ = Describe("Velero tests of snapshot backup deletion", + Label("Backups", "Deletion", "Snapshot", "SkipVanillaZfs"), BackupDeletionWithSnapshots) +var _ = Describe("Local backups and Restic repos will be deleted once the corresponding backup storage location is deleted", + Label("Backups", "TTL", "LongTime", "Snapshot", "SkipVanillaZfs"), TTLTest) +var _ = Describe("Backups in object storage are synced to a new Velero and deleted backups in object storage are synced to be deleted in Velero", + Label("Backups", "BackupsSync"), BackupsSyncTest) -var _ = Describe("[Schedule][BR][Pause][LongTime] Backup will be created periodly by schedule defined by a Cron expression", ScheduleBackupTest) -var _ = Describe("[Schedule][OrderedResources] Backup resources should follow the specific order in schedule", ScheduleOrderedResources) -var _ = Describe("[Schedule][BackupCreation][SKIP_KIND] Schedule controller wouldn't create a new backup when it still has pending or InProgress backup", ScheduleBackupCreationTest) +var _ = Describe("Backup will be created periodically by schedule defined by a Cron expression", + Label("Schedule", "BR", "Pause", "LongTime"), ScheduleBackupTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("Schedule", "OrderedResources"), ScheduleOrderedResources) +var _ = Describe("Schedule controller wouldn't create a new backup when it still has pending or InProgress backup", + Label("Schedule", "BackupCreation", "SKIP_KIND"), ScheduleBackupCreationTest) -var _ = Describe("[PrivilegesMgmt][SSR] Velero test on ssr object when controller namespace mix-ups", SSRTest) +var _ = Describe("Velero test on ssr object when controller namespace mix-ups", + Label("PrivilegesMgmt", "SSR"), SSRTest) -var _ = Describe("[BSL][Deletion][Snapshot][SkipVanillaZfs] Local backups will be deleted once the corresponding backup storage location is deleted", BslDeletionWithSnapshots) -var _ = Describe("[BSL][Deletion][Restic] Local backups and restic repos will be deleted once the corresponding backup storage location is deleted", BslDeletionWithRestic) +var _ = Describe("Local backups will be deleted once the corresponding backup storage location is deleted", + Label("BSL", "Deletion", "Snapshot", "SkipVanillaZfs"), BslDeletionWithSnapshots) +var _ = Describe("Local backups and Restic repos will be deleted once the corresponding backup storage location is deleted", + Label("BSL", "Deletion", "Restic"), BslDeletionWithRestic) -var _ = Describe("[Migration][Restic] Migrate resources between clusters by Restic", MigrationWithRestic) -var _ = Describe("[Migration][Snapshot][SkipVanillaZfs] Migrate resources between clusters by snapshot", MigrationWithSnapshots) +var _ = Describe("Migrate resources between clusters by Restic", + Label("Migration", "Restic"), MigrationWithRestic) +var _ = Describe("Migrate resources between clusters by snapshot", + Label("Migration", "Snapshot", "SkipVanillaZfs"), MigrationWithSnapshots) -var _ = Describe("[NamespaceMapping][Single][Restic] Backup resources should follow the specific order in schedule", OneNamespaceMappingResticTest) -var _ = Describe("[NamespaceMapping][Multiple][Restic] Backup resources should follow the specific order in schedule", MultiNamespacesMappingResticTest) -var _ = Describe("[NamespaceMapping][Single][Snapshot][SkipVanillaZfs] Backup resources should follow the specific order in schedule", OneNamespaceMappingSnapshotTest) -var _ = Describe("[NamespaceMapping][Multiple][Snapshot]SkipVanillaZfs] Backup resources should follow the specific order in schedule", MultiNamespacesMappingSnapshotTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("NamespaceMapping", "Single", "Restic"), OneNamespaceMappingResticTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("NamespaceMapping", "Multiple", "Restic"), MultiNamespacesMappingResticTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("NamespaceMapping", "Single", "Snapshot", "SkipVanillaZfs"), OneNamespaceMappingSnapshotTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("NamespaceMapping", "Multiple", "Snapshot", "SkipVanillaZfs"), MultiNamespacesMappingSnapshotTest) -var _ = Describe("Backup resources should follow the specific order in schedule", Label("pv-backup", "Opt-In"), OptInPVBackupTest) -var _ = Describe("Backup resources should follow the specific order in schedule", Label("pv-backup", "Opt-Out"), OptOutPVBackupTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("PVBackup", "OptIn"), OptInPVBackupTest) +var _ = Describe("Backup resources should follow the specific order in schedule", + Label("PVBackup", "OptOut"), OptOutPVBackupTest) -var _ = Describe("[Basic][Nodeport] Service nodeport reservation during restore is configurable", NodePortTest) -var _ = Describe("[Basic][StorageClass] Storage class of persistent volumes and persistent volume claims can be changed during restores", StorageClasssChangingTest) -var _ = Describe("[Basic][SelectedNode][SKIP_KIND] Node selectors of persistent volume claims can be changed during restores", PVCSelectedNodeChangingTest) - -var _ = Describe("[UploaderConfig][ParallelFilesUpload] Velero test on parallel files upload", ParallelFilesUploadTest) -var _ = Describe("[UploaderConfig][ParallelFilesDownload] Velero test on parallel files download", ParallelFilesDownloadTest) +var _ = Describe("Velero test on parallel files upload", + Label("UploaderConfig", "ParallelFilesUpload"), ParallelFilesUploadTest) +var _ = Describe("Velero test on parallel files download", + Label("UploaderConfig", "ParallelFilesDownload"), ParallelFilesDownloadTest) func GetKubeconfigContext() error { var err error diff --git a/test/perf/e2e_suite_test.go b/test/perf/e2e_suite_test.go index a2aa85d63..e047749c2 100644 --- a/test/perf/e2e_suite_test.go +++ b/test/perf/e2e_suite_test.go @@ -94,11 +94,14 @@ func initConfig() error { return nil } -var _ = Describe("[PerformanceTest][BackupAndRestore] Velero test on both backup and restore resources", test.TestFunc(&basic.BasicTest{})) +var _ = Describe("Velero test on both backup and restore resources", + Label("PerformanceTest", "BackupAndRestore"), test.TestFunc(&basic.BasicTest{})) -var _ = Describe("[PerformanceTest][Backup] Velero test on only backup resources", test.TestFunc(&backup.BackupTest{})) +var _ = Describe("Velero test on only backup resources", + Label("PerformanceTest", "Backup"), test.TestFunc(&backup.BackupTest{})) -var _ = Describe("[PerformanceTest][Restore] Velero test on only restore resources", test.TestFunc(&restore.RestoreTest{})) +var _ = Describe("Velero test on only restore resources", + Label("PerformanceTest", "Restore"), test.TestFunc(&restore.RestoreTest{})) func TestE2e(t *testing.T) { flag.Parse() From 71c75d6dcba69ccaecde4132117f45cba5b68fe0 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 24 Jul 2024 15:34:25 +0800 Subject: [PATCH 19/69] Set the Ginkgo timeout to 5 hours. Signed-off-by: Xun Jiang --- test/Makefile | 2 ++ test/e2e/README.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Makefile b/test/Makefile index bdf87183b..ff62230b3 100644 --- a/test/Makefile +++ b/test/Makefile @@ -169,6 +169,7 @@ run-e2e: ginkgo -v \ --junit-report report.xml \ --label-filter="$(GINKGO_LABELS)" \ + --timeout=5h \ ./e2e \ -- $(COMMON_ARGS) \ --upgrade-from-velero-cli=$(UPGRADE_FROM_VELERO_CLI) \ @@ -208,6 +209,7 @@ run-perf: ginkgo -v \ --junit-report report.xml \ --label-filter="$(GINKGO_LABELS)" \ + --timeout=5h \ ./perf \ -- $(COMMON_ARGS) \ --nfs-server-path=$(NFS_SERVER_PATH) \ diff --git a/test/e2e/README.md b/test/e2e/README.md index 4da989f8e..1afb113fb 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -255,7 +255,7 @@ E2E tests can be run with specific cases to be included and/or excluded using th 1. Run Velero tests with specific cases to be excluded: ```bash - GINKGO_LABELS="!Scale || !Schedule || !TTL || !(Upgrade && Restic) || !(Migration && Restic)" \ + GINKGO_LABELS="!(Scale || Schedule || TTL || (Upgrade && Restic) || (Migration && Restic))" \ CLOUD_PROVIDER=aws \ BSL_BUCKET=example-bucket \ CREDS_FILE=/path/to/aws-creds \ From faa704d9091814c989d04336b396b91b5ca2e3e9 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 11 Jul 2024 14:15:28 +0800 Subject: [PATCH 20/69] data mover ms watcher Signed-off-by: Lyndon-Li --- changelogs/unreleased/7999-Lyndon-Li | 1 + pkg/cmd/cli/nodeagent/server.go | 3 + pkg/datapath/manager.go | 20 + pkg/datapath/manager_test.go | 36 +- pkg/datapath/micro_service_watcher.go | 437 +++++++++++++++ pkg/datapath/micro_service_watcher_test.go | 603 +++++++++++++++++++++ pkg/datapath/types.go | 4 +- pkg/exposer/csi_snapshot_test.go | 2 +- pkg/exposer/generic_restore_test.go | 2 +- pkg/test/test_logger.go | 12 + pkg/util/kube/pod.go | 72 ++- pkg/util/kube/pod_test.go | 275 ++++++++++ pkg/util/logging/default_logger.go | 16 +- pkg/util/logging/default_logger_test.go | 2 +- pkg/util/logging/log_merge_hook.go | 113 ++++ pkg/util/logging/log_merge_hook_test.go | 185 +++++++ 16 files changed, 1772 insertions(+), 11 deletions(-) create mode 100644 changelogs/unreleased/7999-Lyndon-Li create mode 100644 pkg/datapath/micro_service_watcher.go create mode 100644 pkg/datapath/micro_service_watcher_test.go create mode 100644 pkg/util/logging/log_merge_hook.go create mode 100644 pkg/util/logging/log_merge_hook_test.go diff --git a/changelogs/unreleased/7999-Lyndon-Li b/changelogs/unreleased/7999-Lyndon-Li new file mode 100644 index 000000000..87a719248 --- /dev/null +++ b/changelogs/unreleased/7999-Lyndon-Li @@ -0,0 +1 @@ +Data mover ms watcher according to design #7576 \ No newline at end of file diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 2748569f3..3a18ab862 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -189,6 +189,9 @@ func newNodeAgentServer(logger logrus.FieldLogger, factory client.Factory, confi &velerov2alpha1api.DataDownload{}: { Field: fields.Set{"metadata.namespace": factory.Namespace()}.AsSelector(), }, + &v1.Event{}: { + Field: fields.Set{"metadata.namespace": factory.Namespace()}.AsSelector(), + }, }, } mgr, err := ctrl.NewManager(clientConfig, ctrl.Options{ diff --git a/pkg/datapath/manager.go b/pkg/datapath/manager.go index df60f165b..0b790a5cc 100644 --- a/pkg/datapath/manager.go +++ b/pkg/datapath/manager.go @@ -22,11 +22,14 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" + "k8s.io/client-go/kubernetes" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/manager" ) var ConcurrentLimitExceed error = errors.New("Concurrent number exceeds") var FSBRCreator = newFileSystemBR +var MicroServiceBRWatcherCreator = newMicroServiceBRWatcher type Manager struct { cocurrentNum int @@ -56,6 +59,23 @@ func (m *Manager) CreateFileSystemBR(jobName string, requestorType string, ctx c return m.tracker[jobName], nil } +// CreateMicroServiceBRWatcher creates a new micro service watcher instance +func (m *Manager) CreateMicroServiceBRWatcher(ctx context.Context, client client.Client, kubeClient kubernetes.Interface, mgr manager.Manager, taskType string, + taskName string, namespace string, podName string, containerName string, associatedObject string, callbacks Callbacks, resume bool, log logrus.FieldLogger) (AsyncBR, error) { + m.trackerLock.Lock() + defer m.trackerLock.Unlock() + + if !resume { + if len(m.tracker) >= m.cocurrentNum { + return nil, ConcurrentLimitExceed + } + } + + m.tracker[taskName] = MicroServiceBRWatcherCreator(client, kubeClient, mgr, taskType, taskName, namespace, podName, containerName, associatedObject, callbacks, log) + + return m.tracker[taskName], nil +} + // RemoveAsyncBR removes a file system backup/restore data path instance func (m *Manager) RemoveAsyncBR(jobName string) { m.trackerLock.Lock() diff --git a/pkg/datapath/manager_test.go b/pkg/datapath/manager_test.go index fda574400..0db605134 100644 --- a/pkg/datapath/manager_test.go +++ b/pkg/datapath/manager_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestManager(t *testing.T) { +func TestCreateFileSystemBR(t *testing.T) { m := NewManager(2) async_job_1, err := m.CreateFileSystemBR("job-1", "test", context.TODO(), nil, "velero", Callbacks{}, nil) @@ -50,3 +50,37 @@ func TestManager(t *testing.T) { ret = m.GetAsyncBR("job-1") assert.Nil(t, ret) } + +func TestCreateMicroServiceBRWatcher(t *testing.T) { + m := NewManager(2) + + async_job_1, err := m.CreateMicroServiceBRWatcher(context.TODO(), nil, nil, nil, "test", "job-1", "velero", "pod-1", "container", "du-1", Callbacks{}, false, nil) + assert.NoError(t, err) + + _, err = m.CreateMicroServiceBRWatcher(context.TODO(), nil, nil, nil, "test", "job-2", "velero", "pod-2", "container", "du-2", Callbacks{}, false, nil) + assert.NoError(t, err) + + _, err = m.CreateMicroServiceBRWatcher(context.TODO(), nil, nil, nil, "test", "job-3", "velero", "pod-3", "container", "du-3", Callbacks{}, false, nil) + assert.Equal(t, ConcurrentLimitExceed, err) + + async_job_4, err := m.CreateMicroServiceBRWatcher(context.TODO(), nil, nil, nil, "test", "job-4", "velero", "pod-4", "container", "du-4", Callbacks{}, true, nil) + assert.NoError(t, err) + + ret := m.GetAsyncBR("job-0") + assert.Nil(t, ret) + + ret = m.GetAsyncBR("job-1") + assert.Equal(t, async_job_1, ret) + + ret = m.GetAsyncBR("job-4") + assert.Equal(t, async_job_4, ret) + + m.RemoveAsyncBR("job-0") + assert.Len(t, m.tracker, 3) + + m.RemoveAsyncBR("job-1") + assert.Len(t, m.tracker, 2) + + ret = m.GetAsyncBR("job-1") + assert.Nil(t, ret) +} diff --git a/pkg/datapath/micro_service_watcher.go b/pkg/datapath/micro_service_watcher.go new file mode 100644 index 000000000..a5826459a --- /dev/null +++ b/pkg/datapath/micro_service_watcher.go @@ -0,0 +1,437 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datapath + +import ( + "context" + "encoding/json" + "os" + "sync" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/kube" + + ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/manager" + + "github.com/vmware-tanzu/velero/pkg/util/logging" +) + +const ( + TaskTypeBackup = "backup" + TaskTypeRestore = "restore" + + ErrCancelled = "data path is canceled" + + EventReasonStarted = "Data-Path-Started" + EventReasonCompleted = "Data-Path-Completed" + EventReasonFailed = "Data-Path-Failed" + EventReasonCancelled = "Data-Path-Canceled" + EventReasonProgress = "Data-Path-Progress" +) + +type microServiceBRWatcher struct { + ctx context.Context + cancel context.CancelFunc + log logrus.FieldLogger + client client.Client + kubeClient kubernetes.Interface + mgr manager.Manager + namespace string + callbacks Callbacks + taskName string + taskType string + thisPod string + thisContainer string + associatedObject string + eventCh chan *v1.Event + podCh chan *v1.Pod + startedFromEvent bool + terminatedFromEvent bool + wgWatcher sync.WaitGroup + eventInformer ctrlcache.Informer + podInformer ctrlcache.Informer + eventHandler cache.ResourceEventHandlerRegistration + podHandler cache.ResourceEventHandlerRegistration +} + +func newMicroServiceBRWatcher(client client.Client, kubeClient kubernetes.Interface, mgr manager.Manager, taskType string, taskName string, namespace string, + podName string, containerName string, associatedObject string, callbacks Callbacks, log logrus.FieldLogger) AsyncBR { + ms := µServiceBRWatcher{ + mgr: mgr, + client: client, + kubeClient: kubeClient, + namespace: namespace, + callbacks: callbacks, + taskType: taskType, + taskName: taskName, + thisPod: podName, + thisContainer: containerName, + associatedObject: associatedObject, + eventCh: make(chan *v1.Event, 10), + podCh: make(chan *v1.Pod, 2), + wgWatcher: sync.WaitGroup{}, + log: log, + } + + return ms +} + +func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) error { + succeeded := false + + eventInformer, err := ms.mgr.GetCache().GetInformer(ctx, &v1.Event{}) + if err != nil { + return errors.Wrap(err, "error getting event informer") + } + + podInformer, err := ms.mgr.GetCache().GetInformer(ctx, &v1.Pod{}) + if err != nil { + return errors.Wrap(err, "error getting pod informer") + } + + eventHandler, err := eventInformer.AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + evt := obj.(*v1.Event) + if evt.InvolvedObject.Namespace != ms.namespace || evt.InvolvedObject.Name != ms.associatedObject { + return + } + + ms.log.Infof("Pushed adding event %s/%s, message %s for object %v", evt.Namespace, evt.Name, evt.Message, evt.InvolvedObject) + + ms.eventCh <- evt + }, + UpdateFunc: func(_, obj interface{}) { + evt := obj.(*v1.Event) + if evt.InvolvedObject.Namespace != ms.namespace || evt.InvolvedObject.Name != ms.associatedObject { + return + } + + ms.log.Infof("Pushed updating event %s/%s, message %s for object %v", evt.Namespace, evt.Name, evt.Message, evt.InvolvedObject) + + ms.eventCh <- evt + }, + }, + ) + + if err != nil { + return errors.Wrap(err, "error registering event handler") + } + + defer func() { + if !succeeded { + if err := eventInformer.RemoveEventHandler(eventHandler); err != nil { + ms.log.WithError(err).Warn("Failed to remove event handler") + } + } + }() + + podHandler, err := podInformer.AddEventHandler( + cache.ResourceEventHandlerFuncs{ + UpdateFunc: func(_, obj interface{}) { + pod := obj.(*v1.Pod) + if pod.Namespace != ms.namespace || pod.Name != ms.thisPod { + return + } + + if pod.Status.Phase == v1.PodSucceeded || pod.Status.Phase == v1.PodFailed { + ms.podCh <- pod + } + }, + }, + ) + + if err != nil { + return errors.Wrap(err, "error registering pod handler") + } + + defer func() { + if !succeeded { + if err := podInformer.RemoveEventHandler(podHandler); err != nil { + ms.log.WithError(err).Warn("Failed to remove pod handler") + } + } + }() + + ms.log.WithFields( + logrus.Fields{ + "taskType": ms.taskType, + "taskName": ms.taskName, + "thisPod": ms.thisPod, + }).Info("MicroServiceBR is initialized") + + ms.eventInformer = eventInformer + ms.podInformer = podInformer + ms.eventHandler = eventHandler + ms.podHandler = podHandler + + ms.ctx, ms.cancel = context.WithCancel(ctx) + + succeeded = true + + return nil +} + +func (ms *microServiceBRWatcher) Close(ctx context.Context) { + if ms.cancel != nil { + ms.cancel() + ms.cancel = nil + } + + ms.log.WithField("taskType", ms.taskType).WithField("taskName", ms.taskName).Info("Closing MicroServiceBR") + + ms.wgWatcher.Wait() + + if ms.eventInformer != nil && ms.eventHandler != nil { + if err := ms.eventInformer.RemoveEventHandler(ms.eventHandler); err != nil { + ms.log.WithError(err).Warn("Failed to remove event handler") + } + } + + if ms.podInformer != nil && ms.podHandler != nil { + if err := ms.podInformer.RemoveEventHandler(ms.podHandler); err != nil { + ms.log.WithError(err).Warn("Failed to remove pod handler") + } + } + + ms.log.WithField("taskType", ms.taskType).WithField("taskName", ms.taskName).Info("MicroServiceBR is closed") +} + +func (ms *microServiceBRWatcher) StartBackup(source AccessPoint, uploaderConfig map[string]string, param interface{}) error { + ms.log.Infof("Start watching backup ms for source %v", source) + + if err := ms.reEnsureThisPod(); err != nil { + return err + } + + ms.startWatch() + + return nil +} + +func (ms *microServiceBRWatcher) StartRestore(snapshotID string, target AccessPoint, uploaderConfigs map[string]string) error { + ms.log.Infof("Start watching restore ms to target %v, from snapshot %s", target, snapshotID) + + if err := ms.reEnsureThisPod(); err != nil { + return err + } + + ms.startWatch() + + return nil +} + +func (ms *microServiceBRWatcher) reEnsureThisPod() error { + thisPod := &v1.Pod{} + if err := ms.client.Get(ms.ctx, types.NamespacedName{ + Namespace: ms.namespace, + Name: ms.thisPod, + }, thisPod); err != nil { + return errors.Wrapf(err, "error getting this pod %s", ms.thisPod) + } + + if thisPod.Status.Phase == v1.PodSucceeded || thisPod.Status.Phase == v1.PodFailed { + ms.podCh <- thisPod + ms.log.WithField("this pod", ms.thisPod).Infof("This pod comes to terminital status %s before watch start", thisPod.Status.Phase) + } + + return nil +} + +var funcGetPodTerminationMessage = kube.GetPodContainerTerminateMessage +var funcRedirectLog = redirectDataMoverLogs +var funcGetResultFromMessage = getResultFromMessage +var funcGetProgressFromMessage = getProgressFromMessage + +var eventWaitTimeout time.Duration = time.Minute + +func (ms *microServiceBRWatcher) startWatch() { + ms.wgWatcher.Add(1) + + go func() { + ms.log.Info("Start watching data path pod") + + var lastPod *v1.Pod + + watchLoop: + for { + select { + case <-ms.ctx.Done(): + break watchLoop + case pod := <-ms.podCh: + lastPod = pod + break watchLoop + case evt := <-ms.eventCh: + ms.onEvent(evt) + } + } + + if lastPod == nil { + ms.log.Warn("Data path pod watch loop is canceled") + ms.wgWatcher.Done() + return + } + + epilogLoop: + for !ms.startedFromEvent || !ms.terminatedFromEvent { + select { + case <-time.After(eventWaitTimeout): + break epilogLoop + case evt := <-ms.eventCh: + ms.onEvent(evt) + } + } + + terminateMessage := funcGetPodTerminationMessage(lastPod, ms.thisContainer) + + logger := ms.log.WithField("data path pod", lastPod.Name) + + logger.Infof("Finish waiting data path pod, phase %s, message %s", lastPod.Status.Phase, terminateMessage) + + if !ms.startedFromEvent { + logger.Warn("VGDP seems not started") + } + + if ms.startedFromEvent && !ms.terminatedFromEvent { + logger.Warn("VGDP started but termination event is not received") + } + + logger.Info("Recording data path pod logs") + + if err := funcRedirectLog(ms.ctx, ms.kubeClient, ms.namespace, lastPod.Name, ms.thisContainer, ms.log); err != nil { + logger.WithError(err).Warn("Failed to collect data mover logs") + } + + logger.Info("Calling callback on data path pod termination") + + if lastPod.Status.Phase == v1.PodSucceeded { + ms.callbacks.OnCompleted(ms.ctx, ms.namespace, ms.taskName, funcGetResultFromMessage(ms.taskType, terminateMessage, ms.log)) + } else { + if terminateMessage == ErrCancelled { + ms.callbacks.OnCancelled(ms.ctx, ms.namespace, ms.taskName) + } else { + ms.callbacks.OnFailed(ms.ctx, ms.namespace, ms.taskName, errors.New(terminateMessage)) + } + } + + logger.Info("Complete callback on data path pod termination") + + ms.wgWatcher.Done() + }() +} + +func (ms *microServiceBRWatcher) onEvent(evt *v1.Event) { + switch evt.Reason { + case EventReasonStarted: + ms.startedFromEvent = true + ms.log.Infof("Received data path start message %s", evt.Message) + case EventReasonProgress: + ms.callbacks.OnProgress(ms.ctx, ms.namespace, ms.taskName, funcGetProgressFromMessage(evt.Message, ms.log)) + case EventReasonCompleted: + ms.log.Infof("Received data path completed message %v", funcGetResultFromMessage(ms.taskType, evt.Message, ms.log)) + ms.terminatedFromEvent = true + case EventReasonCancelled: + ms.log.Infof("Received data path canceled message %s", evt.Message) + ms.terminatedFromEvent = true + case EventReasonFailed: + ms.log.Infof("Received data path failed message %s", evt.Message) + ms.terminatedFromEvent = true + default: + ms.log.Debugf("Received event for data mover %s.[reason %s, message %s]", ms.taskName, evt.Reason, evt.Message) + } +} + +func getResultFromMessage(taskType string, message string, logger logrus.FieldLogger) Result { + result := Result{} + + if taskType == TaskTypeBackup { + backupResult := BackupResult{} + err := json.Unmarshal([]byte(message), &backupResult) + if err != nil { + logger.WithError(err).Errorf("Failed to unmarshal result message %s", message) + } else { + result.Backup = backupResult + } + } else { + restoreResult := RestoreResult{} + err := json.Unmarshal([]byte(message), &restoreResult) + if err != nil { + logger.WithError(err).Errorf("Failed to unmarshal result message %s", message) + } else { + result.Restore = restoreResult + } + } + + return result +} + +func getProgressFromMessage(message string, logger logrus.FieldLogger) *uploader.Progress { + progress := &uploader.Progress{} + err := json.Unmarshal([]byte(message), progress) + if err != nil { + logger.WithError(err).Debugf("Failed to unmarshal progress message %s", message) + } + + return progress +} + +func (ms *microServiceBRWatcher) Cancel() { + ms.log.WithField("taskType", ms.taskType).WithField("taskName", ms.taskName).Info("MicroServiceBR is canceled") +} + +var funcCreateTemp = os.CreateTemp +var funcCollectPodLogs = kube.CollectPodLogs + +func redirectDataMoverLogs(ctx context.Context, kubeClient kubernetes.Interface, namespace string, thisPod string, thisContainer string, logger logrus.FieldLogger) error { + logger.Infof("Starting to collect data mover pod log for %s", thisPod) + + logFile, err := funcCreateTemp("", "") + if err != nil { + return errors.Wrap(err, "error to create temp file for data mover pod log") + } + + defer logFile.Close() + + logFileName := logFile.Name() + logger.Infof("Created log file %s", logFileName) + + err = funcCollectPodLogs(ctx, kubeClient.CoreV1(), thisPod, namespace, thisContainer, logFile) + if err != nil { + return errors.Wrapf(err, "error to collect logs to %s for data mover pod %s", logFileName, thisPod) + } + + logFile.Close() + + logger.Infof("Redirecting to log file %s", logFileName) + + hookLogger := logger.WithField(logging.LogSourceKey, logFileName) + hookLogger.Logln(logging.ListeningLevel, logging.ListeningMessage) + + logger.Infof("Completed to collect data mover pod log for %s", thisPod) + + return nil +} diff --git a/pkg/datapath/micro_service_watcher_test.go b/pkg/datapath/micro_service_watcher_test.go new file mode 100644 index 000000000..f10f6b331 --- /dev/null +++ b/pkg/datapath/micro_service_watcher_test.go @@ -0,0 +1,603 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datapath + +import ( + "context" + "errors" + "fmt" + "io" + "os" + "path" + "strings" + "testing" + "time" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + kubeclientfake "k8s.io/client-go/kubernetes/fake" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/vmware-tanzu/velero/pkg/builder" + velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/logging" +) + +func TestReEnsureThisPod(t *testing.T) { + tests := []struct { + name string + namespace string + thisPod string + kubeClientObj []runtime.Object + expectChan bool + expectErr string + }{ + { + name: "get pod error", + thisPod: "fak-pod-1", + expectErr: "error getting this pod fak-pod-1: pods \"fak-pod-1\" not found", + }, + { + name: "get pod not in terminated state", + namespace: "velero", + thisPod: "fake-pod-1", + kubeClientObj: []runtime.Object{ + builder.ForPod("velero", "fake-pod-1").Phase(v1.PodRunning).Result(), + }, + }, + { + name: "get pod succeed state", + namespace: "velero", + thisPod: "fake-pod-1", + kubeClientObj: []runtime.Object{ + builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + }, + expectChan: true, + }, + { + name: "get pod failed state", + namespace: "velero", + thisPod: "fake-pod-1", + kubeClientObj: []runtime.Object{ + builder.ForPod("velero", "fake-pod-1").Phase(v1.PodFailed).Result(), + }, + expectChan: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + scheme := runtime.NewScheme() + v1.AddToScheme(scheme) + fakeClientBuilder := fake.NewClientBuilder() + fakeClientBuilder = fakeClientBuilder.WithScheme(scheme) + + fakeClient := fakeClientBuilder.WithRuntimeObjects(test.kubeClientObj...).Build() + + ms := µServiceBRWatcher{ + namespace: test.namespace, + thisPod: test.thisPod, + client: fakeClient, + podCh: make(chan *v1.Pod, 2), + log: velerotest.NewLogger(), + } + + err := ms.reEnsureThisPod() + if test.expectErr != "" { + assert.EqualError(t, err, test.expectErr) + } else { + if test.expectChan { + assert.Len(t, ms.podCh, 1) + pod := <-ms.podCh + assert.Equal(t, pod.Name, test.thisPod) + } + } + }) + } +} + +type startWatchFake struct { + terminationMessage string + redirectErr error + complete bool + failed bool + canceled bool + progress int +} + +func (sw *startWatchFake) getPodContainerTerminateMessage(pod *v1.Pod, container string) string { + return sw.terminationMessage +} + +func (sw *startWatchFake) redirectDataMoverLogs(ctx context.Context, kubeClient kubernetes.Interface, namespace string, thisPod string, thisContainer string, logger logrus.FieldLogger) error { + return sw.redirectErr +} + +func (sw *startWatchFake) getResultFromMessage(_ string, _ string, _ logrus.FieldLogger) Result { + return Result{} +} + +func (sw *startWatchFake) OnCompleted(ctx context.Context, namespace string, task string, result Result) { + sw.complete = true +} + +func (sw *startWatchFake) OnFailed(ctx context.Context, namespace string, task string, err error) { + sw.failed = true +} + +func (sw *startWatchFake) OnCancelled(ctx context.Context, namespace string, task string) { + sw.canceled = true +} + +func (sw *startWatchFake) OnProgress(ctx context.Context, namespace string, task string, progress *uploader.Progress) { + sw.progress++ +} + +type insertEvent struct { + event *v1.Event + after time.Duration + delay time.Duration +} + +func TestStartWatch(t *testing.T) { + tests := []struct { + name string + namespace string + thisPod string + thisContainer string + terminationMessage string + redirectLogErr error + insertPod *v1.Pod + insertEventsBefore []insertEvent + insertEventsAfter []insertEvent + ctxCancel bool + expectStartEvent bool + expectTerminateEvent bool + expectComplete bool + expectCancel bool + expectFail bool + expectProgress int + }{ + { + name: "exit from ctx", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + ctxCancel: true, + }, + { + name: "completed with rantional sequence", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + { + event: &v1.Event{Reason: EventReasonCompleted}, + delay: time.Second, + }, + }, + expectStartEvent: true, + expectTerminateEvent: true, + expectComplete: true, + }, + { + name: "completed", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + { + event: &v1.Event{Reason: EventReasonCompleted}, + }, + }, + expectStartEvent: true, + expectTerminateEvent: true, + expectComplete: true, + }, + { + name: "completed with redirect error", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + { + event: &v1.Event{Reason: EventReasonCompleted}, + }, + }, + redirectLogErr: errors.New("fake-error"), + expectStartEvent: true, + expectTerminateEvent: true, + expectComplete: true, + }, + { + name: "complete but terminated event not received in time", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + }, + insertEventsAfter: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + after: time.Second * 6, + }, + }, + expectStartEvent: true, + expectComplete: true, + }, + { + name: "complete but terminated event not received immediately", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + }, + insertEventsAfter: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonCompleted}, + after: time.Second, + }, + }, + expectStartEvent: true, + expectTerminateEvent: true, + expectComplete: true, + }, + { + name: "completed with progress", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodSucceeded).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + { + event: &v1.Event{Reason: EventReasonProgress, Message: "fake-progress-1"}, + }, + { + event: &v1.Event{Reason: EventReasonProgress, Message: "fake-progress-2"}, + }, + { + event: &v1.Event{Reason: EventReasonCompleted}, + delay: time.Second, + }, + }, + expectStartEvent: true, + expectTerminateEvent: true, + expectComplete: true, + expectProgress: 2, + }, + { + name: "failed", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodFailed).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + { + event: &v1.Event{Reason: EventReasonCancelled}, + }, + }, + terminationMessage: "fake-termination-message-1", + expectStartEvent: true, + expectTerminateEvent: true, + expectFail: true, + }, + { + name: "pod crash", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodFailed).Result(), + terminationMessage: "fake-termination-message-2", + expectFail: true, + }, + { + name: "canceled", + thisPod: "fak-pod-1", + thisContainer: "fake-container-1", + insertPod: builder.ForPod("velero", "fake-pod-1").Phase(v1.PodFailed).Result(), + insertEventsBefore: []insertEvent{ + { + event: &v1.Event{Reason: EventReasonStarted}, + }, + { + event: &v1.Event{Reason: EventReasonCancelled}, + }, + }, + terminationMessage: ErrCancelled, + expectStartEvent: true, + expectTerminateEvent: true, + expectCancel: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + eventWaitTimeout = time.Second * 5 + + sw := startWatchFake{ + terminationMessage: test.terminationMessage, + redirectErr: test.redirectLogErr, + } + funcGetPodTerminationMessage = sw.getPodContainerTerminateMessage + funcRedirectLog = sw.redirectDataMoverLogs + funcGetResultFromMessage = sw.getResultFromMessage + + ms := µServiceBRWatcher{ + ctx: ctx, + namespace: test.namespace, + thisPod: test.thisPod, + thisContainer: test.thisContainer, + podCh: make(chan *v1.Pod, 2), + eventCh: make(chan *v1.Event, 10), + log: velerotest.NewLogger(), + callbacks: Callbacks{ + OnCompleted: sw.OnCompleted, + OnFailed: sw.OnFailed, + OnCancelled: sw.OnCancelled, + OnProgress: sw.OnProgress, + }, + } + + ms.startWatch() + + if test.ctxCancel { + cancel() + } + + for _, ev := range test.insertEventsBefore { + if ev.after != 0 { + time.Sleep(ev.after) + } + + ms.eventCh <- ev.event + + if ev.delay != 0 { + time.Sleep(ev.delay) + } + } + + if test.insertPod != nil { + ms.podCh <- test.insertPod + } + + for _, ev := range test.insertEventsAfter { + if ev.after != 0 { + time.Sleep(ev.after) + } + + ms.eventCh <- ev.event + + if ev.delay != 0 { + time.Sleep(ev.delay) + } + } + + ms.wgWatcher.Wait() + + assert.Equal(t, test.expectStartEvent, ms.startedFromEvent) + assert.Equal(t, test.expectTerminateEvent, ms.terminatedFromEvent) + assert.Equal(t, test.expectComplete, sw.complete) + assert.Equal(t, test.expectCancel, sw.canceled) + assert.Equal(t, test.expectFail, sw.failed) + assert.Equal(t, test.expectProgress, sw.progress) + + cancel() + }) + } +} + +func TestGetResultFromMessage(t *testing.T) { + tests := []struct { + name string + taskType string + message string + expectResult Result + }{ + { + name: "error to unmarshall backup result", + taskType: TaskTypeBackup, + message: "fake-message", + expectResult: Result{}, + }, + { + name: "error to unmarshall restore result", + taskType: TaskTypeRestore, + message: "fake-message", + expectResult: Result{}, + }, + { + name: "succeed to unmarshall backup result", + taskType: TaskTypeBackup, + message: "{\"snapshotID\":\"fake-snapshot-id\",\"emptySnapshot\":true,\"source\":{\"byPath\":\"fake-path-1\",\"volumeMode\":\"Block\"}}", + expectResult: Result{ + Backup: BackupResult{ + SnapshotID: "fake-snapshot-id", + EmptySnapshot: true, + Source: AccessPoint{ + ByPath: "fake-path-1", + VolMode: uploader.PersistentVolumeBlock, + }, + }, + }, + }, + { + name: "succeed to unmarshall restore result", + taskType: TaskTypeRestore, + message: "{\"target\":{\"byPath\":\"fake-path-2\",\"volumeMode\":\"Filesystem\"}}", + expectResult: Result{ + Restore: RestoreResult{ + Target: AccessPoint{ + ByPath: "fake-path-2", + VolMode: uploader.PersistentVolumeFilesystem, + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := getResultFromMessage(test.taskType, test.message, velerotest.NewLogger()) + assert.Equal(t, test.expectResult, result) + }) + } +} + +func TestGetProgressFromMessage(t *testing.T) { + tests := []struct { + name string + message string + expectProgress uploader.Progress + }{ + { + name: "error to unmarshall progress", + message: "fake-message", + expectProgress: uploader.Progress{}, + }, + { + name: "succeed to unmarshall progress", + message: "{\"totalBytes\":1000,\"doneBytes\":200}", + expectProgress: uploader.Progress{ + TotalBytes: 1000, + BytesDone: 200, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + progress := getProgressFromMessage(test.message, velerotest.NewLogger()) + assert.Equal(t, test.expectProgress, *progress) + }) + } +} + +type redirectFake struct { + logFile *os.File + createTempErr error + getPodLogErr error + logMessage string +} + +func (rf *redirectFake) fakeCreateTempFile(_ string, _ string) (*os.File, error) { + if rf.createTempErr != nil { + return nil, rf.createTempErr + } + + return rf.logFile, nil +} + +func (rf *redirectFake) fakeCollectPodLogs(_ context.Context, _ corev1client.CoreV1Interface, _ string, _ string, _ string, output io.Writer) error { + if rf.getPodLogErr != nil { + return rf.getPodLogErr + } + + _, err := output.Write([]byte(rf.logMessage)) + + return err +} + +func TestRedirectDataMoverLogs(t *testing.T) { + logFileName := path.Join(os.TempDir(), "test-logger-file.log") + + var buffer string + + tests := []struct { + name string + thisPod string + logMessage string + logger logrus.FieldLogger + createTempErr error + collectLogErr error + expectErr string + }{ + { + name: "error to create temp file", + thisPod: "fake-pod", + createTempErr: errors.New("fake-create-temp-error"), + logger: velerotest.NewLogger(), + expectErr: "error to create temp file for data mover pod log: fake-create-temp-error", + }, + { + name: "error to collect pod log", + thisPod: "fake-pod", + collectLogErr: errors.New("fake-collect-log-error"), + logger: velerotest.NewLogger(), + expectErr: fmt.Sprintf("error to collect logs to %s for data mover pod fake-pod: fake-collect-log-error", logFileName), + }, + { + name: "succeed", + thisPod: "fake-pod", + logMessage: "fake-log-message-01\nfake-log-message-02\nfake-log-message-03\n", + logger: velerotest.NewSingleLoggerWithHooks(&buffer, logging.DefaultHooks(true)), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + buffer = "" + + logFile, err := os.Create(logFileName) + require.NoError(t, err) + + rf := redirectFake{ + logFile: logFile, + createTempErr: test.createTempErr, + getPodLogErr: test.collectLogErr, + logMessage: test.logMessage, + } + + funcCreateTemp = rf.fakeCreateTempFile + funcCollectPodLogs = rf.fakeCollectPodLogs + + fakeKubeClient := kubeclientfake.NewSimpleClientset() + + err = redirectDataMoverLogs(context.Background(), fakeKubeClient, "", test.thisPod, "", test.logger) + if test.expectErr != "" { + assert.EqualError(t, err, test.expectErr) + } else { + assert.NoError(t, err) + + assert.True(t, strings.Contains(buffer, test.logMessage)) + } + }) + } +} diff --git a/pkg/datapath/types.go b/pkg/datapath/types.go index c98cd284a..a2fac3ed5 100644 --- a/pkg/datapath/types.go +++ b/pkg/datapath/types.go @@ -50,8 +50,8 @@ type Callbacks struct { // AccessPoint represents an access point that has been exposed to a data path instance type AccessPoint struct { - ByPath string - VolMode uploader.PersistentVolumeMode + ByPath string `json:"byPath"` + VolMode uploader.PersistentVolumeMode `json:"volumeMode"` } // AsyncBR is the interface for asynchronous data path methods diff --git a/pkg/exposer/csi_snapshot_test.go b/pkg/exposer/csi_snapshot_test.go index cc0a895e1..44c29b1d5 100644 --- a/pkg/exposer/csi_snapshot_test.go +++ b/pkg/exposer/csi_snapshot_test.go @@ -672,7 +672,7 @@ func TestPeekExpose(t *testing.T) { kubeClientObj: []runtime.Object{ backupPodUrecoverable, }, - err: "Pod is in abnormal state Failed", + err: "Pod is in abnormal state [Failed], message []", }, { name: "succeed", diff --git a/pkg/exposer/generic_restore_test.go b/pkg/exposer/generic_restore_test.go index 6080f8b97..9108ba228 100644 --- a/pkg/exposer/generic_restore_test.go +++ b/pkg/exposer/generic_restore_test.go @@ -456,7 +456,7 @@ func TestRestorePeekExpose(t *testing.T) { kubeClientObj: []runtime.Object{ restorePodUrecoverable, }, - err: "Pod is in abnormal state Failed", + err: "Pod is in abnormal state [Failed], message []", }, { name: "succeed", diff --git a/pkg/test/test_logger.go b/pkg/test/test_logger.go index b890fd5da..65dc8422a 100644 --- a/pkg/test/test_logger.go +++ b/pkg/test/test_logger.go @@ -50,3 +50,15 @@ func NewSingleLogger(buffer *string) logrus.FieldLogger { logger.Level = logrus.TraceLevel return logrus.NewEntry(logger) } + +func NewSingleLoggerWithHooks(buffer *string, hooks []logrus.Hook) logrus.FieldLogger { + logger := logrus.New() + logger.Out = &singleLogRecorder{buffer: buffer} + logger.Level = logrus.TraceLevel + + for _, hook := range hooks { + logger.Hooks.Add(hook) + } + + return logrus.NewEntry(logger) +} diff --git a/pkg/util/kube/pod.go b/pkg/util/kube/pod.go index 857fe9420..9def5d514 100644 --- a/pkg/util/kube/pod.go +++ b/pkg/util/kube/pod.go @@ -18,6 +18,7 @@ package kube import ( "context" "fmt" + "io" "time" "github.com/pkg/errors" @@ -117,8 +118,9 @@ func EnsureDeletePod(ctx context.Context, podGetter corev1client.CoreV1Interface func IsPodUnrecoverable(pod *corev1api.Pod, log logrus.FieldLogger) (bool, string) { // Check the Phase field if pod.Status.Phase == corev1api.PodFailed || pod.Status.Phase == corev1api.PodUnknown { - log.Warnf("Pod is in abnormal state %s", pod.Status.Phase) - return true, fmt.Sprintf("Pod is in abnormal state %s", pod.Status.Phase) + message := GetPodTerminateMessage(pod) + log.Warnf("Pod is in abnormal state %s, message [%s]", pod.Status.Phase, message) + return true, fmt.Sprintf("Pod is in abnormal state [%s], message [%s]", pod.Status.Phase, message) } // removed "Unschedulable" check since unschedulable condition isn't always permanent @@ -133,3 +135,69 @@ func IsPodUnrecoverable(pod *corev1api.Pod, log logrus.FieldLogger) (bool, strin } return false, "" } + +// GetPodContainerTerminateMessage returns the terminate message for a specific container of a pod +func GetPodContainerTerminateMessage(pod *corev1api.Pod, container string) string { + message := "" + for _, containerStatus := range pod.Status.ContainerStatuses { + if containerStatus.Name == container { + if containerStatus.State.Terminated != nil { + message = containerStatus.State.Terminated.Message + } + break + } + } + + return message +} + +// GetPodTerminateMessage returns the terminate message for all containers of a pod +func GetPodTerminateMessage(pod *corev1api.Pod) string { + message := "" + for _, containerStatus := range pod.Status.ContainerStatuses { + if containerStatus.State.Terminated != nil { + if containerStatus.State.Terminated.Message != "" { + message += containerStatus.State.Terminated.Message + "/" + } + } + } + + return message +} + +func getPodLogReader(ctx context.Context, podGetter corev1client.CoreV1Interface, pod string, namespace string, logOptions *corev1api.PodLogOptions) (io.ReadCloser, error) { + request := podGetter.Pods(namespace).GetLogs(pod, logOptions) + return request.Stream(ctx) +} + +var podLogReaderGetter = getPodLogReader + +// CollectPodLogs collects logs of the specified container of a pod and write to the output +func CollectPodLogs(ctx context.Context, podGetter corev1client.CoreV1Interface, pod string, namespace string, container string, output io.Writer) error { + logIndicator := fmt.Sprintf("***************************begin pod logs[%s/%s]***************************\n", pod, container) + + if _, err := output.Write([]byte(logIndicator)); err != nil { + return errors.Wrap(err, "error to write begin pod log indicator") + } + + logOptions := &corev1api.PodLogOptions{ + Container: container, + } + + if input, err := podLogReaderGetter(ctx, podGetter, pod, namespace, logOptions); err != nil { + logIndicator = fmt.Sprintf("No present log retrieved, err: %v\n", err) + } else { + if _, err := io.Copy(output, input); err != nil { + return errors.Wrap(err, "error to copy input") + } + + logIndicator = "" + } + + logIndicator += fmt.Sprintf("***************************end pod logs[%s/%s]***************************\n", pod, container) + if _, err := output.Write([]byte(logIndicator)); err != nil { + return errors.Wrap(err, "error to write end pod log indicator") + } + + return nil +} diff --git a/pkg/util/kube/pod_test.go b/pkg/util/kube/pod_test.go index f1cdac043..7ccb22578 100644 --- a/pkg/util/kube/pod_test.go +++ b/pkg/util/kube/pod_test.go @@ -18,6 +18,8 @@ package kube import ( "context" + "io" + "strings" "testing" "time" @@ -32,6 +34,8 @@ import ( clientTesting "k8s.io/client-go/testing" velerotest "github.com/vmware-tanzu/velero/pkg/test" + + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" ) func TestEnsureDeletePod(t *testing.T) { @@ -422,3 +426,274 @@ func TestIsPodUnrecoverable(t *testing.T) { }) } } + +func TestGetPodTerminateMessage(t *testing.T) { + tests := []struct { + name string + pod *corev1api.Pod + message string + }{ + { + name: "empty message when no container status", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + Phase: corev1api.PodFailed, + }, + }, + }, + { + name: "empty message when no termination status", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Waiting: &corev1api.ContainerStateWaiting{Reason: "ImagePullBackOff"}}}, + }, + }, + }, + }, + { + name: "empty message when no termination message", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Reason: "fake-reason"}}}, + }, + }, + }, + }, + { + name: "with termination message", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-1"}}}, + {Name: "container-2", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-2"}}}, + {Name: "container-3", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-3"}}}, + }, + }, + }, + message: "message-1/message-2/message-3/", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + message := GetPodTerminateMessage(test.pod) + assert.Equal(t, test.message, message) + }) + } +} + +func TestGetPodContainerTerminateMessage(t *testing.T) { + tests := []struct { + name string + pod *corev1api.Pod + container string + message string + }{ + { + name: "empty message when no container status", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + Phase: corev1api.PodFailed, + }, + }, + }, + { + name: "empty message when no termination status", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Waiting: &corev1api.ContainerStateWaiting{Reason: "ImagePullBackOff"}}}, + }, + }, + }, + container: "container-1", + }, + { + name: "empty message when no termination message", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Reason: "fake-reason"}}}, + }, + }, + }, + container: "container-1", + }, + { + name: "not matched container name", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-1"}}}, + {Name: "container-2", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-2"}}}, + {Name: "container-3", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-3"}}}, + }, + }, + }, + container: "container-0", + }, + { + name: "with termination message", + pod: &corev1api.Pod{ + Status: corev1api.PodStatus{ + ContainerStatuses: []corev1api.ContainerStatus{ + {Name: "container-1", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-1"}}}, + {Name: "container-2", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-2"}}}, + {Name: "container-3", State: corev1api.ContainerState{Terminated: &corev1api.ContainerStateTerminated{Message: "message-3"}}}, + }, + }, + }, + container: "container-2", + message: "message-2", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + message := GetPodContainerTerminateMessage(test.pod, test.container) + assert.Equal(t, test.message, message) + }) + } +} + +type fakePodLog struct { + getError error + readError error + beginWriteError error + endWriteError error + writeError error + logMessage string + outputMessage string + readPos int +} + +func (fp *fakePodLog) GetPodLogReader(ctx context.Context, podGetter corev1client.CoreV1Interface, pod string, namespace string, logOptions *corev1api.PodLogOptions) (io.ReadCloser, error) { + if fp.getError != nil { + return nil, fp.getError + } + + return fp, nil +} + +func (fp *fakePodLog) Read(p []byte) (n int, err error) { + if fp.readError != nil { + return -1, fp.readError + } + + if fp.readPos == len(fp.logMessage) { + return 0, io.EOF + } + + copy(p, []byte(fp.logMessage)) + fp.readPos += len(fp.logMessage) + + return len(fp.logMessage), nil +} + +func (fp *fakePodLog) Close() error { + return nil +} + +func (fp *fakePodLog) Write(p []byte) (n int, err error) { + message := string(p) + if strings.Contains(message, "begin pod logs") { + if fp.beginWriteError != nil { + return -1, fp.beginWriteError + } + } else if strings.Contains(message, "end pod logs") { + if fp.endWriteError != nil { + return -1, fp.endWriteError + } + } else { + if fp.writeError != nil { + return -1, fp.writeError + } + } + + fp.outputMessage += message + + return len(message), nil +} + +func TestCollectPodLogs(t *testing.T) { + tests := []struct { + name string + pod string + container string + getError error + readError error + beginWriteError error + endWriteError error + writeError error + readMessage string + message string + expectErr string + }{ + { + name: "error to write begin indicator", + beginWriteError: errors.New("fake-write-error-01"), + expectErr: "error to write begin pod log indicator: fake-write-error-01", + }, + { + name: "error to get log", + pod: "fake-pod", + container: "fake-container", + getError: errors.New("fake-get-error"), + message: "***************************begin pod logs[fake-pod/fake-container]***************************\nNo present log retrieved, err: fake-get-error\n***************************end pod logs[fake-pod/fake-container]***************************\n", + }, + { + name: "error to read pod log", + pod: "fake-pod", + container: "fake-container", + readError: errors.New("fake-read-error"), + expectErr: "error to copy input: fake-read-error", + }, + { + name: "error to write pod log", + pod: "fake-pod", + container: "fake-container", + writeError: errors.New("fake-write-error-03"), + readMessage: "fake pod message 01\n fake pod message 02\n fake pod message 03\n", + expectErr: "error to copy input: fake-write-error-03", + }, + { + name: "error to write end indicator", + pod: "fake-pod", + container: "fake-container", + endWriteError: errors.New("fake-write-error-02"), + readMessage: "fake pod message 01\n fake pod message 02\n fake pod message 03\n", + expectErr: "error to write end pod log indicator: fake-write-error-02", + }, + { + name: "succeed", + pod: "fake-pod", + container: "fake-container", + readMessage: "fake pod message 01\n fake pod message 02\n fake pod message 03\n", + message: "***************************begin pod logs[fake-pod/fake-container]***************************\nfake pod message 01\n fake pod message 02\n fake pod message 03\n***************************end pod logs[fake-pod/fake-container]***************************\n", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fp := &fakePodLog{ + getError: test.getError, + readError: test.readError, + beginWriteError: test.beginWriteError, + endWriteError: test.endWriteError, + writeError: test.writeError, + logMessage: test.readMessage, + } + podLogReaderGetter = fp.GetPodLogReader + + err := CollectPodLogs(context.Background(), nil, test.pod, "", test.container, fp) + if test.expectErr != "" { + assert.EqualError(t, err, test.expectErr) + } else { + assert.NoError(t, err) + assert.Equal(t, fp.outputMessage, test.message) + } + }) + } +} diff --git a/pkg/util/logging/default_logger.go b/pkg/util/logging/default_logger.go index f1c22f80c..b374c3d84 100644 --- a/pkg/util/logging/default_logger.go +++ b/pkg/util/logging/default_logger.go @@ -24,16 +24,26 @@ import ( // DefaultHooks returns a slice of the default // logrus hooks to be used by a logger. -func DefaultHooks() []logrus.Hook { - return []logrus.Hook{ +func DefaultHooks(merge bool) []logrus.Hook { + hooks := []logrus.Hook{ &LogLocationHook{}, &ErrorLocationHook{}, } + + if merge { + hooks = append(hooks, &MergeHook{}) + } + + return hooks } // DefaultLogger returns a Logger with the default properties // and hooks. The desired output format is passed as a LogFormat Enum. func DefaultLogger(level logrus.Level, format Format) *logrus.Logger { + return createLogger(level, format, false) +} + +func createLogger(level logrus.Level, format Format, merge bool) *logrus.Logger { logger := logrus.New() if format == FormatJSON { @@ -62,7 +72,7 @@ func DefaultLogger(level logrus.Level, format Format) *logrus.Logger { logger.Level = level - for _, hook := range DefaultHooks() { + for _, hook := range DefaultHooks(merge) { logger.Hooks.Add(hook) } diff --git a/pkg/util/logging/default_logger_test.go b/pkg/util/logging/default_logger_test.go index 7aab50496..10f690757 100644 --- a/pkg/util/logging/default_logger_test.go +++ b/pkg/util/logging/default_logger_test.go @@ -34,7 +34,7 @@ func TestDefaultLogger(t *testing.T) { assert.Equal(t, os.Stdout, logger.Out) for _, level := range logrus.AllLevels { - assert.Equal(t, DefaultHooks(), logger.Hooks[level]) + assert.Equal(t, DefaultHooks(false), logger.Hooks[level]) } } } diff --git a/pkg/util/logging/log_merge_hook.go b/pkg/util/logging/log_merge_hook.go new file mode 100644 index 000000000..b993cb38a --- /dev/null +++ b/pkg/util/logging/log_merge_hook.go @@ -0,0 +1,113 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logging + +import ( + "bytes" + "io" + "os" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +const ( + ListeningLevel = logrus.ErrorLevel + ListeningMessage = "merge-log-57847fd0-0c7c-48e3-b5f7-984b293d8376" + LogSourceKey = "log-source" +) + +// MergeHook is used to redirect a batch of logs to another logger atomically. +// It hooks a log with ListeningMessage message, once the message is hit it replaces +// the logger's output to HookWriter so that HookWriter retrieves the logs from a file indicated +// by LogSourceKey field. +type MergeHook struct { +} + +type hookWriter struct { + orgWriter io.Writer + source string + logger *logrus.Logger +} + +func newHookWriter(orgWriter io.Writer, source string, logger *logrus.Logger) io.Writer { + return &hookWriter{ + orgWriter: orgWriter, + source: source, + logger: logger, + } +} + +func (h *MergeHook) Levels() []logrus.Level { + return []logrus.Level{ListeningLevel} +} + +func (h *MergeHook) Fire(entry *logrus.Entry) error { + if entry.Message != ListeningMessage { + return nil + } + + source, exist := entry.Data[LogSourceKey] + if !exist { + return nil + } + + entry.Logger.SetOutput(newHookWriter(entry.Logger.Out, source.(string), entry.Logger)) + + return nil +} + +func (w *hookWriter) Write(p []byte) (n int, err error) { + if !bytes.Contains(p, []byte(ListeningMessage)) { + return w.orgWriter.Write(p) + } + + defer func() { + w.logger.Out = w.orgWriter + }() + + sourceFile, err := os.OpenFile(w.source, os.O_RDONLY, 0400) + if err != nil { + return 0, err + } + defer sourceFile.Close() + + total := 0 + + buffer := make([]byte, 2048) + for { + read, err := sourceFile.Read(buffer) + if err == io.EOF { + return total, nil + } + + if err != nil { + return total, errors.Wrapf(err, "error to read source file %s at pos %v", w.source, total) + } + + written, err := w.orgWriter.Write(buffer[0:read]) + if err != nil { + return total, errors.Wrapf(err, "error to write log at pos %v", total) + } + + if written != read { + return total, errors.Errorf("error to write log at pos %v, read %v but written %v", total, read, written) + } + + total += read + } +} diff --git a/pkg/util/logging/log_merge_hook_test.go b/pkg/util/logging/log_merge_hook_test.go new file mode 100644 index 000000000..d103152b8 --- /dev/null +++ b/pkg/util/logging/log_merge_hook_test.go @@ -0,0 +1,185 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package logging + +import ( + "fmt" + "os" + "testing" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestMergeHook_Fire(t *testing.T) { + tests := []struct { + name string + entry logrus.Entry + expectHook bool + }{ + { + name: "normal message", + entry: logrus.Entry{ + Level: logrus.ErrorLevel, + Message: "fake-message", + }, + expectHook: false, + }, + { + name: "normal source", + entry: logrus.Entry{ + Level: logrus.ErrorLevel, + Message: ListeningMessage, + Data: logrus.Fields{"fake-key": "fake-value"}, + }, + expectHook: false, + }, + { + name: "hook hit", + entry: logrus.Entry{ + Level: logrus.ErrorLevel, + Message: ListeningMessage, + Data: logrus.Fields{LogSourceKey: "any-value"}, + Logger: &logrus.Logger{}, + }, + expectHook: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + hook := &MergeHook{} + // method under test + err := hook.Fire(&test.entry) + + assert.NoError(t, err) + + if test.expectHook { + assert.NotNil(t, test.entry.Logger.Out.(*hookWriter)) + } + }) + } +} + +type fakeWriter struct { + p []byte + writeError error + writtenLen int +} + +func (fw *fakeWriter) Write(p []byte) (n int, err error) { + if fw.writeError != nil || fw.writtenLen != -1 { + return fw.writtenLen, fw.writeError + } + + fw.p = append(fw.p, p...) + + return len(p), nil +} + +func TestMergeHook_Write(t *testing.T) { + sourceFile, err := os.CreateTemp("", "") + require.NoError(t, err) + + logMessage := "fake-message-1\nfake-message-2" + _, err = sourceFile.WriteString(logMessage) + require.NoError(t, err) + + tests := []struct { + name string + content []byte + source string + writeErr error + writtenLen int + expectError string + needRollBackHook bool + }{ + { + name: "normal message", + content: []byte("fake-message"), + writtenLen: -1, + }, + { + name: "failed to open source file", + content: []byte(ListeningMessage), + source: "non-exist", + needRollBackHook: true, + expectError: "open non-exist: no such file or directory", + }, + { + name: "write error", + content: []byte(ListeningMessage), + source: sourceFile.Name(), + writeErr: errors.New("fake-error"), + expectError: "error to write log at pos 0: fake-error", + needRollBackHook: true, + }, + { + name: "write len mismatch", + content: []byte(ListeningMessage), + source: sourceFile.Name(), + writtenLen: 100, + expectError: fmt.Sprintf("error to write log at pos 0, read %v but written 100", len(logMessage)), + needRollBackHook: true, + }, + { + name: "success", + content: []byte(ListeningMessage), + source: sourceFile.Name(), + writtenLen: -1, + needRollBackHook: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + writer := hookWriter{ + orgWriter: &fakeWriter{ + writeError: test.writeErr, + writtenLen: test.writtenLen, + }, + source: test.source, + logger: &logrus.Logger{}, + } + + n, err := writer.Write(test.content) + + if test.expectError == "" { + assert.NoError(t, err) + + expectStr := string(test.content) + if expectStr == ListeningMessage { + expectStr = logMessage + } + + assert.Len(t, expectStr, n) + + fakeWriter := writer.orgWriter.(*fakeWriter) + writtenStr := string(fakeWriter.p) + assert.Equal(t, writtenStr, expectStr) + } else { + assert.EqualError(t, err, test.expectError) + } + + if test.needRollBackHook { + assert.Equal(t, writer.logger.Out, writer.orgWriter) + } + }) + } +} From 8742f1b1f33e1446a20132be5cb1bf666168581a Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 24 Jul 2024 15:41:26 +0800 Subject: [PATCH 21/69] data mover micro service backup Signed-off-by: Lyndon-Li --- changelogs/unreleased/8046-Lyndon-Li | 1 + pkg/cmd/cli/datamover/backup.go | 210 +++++++++- pkg/cmd/cli/datamover/backup_test.go | 216 ++++++++++ pkg/cmd/cli/datamover/data_mover.go | 7 + pkg/cmd/cli/datamover/mocks/Cache.go | 231 +++++++++++ pkg/datamover/backup_micro_service.go | 324 +++++++++++++++ pkg/datamover/backup_micro_service_test.go | 441 +++++++++++++++++++++ pkg/datapath/micro_service_watcher.go | 24 ++ pkg/util/kube/event.go | 74 ++++ pkg/util/kube/event_test.go | 62 +++ 10 files changed, 1581 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/8046-Lyndon-Li create mode 100644 pkg/cmd/cli/datamover/backup_test.go create mode 100644 pkg/cmd/cli/datamover/mocks/Cache.go create mode 100644 pkg/datamover/backup_micro_service.go create mode 100644 pkg/datamover/backup_micro_service_test.go create mode 100644 pkg/datapath/micro_service_watcher.go create mode 100644 pkg/util/kube/event.go create mode 100644 pkg/util/kube/event_test.go diff --git a/changelogs/unreleased/8046-Lyndon-Li b/changelogs/unreleased/8046-Lyndon-Li new file mode 100644 index 000000000..a3592d7e7 --- /dev/null +++ b/changelogs/unreleased/8046-Lyndon-Li @@ -0,0 +1 @@ +Data mover micro service backup according to design #7576 \ No newline at end of file diff --git a/pkg/cmd/cli/datamover/backup.go b/pkg/cmd/cli/datamover/backup.go index aabcd0acc..35f483d92 100644 --- a/pkg/cmd/cli/datamover/backup.go +++ b/pkg/cmd/cli/datamover/backup.go @@ -14,16 +14,39 @@ limitations under the License. package datamover import ( + "context" "fmt" + "os" "strings" "time" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/cmd/util/signals" + "github.com/vmware-tanzu/velero/pkg/datamover" + "github.com/vmware-tanzu/velero/pkg/datapath" + "github.com/vmware-tanzu/velero/pkg/repository" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" + + ctrl "sigs.k8s.io/controller-runtime" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + + ctlcache "sigs.k8s.io/controller-runtime/pkg/cache" + ctlclient "sigs.k8s.io/controller-runtime/pkg/client" ) type dataMoverBackupConfig struct { @@ -52,7 +75,10 @@ func NewBackupCommand(f client.Factory) *cobra.Command { logger.Infof("Starting Velero data-mover backup %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) - s := newdataMoverBackup(logger, config) + s, err := newdataMoverBackup(logger, f, config) + if err != nil { + exitWithMessage(logger, false, "Failed to create data mover backup, %v", err) + } s.run() }, @@ -73,20 +99,186 @@ func NewBackupCommand(f client.Factory) *cobra.Command { return command } +const ( + // defaultCredentialsDirectory is the path on disk where credential + // files will be written to + defaultCredentialsDirectory = "/tmp/credentials" +) + type dataMoverBackup struct { - logger logrus.FieldLogger - config dataMoverBackupConfig + logger logrus.FieldLogger + ctx context.Context + cancelFunc context.CancelFunc + client ctlclient.Client + cache ctlcache.Cache + namespace string + nodeName string + config dataMoverBackupConfig + kubeClient kubernetes.Interface + dataPathMgr *datapath.Manager } -func newdataMoverBackup(logger logrus.FieldLogger, config dataMoverBackupConfig) *dataMoverBackup { - s := &dataMoverBackup{ - logger: logger, - config: config, +func newdataMoverBackup(logger logrus.FieldLogger, factory client.Factory, config dataMoverBackupConfig) (*dataMoverBackup, error) { + ctx, cancelFunc := context.WithCancel(context.Background()) + + clientConfig, err := factory.ClientConfig() + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create client config") } - return s + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + + scheme := runtime.NewScheme() + if err := velerov1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to add velero v1 scheme") + } + + if err := velerov2alpha1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to add velero v2alpha1 scheme") + } + + if err := v1.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to add core v1 scheme") + } + + nodeName := os.Getenv("NODE_NAME") + + // use a field selector to filter to only pods scheduled on this node. + cacheOption := ctlcache.Options{ + Scheme: scheme, + ByObject: map[ctlclient.Object]ctlcache.ByObject{ + &v1.Pod{}: { + Field: fields.Set{"spec.nodeName": nodeName}.AsSelector(), + }, + &velerov2alpha1api.DataUpload{}: { + Field: fields.Set{"metadata.namespace": factory.Namespace()}.AsSelector(), + }, + }, + } + + cli, err := ctlclient.New(clientConfig, ctlclient.Options{ + Scheme: scheme, + }) + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create client") + } + + cache, err := ctlcache.New(clientConfig, cacheOption) + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create client cache") + } + + s := &dataMoverBackup{ + logger: logger, + ctx: ctx, + cancelFunc: cancelFunc, + client: cli, + cache: cache, + config: config, + namespace: factory.Namespace(), + nodeName: nodeName, + } + + s.kubeClient, err = factory.KubeClient() + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create kube client") + } + + s.dataPathMgr = datapath.NewManager(1) + + return s, nil } +var funcExitWithMessage = exitWithMessage +var funcCreateDataPathService = (*dataMoverBackup).createDataPathService + func (s *dataMoverBackup) run() { - time.Sleep(time.Duration(1<<63 - 1)) + signals.CancelOnShutdown(s.cancelFunc, s.logger) + go func() { + if err := s.cache.Start(s.ctx); err != nil { + s.logger.WithError(err).Warn("error starting cache") + } + }() + + s.runDataPath() +} + +func (s *dataMoverBackup) runDataPath() { + s.logger.Infof("Starting micro service in node %s for du %s", s.nodeName, s.config.duName) + + dpService, err := funcCreateDataPathService(s) + if err != nil { + s.cancelFunc() + funcExitWithMessage(s.logger, false, "Failed to create data path service for DataUpload %s: %v", s.config.duName, err) + return + } + + s.logger.Infof("Starting data path service %s", s.config.duName) + + err = dpService.Init() + if err != nil { + s.cancelFunc() + funcExitWithMessage(s.logger, false, "Failed to init data path service for DataUpload %s: %v", s.config.duName, err) + return + } + + s.logger.Infof("Running data path service %s", s.config.duName) + + result, err := dpService.RunCancelableDataPath(s.ctx) + if err != nil { + s.cancelFunc() + funcExitWithMessage(s.logger, false, "Failed to run data path service for DataUpload %s: %v", s.config.duName, err) + return + } + + s.logger.WithField("du", s.config.duName).Info("Data path service completed") + + dpService.Shutdown() + + s.logger.WithField("du", s.config.duName).Info("Data path service is shut down") + + s.cancelFunc() + + funcExitWithMessage(s.logger, true, result) +} + +var funcNewCredentialFileStore = credentials.NewNamespacedFileStore +var funcNewCredentialSecretStore = credentials.NewNamespacedSecretStore + +func (s *dataMoverBackup) createDataPathService() (dataPathService, error) { + credentialFileStore, err := funcNewCredentialFileStore( + s.client, + s.namespace, + defaultCredentialsDirectory, + filesystem.NewFileSystem(), + ) + if err != nil { + return nil, errors.Wrapf(err, "error to create credential file store") + } + + credSecretStore, err := funcNewCredentialSecretStore(s.client, s.namespace) + if err != nil { + return nil, errors.Wrapf(err, "error to create credential secret store") + } + + credGetter := &credentials.CredentialGetter{FromFile: credentialFileStore, FromSecret: credSecretStore} + + duInformer, err := s.cache.GetInformer(s.ctx, &velerov2alpha1api.DataUpload{}) + if err != nil { + return nil, errors.Wrap(err, "error to get controller-runtime informer from manager") + } + + repoEnsurer := repository.NewEnsurer(s.client, s.logger, s.config.resourceTimeout) + + return datamover.NewBackupMicroService(s.ctx, s.client, s.kubeClient, s.config.duName, s.namespace, s.nodeName, datapath.AccessPoint{ + ByPath: s.config.volumePath, + VolMode: uploader.PersistentVolumeMode(s.config.volumeMode), + }, s.dataPathMgr, repoEnsurer, credGetter, duInformer, s.logger), nil } diff --git a/pkg/cmd/cli/datamover/backup_test.go b/pkg/cmd/cli/datamover/backup_test.go new file mode 100644 index 000000000..2dd1e681d --- /dev/null +++ b/pkg/cmd/cli/datamover/backup_test.go @@ -0,0 +1,216 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "context" + "errors" + "fmt" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + ctlclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/internal/credentials" + cacheMock "github.com/vmware-tanzu/velero/pkg/cmd/cli/datamover/mocks" + velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" +) + +func fakeCreateDataPathServiceWithErr(_ *dataMoverBackup) (dataPathService, error) { + return nil, errors.New("fake-create-data-path-error") +} + +var frHelper *fakeRunHelper + +func fakeCreateDataPathService(_ *dataMoverBackup) (dataPathService, error) { + return frHelper, nil +} + +type fakeRunHelper struct { + initErr error + runCancelableDataPathErr error + runCancelableDataPathResult string + exitMessage string + succeed bool +} + +func (fr *fakeRunHelper) Init() error { + return fr.initErr +} + +func (fr *fakeRunHelper) RunCancelableDataPath(_ context.Context) (string, error) { + if fr.runCancelableDataPathErr != nil { + return "", fr.runCancelableDataPathErr + } else { + return fr.runCancelableDataPathResult, nil + } +} + +func (fr *fakeRunHelper) Shutdown() { + +} + +func (fr *fakeRunHelper) ExitWithMessage(logger logrus.FieldLogger, succeed bool, message string, a ...any) { + fr.succeed = succeed + fr.exitMessage = fmt.Sprintf(message, a...) +} + +func TestRunDataPath(t *testing.T) { + tests := []struct { + name string + duName string + createDataPathFail bool + initDataPathErr error + runCancelableDataPathErr error + runCancelableDataPathResult string + expectedMessage string + expectedSucceed bool + }{ + { + name: "create data path failed", + duName: "fake-name", + createDataPathFail: true, + expectedMessage: "Failed to create data path service for DataUpload fake-name: fake-create-data-path-error", + }, + { + name: "init data path failed", + duName: "fake-name", + initDataPathErr: errors.New("fake-init-data-path-error"), + expectedMessage: "Failed to init data path service for DataUpload fake-name: fake-init-data-path-error", + }, + { + name: "run data path failed", + duName: "fake-name", + runCancelableDataPathErr: errors.New("fake-run-data-path-error"), + expectedMessage: "Failed to run data path service for DataUpload fake-name: fake-run-data-path-error", + }, + { + name: "succeed", + duName: "fake-name", + runCancelableDataPathResult: "fake-run-data-path-result", + expectedMessage: "fake-run-data-path-result", + expectedSucceed: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + frHelper = &fakeRunHelper{ + initErr: test.initDataPathErr, + runCancelableDataPathErr: test.runCancelableDataPathErr, + runCancelableDataPathResult: test.runCancelableDataPathResult, + } + + if test.createDataPathFail { + funcCreateDataPathService = fakeCreateDataPathServiceWithErr + } else { + funcCreateDataPathService = fakeCreateDataPathService + } + + funcExitWithMessage = frHelper.ExitWithMessage + + s := &dataMoverBackup{ + logger: velerotest.NewLogger(), + cancelFunc: func() {}, + config: dataMoverBackupConfig{ + duName: test.duName, + }, + } + + s.runDataPath() + + assert.Equal(t, test.expectedMessage, frHelper.exitMessage) + assert.Equal(t, test.expectedSucceed, frHelper.succeed) + }) + } +} + +type fakeCreateDataPathServiceHelper struct { + fileStoreErr error + secretStoreErr error +} + +func (fc *fakeCreateDataPathServiceHelper) NewNamespacedFileStore(_ ctlclient.Client, _ string, _ string, _ filesystem.Interface) (credentials.FileStore, error) { + return nil, fc.fileStoreErr +} + +func (fc *fakeCreateDataPathServiceHelper) NewNamespacedSecretStore(_ ctlclient.Client, _ string) (credentials.SecretStore, error) { + return nil, fc.secretStoreErr +} + +func TestCreateDataPathService(t *testing.T) { + tests := []struct { + name string + fileStoreErr error + secretStoreErr error + mockGetInformer bool + getInformerErr error + expectedError string + }{ + { + name: "create credential file store error", + fileStoreErr: errors.New("fake-file-store-error"), + expectedError: "error to create credential file store: fake-file-store-error", + }, + { + name: "create credential secret store", + secretStoreErr: errors.New("fake-secret-store-error"), + expectedError: "error to create credential secret store: fake-secret-store-error", + }, + { + name: "get informer error", + mockGetInformer: true, + getInformerErr: errors.New("fake-get-informer-error"), + expectedError: "error to get controller-runtime informer from manager: fake-get-informer-error", + }, + { + name: "succeed", + mockGetInformer: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fcHelper := &fakeCreateDataPathServiceHelper{ + fileStoreErr: test.fileStoreErr, + secretStoreErr: test.secretStoreErr, + } + + funcNewCredentialFileStore = fcHelper.NewNamespacedFileStore + funcNewCredentialSecretStore = fcHelper.NewNamespacedSecretStore + + cache := cacheMock.NewCache(t) + if test.mockGetInformer { + cache.On("GetInformer", mock.Anything, mock.Anything).Return(nil, test.getInformerErr) + } + + funcExitWithMessage = frHelper.ExitWithMessage + + s := &dataMoverBackup{ + cache: cache, + } + + _, err := s.createDataPathService() + + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/pkg/cmd/cli/datamover/data_mover.go b/pkg/cmd/cli/datamover/data_mover.go index c4400a344..6786f4e7c 100644 --- a/pkg/cmd/cli/datamover/data_mover.go +++ b/pkg/cmd/cli/datamover/data_mover.go @@ -14,6 +14,7 @@ limitations under the License. package datamover import ( + "context" "fmt" "os" @@ -39,6 +40,12 @@ func NewCommand(f client.Factory) *cobra.Command { return command } +type dataPathService interface { + Init() error + RunCancelableDataPath(context.Context) (string, error) + Shutdown() +} + var funcExit = os.Exit var funcCreateFile = os.Create diff --git a/pkg/cmd/cli/datamover/mocks/Cache.go b/pkg/cmd/cli/datamover/mocks/Cache.go new file mode 100644 index 000000000..ac20ae0bd --- /dev/null +++ b/pkg/cmd/cli/datamover/mocks/Cache.go @@ -0,0 +1,231 @@ +// Code generated by mockery v2.39.1. DO NOT EDIT. + +package mocks + +import ( + cache "sigs.k8s.io/controller-runtime/pkg/cache" + client "sigs.k8s.io/controller-runtime/pkg/client" + + context "context" + + mock "github.com/stretchr/testify/mock" + + schema "k8s.io/apimachinery/pkg/runtime/schema" + + types "k8s.io/apimachinery/pkg/types" +) + +// Cache is an autogenerated mock type for the Cache type +type Cache struct { + mock.Mock +} + +// Get provides a mock function with given fields: ctx, key, obj, opts +func (_m *Cache) Get(ctx context.Context, key types.NamespacedName, obj client.Object, opts ...client.GetOption) error { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, key, obj) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, types.NamespacedName, client.Object, ...client.GetOption) error); ok { + r0 = rf(ctx, key, obj, opts...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetInformer provides a mock function with given fields: ctx, obj, opts +func (_m *Cache) GetInformer(ctx context.Context, obj client.Object, opts ...cache.InformerGetOption) (cache.Informer, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, obj) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetInformer") + } + + var r0 cache.Informer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, client.Object, ...cache.InformerGetOption) (cache.Informer, error)); ok { + return rf(ctx, obj, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, client.Object, ...cache.InformerGetOption) cache.Informer); ok { + r0 = rf(ctx, obj, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.Informer) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, client.Object, ...cache.InformerGetOption) error); ok { + r1 = rf(ctx, obj, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetInformerForKind provides a mock function with given fields: ctx, gvk, opts +func (_m *Cache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind, opts ...cache.InformerGetOption) (cache.Informer, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, gvk) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for GetInformerForKind") + } + + var r0 cache.Informer + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, schema.GroupVersionKind, ...cache.InformerGetOption) (cache.Informer, error)); ok { + return rf(ctx, gvk, opts...) + } + if rf, ok := ret.Get(0).(func(context.Context, schema.GroupVersionKind, ...cache.InformerGetOption) cache.Informer); ok { + r0 = rf(ctx, gvk, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(cache.Informer) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, schema.GroupVersionKind, ...cache.InformerGetOption) error); ok { + r1 = rf(ctx, gvk, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// IndexField provides a mock function with given fields: ctx, obj, field, extractValue +func (_m *Cache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error { + ret := _m.Called(ctx, obj, field, extractValue) + + if len(ret) == 0 { + panic("no return value specified for IndexField") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, client.Object, string, client.IndexerFunc) error); ok { + r0 = rf(ctx, obj, field, extractValue) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// List provides a mock function with given fields: ctx, list, opts +func (_m *Cache) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, list) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, client.ObjectList, ...client.ListOption) error); ok { + r0 = rf(ctx, list, opts...) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// RemoveInformer provides a mock function with given fields: ctx, obj +func (_m *Cache) RemoveInformer(ctx context.Context, obj client.Object) error { + ret := _m.Called(ctx, obj) + + if len(ret) == 0 { + panic("no return value specified for RemoveInformer") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, client.Object) error); ok { + r0 = rf(ctx, obj) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Start provides a mock function with given fields: ctx +func (_m *Cache) Start(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for Start") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// WaitForCacheSync provides a mock function with given fields: ctx +func (_m *Cache) WaitForCacheSync(ctx context.Context) bool { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for WaitForCacheSync") + } + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// NewCache creates a new instance of Cache. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCache(t interface { + mock.TestingT + Cleanup(func()) +}) *Cache { + mock := &Cache{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/datamover/backup_micro_service.go b/pkg/datamover/backup_micro_service.go new file mode 100644 index 000000000..2f4fa6e56 --- /dev/null +++ b/pkg/datamover/backup_micro_service.go @@ -0,0 +1,324 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "context" + "encoding/json" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + + cachetool "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/cache" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "github.com/vmware-tanzu/velero/pkg/datapath" + "github.com/vmware-tanzu/velero/pkg/repository" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/kube" + + apierrors "k8s.io/apimachinery/pkg/api/errors" +) + +const ( + dataUploadDownloadRequestor = "snapshot-data-upload-download" +) + +// BackupMicroService process data mover backups inside the backup pod +type BackupMicroService struct { + ctx context.Context + client client.Client + kubeClient kubernetes.Interface + repoEnsurer *repository.Ensurer + credentialGetter *credentials.CredentialGetter + logger logrus.FieldLogger + dataPathMgr *datapath.Manager + eventRecorder kube.EventRecorder + + namespace string + dataUploadName string + dataUpload *velerov2alpha1api.DataUpload + sourceTargetPath datapath.AccessPoint + + resultSignal chan dataPathResult + + duInformer cache.Informer + duHandler cachetool.ResourceEventHandlerRegistration + nodeName string +} + +type dataPathResult struct { + err error + result string +} + +func NewBackupMicroService(ctx context.Context, client client.Client, kubeClient kubernetes.Interface, dataUploadName string, namespace string, nodeName string, + sourceTargetPath datapath.AccessPoint, dataPathMgr *datapath.Manager, repoEnsurer *repository.Ensurer, cred *credentials.CredentialGetter, + duInformer cache.Informer, log logrus.FieldLogger) *BackupMicroService { + return &BackupMicroService{ + ctx: ctx, + client: client, + kubeClient: kubeClient, + credentialGetter: cred, + logger: log, + repoEnsurer: repoEnsurer, + dataPathMgr: dataPathMgr, + namespace: namespace, + dataUploadName: dataUploadName, + sourceTargetPath: sourceTargetPath, + nodeName: nodeName, + resultSignal: make(chan dataPathResult), + duInformer: duInformer, + } +} + +func (r *BackupMicroService) Init() error { + r.eventRecorder = kube.NewEventRecorder(r.kubeClient, r.client.Scheme(), r.dataUploadName, r.nodeName) + + handler, err := r.duInformer.AddEventHandler( + cachetool.ResourceEventHandlerFuncs{ + UpdateFunc: func(oldObj interface{}, newObj interface{}) { + oldDu := oldObj.(*velerov2alpha1api.DataUpload) + newDu := newObj.(*velerov2alpha1api.DataUpload) + + if newDu.Name != r.dataUpload.Name { + return + } + + if newDu.Status.Phase != velerov2alpha1api.DataUploadPhaseInProgress { + return + } + + if newDu.Spec.Cancel && !oldDu.Spec.Cancel { + r.cancelDataUpload(newDu) + } + }, + }, + ) + + if err != nil { + return errors.Wrap(err, "error adding du handler") + } + + r.duHandler = handler + + return err +} + +var waitDuTimeout time.Duration = time.Minute * 2 + +func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string, error) { + log := r.logger.WithFields(logrus.Fields{ + "dataupload": r.dataUploadName, + }) + + du := &velerov2alpha1api.DataUpload{} + err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, waitDuTimeout, true, func(ctx context.Context) (bool, error) { + err := r.client.Get(ctx, types.NamespacedName{ + Namespace: r.namespace, + Name: r.dataUploadName, + }, du) + + if apierrors.IsNotFound(err) { + return false, nil + } + + if err != nil { + return true, errors.Wrapf(err, "error to get du %s", r.dataUploadName) + } + + if du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress { + return true, nil + } else { + return false, nil + } + }) + + if err != nil { + log.WithError(err).Error("Failed to wait du") + return "", errors.Wrap(err, "error waiting for du") + } + + r.dataUpload = du + + log.Info("Run cancelable dataUpload") + + callbacks := datapath.Callbacks{ + OnCompleted: r.OnDataUploadCompleted, + OnFailed: r.OnDataUploadFailed, + OnCancelled: r.OnDataUploadCancelled, + OnProgress: r.OnDataUploadProgress, + } + + fsBackup, err := r.dataPathMgr.CreateFileSystemBR(du.Name, dataUploadDownloadRequestor, ctx, r.client, du.Namespace, callbacks, log) + if err != nil { + return "", errors.Wrap(err, "error to create data path") + } + + log.Debug("Async fs br created") + + if err := fsBackup.Init(ctx, &datapath.FSBRInitParam{ + BSLName: du.Spec.BackupStorageLocation, + SourceNamespace: du.Spec.SourceNamespace, + UploaderType: GetUploaderType(du.Spec.DataMover), + RepositoryType: velerov1api.BackupRepositoryTypeKopia, + RepoIdentifier: "", + RepositoryEnsurer: r.repoEnsurer, + CredentialGetter: r.credentialGetter, + }); err != nil { + return "", errors.Wrap(err, "error to initialize data path") + } + + log.Info("Async fs br init") + + tags := map[string]string{ + velerov1api.AsyncOperationIDLabel: du.Labels[velerov1api.AsyncOperationIDLabel], + } + + if err := fsBackup.StartBackup(r.sourceTargetPath, du.Spec.DataMoverConfig, &datapath.FSBRStartParam{ + RealSource: GetRealSource(du.Spec.SourceNamespace, du.Spec.SourcePVC), + ParentSnapshot: "", + ForceFull: false, + Tags: tags, + }); err != nil { + return "", errors.Wrap(err, "error starting data path backup") + } + + log.Info("Async fs backup data path started") + r.eventRecorder.Event(du, false, datapath.EventReasonStarted, "Data path for %s started", du.Name) + + result := "" + select { + case <-ctx.Done(): + err = errors.New("timed out waiting for fs backup to complete") + break + case res := <-r.resultSignal: + err = res.err + result = res.result + break + } + + if err != nil { + log.WithError(err).Error("Async fs backup was not completed") + } + + return result, err +} + +func (r *BackupMicroService) Shutdown() { + r.eventRecorder.Shutdown() + r.closeDataPath(r.ctx, r.dataUploadName) + + if r.duHandler != nil { + if err := r.duInformer.RemoveEventHandler(r.duHandler); err != nil { + r.logger.WithError(err).Warn("Failed to remove pod handler") + } + } +} + +var funcMarshal = json.Marshal + +func (r *BackupMicroService) OnDataUploadCompleted(ctx context.Context, namespace string, duName string, result datapath.Result) { + defer r.closeDataPath(ctx, duName) + + log := r.logger.WithField("dataupload", duName) + + backupBytes, err := funcMarshal(result.Backup) + if err != nil { + log.WithError(err).Errorf("Failed to marshal backup result %v", result.Backup) + r.resultSignal <- dataPathResult{ + err: errors.Wrapf(err, "Failed to marshal backup result %v", result.Backup), + } + } else { + r.eventRecorder.Event(r.dataUpload, false, datapath.EventReasonCompleted, string(backupBytes)) + r.resultSignal <- dataPathResult{ + result: string(backupBytes), + } + } + + log.Info("Async fs backup completed") +} + +func (r *BackupMicroService) OnDataUploadFailed(ctx context.Context, namespace string, duName string, err error) { + defer r.closeDataPath(ctx, duName) + + log := r.logger.WithField("dataupload", duName) + log.WithError(err).Error("Async fs backup data path failed") + + r.eventRecorder.Event(r.dataUpload, false, datapath.EventReasonFailed, "Data path for data upload %s failed, error %v", r.dataUploadName, err) + r.resultSignal <- dataPathResult{ + err: errors.Wrapf(err, "Data path for data upload %s failed", r.dataUploadName), + } +} + +func (r *BackupMicroService) OnDataUploadCancelled(ctx context.Context, namespace string, duName string) { + defer r.closeDataPath(ctx, duName) + + log := r.logger.WithField("dataupload", duName) + log.Warn("Async fs backup data path canceled") + + r.eventRecorder.Event(r.dataUpload, false, datapath.EventReasonCancelled, "Data path for data upload %s canceled", duName) + r.resultSignal <- dataPathResult{ + err: errors.New(datapath.ErrCancelled), + } +} + +func (r *BackupMicroService) OnDataUploadProgress(ctx context.Context, namespace string, duName string, progress *uploader.Progress) { + log := r.logger.WithFields(logrus.Fields{ + "dataupload": duName, + }) + + progressBytes, err := funcMarshal(progress) + if err != nil { + log.WithError(err).Errorf("Failed to marshal progress %v", progress) + return + } + + log.Infof("Sending event for progress %v (%s)", progress, string(progressBytes)) + + r.eventRecorder.Event(r.dataUpload, false, datapath.EventReasonProgress, string(progressBytes)) +} + +func (r *BackupMicroService) closeDataPath(ctx context.Context, duName string) { + fsBackup := r.dataPathMgr.GetAsyncBR(duName) + if fsBackup != nil { + fsBackup.Close(ctx) + } + + r.dataPathMgr.RemoveAsyncBR(duName) +} + +func (r *BackupMicroService) cancelDataUpload(du *velerov2alpha1api.DataUpload) { + r.logger.WithField("DataUpload", du.Name).Info("Data upload is being canceled") + + r.eventRecorder.Event(du, false, "Canceling", "Canceing for data upload %s", du.Name) + + fsBackup := r.dataPathMgr.GetAsyncBR(du.Name) + if fsBackup == nil { + r.OnDataUploadCancelled(r.ctx, du.GetNamespace(), du.GetName()) + } else { + fsBackup.Cancel() + } +} diff --git a/pkg/datamover/backup_micro_service_test.go b/pkg/datamover/backup_micro_service_test.go new file mode 100644 index 000000000..8fd595e93 --- /dev/null +++ b/pkg/datamover/backup_micro_service_test.go @@ -0,0 +1,441 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "context" + "fmt" + "sync" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/vmware-tanzu/velero/pkg/builder" + "github.com/vmware-tanzu/velero/pkg/datapath" + "github.com/vmware-tanzu/velero/pkg/uploader" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + + clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake" + + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + velerotest "github.com/vmware-tanzu/velero/pkg/test" + + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + datapathmockes "github.com/vmware-tanzu/velero/pkg/datapath/mocks" +) + +type backupMsTestHelper struct { + eventReason string + eventMsg string + marshalErr error + marshalBytes []byte + withEvent bool + eventLock sync.Mutex +} + +func (bt *backupMsTestHelper) Event(_ runtime.Object, _ bool, reason string, message string, a ...any) { + bt.eventLock.Lock() + defer bt.eventLock.Unlock() + + bt.withEvent = true + bt.eventReason = reason + bt.eventMsg = fmt.Sprintf(message, a...) +} +func (bt *backupMsTestHelper) Shutdown() {} + +func (bt *backupMsTestHelper) Marshal(v any) ([]byte, error) { + if bt.marshalErr != nil { + return nil, bt.marshalErr + } + + return bt.marshalBytes, nil +} + +func (bt *backupMsTestHelper) EventReason() string { + bt.eventLock.Lock() + defer bt.eventLock.Unlock() + + return bt.eventReason +} + +func (bt *backupMsTestHelper) EventMessage() string { + bt.eventLock.Lock() + defer bt.eventLock.Unlock() + + return bt.eventMsg +} + +func TestOnDataUploadFailed(t *testing.T) { + dataUploadName := "fake-data-upload" + bt := &backupMsTestHelper{} + + bs := &BackupMicroService{ + dataUploadName: dataUploadName, + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + expectedErr := "Data path for data upload fake-data-upload failed: fake-error" + expectedEventReason := datapath.EventReasonFailed + expectedEventMsg := "Data path for data upload fake-data-upload failed, error fake-error" + + go bs.OnDataUploadFailed(context.TODO(), velerov1api.DefaultNamespace, dataUploadName, errors.New("fake-error")) + + result := <-bs.resultSignal + assert.EqualError(t, result.err, expectedErr) + assert.Equal(t, expectedEventReason, bt.EventReason()) + assert.Equal(t, expectedEventMsg, bt.EventMessage()) +} + +func TestOnDataUploadCancelled(t *testing.T) { + dataUploadName := "fake-data-upload" + bt := &backupMsTestHelper{} + + bs := &BackupMicroService{ + dataUploadName: dataUploadName, + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + expectedErr := datapath.ErrCancelled + expectedEventReason := datapath.EventReasonCancelled + expectedEventMsg := "Data path for data upload fake-data-upload canceled" + + go bs.OnDataUploadCancelled(context.TODO(), velerov1api.DefaultNamespace, dataUploadName) + + result := <-bs.resultSignal + assert.EqualError(t, result.err, expectedErr) + assert.Equal(t, expectedEventReason, bt.EventReason()) + assert.Equal(t, expectedEventMsg, bt.EventMessage()) +} + +func TestOnDataUploadCompleted(t *testing.T) { + tests := []struct { + name string + expectedErr string + expectedEventReason string + expectedEventMsg string + marshalErr error + marshallStr string + }{ + { + name: "marshal fail", + marshalErr: errors.New("fake-marshal-error"), + expectedErr: "Failed to marshal backup result { false { }}: fake-marshal-error", + }, + { + name: "succeed", + marshallStr: "fake-complete-string", + expectedEventReason: datapath.EventReasonCompleted, + expectedEventMsg: "fake-complete-string", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + dataUploadName := "fake-data-upload" + + bt := &backupMsTestHelper{ + marshalErr: test.marshalErr, + marshalBytes: []byte(test.marshallStr), + } + + bs := &BackupMicroService{ + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + funcMarshal = bt.Marshal + + go bs.OnDataUploadCompleted(context.TODO(), velerov1api.DefaultNamespace, dataUploadName, datapath.Result{}) + + result := <-bs.resultSignal + if test.marshalErr != nil { + assert.EqualError(t, result.err, test.expectedErr) + } else { + assert.NoError(t, result.err) + assert.Equal(t, test.expectedEventReason, bt.EventReason()) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + } + }) + } +} + +func TestOnDataUploadProgress(t *testing.T) { + tests := []struct { + name string + expectedErr string + expectedEventReason string + expectedEventMsg string + marshalErr error + marshallStr string + }{ + { + name: "marshal fail", + marshalErr: errors.New("fake-marshal-error"), + expectedErr: "Failed to marshal backup result", + }, + { + name: "succeed", + marshallStr: "fake-progress-string", + expectedEventReason: datapath.EventReasonProgress, + expectedEventMsg: "fake-progress-string", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + dataUploadName := "fake-data-upload" + + bt := &backupMsTestHelper{ + marshalErr: test.marshalErr, + marshalBytes: []byte(test.marshallStr), + } + + bs := &BackupMicroService{ + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + logger: velerotest.NewLogger(), + } + + funcMarshal = bt.Marshal + + bs.OnDataUploadProgress(context.TODO(), velerov1api.DefaultNamespace, dataUploadName, &uploader.Progress{}) + + if test.marshalErr != nil { + assert.False(t, bt.withEvent) + } else { + assert.True(t, bt.withEvent) + assert.Equal(t, test.expectedEventReason, bt.EventReason()) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + } + }) + } +} + +func TestCancelDataUpload(t *testing.T) { + tests := []struct { + name string + expectedEventReason string + expectedEventMsg string + expectedErr string + }{ + { + name: "no fs backup", + expectedEventReason: datapath.EventReasonCancelled, + expectedEventMsg: "Data path for data upload fake-data-upload canceled", + expectedErr: datapath.ErrCancelled, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + dataUploadName := "fake-data-upload" + du := builder.ForDataUpload(velerov1api.DefaultNamespace, dataUploadName).Result() + + bt := &backupMsTestHelper{} + + bs := &BackupMicroService{ + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + go bs.cancelDataUpload(du) + + result := <-bs.resultSignal + + assert.EqualError(t, result.err, test.expectedErr) + assert.True(t, bt.withEvent) + assert.Equal(t, test.expectedEventReason, bt.EventReason()) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + }) + } +} + +func TestRunCancelableDataPath(t *testing.T) { + dataUploadName := "fake-data-upload" + du := builder.ForDataUpload(velerov1api.DefaultNamespace, dataUploadName).Phase(velerov2alpha1api.DataUploadPhaseNew).Result() + duInProgress := builder.ForDataUpload(velerov1api.DefaultNamespace, dataUploadName).Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result() + ctxTimeout, cancel := context.WithTimeout(context.Background(), time.Second) + + tests := []struct { + name string + ctx context.Context + result *dataPathResult + dataPathMgr *datapath.Manager + kubeClientObj []runtime.Object + initErr error + startErr error + dataPathStarted bool + expectedEventMsg string + expectedErr string + }{ + { + name: "no du", + ctx: context.Background(), + expectedErr: "error waiting for du: context deadline exceeded", + }, + { + name: "du not in in-progress", + ctx: context.Background(), + kubeClientObj: []runtime.Object{du}, + expectedErr: "error waiting for du: context deadline exceeded", + }, + { + name: "create data path fail", + ctx: context.Background(), + kubeClientObj: []runtime.Object{duInProgress}, + dataPathMgr: datapath.NewManager(0), + expectedErr: "error to create data path: Concurrent number exceeds", + }, + { + name: "init data path fail", + ctx: context.Background(), + kubeClientObj: []runtime.Object{duInProgress}, + initErr: errors.New("fake-init-error"), + expectedErr: "error to initialize data path: fake-init-error", + }, + { + name: "start data path fail", + ctx: context.Background(), + kubeClientObj: []runtime.Object{duInProgress}, + startErr: errors.New("fake-start-error"), + expectedErr: "error starting data path backup: fake-start-error", + }, + { + name: "data path timeout", + ctx: ctxTimeout, + kubeClientObj: []runtime.Object{duInProgress}, + dataPathStarted: true, + expectedEventMsg: fmt.Sprintf("Data path for %s started", dataUploadName), + expectedErr: "timed out waiting for fs backup to complete", + }, + { + name: "data path returns error", + ctx: context.Background(), + kubeClientObj: []runtime.Object{duInProgress}, + dataPathStarted: true, + result: &dataPathResult{ + err: errors.New("fake-data-path-error"), + }, + expectedEventMsg: fmt.Sprintf("Data path for %s started", dataUploadName), + expectedErr: "fake-data-path-error", + }, + { + name: "succeed", + ctx: context.Background(), + kubeClientObj: []runtime.Object{duInProgress}, + dataPathStarted: true, + result: &dataPathResult{ + result: "fake-succeed-result", + }, + expectedEventMsg: fmt.Sprintf("Data path for %s started", dataUploadName), + }, + } + + scheme := runtime.NewScheme() + velerov2alpha1api.AddToScheme(scheme) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fakeClientBuilder := clientFake.NewClientBuilder() + fakeClientBuilder = fakeClientBuilder.WithScheme(scheme) + + fakeClient := fakeClientBuilder.WithRuntimeObjects(test.kubeClientObj...).Build() + + bt := &backupMsTestHelper{} + + bs := &BackupMicroService{ + namespace: velerov1api.DefaultNamespace, + dataUploadName: dataUploadName, + ctx: context.Background(), + client: fakeClient, + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + if test.ctx != nil { + bs.ctx = test.ctx + } + + if test.dataPathMgr != nil { + bs.dataPathMgr = test.dataPathMgr + } + + datapath.FSBRCreator = func(string, string, kbclient.Client, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { + fsBR := datapathmockes.NewAsyncBR(t) + if test.initErr != nil { + fsBR.On("Init", mock.Anything, mock.Anything).Return(test.initErr) + } + + if test.startErr != nil { + fsBR.On("Init", mock.Anything, mock.Anything).Return(nil) + fsBR.On("StartBackup", mock.Anything, mock.Anything, mock.Anything).Return(test.startErr) + } + + if test.dataPathStarted { + fsBR.On("Init", mock.Anything, mock.Anything).Return(nil) + fsBR.On("StartBackup", mock.Anything, mock.Anything, mock.Anything).Return(nil) + } + + return fsBR + } + + waitDuTimeout = time.Second + + if test.result != nil { + go func() { + time.Sleep(time.Millisecond * 500) + bs.resultSignal <- *test.result + }() + } + + result, err := bs.RunCancelableDataPath(test.ctx) + + if test.expectedErr != "" { + assert.EqualError(t, err, test.expectedErr) + } else { + assert.NoError(t, err) + assert.Equal(t, test.result.result, result) + } + + if test.expectedEventMsg != "" { + assert.True(t, bt.withEvent) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + } + }) + } + + cancel() +} diff --git a/pkg/datapath/micro_service_watcher.go b/pkg/datapath/micro_service_watcher.go new file mode 100644 index 000000000..ed941880c --- /dev/null +++ b/pkg/datapath/micro_service_watcher.go @@ -0,0 +1,24 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datapath + +const ( + ErrCancelled = "data path is canceled" + + EventReasonStarted = "Data-Path-Started" + EventReasonCompleted = "Data-Path-Completed" + EventReasonFailed = "Data-Path-Failed" + EventReasonCancelled = "Data-Path-Canceled" + EventReasonProgress = "Data-Path-Progress" +) diff --git a/pkg/util/kube/event.go b/pkg/util/kube/event.go new file mode 100644 index 000000000..d5a4bb8d2 --- /dev/null +++ b/pkg/util/kube/event.go @@ -0,0 +1,74 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package kube + +import ( + "time" + + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/record" +) + +type EventRecorder interface { + Event(object runtime.Object, warning bool, reason string, message string, a ...any) + Shutdown() +} + +type eventRecorder struct { + broadcaster record.EventBroadcaster + recorder record.EventRecorder +} + +func NewEventRecorder(kubeClient kubernetes.Interface, scheme *runtime.Scheme, eventSource string, eventNode string) EventRecorder { + res := eventRecorder{} + + res.broadcaster = record.NewBroadcasterWithCorrelatorOptions(record.CorrelatorOptions{ + MaxEvents: 1, + MessageFunc: func(event *v1.Event) string { + return event.Message + }, + }) + + res.broadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")}) + res.recorder = res.broadcaster.NewRecorder(scheme, v1.EventSource{ + Component: eventSource, + Host: eventNode, + }) + + return &res +} + +func (er *eventRecorder) Event(object runtime.Object, warning bool, reason string, message string, a ...any) { + eventType := v1.EventTypeNormal + if warning { + eventType = v1.EventTypeWarning + } + + if len(a) > 0 { + er.recorder.Eventf(object, eventType, reason, message, a...) + } else { + er.recorder.Event(object, eventType, reason, message) + } +} + +func (er *eventRecorder) Shutdown() { + // StartEventWatcher doesn't wait for writing all buffered events to API server when Shutdown is called, so have to hardcode a sleep time + time.Sleep(2 * time.Second) + er.broadcaster.Shutdown() +} diff --git a/pkg/util/kube/event_test.go b/pkg/util/kube/event_test.go new file mode 100644 index 000000000..538142596 --- /dev/null +++ b/pkg/util/kube/event_test.go @@ -0,0 +1,62 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kube + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes/fake" + + corev1 "k8s.io/api/core/v1" +) + +func TestEvent(t *testing.T) { + client := fake.NewSimpleClientset() + + scheme := runtime.NewScheme() + err := corev1.AddToScheme(scheme) + require.NoError(t, err) + + recorder := NewEventRecorder(client, scheme, "source-1", "fake-node") + + pod := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "fake-ns", + Name: "fake-pod", + UID: types.UID("fake-uid"), + }, + Spec: corev1.PodSpec{ + NodeName: "fake-node", + }, + } + + recorder.Event(pod, false, "Progress", "progress-1") + recorder.Event(pod, false, "Progress", "progress-2") + + recorder.Shutdown() + + items, err := client.CoreV1().Events("fake-ns").List(context.Background(), metav1.ListOptions{}) + require.NoError(t, err) + + assert.Len(t, items.Items, 1) +} From 6997b8e39354f0f5ecff94aa98a8c026827e5dd4 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 26 Jul 2024 13:54:02 +0800 Subject: [PATCH 22/69] data mover micro service backup Signed-off-by: Lyndon-Li --- pkg/cmd/cli/datamover/backup.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/cli/datamover/backup.go b/pkg/cmd/cli/datamover/backup.go index 35f483d92..2027b0893 100644 --- a/pkg/cmd/cli/datamover/backup.go +++ b/pkg/cmd/cli/datamover/backup.go @@ -207,7 +207,8 @@ func (s *dataMoverBackup) run() { } }() - s.runDataPath() + // TODOOO: call s.runDataPath() + time.Sleep(time.Duration(1<<63 - 1)) } func (s *dataMoverBackup) runDataPath() { From ba9c109868381faaab256d14e5ee81baeb0b1bd8 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 18 Jul 2024 09:57:44 -0400 Subject: [PATCH 23/69] Create new ItemBlockAction (IBA) plugin type Signed-off-by: Scott Seago --- changelogs/unreleased/8026-sseago | 1 + design/backup-performance-improvements.md | 4 +- .../v1/restartable_item_block_action.go | 115 ++++++ .../v1/restartable_item_block_action_test.go | 144 +++++++ pkg/plugin/clientmgmt/manager.go | 46 +++ pkg/plugin/clientmgmt/manager_test.go | 110 +++++ .../clientmgmt/process/client_builder.go | 2 + .../clientmgmt/process/client_builder_test.go | 2 + pkg/plugin/framework/action_resolver.go | 36 ++ pkg/plugin/framework/common/plugin_kinds.go | 4 + .../itemblockaction/v1/item_block_action.go | 45 ++ .../v1/item_block_action_client.go | 122 ++++++ .../v1/item_block_action_server.go | 134 ++++++ .../v1/item_block_action_test.go | 163 ++++++++ pkg/plugin/framework/server.go | 24 ++ .../itemblockaction/v1/ItemBlockAction.pb.go | 388 ++++++++++++++++++ .../v1/ItemBlockAction_grpc.pb.go | 144 +++++++ pkg/plugin/mocks/manager.go | 184 ++++++++- .../itemblockaction/v1/ItemBlockAction.proto | 29 ++ .../itemblockaction/v1/item_block_action.go | 46 +++ .../itemblockaction/v1/ItemBlockAction.go | 122 ++++++ 21 files changed, 1851 insertions(+), 14 deletions(-) create mode 100644 changelogs/unreleased/8026-sseago create mode 100644 pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go create mode 100644 pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go create mode 100644 pkg/plugin/framework/itemblockaction/v1/item_block_action.go create mode 100644 pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go create mode 100644 pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go create mode 100644 pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go create mode 100644 pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go create mode 100644 pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go create mode 100644 pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto create mode 100644 pkg/plugin/velero/itemblockaction/v1/item_block_action.go create mode 100644 pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go diff --git a/changelogs/unreleased/8026-sseago b/changelogs/unreleased/8026-sseago new file mode 100644 index 000000000..a1f1c0897 --- /dev/null +++ b/changelogs/unreleased/8026-sseago @@ -0,0 +1 @@ +Created new ItemBlockAction (IBA) plugin type diff --git a/design/backup-performance-improvements.md b/design/backup-performance-improvements.md index 2d90a7189..9105a7880 100644 --- a/design/backup-performance-improvements.md +++ b/design/backup-performance-improvements.md @@ -91,13 +91,13 @@ message ItemBlockActionAppliesToResponse { ResourceSelector ResourceSelector = 1; } -message ItemBlockActionRelatedItemsRequest { +message ItemBlockActionGetRelatedItemsRequest { string plugin = 1; bytes item = 2; bytes backup = 3; } -message ItemBlockActionRelatedItemsResponse { +message ItemBlockActionGetRelatedItemsResponse { repeated generated.ResourceIdentifier relatedItems = 1; } ``` diff --git a/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go new file mode 100644 index 000000000..83742978f --- /dev/null +++ b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action.go @@ -0,0 +1,115 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/runtime" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" +) + +// AdaptedItemBlockAction is an ItemBlock action adapted to the v1 ItemBlockAction API +type AdaptedItemBlockAction struct { + Kind common.PluginKind + + // Get returns a restartable ItemBlockAction for the given name and process, wrapping if necessary + GetRestartable func(name string, restartableProcess process.RestartableProcess) ibav1.ItemBlockAction +} + +func AdaptedItemBlockActions() []AdaptedItemBlockAction { + return []AdaptedItemBlockAction{ + { + Kind: common.PluginKindItemBlockAction, + GetRestartable: func(name string, restartableProcess process.RestartableProcess) ibav1.ItemBlockAction { + return NewRestartableItemBlockAction(name, restartableProcess) + }, + }, + } +} + +// RestartableItemBlockAction is an ItemBlock action for a given implementation (such as "pod"). It is associated with +// a restartableProcess, which may be shared and used to run multiple plugins. At the beginning of each method +// call, the restartableItemBlockAction asks its restartableProcess to restart itself if needed (e.g. if the +// process terminated for any reason), then it proceeds with the actual call. +type RestartableItemBlockAction struct { + Key process.KindAndName + SharedPluginProcess process.RestartableProcess +} + +// NewRestartableItemBlockAction returns a new RestartableItemBlockAction. +func NewRestartableItemBlockAction(name string, sharedPluginProcess process.RestartableProcess) *RestartableItemBlockAction { + r := &RestartableItemBlockAction{ + Key: process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name}, + SharedPluginProcess: sharedPluginProcess, + } + return r +} + +// getItemBlockAction returns the ItemBlock action for this restartableItemBlockAction. It does *not* restart the +// plugin process. +func (r *RestartableItemBlockAction) getItemBlockAction() (ibav1.ItemBlockAction, error) { + plugin, err := r.SharedPluginProcess.GetByKindAndName(r.Key) + if err != nil { + return nil, err + } + + itemBlockAction, ok := plugin.(ibav1.ItemBlockAction) + if !ok { + return nil, errors.Errorf("plugin %T is not an ItemBlockAction", plugin) + } + + return itemBlockAction, nil +} + +// getDelegate restarts the plugin process (if needed) and returns the ItemBlock action for this restartableItemBlockAction. +func (r *RestartableItemBlockAction) getDelegate() (ibav1.ItemBlockAction, error) { + if err := r.SharedPluginProcess.ResetIfNeeded(); err != nil { + return nil, err + } + + return r.getItemBlockAction() +} + +// Name returns the plugin's name. +func (r *RestartableItemBlockAction) Name() string { + return r.Key.Name +} + +// AppliesTo restarts the plugin's process if needed, then delegates the call. +func (r *RestartableItemBlockAction) AppliesTo() (velero.ResourceSelector, error) { + delegate, err := r.getDelegate() + if err != nil { + return velero.ResourceSelector{}, err + } + + return delegate.AppliesTo() +} + +// GetRelatedItems restarts the plugin's process if needed, then delegates the call. +func (r *RestartableItemBlockAction) GetRelatedItems(item runtime.Unstructured, backup *api.Backup) ([]velero.ResourceIdentifier, error) { + delegate, err := r.getDelegate() + if err != nil { + return nil, err + } + + return delegate.GetRelatedItems(item, backup) +} diff --git a/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go new file mode 100644 index 000000000..eeec9c74c --- /dev/null +++ b/pkg/plugin/clientmgmt/itemblockaction/v1/restartable_item_block_action_test.go @@ -0,0 +1,144 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/vmware-tanzu/velero/internal/restartabletest" + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/itemblockaction/v1" +) + +func TestRestartableGetItemBlockAction(t *testing.T) { + tests := []struct { + name string + plugin interface{} + getError error + expectedError string + }{ + { + name: "error getting by kind and name", + getError: errors.Errorf("get error"), + expectedError: "get error", + }, + { + name: "wrong type", + plugin: 3, + expectedError: "plugin int is not an ItemBlockAction", + }, + { + name: "happy path", + plugin: new(mocks.ItemBlockAction), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + p := new(restartabletest.MockRestartableProcess) + defer p.AssertExpectations(t) + + name := "pod" + key := process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name} + p.On("GetByKindAndName", key).Return(tc.plugin, tc.getError) + + r := NewRestartableItemBlockAction(name, p) + a, err := r.getItemBlockAction() + if tc.expectedError != "" { + assert.EqualError(t, err, tc.expectedError) + return + } + require.NoError(t, err) + + assert.Equal(t, tc.plugin, a) + }) + } +} + +func TestRestartableItemBlockActionGetDelegate(t *testing.T) { + p := new(restartabletest.MockRestartableProcess) + defer p.AssertExpectations(t) + + // Reset error + p.On("ResetIfNeeded").Return(errors.Errorf("reset error")).Once() + name := "pod" + r := NewRestartableItemBlockAction(name, p) + a, err := r.getDelegate() + assert.Nil(t, a) + assert.EqualError(t, err, "reset error") + + // Happy path + p.On("ResetIfNeeded").Return(nil) + expected := new(mocks.ItemBlockAction) + key := process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name} + p.On("GetByKindAndName", key).Return(expected, nil) + + a, err = r.getDelegate() + assert.NoError(t, err) + assert.Equal(t, expected, a) +} + +func TestRestartableItemBlockActionDelegatedFunctions(t *testing.T) { + b := new(v1.Backup) + + pv := &unstructured.Unstructured{ + Object: map[string]interface{}{ + "color": "blue", + }, + } + + relatedItems := []velero.ResourceIdentifier{ + { + GroupResource: schema.GroupResource{Group: "velero.io", Resource: "backups"}, + }, + } + + restartabletest.RunRestartableDelegateTests( + t, + common.PluginKindItemBlockAction, + func(key process.KindAndName, p process.RestartableProcess) interface{} { + return &RestartableItemBlockAction{ + Key: key, + SharedPluginProcess: p, + } + }, + func() restartabletest.Mockable { + return new(mocks.ItemBlockAction) + }, + restartabletest.RestartableDelegateTest{ + Function: "AppliesTo", + Inputs: []interface{}{}, + ExpectedErrorOutputs: []interface{}{velero.ResourceSelector{}, errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{velero.ResourceSelector{IncludedNamespaces: []string{"a"}}, errors.Errorf("delegate error")}, + }, + restartabletest.RestartableDelegateTest{ + Function: "GetRelatedItems", + Inputs: []interface{}{pv, b}, + ExpectedErrorOutputs: []interface{}{([]velero.ResourceIdentifier)(nil), errors.Errorf("reset error")}, + ExpectedDelegateOutputs: []interface{}{relatedItems, errors.Errorf("delegate error")}, + }, + ) +} diff --git a/pkg/plugin/clientmgmt/manager.go b/pkg/plugin/clientmgmt/manager.go index aafb398a0..1c895d86e 100644 --- a/pkg/plugin/clientmgmt/manager.go +++ b/pkg/plugin/clientmgmt/manager.go @@ -26,6 +26,7 @@ import ( biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" biav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v2" + ibav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/itemblockaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1" riav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v2" @@ -34,6 +35,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" vsv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/volumesnapshotter/v1" @@ -77,6 +79,12 @@ type Manager interface { // GetDeleteItemAction returns the delete item action plugin for name. GetDeleteItemAction(name string) (velero.DeleteItemAction, error) + // GetItemBlockActions returns all v1 ItemBlock action plugins. + GetItemBlockActions() ([]ibav1.ItemBlockAction, error) + + // GetItemBlockAction returns the ItemBlock action plugin for name. + GetItemBlockAction(name string) (ibav1.ItemBlockAction, error) + // CleanupClients terminates all of the Manager's running plugin processes. CleanupClients() } @@ -374,6 +382,44 @@ func (m *manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, err return r, nil } +// GetItemBlockActions returns all ItemBlock actions as restartableItemBlockActions. +func (m *manager) GetItemBlockActions() ([]ibav1.ItemBlockAction, error) { + list := m.registry.List(common.PluginKindItemBlockAction) + + actions := make([]ibav1.ItemBlockAction, 0, len(list)) + + for i := range list { + id := list[i] + + r, err := m.GetItemBlockAction(id.Name) + if err != nil { + return nil, err + } + + actions = append(actions, r) + } + + return actions, nil +} + +// GetItemBlockAction returns a restartableItemBlockAction for name. +func (m *manager) GetItemBlockAction(name string) (ibav1.ItemBlockAction, error) { + name = sanitizeName(name) + + for _, adaptedItemBlockAction := range ibav1cli.AdaptedItemBlockActions() { + restartableProcess, err := m.getRestartableProcess(adaptedItemBlockAction.Kind, name) + // Check if plugin was not found + if errors.As(err, &pluginNotFoundErrType) { + continue + } + if err != nil { + return nil, err + } + return adaptedItemBlockAction.GetRestartable(name, restartableProcess), nil + } + return nil, fmt.Errorf("unable to get valid ItemBlockAction for %q", name) +} + // sanitizeName adds "velero.io" to legacy plugins that weren't namespaced. func sanitizeName(name string) string { // Backwards compatibility with non-namespaced Velero plugins, following principle of least surprise diff --git a/pkg/plugin/clientmgmt/manager_test.go b/pkg/plugin/clientmgmt/manager_test.go index 816fef6c0..6919fc9c3 100644 --- a/pkg/plugin/clientmgmt/manager_test.go +++ b/pkg/plugin/clientmgmt/manager_test.go @@ -29,6 +29,7 @@ import ( "github.com/vmware-tanzu/velero/internal/restartabletest" biav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v1" biav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/backupitemaction/v2" + ibav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/itemblockaction/v1" "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/process" riav1cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v1" riav2cli "github.com/vmware-tanzu/velero/pkg/plugin/clientmgmt/restoreitemaction/v2" @@ -256,6 +257,23 @@ func TestGetRestoreItemActionV2(t *testing.T) { ) } +func TestGetItemBlockAction(t *testing.T) { + getPluginTest(t, + common.PluginKindItemBlockAction, + "velero.io/pod", + func(m Manager, name string) (interface{}, error) { + return m.GetItemBlockAction(name) + }, + func(name string, sharedPluginProcess process.RestartableProcess) interface{} { + return &ibav1cli.RestartableItemBlockAction{ + Key: process.KindAndName{Kind: common.PluginKindItemBlockAction, Name: name}, + SharedPluginProcess: sharedPluginProcess, + } + }, + false, + ) +} + func getPluginTest( t *testing.T, kind common.PluginKind, @@ -784,6 +802,98 @@ func TestGetDeleteItemActions(t *testing.T) { } } +func TestGetItemBlockActions(t *testing.T) { + tests := []struct { + name string + names []string + newRestartableProcessError error + expectedError string + }{ + { + name: "No items", + names: []string{}, + }, + { + name: "Error getting restartable process", + names: []string{"velero.io/a", "velero.io/b", "velero.io/c"}, + newRestartableProcessError: errors.Errorf("NewRestartableProcess"), + expectedError: "NewRestartableProcess", + }, + { + name: "Happy path", + names: []string{"velero.io/a", "velero.io/b", "velero.io/c"}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + logger := test.NewLogger() + logLevel := logrus.InfoLevel + + registry := &mockRegistry{} + defer registry.AssertExpectations(t) + + m := NewManager(logger, logLevel, registry).(*manager) + factory := &mockRestartableProcessFactory{} + defer factory.AssertExpectations(t) + m.restartableProcessFactory = factory + + pluginKind := common.PluginKindItemBlockAction + var pluginIDs []framework.PluginIdentifier + for i := range tc.names { + pluginID := framework.PluginIdentifier{ + Command: "/command", + Kind: pluginKind, + Name: tc.names[i], + } + pluginIDs = append(pluginIDs, pluginID) + } + registry.On("List", pluginKind).Return(pluginIDs) + + var expectedActions []interface{} + for i := range pluginIDs { + pluginID := pluginIDs[i] + pluginName := pluginID.Name + + registry.On("Get", pluginKind, pluginName).Return(pluginID, nil) + + restartableProcess := &restartabletest.MockRestartableProcess{} + defer restartableProcess.AssertExpectations(t) + + expected := &ibav1cli.RestartableItemBlockAction{ + Key: process.KindAndName{Kind: pluginKind, Name: pluginName}, + SharedPluginProcess: restartableProcess, + } + + if tc.newRestartableProcessError != nil { + // Test 1: error getting restartable process + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(nil, errors.Errorf("NewRestartableProcess")).Once() + break + } + + // Test 2: happy path + if i == 0 { + factory.On("NewRestartableProcess", pluginID.Command, logger, logLevel).Return(restartableProcess, nil).Once() + } + + expectedActions = append(expectedActions, expected) + } + + itemBlockActions, err := m.GetItemBlockActions() + if tc.newRestartableProcessError != nil { + assert.Nil(t, itemBlockActions) + assert.EqualError(t, err, "NewRestartableProcess") + } else { + require.NoError(t, err) + var actual []interface{} + for i := range itemBlockActions { + actual = append(actual, itemBlockActions[i]) + } + assert.Equal(t, expectedActions, actual) + } + }) + } +} + func TestSanitizeName(t *testing.T) { tests := []struct { name, pluginName, expectedName string diff --git a/pkg/plugin/clientmgmt/process/client_builder.go b/pkg/plugin/clientmgmt/process/client_builder.go index d41445779..1ae3f5af1 100644 --- a/pkg/plugin/clientmgmt/process/client_builder.go +++ b/pkg/plugin/clientmgmt/process/client_builder.go @@ -29,6 +29,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" biav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/backupitemaction/v2" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/framework/itemblockaction/v1" riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2" ) @@ -78,6 +79,7 @@ func (b *clientBuilder) clientConfig() *hcplugin.ClientConfig { string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(b.clientLogger)), string(common.PluginKindRestoreItemActionV2): riav2.NewRestoreItemActionPlugin(common.ClientLogger(b.clientLogger)), string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(b.clientLogger)), + string(common.PluginKindItemBlockAction): ibav1.NewItemBlockActionPlugin(common.ClientLogger(b.clientLogger)), }, Logger: b.pluginLogger, Cmd: exec.Command(b.commandName, b.commandArgs...), //nolint:gosec // Internal call. No need to check the command line. diff --git a/pkg/plugin/clientmgmt/process/client_builder_test.go b/pkg/plugin/clientmgmt/process/client_builder_test.go index cff06d93f..b0e7fa700 100644 --- a/pkg/plugin/clientmgmt/process/client_builder_test.go +++ b/pkg/plugin/clientmgmt/process/client_builder_test.go @@ -29,6 +29,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" biav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/backupitemaction/v2" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/framework/itemblockaction/v1" riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2" "github.com/vmware-tanzu/velero/pkg/test" ) @@ -70,6 +71,7 @@ func TestClientConfig(t *testing.T) { string(common.PluginKindRestoreItemAction): framework.NewRestoreItemActionPlugin(common.ClientLogger(logger)), string(common.PluginKindRestoreItemActionV2): riav2.NewRestoreItemActionPlugin(common.ClientLogger(logger)), string(common.PluginKindDeleteItemAction): framework.NewDeleteItemActionPlugin(common.ClientLogger(logger)), + string(common.PluginKindItemBlockAction): ibav1.NewItemBlockActionPlugin(common.ClientLogger(logger)), }, Logger: cb.pluginLogger, Cmd: exec.Command(cb.commandName, cb.commandArgs...), diff --git a/pkg/plugin/framework/action_resolver.go b/pkg/plugin/framework/action_resolver.go index 4b79fea9c..ac8a0b1d0 100644 --- a/pkg/plugin/framework/action_resolver.go +++ b/pkg/plugin/framework/action_resolver.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/velero" biav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v1" biav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/backupitemaction/v2" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" riav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" riav2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" "github.com/vmware-tanzu/velero/pkg/util/collections" @@ -280,3 +281,38 @@ func (recv DeleteItemActionResolver) ResolveActions(helper discovery.Helper, log } return resolved, nil } + +type ItemBlockResolvedAction struct { + ibav1.ItemBlockAction + resolvedAction +} + +type ItemBlockActionResolver struct { + actions []ibav1.ItemBlockAction +} + +func NewItemBlockActionResolver(actions []ibav1.ItemBlockAction) ItemBlockActionResolver { + return ItemBlockActionResolver{ + actions: actions, + } +} + +func (recv ItemBlockActionResolver) ResolveActions(helper discovery.Helper, log logrus.FieldLogger) ([]ItemBlockResolvedAction, error) { + var resolved []ItemBlockResolvedAction + for _, action := range recv.actions { + resources, namespaces, selector, err := resolveAction(helper, action) + if err != nil { + return nil, err + } + res := ItemBlockResolvedAction{ + ItemBlockAction: action, + resolvedAction: resolvedAction{ + ResourceIncludesExcludes: resources, + NamespaceIncludesExcludes: namespaces, + Selector: selector, + }, + } + resolved = append(resolved, res) + } + return resolved, nil +} diff --git a/pkg/plugin/framework/common/plugin_kinds.go b/pkg/plugin/framework/common/plugin_kinds.go index 83cbcef31..b5c2c1c82 100644 --- a/pkg/plugin/framework/common/plugin_kinds.go +++ b/pkg/plugin/framework/common/plugin_kinds.go @@ -47,6 +47,9 @@ const ( // PluginKindDeleteItemAction represents a delete item action plugin. PluginKindDeleteItemAction PluginKind = "DeleteItemAction" + // PluginKindItemBlockAction represents a v1 ItemBlock action plugin. + PluginKindItemBlockAction PluginKind = "ItemBlockAction" + // PluginKindPluginLister represents a plugin lister plugin. PluginKindPluginLister PluginKind = "PluginLister" ) @@ -70,5 +73,6 @@ func AllPluginKinds() map[string]PluginKind { allPluginKinds[PluginKindRestoreItemAction.String()] = PluginKindRestoreItemAction allPluginKinds[PluginKindRestoreItemActionV2.String()] = PluginKindRestoreItemActionV2 allPluginKinds[PluginKindDeleteItemAction.String()] = PluginKindDeleteItemAction + allPluginKinds[PluginKindItemBlockAction.String()] = PluginKindItemBlockAction return allPluginKinds } diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action.go new file mode 100644 index 000000000..dfe7a83b4 --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action.go @@ -0,0 +1,45 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + plugin "github.com/hashicorp/go-plugin" + "golang.org/x/net/context" + "google.golang.org/grpc" + + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" +) + +// ItemBlockActionPlugin is an implementation of go-plugin's Plugin +// interface with support for gRPC for the backup/ItemAction +// interface. +type ItemBlockActionPlugin struct { + plugin.NetRPCUnsupportedPlugin + *common.PluginBase +} + +// GRPCClient returns a clientDispenser for ItemBlockAction gRPC clients. +func (p *ItemBlockActionPlugin) GRPCClient(_ context.Context, _ *plugin.GRPCBroker, clientConn *grpc.ClientConn) (interface{}, error) { + return common.NewClientDispenser(p.ClientLogger, clientConn, newItemBlockActionGRPCClient), nil +} + +// GRPCServer registers a ItemBlockAction gRPC server. +func (p *ItemBlockActionPlugin) GRPCServer(_ *plugin.GRPCBroker, server *grpc.Server) error { + protoibav1.RegisterItemBlockActionServer(server, &ItemBlockActionGRPCServer{mux: p.ServerMux}) + return nil +} diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go new file mode 100644 index 000000000..06287da33 --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action_client.go @@ -0,0 +1,122 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "encoding/json" + + "github.com/pkg/errors" + "golang.org/x/net/context" + "google.golang.org/grpc" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +// NewItemBlockActionPlugin constructs a ItemBlockActionPlugin. +func NewItemBlockActionPlugin(options ...common.PluginOption) *ItemBlockActionPlugin { + return &ItemBlockActionPlugin{ + PluginBase: common.NewPluginBase(options...), + } +} + +// ItemBlockActionGRPCClient implements the backup/ItemAction interface and uses a +// gRPC client to make calls to the plugin server. +type ItemBlockActionGRPCClient struct { + *common.ClientBase + grpcClient protoibav1.ItemBlockActionClient +} + +func newItemBlockActionGRPCClient(base *common.ClientBase, clientConn *grpc.ClientConn) interface{} { + return &ItemBlockActionGRPCClient{ + ClientBase: base, + grpcClient: protoibav1.NewItemBlockActionClient(clientConn), + } +} + +func (c *ItemBlockActionGRPCClient) AppliesTo() (velero.ResourceSelector, error) { + req := &protoibav1.ItemBlockActionAppliesToRequest{ + Plugin: c.Plugin, + } + + res, err := c.grpcClient.AppliesTo(context.Background(), req) + if err != nil { + return velero.ResourceSelector{}, common.FromGRPCError(err) + } + + if res.ResourceSelector == nil { + return velero.ResourceSelector{}, nil + } + + return velero.ResourceSelector{ + IncludedNamespaces: res.ResourceSelector.IncludedNamespaces, + ExcludedNamespaces: res.ResourceSelector.ExcludedNamespaces, + IncludedResources: res.ResourceSelector.IncludedResources, + ExcludedResources: res.ResourceSelector.ExcludedResources, + LabelSelector: res.ResourceSelector.Selector, + }, nil +} + +func (c *ItemBlockActionGRPCClient) GetRelatedItems(item runtime.Unstructured, backup *api.Backup) ([]velero.ResourceIdentifier, error) { + itemJSON, err := json.Marshal(item.UnstructuredContent()) + if err != nil { + return nil, errors.WithStack(err) + } + + backupJSON, err := json.Marshal(backup) + if err != nil { + return nil, errors.WithStack(err) + } + + req := &protoibav1.ItemBlockActionGetRelatedItemsRequest{ + Plugin: c.Plugin, + Item: itemJSON, + Backup: backupJSON, + } + + res, err := c.grpcClient.GetRelatedItems(context.Background(), req) + if err != nil { + return nil, common.FromGRPCError(err) + } + + var relatedItems []velero.ResourceIdentifier + + for _, itm := range res.RelatedItems { + newItem := velero.ResourceIdentifier{ + GroupResource: schema.GroupResource{ + Group: itm.Group, + Resource: itm.Resource, + }, + Namespace: itm.Namespace, + Name: itm.Name, + } + + relatedItems = append(relatedItems, newItem) + } + + return relatedItems, nil +} + +// This shouldn't be called on the GRPC client since the RestartableItemBlockAction won't delegate +// this method +func (c *ItemBlockActionGRPCClient) Name() string { + return "" +} diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go new file mode 100644 index 000000000..17ece020d --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action_server.go @@ -0,0 +1,134 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "encoding/json" + + "github.com/pkg/errors" + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" +) + +// ItemBlockActionGRPCServer implements the proto-generated ItemBlockAction interface, and accepts +// gRPC calls and forwards them to an implementation of the pluggable interface. +type ItemBlockActionGRPCServer struct { + mux *common.ServerMux +} + +func (s *ItemBlockActionGRPCServer) getImpl(name string) (ibav1.ItemBlockAction, error) { + impl, err := s.mux.GetHandler(name) + if err != nil { + return nil, err + } + + itemAction, ok := impl.(ibav1.ItemBlockAction) + if !ok { + return nil, errors.Errorf("%T is not a backup item action", impl) + } + + return itemAction, nil +} + +func (s *ItemBlockActionGRPCServer) AppliesTo( + ctx context.Context, req *protoibav1.ItemBlockActionAppliesToRequest) ( + response *protoibav1.ItemBlockActionAppliesToResponse, err error) { + defer func() { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { + err = recoveredErr + } + }() + + impl, err := s.getImpl(req.Plugin) + if err != nil { + return nil, common.NewGRPCError(err) + } + + resourceSelector, err := impl.AppliesTo() + if err != nil { + return nil, common.NewGRPCError(err) + } + + return &protoibav1.ItemBlockActionAppliesToResponse{ + ResourceSelector: &proto.ResourceSelector{ + IncludedNamespaces: resourceSelector.IncludedNamespaces, + ExcludedNamespaces: resourceSelector.ExcludedNamespaces, + IncludedResources: resourceSelector.IncludedResources, + ExcludedResources: resourceSelector.ExcludedResources, + Selector: resourceSelector.LabelSelector, + }, + }, nil +} + +func (s *ItemBlockActionGRPCServer) GetRelatedItems( + ctx context.Context, req *protoibav1.ItemBlockActionGetRelatedItemsRequest) (response *protoibav1.ItemBlockActionGetRelatedItemsResponse, err error) { + defer func() { + if recoveredErr := common.HandlePanic(recover()); recoveredErr != nil { + err = recoveredErr + } + }() + + impl, err := s.getImpl(req.Plugin) + if err != nil { + return nil, common.NewGRPCError(err) + } + + var item unstructured.Unstructured + var backup api.Backup + + if err := json.Unmarshal(req.Item, &item); err != nil { + return nil, common.NewGRPCError(errors.WithStack(err)) + } + if err := json.Unmarshal(req.Backup, &backup); err != nil { + return nil, common.NewGRPCError(errors.WithStack(err)) + } + + relatedItems, err := impl.GetRelatedItems(&item, &backup) + if err != nil { + return nil, common.NewGRPCError(err) + } + + res := &protoibav1.ItemBlockActionGetRelatedItemsResponse{} + + for _, item := range relatedItems { + res.RelatedItems = append(res.RelatedItems, backupResourceIdentifierToProto(item)) + } + + return res, nil +} + +func backupResourceIdentifierToProto(id velero.ResourceIdentifier) *proto.ResourceIdentifier { + return &proto.ResourceIdentifier{ + Group: id.Group, + Resource: id.Resource, + Namespace: id.Namespace, + Name: id.Name, + } +} + +// This shouldn't be called on the GRPC server since the server won't ever receive this request, as +// the RestartableItemBlockAction in Velero won't delegate this to the server +func (s *ItemBlockActionGRPCServer) Name() string { + return "" +} diff --git a/pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go b/pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go new file mode 100644 index 000000000..ecfa693ae --- /dev/null +++ b/pkg/plugin/framework/itemblockaction/v1/item_block_action_test.go @@ -0,0 +1,163 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "encoding/json" + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + proto "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protoibav1 "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + mocks "github.com/vmware-tanzu/velero/pkg/plugin/velero/mocks/itemblockaction/v1" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestItemBlockActionGRPCServerGetRelatedItems(t *testing.T) { + invalidItem := []byte("this is gibberish json") + validItem := []byte(` + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "namespace": "myns", + "name": "myconfigmap" + }, + "data": { + "key": "value" + } + }`) + var validItemObject unstructured.Unstructured + err := json.Unmarshal(validItem, &validItemObject) + require.NoError(t, err) + + invalidBackup := []byte("this is gibberish json") + validBackup := []byte(` + { + "apiVersion": "velero.io/v1", + "kind": "Backup", + "metadata": { + "namespace": "myns", + "name": "mybackup" + }, + "spec": { + "includedNamespaces": ["*"], + "includedResources": ["*"], + "ttl": "60m" + } + }`) + var validBackupObject v1.Backup + err = json.Unmarshal(validBackup, &validBackupObject) + require.NoError(t, err) + + tests := []struct { + name string + backup []byte + item []byte + implRelatedItems []velero.ResourceIdentifier + implError error + expectError bool + skipMock bool + }{ + { + name: "error unmarshaling item", + item: invalidItem, + backup: validBackup, + expectError: true, + skipMock: true, + }, + { + name: "error unmarshaling backup", + item: validItem, + backup: invalidBackup, + expectError: true, + skipMock: true, + }, + { + name: "error running impl", + item: validItem, + backup: validBackup, + implError: errors.New("impl error"), + expectError: true, + }, + { + name: "no relatedItems", + item: validItem, + backup: validBackup, + }, + { + name: "some relatedItems", + item: validItem, + backup: validBackup, + implRelatedItems: []velero.ResourceIdentifier{ + { + GroupResource: schema.GroupResource{Group: "v1", Resource: "pods"}, + Namespace: "myns", + Name: "mypod", + }, + }, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + itemAction := &mocks.ItemBlockAction{} + defer itemAction.AssertExpectations(t) + + if !test.skipMock { + itemAction.On("GetRelatedItems", &validItemObject, &validBackupObject).Return(test.implRelatedItems, test.implError) + } + + s := &ItemBlockActionGRPCServer{mux: &common.ServerMux{ + ServerLog: velerotest.NewLogger(), + Handlers: map[string]interface{}{ + "xyz": itemAction, + }, + }} + + req := &protoibav1.ItemBlockActionGetRelatedItemsRequest{ + Plugin: "xyz", + Item: test.item, + Backup: test.backup, + } + + resp, err := s.GetRelatedItems(context.Background(), req) + + // Verify error + assert.Equal(t, test.expectError, err != nil) + if err != nil { + return + } + require.NotNil(t, resp) + + // Verify related items + var expectedRelatedItems []*proto.ResourceIdentifier + for _, item := range test.implRelatedItems { + expectedRelatedItems = append(expectedRelatedItems, backupResourceIdentifierToProto(item)) + } + assert.Equal(t, expectedRelatedItems, resp.RelatedItems) + }) + } +} diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index d1d87aecb..0e71c3b24 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -27,6 +27,7 @@ import ( biav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/backupitemaction/v2" "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + ibav1 "github.com/vmware-tanzu/velero/pkg/plugin/framework/itemblockaction/v1" riav2 "github.com/vmware-tanzu/velero/pkg/plugin/framework/restoreitemaction/v2" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -90,6 +91,13 @@ type Server interface { // RegisterDeleteItemActions registers multiple Delete item actions. RegisterDeleteItemActions(map[string]common.HandlerInitializer) Server + // RegisterItemBlockAction registers a ItemBlock action. Accepted format + // for the plugin name is /. + RegisterItemBlockAction(pluginName string, initializer common.HandlerInitializer) Server + + // RegisterItemBlockActions registers multiple ItemBlock actions. + RegisterItemBlockActions(map[string]common.HandlerInitializer) Server + // Server runs the plugin server. Serve() } @@ -106,6 +114,7 @@ type server struct { restoreItemAction *RestoreItemActionPlugin restoreItemActionV2 *riav2.RestoreItemActionPlugin deleteItemAction *DeleteItemActionPlugin + itemBlockAction *ibav1.ItemBlockActionPlugin } // NewServer returns a new Server @@ -122,6 +131,7 @@ func NewServer() Server { restoreItemAction: NewRestoreItemActionPlugin(common.ServerLogger(log)), restoreItemActionV2: riav2.NewRestoreItemActionPlugin(common.ServerLogger(log)), deleteItemAction: NewDeleteItemActionPlugin(common.ServerLogger(log)), + itemBlockAction: ibav1.NewItemBlockActionPlugin(common.ServerLogger(log)), } } @@ -217,6 +227,18 @@ func (s *server) RegisterDeleteItemActions(m map[string]common.HandlerInitialize return s } +func (s *server) RegisterItemBlockAction(name string, initializer common.HandlerInitializer) Server { + s.itemBlockAction.Register(name, initializer) + return s +} + +func (s *server) RegisterItemBlockActions(m map[string]common.HandlerInitializer) Server { + for name := range m { + s.RegisterItemBlockAction(name, m[name]) + } + return s +} + // getNames returns a list of PluginIdentifiers registered with plugin. func getNames(command string, kind common.PluginKind, plugin Interface) []PluginIdentifier { var pluginIdentifiers []PluginIdentifier @@ -251,6 +273,7 @@ func (s *server) Serve() { pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindRestoreItemAction, s.restoreItemAction)...) pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindRestoreItemActionV2, s.restoreItemActionV2)...) pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindDeleteItemAction, s.deleteItemAction)...) + pluginIdentifiers = append(pluginIdentifiers, getNames(command, common.PluginKindItemBlockAction, s.itemBlockAction)...) pluginLister := NewPluginLister(pluginIdentifiers...) @@ -265,6 +288,7 @@ func (s *server) Serve() { string(common.PluginKindRestoreItemAction): s.restoreItemAction, string(common.PluginKindRestoreItemActionV2): s.restoreItemActionV2, string(common.PluginKindDeleteItemAction): s.deleteItemAction, + string(common.PluginKindItemBlockAction): s.itemBlockAction, }, GRPCServer: plugin.DefaultGRPCServer, }) diff --git a/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go new file mode 100644 index 000000000..cec604477 --- /dev/null +++ b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction.pb.go @@ -0,0 +1,388 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.33.0 +// protoc v4.25.2 +// source: itemblockaction/v1/ItemBlockAction.proto + +package v1 + +import ( + generated "github.com/vmware-tanzu/velero/pkg/plugin/generated" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type ItemBlockActionAppliesToRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` +} + +func (x *ItemBlockActionAppliesToRequest) Reset() { + *x = ItemBlockActionAppliesToRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionAppliesToRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionAppliesToRequest) ProtoMessage() {} + +func (x *ItemBlockActionAppliesToRequest) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionAppliesToRequest.ProtoReflect.Descriptor instead. +func (*ItemBlockActionAppliesToRequest) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{0} +} + +func (x *ItemBlockActionAppliesToRequest) GetPlugin() string { + if x != nil { + return x.Plugin + } + return "" +} + +type ItemBlockActionAppliesToResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ResourceSelector *generated.ResourceSelector `protobuf:"bytes,1,opt,name=ResourceSelector,proto3" json:"ResourceSelector,omitempty"` +} + +func (x *ItemBlockActionAppliesToResponse) Reset() { + *x = ItemBlockActionAppliesToResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionAppliesToResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionAppliesToResponse) ProtoMessage() {} + +func (x *ItemBlockActionAppliesToResponse) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionAppliesToResponse.ProtoReflect.Descriptor instead. +func (*ItemBlockActionAppliesToResponse) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{1} +} + +func (x *ItemBlockActionAppliesToResponse) GetResourceSelector() *generated.ResourceSelector { + if x != nil { + return x.ResourceSelector + } + return nil +} + +type ItemBlockActionGetRelatedItemsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Plugin string `protobuf:"bytes,1,opt,name=plugin,proto3" json:"plugin,omitempty"` + Item []byte `protobuf:"bytes,2,opt,name=item,proto3" json:"item,omitempty"` + Backup []byte `protobuf:"bytes,3,opt,name=backup,proto3" json:"backup,omitempty"` +} + +func (x *ItemBlockActionGetRelatedItemsRequest) Reset() { + *x = ItemBlockActionGetRelatedItemsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionGetRelatedItemsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionGetRelatedItemsRequest) ProtoMessage() {} + +func (x *ItemBlockActionGetRelatedItemsRequest) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionGetRelatedItemsRequest.ProtoReflect.Descriptor instead. +func (*ItemBlockActionGetRelatedItemsRequest) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{2} +} + +func (x *ItemBlockActionGetRelatedItemsRequest) GetPlugin() string { + if x != nil { + return x.Plugin + } + return "" +} + +func (x *ItemBlockActionGetRelatedItemsRequest) GetItem() []byte { + if x != nil { + return x.Item + } + return nil +} + +func (x *ItemBlockActionGetRelatedItemsRequest) GetBackup() []byte { + if x != nil { + return x.Backup + } + return nil +} + +type ItemBlockActionGetRelatedItemsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RelatedItems []*generated.ResourceIdentifier `protobuf:"bytes,1,rep,name=relatedItems,proto3" json:"relatedItems,omitempty"` +} + +func (x *ItemBlockActionGetRelatedItemsResponse) Reset() { + *x = ItemBlockActionGetRelatedItemsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ItemBlockActionGetRelatedItemsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ItemBlockActionGetRelatedItemsResponse) ProtoMessage() {} + +func (x *ItemBlockActionGetRelatedItemsResponse) ProtoReflect() protoreflect.Message { + mi := &file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ItemBlockActionGetRelatedItemsResponse.ProtoReflect.Descriptor instead. +func (*ItemBlockActionGetRelatedItemsResponse) Descriptor() ([]byte, []int) { + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP(), []int{3} +} + +func (x *ItemBlockActionGetRelatedItemsResponse) GetRelatedItems() []*generated.ResourceIdentifier { + if x != nil { + return x.RelatedItems + } + return nil +} + +var File_itemblockaction_v1_ItemBlockAction_proto protoreflect.FileDescriptor + +var file_itemblockaction_v1_ItemBlockAction_proto_rawDesc = []byte{ + 0x0a, 0x28, 0x69, 0x74, 0x65, 0x6d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x61, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x76, 0x31, 0x2f, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x02, 0x76, 0x31, 0x1a, 0x0c, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x39, 0x0a, 0x1f, + 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, + 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x0a, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22, 0x6b, 0x0a, 0x20, 0x49, 0x74, 0x65, 0x6d, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, + 0x73, 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x10, 0x52, + 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, + 0x6f, 0x72, 0x52, 0x10, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x53, 0x65, 0x6c, 0x65, + 0x63, 0x74, 0x6f, 0x72, 0x22, 0x6b, 0x0a, 0x25, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, + 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x16, 0x0a, + 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, + 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x04, 0x69, 0x74, 0x65, 0x6d, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x61, 0x63, + 0x6b, 0x75, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x62, 0x61, 0x63, 0x6b, 0x75, + 0x70, 0x22, 0x6b, 0x0a, 0x26, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, + 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x41, 0x0a, 0x0c, 0x72, + 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x52, 0x0c, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x32, 0xd3, + 0x01, 0x0a, 0x0f, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x56, 0x0a, 0x09, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x12, + 0x23, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x65, 0x73, + 0x54, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x12, 0x29, 0x2e, + 0x76, 0x31, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x74, + 0x65, 0x6d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x47, 0x65, 0x74, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, 0x74, 0x65, 0x6d, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x48, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x76, 0x6d, 0x77, 0x61, 0x72, 0x65, 0x2d, 0x74, 0x61, 0x6e, 0x7a, 0x75, 0x2f, + 0x76, 0x65, 0x6c, 0x65, 0x72, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, + 0x6e, 0x2f, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2f, 0x69, 0x74, 0x65, 0x6d, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_itemblockaction_v1_ItemBlockAction_proto_rawDescOnce sync.Once + file_itemblockaction_v1_ItemBlockAction_proto_rawDescData = file_itemblockaction_v1_ItemBlockAction_proto_rawDesc +) + +func file_itemblockaction_v1_ItemBlockAction_proto_rawDescGZIP() []byte { + file_itemblockaction_v1_ItemBlockAction_proto_rawDescOnce.Do(func() { + file_itemblockaction_v1_ItemBlockAction_proto_rawDescData = protoimpl.X.CompressGZIP(file_itemblockaction_v1_ItemBlockAction_proto_rawDescData) + }) + return file_itemblockaction_v1_ItemBlockAction_proto_rawDescData +} + +var file_itemblockaction_v1_ItemBlockAction_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_itemblockaction_v1_ItemBlockAction_proto_goTypes = []interface{}{ + (*ItemBlockActionAppliesToRequest)(nil), // 0: v1.ItemBlockActionAppliesToRequest + (*ItemBlockActionAppliesToResponse)(nil), // 1: v1.ItemBlockActionAppliesToResponse + (*ItemBlockActionGetRelatedItemsRequest)(nil), // 2: v1.ItemBlockActionGetRelatedItemsRequest + (*ItemBlockActionGetRelatedItemsResponse)(nil), // 3: v1.ItemBlockActionGetRelatedItemsResponse + (*generated.ResourceSelector)(nil), // 4: generated.ResourceSelector + (*generated.ResourceIdentifier)(nil), // 5: generated.ResourceIdentifier +} +var file_itemblockaction_v1_ItemBlockAction_proto_depIdxs = []int32{ + 4, // 0: v1.ItemBlockActionAppliesToResponse.ResourceSelector:type_name -> generated.ResourceSelector + 5, // 1: v1.ItemBlockActionGetRelatedItemsResponse.relatedItems:type_name -> generated.ResourceIdentifier + 0, // 2: v1.ItemBlockAction.AppliesTo:input_type -> v1.ItemBlockActionAppliesToRequest + 2, // 3: v1.ItemBlockAction.GetRelatedItems:input_type -> v1.ItemBlockActionGetRelatedItemsRequest + 1, // 4: v1.ItemBlockAction.AppliesTo:output_type -> v1.ItemBlockActionAppliesToResponse + 3, // 5: v1.ItemBlockAction.GetRelatedItems:output_type -> v1.ItemBlockActionGetRelatedItemsResponse + 4, // [4:6] is the sub-list for method output_type + 2, // [2:4] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_itemblockaction_v1_ItemBlockAction_proto_init() } +func file_itemblockaction_v1_ItemBlockAction_proto_init() { + if File_itemblockaction_v1_ItemBlockAction_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionAppliesToRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionAppliesToResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionGetRelatedItemsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_itemblockaction_v1_ItemBlockAction_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ItemBlockActionGetRelatedItemsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_itemblockaction_v1_ItemBlockAction_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_itemblockaction_v1_ItemBlockAction_proto_goTypes, + DependencyIndexes: file_itemblockaction_v1_ItemBlockAction_proto_depIdxs, + MessageInfos: file_itemblockaction_v1_ItemBlockAction_proto_msgTypes, + }.Build() + File_itemblockaction_v1_ItemBlockAction_proto = out.File + file_itemblockaction_v1_ItemBlockAction_proto_rawDesc = nil + file_itemblockaction_v1_ItemBlockAction_proto_goTypes = nil + file_itemblockaction_v1_ItemBlockAction_proto_depIdxs = nil +} diff --git a/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go new file mode 100644 index 000000000..a83818d48 --- /dev/null +++ b/pkg/plugin/generated/itemblockaction/v1/ItemBlockAction_grpc.pb.go @@ -0,0 +1,144 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v4.25.2 +// source: itemblockaction/v1/ItemBlockAction.proto + +package v1 + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +const ( + ItemBlockAction_AppliesTo_FullMethodName = "/v1.ItemBlockAction/AppliesTo" + ItemBlockAction_GetRelatedItems_FullMethodName = "/v1.ItemBlockAction/GetRelatedItems" +) + +// ItemBlockActionClient is the client API for ItemBlockAction service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type ItemBlockActionClient interface { + AppliesTo(ctx context.Context, in *ItemBlockActionAppliesToRequest, opts ...grpc.CallOption) (*ItemBlockActionAppliesToResponse, error) + GetRelatedItems(ctx context.Context, in *ItemBlockActionGetRelatedItemsRequest, opts ...grpc.CallOption) (*ItemBlockActionGetRelatedItemsResponse, error) +} + +type itemBlockActionClient struct { + cc grpc.ClientConnInterface +} + +func NewItemBlockActionClient(cc grpc.ClientConnInterface) ItemBlockActionClient { + return &itemBlockActionClient{cc} +} + +func (c *itemBlockActionClient) AppliesTo(ctx context.Context, in *ItemBlockActionAppliesToRequest, opts ...grpc.CallOption) (*ItemBlockActionAppliesToResponse, error) { + out := new(ItemBlockActionAppliesToResponse) + err := c.cc.Invoke(ctx, ItemBlockAction_AppliesTo_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *itemBlockActionClient) GetRelatedItems(ctx context.Context, in *ItemBlockActionGetRelatedItemsRequest, opts ...grpc.CallOption) (*ItemBlockActionGetRelatedItemsResponse, error) { + out := new(ItemBlockActionGetRelatedItemsResponse) + err := c.cc.Invoke(ctx, ItemBlockAction_GetRelatedItems_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// ItemBlockActionServer is the server API for ItemBlockAction service. +// All implementations should embed UnimplementedItemBlockActionServer +// for forward compatibility +type ItemBlockActionServer interface { + AppliesTo(context.Context, *ItemBlockActionAppliesToRequest) (*ItemBlockActionAppliesToResponse, error) + GetRelatedItems(context.Context, *ItemBlockActionGetRelatedItemsRequest) (*ItemBlockActionGetRelatedItemsResponse, error) +} + +// UnimplementedItemBlockActionServer should be embedded to have forward compatible implementations. +type UnimplementedItemBlockActionServer struct { +} + +func (UnimplementedItemBlockActionServer) AppliesTo(context.Context, *ItemBlockActionAppliesToRequest) (*ItemBlockActionAppliesToResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method AppliesTo not implemented") +} +func (UnimplementedItemBlockActionServer) GetRelatedItems(context.Context, *ItemBlockActionGetRelatedItemsRequest) (*ItemBlockActionGetRelatedItemsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetRelatedItems not implemented") +} + +// UnsafeItemBlockActionServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to ItemBlockActionServer will +// result in compilation errors. +type UnsafeItemBlockActionServer interface { + mustEmbedUnimplementedItemBlockActionServer() +} + +func RegisterItemBlockActionServer(s grpc.ServiceRegistrar, srv ItemBlockActionServer) { + s.RegisterService(&ItemBlockAction_ServiceDesc, srv) +} + +func _ItemBlockAction_AppliesTo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ItemBlockActionAppliesToRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ItemBlockActionServer).AppliesTo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ItemBlockAction_AppliesTo_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ItemBlockActionServer).AppliesTo(ctx, req.(*ItemBlockActionAppliesToRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ItemBlockAction_GetRelatedItems_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ItemBlockActionGetRelatedItemsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ItemBlockActionServer).GetRelatedItems(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: ItemBlockAction_GetRelatedItems_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ItemBlockActionServer).GetRelatedItems(ctx, req.(*ItemBlockActionGetRelatedItemsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// ItemBlockAction_ServiceDesc is the grpc.ServiceDesc for ItemBlockAction service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var ItemBlockAction_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "v1.ItemBlockAction", + HandlerType: (*ItemBlockActionServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "AppliesTo", + Handler: _ItemBlockAction_AppliesTo_Handler, + }, + { + MethodName: "GetRelatedItems", + Handler: _ItemBlockAction_GetRelatedItems_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "itemblockaction/v1/ItemBlockAction.proto", +} diff --git a/pkg/plugin/mocks/manager.go b/pkg/plugin/mocks/manager.go index 8cb327474..c12510fdd 100644 --- a/pkg/plugin/mocks/manager.go +++ b/pkg/plugin/mocks/manager.go @@ -1,9 +1,11 @@ -// Code generated by mockery v1.0.0. DO NOT EDIT. +// Code generated by mockery v2.43.2. DO NOT EDIT. package mocks import ( mock "github.com/stretchr/testify/mock" + itemblockactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/itemblockaction/v1" + restoreitemactionv1 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v1" restoreitemactionv2 "github.com/vmware-tanzu/velero/pkg/plugin/velero/restoreitemaction/v2" @@ -31,7 +33,15 @@ func (_m *Manager) CleanupClients() { func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetBackupItemAction") + } + var r0 v1.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (v1.BackupItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) v1.BackupItemAction); ok { r0 = rf(name) } else { @@ -40,7 +50,6 @@ func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -54,7 +63,15 @@ func (_m *Manager) GetBackupItemAction(name string) (v1.BackupItemAction, error) func (_m *Manager) GetBackupItemActionV2(name string) (v2.BackupItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetBackupItemActionV2") + } + var r0 v2.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (v2.BackupItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) v2.BackupItemAction); ok { r0 = rf(name) } else { @@ -63,7 +80,6 @@ func (_m *Manager) GetBackupItemActionV2(name string) (v2.BackupItemAction, erro } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -77,7 +93,15 @@ func (_m *Manager) GetBackupItemActionV2(name string) (v2.BackupItemAction, erro func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetBackupItemActions") + } + var r0 []v1.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]v1.BackupItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []v1.BackupItemAction); ok { r0 = rf() } else { @@ -86,7 +110,6 @@ func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -100,7 +123,15 @@ func (_m *Manager) GetBackupItemActions() ([]v1.BackupItemAction, error) { func (_m *Manager) GetBackupItemActionsV2() ([]v2.BackupItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetBackupItemActionsV2") + } + var r0 []v2.BackupItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]v2.BackupItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []v2.BackupItemAction); ok { r0 = rf() } else { @@ -109,7 +140,6 @@ func (_m *Manager) GetBackupItemActionsV2() ([]v2.BackupItemAction, error) { } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -123,7 +153,15 @@ func (_m *Manager) GetBackupItemActionsV2() ([]v2.BackupItemAction, error) { func (_m *Manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetDeleteItemAction") + } + var r0 velero.DeleteItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (velero.DeleteItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) velero.DeleteItemAction); ok { r0 = rf(name) } else { @@ -132,7 +170,6 @@ func (_m *Manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, er } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -146,7 +183,15 @@ func (_m *Manager) GetDeleteItemAction(name string) (velero.DeleteItemAction, er func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetDeleteItemActions") + } + var r0 []velero.DeleteItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]velero.DeleteItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []velero.DeleteItemAction); ok { r0 = rf() } else { @@ -155,7 +200,66 @@ func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { } } + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetItemBlockAction provides a mock function with given fields: name +func (_m *Manager) GetItemBlockAction(name string) (itemblockactionv1.ItemBlockAction, error) { + ret := _m.Called(name) + + if len(ret) == 0 { + panic("no return value specified for GetItemBlockAction") + } + + var r0 itemblockactionv1.ItemBlockAction var r1 error + if rf, ok := ret.Get(0).(func(string) (itemblockactionv1.ItemBlockAction, error)); ok { + return rf(name) + } + if rf, ok := ret.Get(0).(func(string) itemblockactionv1.ItemBlockAction); ok { + r0 = rf(name) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(itemblockactionv1.ItemBlockAction) + } + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(name) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetItemBlockActions provides a mock function with given fields: +func (_m *Manager) GetItemBlockActions() ([]itemblockactionv1.ItemBlockAction, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetItemBlockActions") + } + + var r0 []itemblockactionv1.ItemBlockAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]itemblockactionv1.ItemBlockAction, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []itemblockactionv1.ItemBlockAction); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]itemblockactionv1.ItemBlockAction) + } + } + if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -169,7 +273,15 @@ func (_m *Manager) GetDeleteItemActions() ([]velero.DeleteItemAction, error) { func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetObjectStore") + } + var r0 velero.ObjectStore + var r1 error + if rf, ok := ret.Get(0).(func(string) (velero.ObjectStore, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) velero.ObjectStore); ok { r0 = rf(name) } else { @@ -178,7 +290,6 @@ func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -192,7 +303,15 @@ func (_m *Manager) GetObjectStore(name string) (velero.ObjectStore, error) { func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.RestoreItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemAction") + } + var r0 restoreitemactionv1.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (restoreitemactionv1.RestoreItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) restoreitemactionv1.RestoreItemAction); ok { r0 = rf(name) } else { @@ -201,7 +320,6 @@ func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.Restor } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -215,7 +333,15 @@ func (_m *Manager) GetRestoreItemAction(name string) (restoreitemactionv1.Restor func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.RestoreItemAction, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemActionV2") + } + var r0 restoreitemactionv2.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func(string) (restoreitemactionv2.RestoreItemAction, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) restoreitemactionv2.RestoreItemAction); ok { r0 = rf(name) } else { @@ -224,7 +350,6 @@ func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.Rest } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -238,7 +363,15 @@ func (_m *Manager) GetRestoreItemActionV2(name string) (restoreitemactionv2.Rest func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemActions") + } + var r0 []restoreitemactionv1.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]restoreitemactionv1.RestoreItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []restoreitemactionv1.RestoreItemAction); ok { r0 = rf() } else { @@ -247,7 +380,6 @@ func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAct } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -261,7 +393,15 @@ func (_m *Manager) GetRestoreItemActions() ([]restoreitemactionv1.RestoreItemAct func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemAction, error) { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetRestoreItemActionsV2") + } + var r0 []restoreitemactionv2.RestoreItemAction + var r1 error + if rf, ok := ret.Get(0).(func() ([]restoreitemactionv2.RestoreItemAction, error)); ok { + return rf() + } if rf, ok := ret.Get(0).(func() []restoreitemactionv2.RestoreItemAction); ok { r0 = rf() } else { @@ -270,7 +410,6 @@ func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemA } } - var r1 error if rf, ok := ret.Get(1).(func() error); ok { r1 = rf() } else { @@ -284,7 +423,15 @@ func (_m *Manager) GetRestoreItemActionsV2() ([]restoreitemactionv2.RestoreItemA func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.VolumeSnapshotter, error) { ret := _m.Called(name) + if len(ret) == 0 { + panic("no return value specified for GetVolumeSnapshotter") + } + var r0 volumesnapshotterv1.VolumeSnapshotter + var r1 error + if rf, ok := ret.Get(0).(func(string) (volumesnapshotterv1.VolumeSnapshotter, error)); ok { + return rf(name) + } if rf, ok := ret.Get(0).(func(string) volumesnapshotterv1.VolumeSnapshotter); ok { r0 = rf(name) } else { @@ -293,7 +440,6 @@ func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.Volume } } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(name) } else { @@ -302,3 +448,17 @@ func (_m *Manager) GetVolumeSnapshotter(name string) (volumesnapshotterv1.Volume return r0, r1 } + +// NewManager creates a new instance of Manager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewManager(t interface { + mock.TestingT + Cleanup(func()) +}) *Manager { + mock := &Manager{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto b/pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto new file mode 100644 index 000000000..5c0f86a3d --- /dev/null +++ b/pkg/plugin/proto/itemblockaction/v1/ItemBlockAction.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; +package v1; +option go_package = "github.com/vmware-tanzu/velero/pkg/plugin/generated/itemblockaction/v1"; + +import "Shared.proto"; + + +service ItemBlockAction { + rpc AppliesTo(ItemBlockActionAppliesToRequest) returns (ItemBlockActionAppliesToResponse); + rpc GetRelatedItems(ItemBlockActionGetRelatedItemsRequest) returns (ItemBlockActionGetRelatedItemsResponse); +} + +message ItemBlockActionAppliesToRequest { + string plugin = 1; +} + +message ItemBlockActionAppliesToResponse { + generated.ResourceSelector ResourceSelector = 1; +} + +message ItemBlockActionGetRelatedItemsRequest { + string plugin = 1; + bytes item = 2; + bytes backup = 3; +} + +message ItemBlockActionGetRelatedItemsResponse { + repeated generated.ResourceIdentifier relatedItems = 1; +} diff --git a/pkg/plugin/velero/itemblockaction/v1/item_block_action.go b/pkg/plugin/velero/itemblockaction/v1/item_block_action.go new file mode 100644 index 000000000..6d0133700 --- /dev/null +++ b/pkg/plugin/velero/itemblockaction/v1/item_block_action.go @@ -0,0 +1,46 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime" + + api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +// ItemBlockAction is an action that returns a list of related items that must be backed up +// along with the current item (and not in a separate parallel backup thread). +type ItemBlockAction interface { + // Name returns the name of this IBA. Plugins which implement this interface must define Name, + // but its content is unimportant, as it won't actually be called via RPC. Velero's plugin infrastructure + // will implement this directly rather than delegating to the RPC plugin in order to return the name + // that the plugin was registered under. The plugins must implement the method to complete the interface. + Name() string + + // AppliesTo returns information about which resources this action should be invoked for. + // A ItemBlockAction's GetRelatedItems function will only be invoked on items that match the returned + // selector. A zero-valued ResourceSelector matches all resources. + AppliesTo() (velero.ResourceSelector, error) + + // GetRelatedItems allows the ItemBlockAction to identify related items which must be backed up + // along with the current item. In many cases, these will be the same items that a related + // BackupItemAction's Execute method will return as additionalItems, but there may be differences. + // For example, items that are newly-created in the BIA Execute and don't yet exist at backup + // start will *not* be returned here. + GetRelatedItems(item runtime.Unstructured, backup *api.Backup) ([]velero.ResourceIdentifier, error) +} diff --git a/pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go b/pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go new file mode 100644 index 000000000..95a8d960a --- /dev/null +++ b/pkg/plugin/velero/mocks/itemblockaction/v1/ItemBlockAction.go @@ -0,0 +1,122 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by mockery v2.43.2. DO NOT EDIT. + +package v1 + +import ( + mock "github.com/stretchr/testify/mock" + runtime "k8s.io/apimachinery/pkg/runtime" + + velero "github.com/vmware-tanzu/velero/pkg/plugin/velero" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// ItemBlockAction is an autogenerated mock type for the ItemBlockAction type +type ItemBlockAction struct { + mock.Mock +} + +// AppliesTo provides a mock function with given fields: +func (_m *ItemBlockAction) AppliesTo() (velero.ResourceSelector, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for AppliesTo") + } + + var r0 velero.ResourceSelector + var r1 error + if rf, ok := ret.Get(0).(func() (velero.ResourceSelector, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() velero.ResourceSelector); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(velero.ResourceSelector) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetRelatedItems provides a mock function with given fields: item, backup +func (_m *ItemBlockAction) GetRelatedItems(item runtime.Unstructured, backup *velerov1.Backup) ([]velero.ResourceIdentifier, error) { + ret := _m.Called(item, backup) + + if len(ret) == 0 { + panic("no return value specified for GetRelatedItems") + } + + var r0 []velero.ResourceIdentifier + var r1 error + if rf, ok := ret.Get(0).(func(runtime.Unstructured, *velerov1.Backup) ([]velero.ResourceIdentifier, error)); ok { + return rf(item, backup) + } + if rf, ok := ret.Get(0).(func(runtime.Unstructured, *velerov1.Backup) []velero.ResourceIdentifier); ok { + r0 = rf(item, backup) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]velero.ResourceIdentifier) + } + } + + if rf, ok := ret.Get(1).(func(runtime.Unstructured, *velerov1.Backup) error); ok { + r1 = rf(item, backup) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Name provides a mock function with given fields: +func (_m *ItemBlockAction) Name() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Name") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// NewItemBlockAction creates a new instance of ItemBlockAction. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewItemBlockAction(t interface { + mock.TestingT + Cleanup(func()) +}) *ItemBlockAction { + mock := &ItemBlockAction{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 7b26673b29d02d50d1d3c9e25b0eefbfcbe313b1 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Tue, 30 Jul 2024 09:26:58 -0400 Subject: [PATCH 24/69] Move design/secrets.md to Implemented (#8060) Per https://github.com/vmware-tanzu/velero/issues/2425 multi credentials were implemented in #3190 Signed-off-by: Tiger Kaovilai --- design/{ => Implemented}/secrets.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename design/{ => Implemented}/secrets.md (100%) diff --git a/design/secrets.md b/design/Implemented/secrets.md similarity index 100% rename from design/secrets.md rename to design/Implemented/secrets.md From 86e54801c5f5df81c56ceffeb4d0d362eb9a7f55 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 30 Jul 2024 17:14:11 +0800 Subject: [PATCH 25/69] data mover micro service restore Signed-off-by: Lyndon-Li --- changelogs/unreleased/8061-Lyndon-Li | 1 + pkg/cmd/cli/datamover/restore.go | 195 +++++++++- pkg/cmd/cli/datamover/restore_test.go | 166 +++++++++ pkg/datamover/backup_micro_service.go | 6 +- pkg/datamover/backup_micro_service_test.go | 2 +- pkg/datamover/restore_micro_service.go | 295 +++++++++++++++ pkg/datamover/restore_micro_service_test.go | 394 ++++++++++++++++++++ 7 files changed, 1047 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/8061-Lyndon-Li create mode 100644 pkg/cmd/cli/datamover/restore_test.go create mode 100644 pkg/datamover/restore_micro_service.go create mode 100644 pkg/datamover/restore_micro_service_test.go diff --git a/changelogs/unreleased/8061-Lyndon-Li b/changelogs/unreleased/8061-Lyndon-Li new file mode 100644 index 000000000..64236059a --- /dev/null +++ b/changelogs/unreleased/8061-Lyndon-Li @@ -0,0 +1 @@ +Data mover micro service restore according to design #7576 \ No newline at end of file diff --git a/pkg/cmd/cli/datamover/restore.go b/pkg/cmd/cli/datamover/restore.go index ddd44729f..fbd92fa18 100644 --- a/pkg/cmd/cli/datamover/restore.go +++ b/pkg/cmd/cli/datamover/restore.go @@ -14,16 +14,37 @@ limitations under the License. package datamover import ( + "context" "fmt" + "os" "strings" "time" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/buildinfo" "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/cmd/util/signals" + "github.com/vmware-tanzu/velero/pkg/datamover" + "github.com/vmware-tanzu/velero/pkg/datapath" + "github.com/vmware-tanzu/velero/pkg/repository" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" + + ctlcache "sigs.k8s.io/controller-runtime/pkg/cache" + ctlclient "sigs.k8s.io/controller-runtime/pkg/client" ) type dataMoverRestoreConfig struct { @@ -52,7 +73,10 @@ func NewRestoreCommand(f client.Factory) *cobra.Command { logger.Infof("Starting Velero data-mover restore %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) - s := newdataMoverRestore(logger, config) + s, err := newdataMoverRestore(logger, f, config) + if err != nil { + exitWithMessage(logger, false, "Failed to create data mover restore, %v", err) + } s.run() }, @@ -74,19 +98,174 @@ func NewRestoreCommand(f client.Factory) *cobra.Command { } type dataMoverRestore struct { - logger logrus.FieldLogger - config dataMoverRestoreConfig + logger logrus.FieldLogger + ctx context.Context + cancelFunc context.CancelFunc + client ctlclient.Client + cache ctlcache.Cache + namespace string + nodeName string + config dataMoverRestoreConfig + kubeClient kubernetes.Interface + dataPathMgr *datapath.Manager } -func newdataMoverRestore(logger logrus.FieldLogger, config dataMoverRestoreConfig) *dataMoverRestore { - s := &dataMoverRestore{ - logger: logger, - config: config, +func newdataMoverRestore(logger logrus.FieldLogger, factory client.Factory, config dataMoverRestoreConfig) (*dataMoverRestore, error) { + ctx, cancelFunc := context.WithCancel(context.Background()) + + clientConfig, err := factory.ClientConfig() + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create client config") } - return s + ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + + scheme := runtime.NewScheme() + if err := velerov1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to add velero v1 scheme") + } + + if err := velerov2alpha1api.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to add velero v2alpha1 scheme") + } + + if err := v1.AddToScheme(scheme); err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to add core v1 scheme") + } + + nodeName := os.Getenv("NODE_NAME") + + // use a field selector to filter to only pods scheduled on this node. + cacheOption := ctlcache.Options{ + Scheme: scheme, + ByObject: map[ctlclient.Object]ctlcache.ByObject{ + &v1.Pod{}: { + Field: fields.Set{"spec.nodeName": nodeName}.AsSelector(), + }, + &velerov2alpha1api.DataDownload{}: { + Field: fields.Set{"metadata.namespace": factory.Namespace()}.AsSelector(), + }, + }, + } + + cli, err := ctlclient.New(clientConfig, ctlclient.Options{ + Scheme: scheme, + }) + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create client") + } + + cache, err := ctlcache.New(clientConfig, cacheOption) + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create client cache") + } + + s := &dataMoverRestore{ + logger: logger, + ctx: ctx, + cancelFunc: cancelFunc, + client: cli, + cache: cache, + config: config, + namespace: factory.Namespace(), + nodeName: nodeName, + } + + s.kubeClient, err = factory.KubeClient() + if err != nil { + cancelFunc() + return nil, errors.Wrap(err, "error to create kube client") + } + + s.dataPathMgr = datapath.NewManager(1) + + return s, nil } +var funcCreateDataPathRestore = (*dataMoverRestore).createDataPathService + func (s *dataMoverRestore) run() { + signals.CancelOnShutdown(s.cancelFunc, s.logger) + go func() { + if err := s.cache.Start(s.ctx); err != nil { + s.logger.WithError(err).Warn("error starting cache") + } + }() + + // TODOOO: call s.runDataPath() time.Sleep(time.Duration(1<<63 - 1)) } + +func (s *dataMoverRestore) runDataPath() { + s.logger.Infof("Starting micro service in node %s for dd %s", s.nodeName, s.config.ddName) + + dpService, err := funcCreateDataPathRestore(s) + if err != nil { + s.cancelFunc() + funcExitWithMessage(s.logger, false, "Failed to create data path service for DataDownload %s: %v", s.config.ddName, err) + return + } + + s.logger.Infof("Starting data path service %s", s.config.ddName) + + err = dpService.Init() + if err != nil { + s.cancelFunc() + funcExitWithMessage(s.logger, false, "Failed to init data path service for DataDownload %s: %v", s.config.ddName, err) + return + } + + result, err := dpService.RunCancelableDataPath(s.ctx) + if err != nil { + s.cancelFunc() + funcExitWithMessage(s.logger, false, "Failed to run data path service for DataDownload %s: %v", s.config.ddName, err) + return + } + + s.logger.WithField("dd", s.config.ddName).Info("Data path service completed") + + dpService.Shutdown() + + s.logger.WithField("dd", s.config.ddName).Info("Data path service is shut down") + + s.cancelFunc() + + funcExitWithMessage(s.logger, true, result) +} + +func (s *dataMoverRestore) createDataPathService() (dataPathService, error) { + credentialFileStore, err := funcNewCredentialFileStore( + s.client, + s.namespace, + defaultCredentialsDirectory, + filesystem.NewFileSystem(), + ) + if err != nil { + return nil, errors.Wrapf(err, "error to create credential file store") + } + + credSecretStore, err := funcNewCredentialSecretStore(s.client, s.namespace) + if err != nil { + return nil, errors.Wrapf(err, "error to create credential secret store") + } + + credGetter := &credentials.CredentialGetter{FromFile: credentialFileStore, FromSecret: credSecretStore} + + duInformer, err := s.cache.GetInformer(s.ctx, &velerov2alpha1api.DataDownload{}) + if err != nil { + return nil, errors.Wrap(err, "error to get controller-runtime informer from manager") + } + + repoEnsurer := repository.NewEnsurer(s.client, s.logger, s.config.resourceTimeout) + + return datamover.NewRestoreMicroService(s.ctx, s.client, s.kubeClient, s.config.ddName, s.namespace, s.nodeName, datapath.AccessPoint{ + ByPath: s.config.volumePath, + VolMode: uploader.PersistentVolumeMode(s.config.volumeMode), + }, s.dataPathMgr, repoEnsurer, credGetter, duInformer, s.logger), nil +} diff --git a/pkg/cmd/cli/datamover/restore_test.go b/pkg/cmd/cli/datamover/restore_test.go new file mode 100644 index 000000000..664c2bdbc --- /dev/null +++ b/pkg/cmd/cli/datamover/restore_test.go @@ -0,0 +1,166 @@ +/* +Copyright The Velero Contributors. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + cacheMock "github.com/vmware-tanzu/velero/pkg/cmd/cli/datamover/mocks" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func fakeCreateDataPathRestoreWithErr(_ *dataMoverRestore) (dataPathService, error) { + return nil, errors.New("fake-create-data-path-error") +} + +func fakeCreateDataPathRestore(_ *dataMoverRestore) (dataPathService, error) { + return frHelper, nil +} + +func TestRunDataPathRestore(t *testing.T) { + tests := []struct { + name string + ddName string + createDataPathFail bool + initDataPathErr error + runCancelableDataPathErr error + runCancelableDataPathResult string + expectedMessage string + expectedSucceed bool + }{ + { + name: "create data path failed", + ddName: "fake-name", + createDataPathFail: true, + expectedMessage: "Failed to create data path service for DataDownload fake-name: fake-create-data-path-error", + }, + { + name: "init data path failed", + ddName: "fake-name", + initDataPathErr: errors.New("fake-init-data-path-error"), + expectedMessage: "Failed to init data path service for DataDownload fake-name: fake-init-data-path-error", + }, + { + name: "run data path failed", + ddName: "fake-name", + runCancelableDataPathErr: errors.New("fake-run-data-path-error"), + expectedMessage: "Failed to run data path service for DataDownload fake-name: fake-run-data-path-error", + }, + { + name: "succeed", + ddName: "fake-name", + runCancelableDataPathResult: "fake-run-data-path-result", + expectedMessage: "fake-run-data-path-result", + expectedSucceed: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + frHelper = &fakeRunHelper{ + initErr: test.initDataPathErr, + runCancelableDataPathErr: test.runCancelableDataPathErr, + runCancelableDataPathResult: test.runCancelableDataPathResult, + } + + if test.createDataPathFail { + funcCreateDataPathRestore = fakeCreateDataPathRestoreWithErr + } else { + funcCreateDataPathRestore = fakeCreateDataPathRestore + } + + funcExitWithMessage = frHelper.ExitWithMessage + + s := &dataMoverRestore{ + logger: velerotest.NewLogger(), + cancelFunc: func() {}, + config: dataMoverRestoreConfig{ + ddName: test.ddName, + }, + } + + s.runDataPath() + + assert.Equal(t, test.expectedMessage, frHelper.exitMessage) + assert.Equal(t, test.expectedSucceed, frHelper.succeed) + }) + } +} + +func TestCreateDataPathRestore(t *testing.T) { + tests := []struct { + name string + fileStoreErr error + secretStoreErr error + mockGetInformer bool + getInformerErr error + expectedError string + }{ + { + name: "create credential file store error", + fileStoreErr: errors.New("fake-file-store-error"), + expectedError: "error to create credential file store: fake-file-store-error", + }, + { + name: "create credential secret store", + secretStoreErr: errors.New("fake-secret-store-error"), + expectedError: "error to create credential secret store: fake-secret-store-error", + }, + { + name: "get informer error", + mockGetInformer: true, + getInformerErr: errors.New("fake-get-informer-error"), + expectedError: "error to get controller-runtime informer from manager: fake-get-informer-error", + }, + { + name: "succeed", + mockGetInformer: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fcHelper := &fakeCreateDataPathServiceHelper{ + fileStoreErr: test.fileStoreErr, + secretStoreErr: test.secretStoreErr, + } + + funcNewCredentialFileStore = fcHelper.NewNamespacedFileStore + funcNewCredentialSecretStore = fcHelper.NewNamespacedSecretStore + + cache := cacheMock.NewCache(t) + if test.mockGetInformer { + cache.On("GetInformer", mock.Anything, mock.Anything).Return(nil, test.getInformerErr) + } + + funcExitWithMessage = frHelper.ExitWithMessage + + s := &dataMoverRestore{ + cache: cache, + } + + _, err := s.createDataPathService() + + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/pkg/datamover/backup_micro_service.go b/pkg/datamover/backup_micro_service.go index 2f4fa6e56..ee594cac9 100644 --- a/pkg/datamover/backup_micro_service.go +++ b/pkg/datamover/backup_micro_service.go @@ -127,7 +127,7 @@ func (r *BackupMicroService) Init() error { return err } -var waitDuTimeout time.Duration = time.Minute * 2 +var waitControllerTimeout time.Duration = time.Minute * 2 func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string, error) { log := r.logger.WithFields(logrus.Fields{ @@ -135,7 +135,7 @@ func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string, }) du := &velerov2alpha1api.DataUpload{} - err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, waitDuTimeout, true, func(ctx context.Context) (bool, error) { + err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, waitControllerTimeout, true, func(ctx context.Context) (bool, error) { err := r.client.Get(ctx, types.NamespacedName{ Namespace: r.namespace, Name: r.dataUploadName, @@ -313,7 +313,7 @@ func (r *BackupMicroService) closeDataPath(ctx context.Context, duName string) { func (r *BackupMicroService) cancelDataUpload(du *velerov2alpha1api.DataUpload) { r.logger.WithField("DataUpload", du.Name).Info("Data upload is being canceled") - r.eventRecorder.Event(du, false, "Canceling", "Canceing for data upload %s", du.Name) + r.eventRecorder.Event(du, false, "Canceling", "Canceling for data upload %s", du.Name) fsBackup := r.dataPathMgr.GetAsyncBR(du.Name) if fsBackup == nil { diff --git a/pkg/datamover/backup_micro_service_test.go b/pkg/datamover/backup_micro_service_test.go index 8fd595e93..9db38d4c0 100644 --- a/pkg/datamover/backup_micro_service_test.go +++ b/pkg/datamover/backup_micro_service_test.go @@ -412,7 +412,7 @@ func TestRunCancelableDataPath(t *testing.T) { return fsBR } - waitDuTimeout = time.Second + waitControllerTimeout = time.Second if test.result != nil { go func() { diff --git a/pkg/datamover/restore_micro_service.go b/pkg/datamover/restore_micro_service.go new file mode 100644 index 000000000..508469701 --- /dev/null +++ b/pkg/datamover/restore_micro_service.go @@ -0,0 +1,295 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "context" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + "github.com/vmware-tanzu/velero/pkg/datapath" + "github.com/vmware-tanzu/velero/pkg/repository" + "github.com/vmware-tanzu/velero/pkg/uploader" + "github.com/vmware-tanzu/velero/pkg/util/kube" + + cachetool "k8s.io/client-go/tools/cache" +) + +// RestoreMicroService process data mover restores inside the restore pod +type RestoreMicroService struct { + ctx context.Context + client client.Client + kubeClient kubernetes.Interface + repoEnsurer *repository.Ensurer + credentialGetter *credentials.CredentialGetter + logger logrus.FieldLogger + dataPathMgr *datapath.Manager + eventRecorder kube.EventRecorder + + namespace string + dataDownloadName string + dataDownload *velerov2alpha1api.DataDownload + sourceTargetPath datapath.AccessPoint + + resultSignal chan dataPathResult + + ddInformer cache.Informer + ddHandler cachetool.ResourceEventHandlerRegistration + nodeName string +} + +func NewRestoreMicroService(ctx context.Context, client client.Client, kubeClient kubernetes.Interface, dataDownloadName string, namespace string, nodeName string, + sourceTargetPath datapath.AccessPoint, dataPathMgr *datapath.Manager, repoEnsurer *repository.Ensurer, cred *credentials.CredentialGetter, + ddInformer cache.Informer, log logrus.FieldLogger) *RestoreMicroService { + return &RestoreMicroService{ + ctx: ctx, + client: client, + kubeClient: kubeClient, + credentialGetter: cred, + logger: log, + repoEnsurer: repoEnsurer, + dataPathMgr: dataPathMgr, + namespace: namespace, + dataDownloadName: dataDownloadName, + sourceTargetPath: sourceTargetPath, + nodeName: nodeName, + resultSignal: make(chan dataPathResult), + ddInformer: ddInformer, + } +} + +func (r *RestoreMicroService) Init() error { + r.eventRecorder = kube.NewEventRecorder(r.kubeClient, r.client.Scheme(), r.dataDownloadName, r.nodeName) + + handler, err := r.ddInformer.AddEventHandler( + cachetool.ResourceEventHandlerFuncs{ + UpdateFunc: func(oldObj interface{}, newObj interface{}) { + oldDd := oldObj.(*velerov2alpha1api.DataDownload) + newDd := newObj.(*velerov2alpha1api.DataDownload) + + if newDd.Name != r.dataDownloadName { + return + } + + if newDd.Status.Phase != velerov2alpha1api.DataDownloadPhaseInProgress { + return + } + + if newDd.Spec.Cancel && !oldDd.Spec.Cancel { + r.cancelDataDownload(newDd) + } + }, + }, + ) + + if err != nil { + return errors.Wrap(err, "error adding dd handler") + } + + r.ddHandler = handler + + return err +} + +func (r *RestoreMicroService) RunCancelableDataPath(ctx context.Context) (string, error) { + log := r.logger.WithFields(logrus.Fields{ + "datadownload": r.dataDownloadName, + }) + + dd := &velerov2alpha1api.DataDownload{} + err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, waitControllerTimeout, true, func(ctx context.Context) (bool, error) { + err := r.client.Get(ctx, types.NamespacedName{ + Namespace: r.namespace, + Name: r.dataDownloadName, + }, dd) + if apierrors.IsNotFound(err) { + return false, nil + } + + if err != nil { + return true, errors.Wrapf(err, "error to get dd %s", r.dataDownloadName) + } + + if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseInProgress { + return true, nil + } else { + return false, nil + } + }) + if err != nil { + log.WithError(err).Error("Failed to wait dd") + return "", errors.Wrap(err, "error waiting for dd") + } + + r.dataDownload = dd + + log.Info("Run cancelable dataDownload") + + callbacks := datapath.Callbacks{ + OnCompleted: r.OnDataDownloadCompleted, + OnFailed: r.OnDataDownloadFailed, + OnCancelled: r.OnDataDownloadCancelled, + OnProgress: r.OnDataDownloadProgress, + } + + fsRestore, err := r.dataPathMgr.CreateFileSystemBR(dd.Name, dataUploadDownloadRequestor, ctx, r.client, dd.Namespace, callbacks, log) + if err != nil { + return "", errors.Wrap(err, "error to create data path") + } + + log.Debug("Found volume path") + if err := fsRestore.Init(ctx, + &datapath.FSBRInitParam{ + BSLName: dd.Spec.BackupStorageLocation, + SourceNamespace: dd.Spec.SourceNamespace, + UploaderType: GetUploaderType(dd.Spec.DataMover), + RepositoryType: velerov1api.BackupRepositoryTypeKopia, + RepoIdentifier: "", + RepositoryEnsurer: r.repoEnsurer, + CredentialGetter: r.credentialGetter, + }); err != nil { + return "", errors.Wrap(err, "error to initialize data path") + } + log.Info("fs init") + + if err := fsRestore.StartRestore(dd.Spec.SnapshotID, r.sourceTargetPath, dd.Spec.DataMoverConfig); err != nil { + return "", errors.Wrap(err, "error starting data path restore") + } + + log.Info("Async fs restore data path started") + r.eventRecorder.Event(dd, false, datapath.EventReasonStarted, "Data path for %s started", dd.Name) + + result := "" + select { + case <-ctx.Done(): + err = errors.New("timed out waiting for fs restore to complete") + break + case res := <-r.resultSignal: + err = res.err + result = res.result + break + } + + if err != nil { + log.WithError(err).Error("Async fs restore was not completed") + } + + return result, err +} + +func (r *RestoreMicroService) Shutdown() { + r.eventRecorder.Shutdown() + r.closeDataPath(r.ctx, r.dataDownloadName) + + if r.ddHandler != nil { + if err := r.ddInformer.RemoveEventHandler(r.ddHandler); err != nil { + r.logger.WithError(err).Warn("Failed to remove pod handler") + } + } +} + +func (r *RestoreMicroService) OnDataDownloadCompleted(ctx context.Context, namespace string, ddName string, result datapath.Result) { + defer r.closeDataPath(ctx, ddName) + + log := r.logger.WithField("datadownload", ddName) + + restoreBytes, err := funcMarshal(result.Restore) + if err != nil { + log.WithError(err).Errorf("Failed to marshal restore result %v", result.Restore) + r.resultSignal <- dataPathResult{ + err: errors.Wrapf(err, "Failed to marshal restore result %v", result.Restore), + } + } else { + r.eventRecorder.Event(r.dataDownload, false, datapath.EventReasonCompleted, string(restoreBytes)) + r.resultSignal <- dataPathResult{ + result: string(restoreBytes), + } + } + + log.Info("Async fs restore data path completed") +} + +func (r *RestoreMicroService) OnDataDownloadFailed(ctx context.Context, namespace string, ddName string, err error) { + defer r.closeDataPath(ctx, ddName) + + log := r.logger.WithField("datadownload", ddName) + log.WithError(err).Error("Async fs restore data path failed") + + r.eventRecorder.Event(r.dataDownload, false, datapath.EventReasonFailed, "Data path for data download %s failed, error %v", r.dataDownloadName, err) + r.resultSignal <- dataPathResult{ + err: errors.Wrapf(err, "Data path for data download %s failed", r.dataDownloadName), + } +} + +func (r *RestoreMicroService) OnDataDownloadCancelled(ctx context.Context, namespace string, ddName string) { + defer r.closeDataPath(ctx, ddName) + + log := r.logger.WithField("datadownload", ddName) + log.Warn("Async fs restore data path canceled") + + r.eventRecorder.Event(r.dataDownload, false, datapath.EventReasonCancelled, "Data path for data download %s canceled", ddName) + r.resultSignal <- dataPathResult{ + err: errors.New(datapath.ErrCancelled), + } +} + +func (r *RestoreMicroService) OnDataDownloadProgress(ctx context.Context, namespace string, ddName string, progress *uploader.Progress) { + log := r.logger.WithFields(logrus.Fields{ + "datadownload": ddName, + }) + + progressBytes, err := funcMarshal(progress) + if err != nil { + log.WithError(err).Errorf("Failed to marshal progress %v", progress) + return + } + + r.eventRecorder.Event(r.dataDownload, false, datapath.EventReasonProgress, string(progressBytes)) +} + +func (r *RestoreMicroService) closeDataPath(ctx context.Context, ddName string) { + fsRestore := r.dataPathMgr.GetAsyncBR(ddName) + if fsRestore != nil { + fsRestore.Close(ctx) + } + + r.dataPathMgr.RemoveAsyncBR(ddName) +} + +func (r *RestoreMicroService) cancelDataDownload(dd *velerov2alpha1api.DataDownload) { + r.logger.WithField("DataDownload", dd.Name).Info("Data download is being canceled") + + r.eventRecorder.Event(dd, false, "Canceling", "Canceling for data download %s", dd.Name) + + fsBackup := r.dataPathMgr.GetAsyncBR(dd.Name) + if fsBackup == nil { + r.OnDataDownloadCancelled(r.ctx, dd.GetNamespace(), dd.GetName()) + } else { + fsBackup.Cancel() + } +} diff --git a/pkg/datamover/restore_micro_service_test.go b/pkg/datamover/restore_micro_service_test.go new file mode 100644 index 000000000..e3ef8701d --- /dev/null +++ b/pkg/datamover/restore_micro_service_test.go @@ -0,0 +1,394 @@ +/* +Copyright The Velero Contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package datamover + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "k8s.io/apimachinery/pkg/runtime" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake" + + "github.com/vmware-tanzu/velero/pkg/builder" + "github.com/vmware-tanzu/velero/pkg/datapath" + datapathmockes "github.com/vmware-tanzu/velero/pkg/datapath/mocks" + "github.com/vmware-tanzu/velero/pkg/uploader" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" + + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestOnDataDownloadFailed(t *testing.T) { + dataDownloadName := "fake-data-download" + bt := &backupMsTestHelper{} + + bs := &RestoreMicroService{ + dataDownloadName: dataDownloadName, + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + expectedErr := "Data path for data download fake-data-download failed: fake-error" + expectedEventReason := datapath.EventReasonFailed + expectedEventMsg := "Data path for data download fake-data-download failed, error fake-error" + + go bs.OnDataDownloadFailed(context.TODO(), velerov1api.DefaultNamespace, dataDownloadName, errors.New("fake-error")) + + result := <-bs.resultSignal + assert.EqualError(t, result.err, expectedErr) + assert.Equal(t, expectedEventReason, bt.EventReason()) + assert.Equal(t, expectedEventMsg, bt.EventMessage()) +} + +func TestOnDataDownloadCancelled(t *testing.T) { + dataDownloadName := "fake-data-download" + bt := &backupMsTestHelper{} + + bs := &RestoreMicroService{ + dataDownloadName: dataDownloadName, + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + expectedErr := datapath.ErrCancelled + expectedEventReason := datapath.EventReasonCancelled + expectedEventMsg := "Data path for data download fake-data-download canceled" + + go bs.OnDataDownloadCancelled(context.TODO(), velerov1api.DefaultNamespace, dataDownloadName) + + result := <-bs.resultSignal + assert.EqualError(t, result.err, expectedErr) + assert.Equal(t, expectedEventReason, bt.EventReason()) + assert.Equal(t, expectedEventMsg, bt.EventMessage()) +} + +func TestOnDataDownloadCompleted(t *testing.T) { + tests := []struct { + name string + expectedErr string + expectedEventReason string + expectedEventMsg string + marshalErr error + marshallStr string + }{ + { + name: "marshal fail", + marshalErr: errors.New("fake-marshal-error"), + expectedErr: "Failed to marshal restore result {{ }}: fake-marshal-error", + }, + { + name: "succeed", + marshallStr: "fake-complete-string", + expectedEventReason: datapath.EventReasonCompleted, + expectedEventMsg: "fake-complete-string", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + dataDownloadName := "fake-data-download" + + bt := &backupMsTestHelper{ + marshalErr: test.marshalErr, + marshalBytes: []byte(test.marshallStr), + } + + bs := &RestoreMicroService{ + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + funcMarshal = bt.Marshal + + go bs.OnDataDownloadCompleted(context.TODO(), velerov1api.DefaultNamespace, dataDownloadName, datapath.Result{}) + + result := <-bs.resultSignal + if test.marshalErr != nil { + assert.EqualError(t, result.err, test.expectedErr) + } else { + assert.NoError(t, result.err) + assert.Equal(t, test.expectedEventReason, bt.EventReason()) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + } + }) + } +} + +func TestOnDataDownloadProgress(t *testing.T) { + tests := []struct { + name string + expectedEventReason string + expectedEventMsg string + marshalErr error + marshallStr string + }{ + { + name: "marshal fail", + marshalErr: errors.New("fake-marshal-error"), + }, + { + name: "succeed", + marshallStr: "fake-progress-string", + expectedEventReason: datapath.EventReasonProgress, + expectedEventMsg: "fake-progress-string", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + dataDownloadName := "fake-data-download" + + bt := &backupMsTestHelper{ + marshalErr: test.marshalErr, + marshalBytes: []byte(test.marshallStr), + } + + bs := &RestoreMicroService{ + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + logger: velerotest.NewLogger(), + } + + funcMarshal = bt.Marshal + + bs.OnDataDownloadProgress(context.TODO(), velerov1api.DefaultNamespace, dataDownloadName, &uploader.Progress{}) + + if test.marshalErr != nil { + assert.False(t, bt.withEvent) + } else { + assert.True(t, bt.withEvent) + assert.Equal(t, test.expectedEventReason, bt.EventReason()) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + } + }) + } +} + +func TestCancelDataDownload(t *testing.T) { + tests := []struct { + name string + expectedEventReason string + expectedEventMsg string + expectedErr string + }{ + { + name: "no fs restore", + expectedEventReason: datapath.EventReasonCancelled, + expectedEventMsg: "Data path for data download fake-data-download canceled", + expectedErr: datapath.ErrCancelled, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + dataDownloadName := "fake-data-download" + dd := builder.ForDataDownload(velerov1api.DefaultNamespace, dataDownloadName).Result() + + bt := &backupMsTestHelper{} + + bs := &RestoreMicroService{ + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + go bs.cancelDataDownload(dd) + + result := <-bs.resultSignal + + assert.EqualError(t, result.err, test.expectedErr) + assert.True(t, bt.withEvent) + assert.Equal(t, test.expectedEventReason, bt.EventReason()) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + }) + } +} + +func TestRunCancelableRestore(t *testing.T) { + dataDownloadName := "fake-data-download" + dd := builder.ForDataDownload(velerov1api.DefaultNamespace, dataDownloadName).Phase(velerov2alpha1api.DataDownloadPhaseNew).Result() + ddInProgress := builder.ForDataDownload(velerov1api.DefaultNamespace, dataDownloadName).Phase(velerov2alpha1api.DataDownloadPhaseInProgress).Result() + ctxTimeout, cancel := context.WithTimeout(context.Background(), time.Second) + + tests := []struct { + name string + ctx context.Context + result *dataPathResult + dataPathMgr *datapath.Manager + kubeClientObj []runtime.Object + initErr error + startErr error + dataPathStarted bool + expectedEventMsg string + expectedErr string + }{ + { + name: "no dd", + ctx: context.Background(), + expectedErr: "error waiting for dd: context deadline exceeded", + }, + { + name: "dd not in in-progress", + ctx: context.Background(), + kubeClientObj: []runtime.Object{dd}, + expectedErr: "error waiting for dd: context deadline exceeded", + }, + { + name: "create data path fail", + ctx: context.Background(), + kubeClientObj: []runtime.Object{ddInProgress}, + dataPathMgr: datapath.NewManager(0), + expectedErr: "error to create data path: Concurrent number exceeds", + }, + { + name: "init data path fail", + ctx: context.Background(), + kubeClientObj: []runtime.Object{ddInProgress}, + initErr: errors.New("fake-init-error"), + expectedErr: "error to initialize data path: fake-init-error", + }, + { + name: "start data path fail", + ctx: context.Background(), + kubeClientObj: []runtime.Object{ddInProgress}, + startErr: errors.New("fake-start-error"), + expectedErr: "error starting data path restore: fake-start-error", + }, + { + name: "data path timeout", + ctx: ctxTimeout, + kubeClientObj: []runtime.Object{ddInProgress}, + dataPathStarted: true, + expectedEventMsg: fmt.Sprintf("Data path for %s started", dataDownloadName), + expectedErr: "timed out waiting for fs restore to complete", + }, + { + name: "data path returns error", + ctx: context.Background(), + kubeClientObj: []runtime.Object{ddInProgress}, + dataPathStarted: true, + result: &dataPathResult{ + err: errors.New("fake-data-path-error"), + }, + expectedEventMsg: fmt.Sprintf("Data path for %s started", dataDownloadName), + expectedErr: "fake-data-path-error", + }, + { + name: "succeed", + ctx: context.Background(), + kubeClientObj: []runtime.Object{ddInProgress}, + dataPathStarted: true, + result: &dataPathResult{ + result: "fake-succeed-result", + }, + expectedEventMsg: fmt.Sprintf("Data path for %s started", dataDownloadName), + }, + } + + scheme := runtime.NewScheme() + velerov2alpha1api.AddToScheme(scheme) + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fakeClientBuilder := clientFake.NewClientBuilder() + fakeClientBuilder = fakeClientBuilder.WithScheme(scheme) + + fakeClient := fakeClientBuilder.WithRuntimeObjects(test.kubeClientObj...).Build() + + bt := &backupMsTestHelper{} + + rs := &RestoreMicroService{ + namespace: velerov1api.DefaultNamespace, + dataDownloadName: dataDownloadName, + ctx: context.Background(), + client: fakeClient, + dataPathMgr: datapath.NewManager(1), + eventRecorder: bt, + resultSignal: make(chan dataPathResult), + logger: velerotest.NewLogger(), + } + + if test.ctx != nil { + rs.ctx = test.ctx + } + + if test.dataPathMgr != nil { + rs.dataPathMgr = test.dataPathMgr + } + + datapath.FSBRCreator = func(string, string, kbclient.Client, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { + fsBR := datapathmockes.NewAsyncBR(t) + if test.initErr != nil { + fsBR.On("Init", mock.Anything, mock.Anything).Return(test.initErr) + } + + if test.startErr != nil { + fsBR.On("Init", mock.Anything, mock.Anything).Return(nil) + fsBR.On("StartRestore", mock.Anything, mock.Anything, mock.Anything).Return(test.startErr) + } + + if test.dataPathStarted { + fsBR.On("Init", mock.Anything, mock.Anything).Return(nil) + fsBR.On("StartRestore", mock.Anything, mock.Anything, mock.Anything).Return(nil) + } + + return fsBR + } + + waitControllerTimeout = time.Second + + if test.result != nil { + go func() { + time.Sleep(time.Millisecond * 500) + rs.resultSignal <- *test.result + }() + } + + result, err := rs.RunCancelableDataPath(test.ctx) + + if test.expectedErr != "" { + assert.EqualError(t, err, test.expectedErr) + } else { + assert.NoError(t, err) + assert.Equal(t, test.result.result, result) + } + + if test.expectedEventMsg != "" { + assert.True(t, bt.withEvent) + assert.Equal(t, test.expectedEventMsg, bt.EventMessage()) + } + }) + } + + cancel() +} From d48e9762eb952159ab15173dad871e6322e5bee0 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 31 Jul 2024 11:52:26 +0800 Subject: [PATCH 26/69] data mover ms new controller Signed-off-by: Lyndon-Li --- pkg/builder/data_download_builder.go | 6 ++ pkg/cmd/cli/datamover/backup.go | 3 +- pkg/cmd/cli/nodeagent/server.go | 6 +- pkg/controller/data_download_controller.go | 59 +++++++--------- .../data_download_controller_test.go | 26 +++---- pkg/controller/data_upload_controller.go | 68 +++++++------------ pkg/controller/data_upload_controller_test.go | 8 ++- pkg/datamover/backup_micro_service.go | 2 +- pkg/util/logging/default_logger.go | 7 ++ pkg/util/logging/default_logger_test.go | 13 ++++ 10 files changed, 97 insertions(+), 101 deletions(-) diff --git a/pkg/builder/data_download_builder.go b/pkg/builder/data_download_builder.go index 9a85c7905..51dd90e06 100644 --- a/pkg/builder/data_download_builder.go +++ b/pkg/builder/data_download_builder.go @@ -111,6 +111,12 @@ func (d *DataDownloadBuilder) ObjectMeta(opts ...ObjectMetaOpt) *DataDownloadBui return d } +// Labels sets the DataDownload's Labels. +func (d *DataDownloadBuilder) Labels(labels map[string]string) *DataDownloadBuilder { + d.object.Labels = labels + return d +} + // StartTimestamp sets the DataDownload's StartTimestamp. func (d *DataDownloadBuilder) StartTimestamp(startTime *metav1.Time) *DataDownloadBuilder { d.object.Status.StartTimestamp = startTime diff --git a/pkg/cmd/cli/datamover/backup.go b/pkg/cmd/cli/datamover/backup.go index 2027b0893..35f483d92 100644 --- a/pkg/cmd/cli/datamover/backup.go +++ b/pkg/cmd/cli/datamover/backup.go @@ -207,8 +207,7 @@ func (s *dataMoverBackup) run() { } }() - // TODOOO: call s.runDataPath() - time.Sleep(time.Duration(1<<63 - 1)) + s.runDataPath() } func (s *dataMoverBackup) runDataPath() { diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 61dd0b006..e19e1ab7f 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -104,7 +104,7 @@ func NewServerCommand(f client.Factory) *cobra.Command { logLevel := logLevelFlag.Parse() logrus.Infof("Setting log-level to %s", strings.ToUpper(logLevel.String())) - logger := logging.DefaultLogger(logLevel, formatFlag.Parse()) + logger := logging.DefaultMergeLogger(logLevel, formatFlag.Parse()) logger.Infof("Starting Velero node-agent server %s (%s)", buildinfo.Version, buildinfo.FormattedGitSHA()) f.SetBasename(fmt.Sprintf("%s-%s", c.Parent().Name(), c.Name())) @@ -292,13 +292,13 @@ func (s *nodeAgentServer) run() { if s.dataPathConfigs != nil && len(s.dataPathConfigs.LoadAffinity) > 0 { loadAffinity = s.dataPathConfigs.LoadAffinity[0] } - dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) s.attemptDataUploadResume(dataUploadReconciler) if err = dataUploadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data upload controller") } - dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.kubeClient, s.dataPathMgr, repoEnsurer, credentialGetter, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, repoEnsurer, credentialGetter, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) s.attemptDataDownloadResume(dataDownloadReconciler) if err = dataDownloadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data download controller") diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index c8e8cca50..bf3c8e7df 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -56,6 +57,7 @@ import ( type DataDownloadReconciler struct { client client.Client kubeClient kubernetes.Interface + mgr manager.Manager logger logrus.FieldLogger credentialGetter *credentials.CredentialGetter fileSystem filesystem.Interface @@ -68,11 +70,12 @@ type DataDownloadReconciler struct { metrics *metrics.ServerMetrics } -func NewDataDownloadReconciler(client client.Client, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, +func NewDataDownloadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, repoEnsurer *repository.Ensurer, credentialGetter *credentials.CredentialGetter, nodeName string, preparingTimeout time.Duration, logger logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataDownloadReconciler { return &DataDownloadReconciler{ client: client, kubeClient: kubeClient, + mgr: mgr, logger: logger.WithField("controller", "DataDownload"), credentialGetter: credentialGetter, fileSystem: filesystem.NewFileSystem(), @@ -234,9 +237,9 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, nil } - fsRestore := r.dataPathMgr.GetAsyncBR(dd.Name) + asyncBR := r.dataPathMgr.GetAsyncBR(dd.Name) - if fsRestore != nil { + if asyncBR != nil { log.Info("Cancellable data path is already started") return ctrl.Result{}, nil } @@ -259,7 +262,8 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request OnProgress: r.OnDataDownloadProgress, } - fsRestore, err = r.dataPathMgr.CreateFileSystemBR(dd.Name, dataUploadDownloadRequestor, ctx, r.client, dd.Namespace, callbacks, log) + asyncBR, err = r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, r.kubeClient, r.mgr, datapath.TaskTypeRestore, + dd.Name, dd.Namespace, result.ByPod.HostingPod.Name, result.ByPod.HostingContainer, dd.Name, callbacks, false, log) if err != nil { if err == datapath.ConcurrentLimitExceed { log.Info("Data path instance is concurrent limited requeue later") @@ -279,7 +283,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request log.Info("Data download is marked as in progress") - reconcileResult, err := r.runCancelableDataPath(ctx, fsRestore, dd, result, log) + reconcileResult, err := r.runCancelableDataPath(ctx, asyncBR, dd, result, log) if err != nil { log.Errorf("Failed to run cancelable data path for %s with err %v", dd.Name, err) r.closeDataPath(ctx, dd.Name) @@ -289,8 +293,8 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request log.Info("Data download is in progress") if dd.Spec.Cancel { log.Info("Data download is being canceled") - fsRestore := r.dataPathMgr.GetAsyncBR(dd.Name) - if fsRestore == nil { + asyncBR := r.dataPathMgr.GetAsyncBR(dd.Name) + if asyncBR == nil { if r.nodeName == dd.Status.Node { r.OnDataDownloadCancelled(ctx, dd.GetNamespace(), dd.GetName()) } else { @@ -306,7 +310,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request log.WithError(err).Error("error updating data download status") return ctrl.Result{}, err } - fsRestore.Cancel() + asyncBR.Cancel() return ctrl.Result{}, nil } @@ -327,33 +331,20 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request } } -func (r *DataDownloadReconciler) runCancelableDataPath(ctx context.Context, fsRestore datapath.AsyncBR, dd *velerov2alpha1api.DataDownload, res *exposer.ExposeResult, log logrus.FieldLogger) (reconcile.Result, error) { - path, err := exposer.GetPodVolumeHostPath(ctx, res.ByPod.HostingPod, res.ByPod.VolumeName, r.client, r.fileSystem, log) - if err != nil { - return r.errorOut(ctx, dd, err, "error exposing host path for pod volume", log) +func (r *DataDownloadReconciler) runCancelableDataPath(ctx context.Context, asyncBR datapath.AsyncBR, dd *velerov2alpha1api.DataDownload, res *exposer.ExposeResult, log logrus.FieldLogger) (reconcile.Result, error) { + if err := asyncBR.Init(ctx, nil); err != nil { + return r.errorOut(ctx, dd, err, "error to initialize asyncBR", log) } - log.WithField("path", path.ByPath).Debug("Found host path") + log.Infof("async restore init for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) - if err := fsRestore.Init(ctx, &datapath.FSBRInitParam{ - BSLName: dd.Spec.BackupStorageLocation, - SourceNamespace: dd.Spec.SourceNamespace, - UploaderType: datamover.GetUploaderType(dd.Spec.DataMover), - RepositoryType: velerov1api.BackupRepositoryTypeKopia, - RepoIdentifier: "", - RepositoryEnsurer: r.repositoryEnsurer, - CredentialGetter: r.credentialGetter, - }); err != nil { - return r.errorOut(ctx, dd, err, "error to initialize data path", log) + if err := asyncBR.StartRestore(dd.Spec.SnapshotID, datapath.AccessPoint{ + ByPath: res.ByPod.VolumeName, + }, dd.Spec.DataMoverConfig); err != nil { + return r.errorOut(ctx, dd, err, fmt.Sprintf("error starting async restore for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName), log) } - log.WithField("path", path.ByPath).Info("fs init") - - if err := fsRestore.StartRestore(dd.Spec.SnapshotID, path, dd.Spec.DataMoverConfig); err != nil { - return r.errorOut(ctx, dd, err, fmt.Sprintf("error starting data path %s restore", path.ByPath), log) - } - - log.WithField("path", path.ByPath).Info("Async fs restore data path started") + log.Info("Async restore started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) return ctrl.Result{}, nil } @@ -561,7 +552,7 @@ func (r *DataDownloadReconciler) findSnapshotRestoreForPod(ctx context.Context, log.WithError(err).Warn("failed to cancel datadownload, and it will wait for prepare timeout") return []reconcile.Request{} } - log.Info("Exposed pod is in abnormal status, and datadownload is marked as cancel") + log.Infof("Exposed pod is in abnormal status(reason %s) and datadownload is marked as cancel", reason) } else { return []reconcile.Request{} } @@ -754,9 +745,9 @@ func (r *DataDownloadReconciler) getTargetPVC(ctx context.Context, dd *velerov2a } func (r *DataDownloadReconciler) closeDataPath(ctx context.Context, ddName string) { - fsBackup := r.dataPathMgr.GetAsyncBR(ddName) - if fsBackup != nil { - fsBackup.Close(ctx) + asyncBR := r.dataPathMgr.GetAsyncBR(ddName) + if asyncBR != nil { + asyncBR.Close(ctx) } r.dataPathMgr.RemoveAsyncBR(ddName) diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index 7c7c5dbef..c6c2697f7 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -33,10 +33,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" clientgofake "k8s.io/client-go/kubernetes/fake" ctrl "sigs.k8s.io/controller-runtime" kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -149,7 +151,7 @@ func initDataDownloadReconcilerWithError(objects []runtime.Object, needError ... dataPathMgr := datapath.NewManager(1) - return NewDataDownloadReconciler(fakeClient, fakeKubeClient, dataPathMgr, nil, &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil + return NewDataDownloadReconciler(fakeClient, nil, fakeKubeClient, dataPathMgr, nil, &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } func TestDataDownloadReconcile(t *testing.T) { @@ -261,14 +263,6 @@ func TestDataDownloadReconcile(t *testing.T) { notMockCleanUp: true, expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, }, - { - name: "Error getting volume directory name for pvc in pod", - dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), - targetPVC: builder.ForPersistentVolumeClaim("test-ns", "test-pvc").Result(), - notNilExpose: true, - mockClose: true, - expectedStatusMsg: "error identifying unique volume path on host", - }, { name: "Unable to update status to in progress for data download", dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), @@ -402,17 +396,18 @@ func TestDataDownloadReconcile(t *testing.T) { r.dataPathMgr = datapath.NewManager(1) } - datapath.FSBRCreator = func(string, string, kbclient.Client, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { - fsBR := datapathmockes.NewAsyncBR(t) + datapath.MicroServiceBRWatcherCreator = func(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, + string, string, string, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { + asyncBR := datapathmockes.NewAsyncBR(t) if test.mockCancel { - fsBR.On("Cancel").Return() + asyncBR.On("Cancel").Return() } if test.mockClose { - fsBR.On("Close", mock.Anything).Return() + asyncBR.On("Close", mock.Anything).Return() } - return fsBR + return asyncBR } if test.isExposeErr || test.isGetExposeErr || test.isPeekExposeErr || test.isNilExposer || test.notNilExpose { @@ -443,7 +438,8 @@ func TestDataDownloadReconcile(t *testing.T) { if test.needCreateFSBR { if fsBR := r.dataPathMgr.GetAsyncBR(test.dd.Name); fsBR == nil { - _, err := r.dataPathMgr.CreateFileSystemBR(test.dd.Name, pVBRRequestor, ctx, r.client, velerov1api.DefaultNamespace, datapath.Callbacks{OnCancelled: r.OnDataDownloadCancelled}, velerotest.NewLogger()) + _, err := r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, nil, nil, datapath.TaskTypeRestore, test.dd.Name, pVBRRequestor, + velerov1api.DefaultNamespace, "", "", datapath.Callbacks{OnCancelled: r.OnDataDownloadCancelled}, false, velerotest.NewLogger()) require.NoError(t, err) } } diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 4781f04f4..068945870 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -67,6 +68,7 @@ type DataUploadReconciler struct { client client.Client kubeClient kubernetes.Interface csiSnapshotClient snapshotter.SnapshotV1Interface + mgr manager.Manager repoEnsurer *repository.Ensurer Clock clocks.WithTickerAndDelayedExecution credentialGetter *credentials.CredentialGetter @@ -80,11 +82,12 @@ type DataUploadReconciler struct { metrics *metrics.ServerMetrics } -func NewDataUploadReconciler(client client.Client, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, +func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, repoEnsurer *repository.Ensurer, clock clocks.WithTickerAndDelayedExecution, cred *credentials.CredentialGetter, nodeName string, fs filesystem.Interface, preparingTimeout time.Duration, log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { return &DataUploadReconciler{ client: client, + mgr: mgr, kubeClient: kubeClient, csiSnapshotClient: csiSnapshotClient, Clock: clock, @@ -246,8 +249,8 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } - fsBackup := r.dataPathMgr.GetAsyncBR(du.Name) - if fsBackup != nil { + asyncBR := r.dataPathMgr.GetAsyncBR(du.Name) + if asyncBR != nil { log.Info("Cancellable data path is already started") return ctrl.Result{}, nil } @@ -270,7 +273,8 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) OnProgress: r.OnDataUploadProgress, } - fsBackup, err = r.dataPathMgr.CreateFileSystemBR(du.Name, dataUploadDownloadRequestor, ctx, r.client, du.Namespace, callbacks, log) + asyncBR, err = r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, r.kubeClient, r.mgr, datapath.TaskTypeBackup, + du.Name, du.Namespace, res.ByPod.HostingPod.Name, res.ByPod.HostingContainer, du.Name, callbacks, false, log) if err != nil { if err == datapath.ConcurrentLimitExceed { log.Info("Data path instance is concurrent limited requeue later") @@ -288,7 +292,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) } log.Info("Data upload is marked as in progress") - result, err := r.runCancelableDataUpload(ctx, fsBackup, du, res, log) + result, err := r.runCancelableDataUpload(ctx, asyncBR, du, res, log) if err != nil { log.Errorf("Failed to run cancelable data path for %s with err %v", du.Name, err) r.closeDataPath(ctx, du.Name) @@ -299,8 +303,8 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) if du.Spec.Cancel { log.Info("Data upload is being canceled") - fsBackup := r.dataPathMgr.GetAsyncBR(du.Name) - if fsBackup == nil { + asyncBR := r.dataPathMgr.GetAsyncBR(du.Name) + if asyncBR == nil { if du.Status.Node == r.nodeName { r.OnDataUploadCancelled(ctx, du.GetNamespace(), du.GetName()) } else { @@ -316,7 +320,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) log.WithError(err).Error("error updating data upload into canceling status") return ctrl.Result{}, err } - fsBackup.Cancel() + asyncBR.Cancel() return ctrl.Result{}, nil } return ctrl.Result{}, nil @@ -336,44 +340,22 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } -func (r *DataUploadReconciler) runCancelableDataUpload(ctx context.Context, fsBackup datapath.AsyncBR, du *velerov2alpha1api.DataUpload, res *exposer.ExposeResult, log logrus.FieldLogger) (reconcile.Result, error) { +func (r *DataUploadReconciler) runCancelableDataUpload(ctx context.Context, asyncBR datapath.AsyncBR, du *velerov2alpha1api.DataUpload, res *exposer.ExposeResult, log logrus.FieldLogger) (reconcile.Result, error) { log.Info("Run cancelable dataUpload") - path, err := exposer.GetPodVolumeHostPath(ctx, res.ByPod.HostingPod, res.ByPod.VolumeName, r.client, r.fileSystem, log) - if err != nil { - return r.errorOut(ctx, du, err, "error exposing host path for pod volume", log) + if err := asyncBR.Init(ctx, nil); err != nil { + return r.errorOut(ctx, du, err, "error to initialize asyncBR", log) } - log.WithField("path", path.ByPath).Debug("Found host path") + log.Infof("async backup init for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) - if err := fsBackup.Init(ctx, &datapath.FSBRInitParam{ - BSLName: du.Spec.BackupStorageLocation, - SourceNamespace: du.Spec.SourceNamespace, - UploaderType: datamover.GetUploaderType(du.Spec.DataMover), - RepositoryType: velerov1api.BackupRepositoryTypeKopia, - RepoIdentifier: "", - RepositoryEnsurer: r.repoEnsurer, - CredentialGetter: r.credentialGetter, - }); err != nil { - return r.errorOut(ctx, du, err, "error to initialize data path", log) + if err := asyncBR.StartBackup(datapath.AccessPoint{ + ByPath: res.ByPod.VolumeName, + }, du.Spec.DataMoverConfig, nil); err != nil { + return r.errorOut(ctx, du, err, fmt.Sprintf("error starting async backup for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName), log) } - log.WithField("path", path.ByPath).Info("fs init") - - tags := map[string]string{ - velerov1api.AsyncOperationIDLabel: du.Labels[velerov1api.AsyncOperationIDLabel], - } - - if err := fsBackup.StartBackup(path, du.Spec.DataMoverConfig, &datapath.FSBRStartParam{ - RealSource: datamover.GetRealSource(du.Spec.SourceNamespace, du.Spec.SourcePVC), - ParentSnapshot: "", - ForceFull: false, - Tags: tags, - }); err != nil { - return r.errorOut(ctx, du, err, "error starting data path backup", log) - } - - log.WithField("path", path.ByPath).Info("Async fs backup data path started") + log.Info("Async backup started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) return ctrl.Result{}, nil } @@ -608,7 +590,7 @@ func (r *DataUploadReconciler) findDataUploadForPod(ctx context.Context, podObj log.WithError(err).Warn("failed to cancel dataupload, and it will wait for prepare timeout") return []reconcile.Request{} } - log.Info("Exposed pod is in abnormal status and dataupload is marked as cancel") + log.Infof("Exposed pod is in abnormal status(reason %s) and dataupload is marked as cancel", reason) } else { return []reconcile.Request{} } @@ -820,9 +802,9 @@ func (r *DataUploadReconciler) exclusiveUpdateDataUpload(ctx context.Context, du } func (r *DataUploadReconciler) closeDataPath(ctx context.Context, duName string) { - fsBackup := r.dataPathMgr.GetAsyncBR(duName) - if fsBackup != nil { - fsBackup.Close(ctx) + asyncBR := r.dataPathMgr.GetAsyncBR(duName) + if asyncBR != nil { + asyncBR.Close(ctx) } r.dataPathMgr.RemoveAsyncBR(duName) diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index 2bca1d5b9..ee73372b1 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -35,6 +35,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" clientgofake "k8s.io/client-go/kubernetes/fake" "k8s.io/utils/clock" testclocks "k8s.io/utils/clock/testing" @@ -42,6 +43,7 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/vmware-tanzu/velero/internal/credentials" @@ -241,7 +243,7 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci if err != nil { return nil, err } - return NewDataUploadReconciler(fakeClient, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, nil, + return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, nil, testclocks.NewFakeClock(now), &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", fakeFS, time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } @@ -548,7 +550,7 @@ func TestReconcile(t *testing.T) { r.snapshotExposerList = map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer{velerov2alpha1api.SnapshotTypeCSI: exposer.NewCSISnapshotExposer(r.kubeClient, r.csiSnapshotClient, velerotest.NewLogger())} } if !test.notCreateFSBR { - datapath.FSBRCreator = func(string, string, kbclient.Client, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { + datapath.MicroServiceBRWatcherCreator = func(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, string, string, string, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { return &fakeDataUploadFSBR{ du: test.du, kubeClient: r.client, @@ -559,7 +561,7 @@ func TestReconcile(t *testing.T) { if test.du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress && !test.notCreateFSBR { if fsBR := r.dataPathMgr.GetAsyncBR(test.du.Name); fsBR == nil { - _, err := r.dataPathMgr.CreateFileSystemBR(test.du.Name, pVBRRequestor, ctx, r.client, velerov1api.DefaultNamespace, datapath.Callbacks{OnCancelled: r.OnDataUploadCancelled}, velerotest.NewLogger()) + _, err := r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, nil, nil, datapath.TaskTypeBackup, test.du.Name, velerov1api.DefaultNamespace, "", "", "", datapath.Callbacks{OnCancelled: r.OnDataUploadCancelled}, false, velerotest.NewLogger()) require.NoError(t, err) } } diff --git a/pkg/datamover/backup_micro_service.go b/pkg/datamover/backup_micro_service.go index 2f4fa6e56..9db00b529 100644 --- a/pkg/datamover/backup_micro_service.go +++ b/pkg/datamover/backup_micro_service.go @@ -103,7 +103,7 @@ func (r *BackupMicroService) Init() error { oldDu := oldObj.(*velerov2alpha1api.DataUpload) newDu := newObj.(*velerov2alpha1api.DataUpload) - if newDu.Name != r.dataUpload.Name { + if newDu.Name != r.dataUploadName { return } diff --git a/pkg/util/logging/default_logger.go b/pkg/util/logging/default_logger.go index b374c3d84..f745e09ac 100644 --- a/pkg/util/logging/default_logger.go +++ b/pkg/util/logging/default_logger.go @@ -43,6 +43,13 @@ func DefaultLogger(level logrus.Level, format Format) *logrus.Logger { return createLogger(level, format, false) } +// DefaultLogger returns a Logger with the default properties +// and hooks, and also a hook to support log merge. +// The desired output format is passed as a LogFormat Enum. +func DefaultMergeLogger(level logrus.Level, format Format) *logrus.Logger { + return createLogger(level, format, true) +} + func createLogger(level logrus.Level, format Format, merge bool) *logrus.Logger { logger := logrus.New() diff --git a/pkg/util/logging/default_logger_test.go b/pkg/util/logging/default_logger_test.go index 10f690757..148a08e9d 100644 --- a/pkg/util/logging/default_logger_test.go +++ b/pkg/util/logging/default_logger_test.go @@ -38,3 +38,16 @@ func TestDefaultLogger(t *testing.T) { } } } + +func TestDefaultMergeLogger(t *testing.T) { + formatFlag := NewFormatFlag() + + for _, testFormat := range formatFlag.AllowedValues() { + formatFlag.Set(testFormat) + logger := DefaultMergeLogger(logrus.InfoLevel, formatFlag.Parse()) + assert.Equal(t, logrus.InfoLevel, logger.Level) + assert.Equal(t, os.Stdout, logger.Out) + + assert.Equal(t, DefaultHooks(true), logger.Hooks[ListeningLevel]) + } +} From 7b7727e8084c961a88505aa89d79688ae53f300c Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 26 Jul 2024 11:02:40 +0800 Subject: [PATCH 27/69] issue 7620: backup repo config Signed-off-by: Lyndon-Li --- .../bases/velero.io_backuprepositories.yaml | 7 ++ config/crd/v1/crds/crds.go | 2 +- pkg/apis/velero/v1/backup_repository_types.go | 5 + pkg/apis/velero/v1/zz_generated.deepcopy.go | 9 +- pkg/cmd/server/server.go | 5 +- .../backup_repository_controller.go | 51 +++++++- .../backup_repository_controller_test.go | 110 +++++++++++++++++- pkg/repository/provider/unified_repo.go | 12 +- pkg/repository/provider/unified_repo_test.go | 68 +++++++---- .../udmrepo/kopialib/backend/common.go | 19 ++- .../udmrepo/kopialib/backend/utils.go | 15 +++ pkg/repository/udmrepo/repo_options.go | 2 + 12 files changed, 269 insertions(+), 36 deletions(-) diff --git a/config/crd/v1/bases/velero.io_backuprepositories.yaml b/config/crd/v1/bases/velero.io_backuprepositories.yaml index d5cc0c51b..00818bc5e 100644 --- a/config/crd/v1/bases/velero.io_backuprepositories.yaml +++ b/config/crd/v1/bases/velero.io_backuprepositories.yaml @@ -54,6 +54,13 @@ spec: description: MaintenanceFrequency is how often maintenance should be run. type: string + repositoryConfig: + additionalProperties: + type: string + description: RepositoryConfig is for repository-specific configuration + fields. + nullable: true + type: object repositoryType: description: RepositoryType indicates the type of the backend repository enum: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 8722e3686..108949343 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,7 +29,7 @@ import ( ) var rawCRDs = [][]byte{ - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcVMo\xdc6\x10\xbd\xef\xaf\x18\xa4\xd7J\x9b\xa0=\x14\xba%n\v\x04M\x02cm\xf8>\x92fw\x19S$K\x0e\xd7\xdd~\xfc\xf7bHɫ\x95do\xec\x02\xd5M\xc3\xe1㛯G\x16E\xb1B\xa7\xee\xc8\aeM\x05\xe8\x14\xfd\xc1d\xe4/\x94\xf7?\x85R\xd9\xf5\xe1\xdd\xea^\x99\xb6\x82\xab\x18\xd8v\x1b\n6\xfa\x86~\xa6\xad2\x8a\x955\xab\x8e\x18[d\xacV\x00h\x8ce\x14s\x90_\x80\xc6\x1a\xf6Vk\xf2ŎLy\x1fk\xaa\xa3\xd2-\xf9\x04>\x1c}x[\xbe\xfb\xb1|\xbb\x020\xd8Q\x0556\xf7\xd1yr6(\xb6^Q(\x0f\xa4\xc9\xdbR\xd9Up\xd4\b\xfa\xce\xdb\xe8*8-\xe4\xdd\xfdə\xf5\x87\x04\xb4\x19\x80\x8eiI\xab\xc0\xbf-.\x7fR\x81\x93\x8b\xd3ѣ^\"\x92\x96\x832\xbb\xa8\xd1\xcf\x1c\xe4\x80\xd0XG\x15|\x11.\x0e\x1bjW\x00}\xa4\x89[\x01ض)w\xa8\xaf\xbd2L\xfe\xca\xea\xd8\r9+\xe0k\xb0\xe6\x1ay_A9d\xb7l<\xa5\xc4ު\x8e\x02c\xe7\x92\uf430\xf7;\xea\xff\xf9(\x87\xb7\xc84\a\x93̕'\xae\xb7GGg(\xa7D\xc0h-#\x06\xf6\xca\xecV'\xe7û\x9c\x8afO\x1dV\xbd\xafud\xde_\x7f\xbc\xfb\xe1\xe6\xcc\f\xe0\xbcu\xe4Y\r\xe5\xc9ߨ\xfdFV\x80\x96B\xe3\x95\xe3\xd4\x1c\x7f\x17gk\x00r@\xde\x05\xad\xf4!\x05\xe0=\r9\xa6\xb6\xe7\x04v\v\xbcW\x01<9O\x81L\xeeL1\xa3\x01[\x7f\xa5\x86\xcb\t\xf4\ry\x81\x81\xb0\xb7Q\xb7Ҿ\a\xf2\f\x9e\x1a\xbb3\xea\xcfG\xec\x00lӡ\x1a\x99\x02C\xaa\xa2A\r\aԑ\xbe\a4\xed\x04\xb9\xc3#x\x923!\x9a\x11^\xda\x10\xa6<>[O\xa0\xcc\xd6V\xb0gv\xa1Z\xafw\x8a\x87\xa1ll\xd7E\xa3\xf8\xb8N\xf3\xa5\xea\xc8ևuK\a\xd2\xeb\xa0v\x05\xfaf\xaf\x98\x1a\x8e\x9e\xd6\xe8T\x91\x021i0ˮ\xfd\xce\xf7c\x1cΎ\x9d\x15:\x7fi\x92^P\x1e\x19-P\x01\xb0\x87\xca!\x9e\xaa &I\xdd旛[\x18\x98\xe4J墜\\gy\x19\xea#\xd9TfK>\xef\xdbz\xdb%L2\xad\xb3\xcap\xfai\xb4\"\xc3\x10b\xdd)\x966\xf8=R`)\xdd\x14\xf6*\t\x17\xd4\x04\xd1\xc9\xe8\xb4S\x87\x8f\x06\xae\xb0#}\x85\x81\xfe\xe7ZIUB!E\xf8\xa6j\x8d\xe5x\xea\x9c\xd3;Z\x18\xa4\xf4\x89\xd2N\xe5\xf1\xc6Q#\x95\x95\xe4\xcaV\xb5UM\x9e\xa9\xad\xf5\x803\xff\xf3L-K\x80|YDo\xd8z\xdc\xd1'\x9b1\xa7N\x97\xdaN\xbe\x0fK@\x03c\x91\xad\xac\t\xb4\xec\xb8\x00\xc8{\xe4\x91\x180*\xf3\xa8)\x8bA>S\x99T\x1d\x14\xa50h\x1a\xfa5\xf5\xa3i\x8e\x17\x02\xfd\xbc\xb0EB\xda\xdb\a\xb0[&3\x06\xed\xb9.DR\x13\xf8h^D\xf6\xfc\xa6\xb8@ss\xe6\fʴ\xd2\x1b\xbd4\xcb!C\xea\xa5\xd8dZ\xf0\xe7\x97\xf2\xf8#\x13\xbb\xf9q\x05\xdc[\xa7p\xc1\xee)\xb0j\x16\x16\u07bcyY\xbc\x02\xf3\xb1\x95\xe1\xdb*\xf2\xaf\xe9\xc0\xcd\x04ch\xbemԺ?\xa0hl\xe7\x90U\xadiPH\x19\x1f\x95\xf7\x1c\xe7\xbc\x12\xed\xff\xd0t\ay]\xd0\xe3{\xe45aݝC\x8cG*\x1b\x12\xbf<\xc7#\x9a\xc3̄\x05Hg۞Y\xbf/H\x1a^\x10\x98\f\x83\xf24\xb9\x9b\x8ae5\x99\xf8,\xcd\xe1\xc4e\xda\r\x93\xe5IR\xbfIm\x199\x86\x97\xe8m\xda0$\xbb\x89ާ\xfb,[\xe5\x19\xf3j\xc5\xd5\x18x$,\xf2\xa8\xbc\xd0\x16\x9f\xe6;\x06b\x02\x06,\x86\xb1\x12=\xe0R\xd5\x175hk}\x87\x9c_\xad\x85\x00\xcdg\xaf\xfcl\xe9\xb7\x00\xd66\xf2\x13\xa9\xe7\xfd\x9c\x05\\(\xc7\x05\xa6n\x8f\xe1\x12\xcfk\xf1Yj\x88\xc9\xcd\xf6\x1c\x85\xa7\xd4\xf5\v=,X7\x84\xed\\\xa1\v\xf8byy\xe9\xc9\b\x17\xa7bf\f\xf2\xc2kGu\x0ey\x90ǖX?>`+\xf8\xeb\x9fտ\x01\x00\x00\xff\xff\xdd}\xa6m\xca\x0e\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcVMo\xe36\x10\xbd\xfbW\f\xb6\xd7Jޠ=\x14\xba\xed\xba-\x104\t\x02'ȝ\x92F27\x14ɒC\xa7\xee\xc7\x7f/\x86\x94bY\x92כ\x14\xa8n\"\x87o>\xde\xcc#\xb3,[\t+\x9f\xd0yit\x01\xc2J\xfc\x83P\xf3\x9fϟ\x7f\xf2\xb94\xeb\xfd\xd5\xeaY꺀M\xf0d\xba-z\x13\\\x85?c#\xb5$i\xf4\xaaC\x12\xb5 Q\xac\x00\x84ֆ\x04/{\xfe\x05\xa8\x8c&g\x94B\x97\xb5\xa8\xf3\xe7Pb\x19\xa4\xaa\xd1E\xf0\xc1\xf5\xfec~\xf5c\xfeq\x05\xa0E\x87\x05\x94\xa2z\x0e֡5^\x92q\x12}\xbeG\x85\xce\xe4Ҭ\xbcŊ\xd1[g\x82-ฑN\xf7\x9eSԟ#\xd0v\x00:\xc4-%=\xfd\xb6\xb8}#=E\x13\xab\x82\x13j)\x90\xb8\xed\xa5n\x83\x12nf\xc0\x0e|e,\x16pDZXQa\xbd\x02\xe83\x8d\xb1e \xea:\xd6N\xa8{'5\xa1\xdb\x18\x15\xba\xa1f\x19|\xf1F\xdf\v\xda\x15\x90\x0f\xd5\xcd+\x87\xb1\xb0\x8f\xb2CO\xa2\xb3\xd1v(ا\x16\xfb\x7f:\xb0\xf3Z\x10\xce\xc1\xb8r\xf91\xd6ǃ\xc5\x13\x94c!`\xb4\x97\x10=9\xa9\xdb\xd5\xd1x\x7f\x95JQ\xed\xb0\x13Eok,\xeaO\xf7\xd7O?<\x9c,\x03Xg,:\x92\x03=\xe9\x1b\xb5\xdfh\x15\xa0F_9i)6\xc7\xdf\xd9\xc9\x1e\x00;H\xa7\xa0\xe6>D\x0f\xb4á\xc6X\xf71\x81i\x80v҃C\xebУN\x9d\xc9\xcbB\x83)\xbf`E\xf9\x04\xfa\x01\x1dÀߙ\xa0jn\xdf=:\x02\x87\x95i\xb5\xfc\xf3\x15\xdb\x03\x99\xe8T\tBO\x10Y\xd4B\xc1^\xa8\x80߃\xd0\xf5\x04\xb9\x13\ap\xc8>!\xe8\x11^<\xe0\xa7q\xdc\x1a\x87 uc\n\xd8\x11Y_\xac\u05ed\xa4a(+\xd3uAK:\xac\xe3|\xc92\x90q~]\xe3\x1e\xd5\xda\xcb6\x13\xae\xdaI\u008a\x82õ\xb02\x8b\x89\xe88\x98yW\x7f\xe7\xfa1\xf6'ngD\xa7/N\xd2\x1b\xe8\xe1\xd1\x02\xe9A\xf4P)\xc5#\v\xbcĥ\xdb\xfe\xf2\xf0\bC$\x89\xa9D\xca\xd1tV\x97\x81\x1f\xae\xa6\xd4\r\xbat\xaeq\xa6\x8b\x98\xa8kk\xa4\xa6\xf8S)\x89\x9a\xc0\x87\xb2\x93\xc4m\xf0{@OL\xdd\x14v\x13\x85\vJ\x84`yt\xea\xa9\xc1\xb5\x86\x8d\xe8Pm\x84\xc7\xff\x99+f\xc5gL\xc27\xb15\x96\xe3\xa9q*\xefhc\x90\xd23\xd4N\xe5\xf1\xc1b\xc5\xccrq\xf9\xa8ld\x95f\xaa1\x0e\xc4\xcc\xfe\xb4R\xcb\x12\xc0_\x12\xd1\a2N\xb4xc\x12\xe6\xd4\xe8R\xdb\xf1\xf7y\th\x88\x98e+i\x02.\x1b.\x00\xd2N\xd0H\fHH\xfd\xaa)\x8bI~\x85\x99Ȏ`\xa5\xd0BW\xf8k\xecG]\x1d.$z\xbbp\x84Sڙ\x170\r\xa1\x1e\x83\xf6\xb1.dR\"\xb8\xa0\xdf\x14\xec1Ǎэl灎/\xb2s\xe4^p2\xc9v;\xf1ərs\x1dcɆ\xcecB\x1a\xd9\x06w\x8e\xbcF\xa2\xaag\x12\x02\xa0\x83R\xa2TX\x00\xb9\x80g*2\x9b\x95ӊ\xf0\xfdx\x81\xb8\xed\x891H]\xf3\xb4\xf4\x97\x15;\x19\x9a\x91\xdb\x1fu\r\xee\xf4\x992\xfeP\x87n\xee.\x83gc\xa5XXw\xe8IV\v\x1b\x1f>\xbc\xad\x03\x18\xe6\xbaf9j$\xba\xf7\xcc\xe4v\x821\x8cc\x13\x94\xea\x1dd\x95\xe9\xac Y*\x1c\xee\f\xe6\\\xa63\x87\xa5\xa6\x81\xff4\x86{~o\xe1\xeb\v\xed=i=\x9dB\x8cE&-\xc4\xf8\x92\xb2\x8d\xc2\x1cT\xc4/@ZS\xf7\x91\xf5\xe7b\xeb\xbf!1\x96\a\xe9pr[g\xcb\xfa:\xb1YR\xa6\x89ɴ\x1b&ۓ\xa2~\xd3\xfdC\x82\x82\x7f\xcb\r\x14\x0f\fŮ\x82s\xf1\x86O\xab\xfc\xb0{\xf7\x1d\xa4\x84\xa7\x91\xd4\xf23\xfbB[\xdc\xccO\f\x811\x18\x10/\x8c\xb5\xf9E,\xb1\xbe\xa8ʍq\x9d\xa0\xf4\x8e\xcf\x18\xe8}\"\xb6|\a\xa1\xf7\xa2\xbd\x94\xddm\xb2J\x0f\xb9\xfe\b\x88\xd2\x04:Sz\xdaͣ\x80\vt\\\x88\xd4\ue13f\x14\xe7=\xdb,5\xc4\xe4\xae\xffZ\b\xe7\xd4\xf5\x0e_\x16V\xb7(\xea\xb9Bgpghy\xebl\x86\x8bS1[\xf4\xfc\xe6\xadG<\xfb4\xc8\xe3\x95P\xbe>\xe9\v\xf8\xeb\x9fտ\x01\x00\x00\xff\xff\x12%\xb58\xdc\x0f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}_s\xdb8\x92\xf8{>\x05ʿ\x87\xd9ݒ\xecI\xfd\xf6\xe1\xcao\x19'\xb9Q\xedL\xe2\x8a=\xd9g\x88lI\x18\x83\x00\x17\x00ek\xef\xee\xbb_\xa1\x01\xf0\x8f\b\x92\xa0,{\xb2{\xe1Kb\x11l\x00ݍ\xeeFw\xa3\xb1\\.\xdfВ}\x05\xa5\x99\x14ׄ\x96\f\x9e\f\b\xfb\x97\xbe|\xf8\x0f}\xc9\xe4\xd5\xfe\xed\x9b\a&\xf2krSi#\x8b/\xa0e\xa52x\x0f\x1b&\x98aR\xbc)\xc0М\x1az\xfd\x86\x10*\x844\xd4\xfe\xacퟄdR\x18%9\a\xb5܂\xb8|\xa8ְ\xae\x18\xcfA!\xf0\xd0\xf5\xfe\xc7˷\x7f\xbd\xfc\xf1\r!\x82\x16pM\xd64{\xa8J}\xb9\a\x0eJ^2\xf9F\x97\x90Y\x90[%\xab\xf2\x9a4/\xdc'\xbe;7ԟ\xf0k\xfc\x813m\xfe\xd6\xfa\xf1\x17\xa6\r\xbe(y\xa5(\xaf{\xc2\xdf4\x13ۊS\x15~}C\x88\xced\t\xd7\xe4\x93\xed\xa2\xa4\x19\xe4o\b\xf1\xa3\xc6.\x97~\xc0\xfb\xb7\x0eB\xb6\x83\x82\xba\xb1\x10\"K\x10\xefnW_\xff\xff]\xe7gBrЙb\xa5\xc1\xb9\xff\xf7\xb2\xfe\x9d\xf8Q\x12\xa6\t%_q\x8eDy\x94\x13\xb3\xa3\x86((\x15h\x10F\x13\xb3\x03\x92\xd1\xd2T\n\x88ܐ\xbfUkP\x02\f\xe8\x16\xbc\x8cWڀ\"\xdaP\x03\x84\x1aBI)\x990\x84\tbX\x01\xe4O\xefnWD\xae\x7f\x87\xcchBEN\xa8\xd62c\xd4@N\xf6\x92W\x05\xb8o\xff|YC-\x95,A\x19\x16\x90\xee\x9e\x16'\xb5~\x1d\x9b\xab},z\xdcW$\xb7,\x05nZ\x1eŐ{\x8c\xda\xf9\x99\x1d\xd3\xcd\xf4\x91\xc9\xec\xcfT\xf8\xe1_\x1e\x81\xbe\x03e\xc1\x10\xbd\x93\x15\xcf-'\xeeAY\x04fr+\xd8?kؚ\x18\x89\x9drj@[\xcc\x18P\x82r\xb2\xa7\xbc\x82\x85E\xca\x11\xe4\x82\x1e\x88\x02\xdb'\xa9D\v\x1e~\xa0\x8f\xc7\xf1\xabT@\x98\xd8\xc8k\xb23\xa6\xd4\xd7WW[f\xc2\xfa\xcadQT\x82\x99\xc3\x15.\x15\xb6\xae\x8cT\xfa*\x87=\xf0+ͶK\xaa\xb2\x1d3\x90Y2_ђ-q\"\x02\xd7\xd8e\x91\xff\xbf\xc0\x1e\xbaӭ9X\xb6\xd5F1\xb1m\xbd\xc0\xf51\x83\xe7\f\x84!\xbaZ\x17\xccX6\xf8G\x05ڮ\x01y\f\xf6\x06e\x10Y\x03\xa9\xcaܲ\xf1q\x83\x95 7\xb4\x00~C5\xbc2\xad,U\xf4\xd2\x12!\x89Zm\xc9z\xdcء\xb7\xf5\"\b\xc8\x01\xd2:\xc1rWB\xd6Yh\xf6+\xb6a\x99[N\x1b\xa9\x1a\xb9\xe3d`\x17C\xf1\xa5o\x9fL\xb3;AK\xbd\x93\xe6\x9e\x15 +s\xdcb\x8aאxw\xab#(a\x84~\xbc(\xb3*\r\xb9]\xb4\x8f\x94\x19\x1c\xf3\xcd݊|Ea\x15\xbeF\xa1Uib*%,\x97D\xfa\xfa\x024?\xdc\xcb\xdf4\x90\xbcB\xe6\xce\x14 \x1e\x16d\r\x1b\xcb\t\n\xec\xf7\xf6\x15(eq\xa3q\x00\xb2\xea\t\x1b\xfb\xdc\xef\xc0\xe2\x96V\xdc\xf8u\xc24y\xfb#)\x98\xa8L\x8f\xd5\x06\xa9\x8e\x98\xa2\x86\x16r\x0f\xea\x14$\xbe\xa7\x86\xfej?>\u009d\x05J\x10\xaaE\xde\xda\xe3q}\xc0\x971j\xbbg\xb5iAd\x9a\\\\\x10\xa9ȅ\xd3\xc0\x17\v\xf7uŸY2\xd1\xee\xe3\x91q\x1ez\x997y\x87CGP}/?jǼ'\xe1b\x00V\v5\x8f;0;P\xa4\x94\xb5\xc6\xdb0\x0eD\x1f\xb4\x81\xc2#&h\x11?\x9fHO\xb8v8\xf7 \xb4ū\x9fH\x7f\xf2\xa2✮9\\\x13\xa3*\x18\xc0\xcdZJ\x0eTL \xe7\vhòs\xa0\xc6A\x8a F\xf9\x17\x1d\f\xa0Ҥ\x0f@h\x04\xb4Ǚ\xd5Μ\xb7\x10\xdb\xc5ʛ\xe8\xa0J\x05\x99\x15\xdb\xd7^\x1d0ਂ\x84$\\\x8a-(\u05fd5U\x02\x87)\xb0\x1c\x97\x13+i\x15p\xabNȦ\xb2B\xf8\x92\xd8\xe5=\xc8\x04Lh\x034\u009d\xcf \x10\x8cB\xf4꙳\f\xad@o\xf0-\xd1p\x8d\xf1i\xa3\xa5\x0f%8\xdb\xd9\xd2\xd2\x0f\xbbQ\xbf\xa3\x02A\x83\xb1\x1f]\xfc\xe5b\x81$\xee\xf6\xda\xedC\x13\xaa\xa0FK\xb2\xe0\x84\xa24\x87~kf\xa0\x88`qT\xa0$ғ*E\x0f\x03Ԭ7\x00g\xa4\xe7\x10\xcc#\x8a\x8a\xd0\xec\x95iz\xdc\xef\xbf3U\xcfCG\x8d\xdb]ʄ\xa5\x9f\xddyvȧ\xdd\x06\u03a2MH\x13\x81DŽ\x83\x87{\xb3\x11j\xfdA\xc8:\v\xcf\x0f1y\xcd[\x9ey\xff%1\xb5\x93\xf2a\n;?\xdb6ͮ\x88d\xe8V!k\xd8\xd1=\x93\xcaO\xbdѵ\xf0\x04Ye\xa2\xab\x9e\x1a\x92\xb3\xcd\x06\x94\x85S\xee\xa8\x06\xed\xf6\xc9\xc3\b\x19\xb6\xdfIK\x8cD_\x1eͣ!\xa4%\x13\xce|h\xe8\u05908֒\xe1\xb1\x03\xb5\xf65*\xe3\x9c\xedY^Q\x8ez\x99\x8a\xcc͇\xd6\xe3\x8aI\x99\x11\"\xf7\xc6\x1c\xe5L\xf78\x83 L\xca\x12\xa9\xb3U\x92\x02\xac\xd1[\xd8MA\xbf\xe9\xf0\xcc\xd7\xd4\xda*rh\xf6\x04\x89\xa5*\x0e\xdaw\x95\xa3\x1d\xd9ȌEC\x14\xf4D\x10N\xd7\xc0\x89\x06\x0e\x99\x91*\x8e\x91):\xbb'E\b\x0e 2\"\xf9\xba[\x8df\x02# \t\xee\xe1v,\xdb9S\xcf2\x11\xc2!\xb9\x04k\xf0\x19B˒G\xd4E\xf3\x8c\x12\xdfw2\xb6֛gb\xd5\x1fË\xad\xff\xe6I\x90\x99\xcd\x13Em\xb3\xbe\xba\x98\xad\xd9!\xbe\xa9m\x9e\x7fO\xc4\x06\xc9\x7f\x02ӎ\xac~\x82n\xa1d\x9e\x1e\xe4[\x8bU\x06\xfaҚSh\xe9,\b3\xe1ש\x95б\xb9z\u07b2\x0e\x12\xbem\xda\xccg\xfaDҤ\xac\x89\x17\"L\xddſ ]Pe\xdcy\x8d\x91L\x93_\xda_-\b\xdb\xd4H\xcf\x17dø\x01u\x84\xfd\x93D}\xa0\xcc9\x90\x91\xa2\xf5\b\xfa\xefM\xb6\xfb\xf0dM0݄\xaa\x12\xf1r\xfc\xb13d\x83\xb5\xdfU\xcf\x13p\t\xfa\xb1\x99\x82\x02\xfd\xe3\xb8cj\xff\x82\xa6ջO\xef\xe3\xfb\xab\xf6\x93\xc0y\xbd\x89L,:\xf7\xbc;\x9aQ{|ބ\x0fo\xd0\x06\xaa7@.\x16\xb2 \x94<\xc0\xc1\x99.T\x10K\x1f\x1a\x1a't\xaf\x00\x832\xc8g\x0fp@0\xf1(K\xffI\xe5\x06\xf7<\xc0!\xa5\xd9\x11\x0e혘\xf6\xd1#\x8b'\xfb\x03\"\x02\x9d\xeb\xa9l\xe0\x1e\xbf\x14\"1\x8d\xf8\x93(K\xc2\x13p\x7f\xc24\x93X\xa5\xddG;L\x89\x1c\xf0\x83v\xb4\xb4+f\xc7J\x14\xab\xe8q\x90\x9bd\x82\xba\xe7+\xe5,\xaf;rkd%\x16\xe4\x934\xf6\x9f\x0fOL\xfbH\xe6{\t\xfa\x934\xf8ˋ`\xd4\r\xfc%\xf1\xe9z\xc0\x85&\x9c\x94\xb7\bk\xc7\xe2\x9cN\xb3\xdcV\xe3\x9ei\xb2\x12v\xbb\xe2P\x92\xd8\x15\x86]]w\xae\xa3\xa2\xd2\x18F\x13R,\x9d\xdb&֓ǷT\x1dt?\xbbS\xdf\xe1\xbdU\x16\xee\x8d\v\xfer\x9aA\x1e\xe25\x18\x95\xa4\x06\xb6,K\xec\xaf\x00\xb5\x05RZ\x11\x9e\xc6\x11\x89\x82\xd5\xcff\x1e\xfb\xa4i\xef\xf0x\xc1\x9bO\x0ffi\x17\\B\xab@\xc6ɦ\x03\x11\xc7\xe1\xa6\xd33B-\x8a&\xc6$vi\x9ec\xa2\t\xe5\xb73$\xfa\fZ\xcc]\x9a\xad\xb1;\x15XP\x8cu\xfc\x97\xd5t\xc8\xcd\xffCJʔ\xbe$\xef0\xa7\x84C\xe7\x9dwZ\xb5\xc0$t\x899!\x96\x05\xf6\x94[\xddk\x05\xa8 \xc0\x9d&\x96\x9b\x9e]\xb2 \x8f;\xa9\x9dڬ\x83(\x17\x0fpp!\xbb\xc9.ۋ\xfcb%.\x9c\x0e\xef-\xd8Z\xe1K\xc1\x0f\xe4\x02\xdf]<ǔId\xb6\xc4fOˇ:-fY\xd0r\xe9\x19\xd4\xc8bDh`NO\xaa\xa1l7\x8c\xc1\b\xb0\x1fֹ*\xd6\xc8\x1d\x9bm\x12\x8b\x96RG\"\xe9\x03C\x99`\xde[\xa9\x8d\xf3Wul֨CK\x06'\x16\xa1\x1b\x97@$U\xc8\xf6\xb0Bq\xca\xf5\xda~\xeew\xa0\xc1\xc7\v\xbcc\xcc\x01\xb5;\xab\x8bf};i{\xe1\xe2\x15\xd8\t\xcd\xd0b\xc0oK%3\xd0\xd1`r\xf3$\xc8\xebHZD{\xee\xb5Ϗ\xba]\x8aˉ\x18wA\x86'\xdd䴈\x98i\xaf\x7fxj9$\xedڷ\x7fO\xf1\xd8\xdcq\x11\xcc\xd9+\nz\x9c'\x944\xc4\x1b\xf7eX\r\x1e\x903\xfeնBI\x90\xaaKk\x06\xfc\x16\x14u\xc1\xc4\n; oϮ\xd8I\x90\xa1\xb1l\x8f\xd8s\x9a)y\x13:i\xa8S\xff\xe0\x96r)\xd1U\xaf\xa0C\xbc\xbeW\x1b\xed@!M\xcb!0\xc3\xdc+e\xfe\x83&\x1b\xa6\xb4i\x0fA\x0f\xe4\x89D\xc1\xcc\xdc\xf8\x88\x0fJ\x9d\xb4\xef\xf9\xec\xbel\xb9\x9bv\xf21\xe4G9\xc4$\xce\x1c\xe3;@؆0C@d\xb2\x12\xe8@\xb1\xeb\x18\xbbp\xc8u\x12\x96\xa5.\x92\xb4\xd5o\x1f\x10U\x91\x86\x80%r\n\x13\xa3\x9e\x96v\xf3\x8f\x94\xf1\x97 \x9b\x19J#\x8b=\xa7\xad\x89\x90c\xd6Έ+\xe8\x13+\xaa\x82\xd0\xc2\xd2\b\x959+\xa0K\xf4&\xf3\xcc~\x81j\xc2H\xbbbJ\x0e\x06|\xf6X\xe2\x182)4ˡV\xae\x9e\x11\xa4 \x94l(\xe3\x95J\x94\x80\xb3\xd0;g7\xe1%\xc1\xf9\xb6\ti\x9d/\x11\x15\t\xde\xd4D[q\\\x1a\x97*\xdd\xe2\x9b2\xb3\x14̷\xb2J\xc5$\xe6\xe5\x9d\xd9\xd0\xf2\x99\x8cT\x1c\xbe[Z\xa9C\xfdni\x8d=\xdf-\xad\x89绥\xf5\xdd\xd2Ji\xf9\xdd\xd2\xfani\xb5\x9f\xff\x13\x96\xd6Ԉ܁\xba\x81\x97\x93\xa3H\b\x15\x8f\rq\x04\xbeOn\xf09\xd8\xcfʅ\\\xc5AE2\xef\aҪcB\xabQ\x1eur\xa4]5\x81\xe7\xdd\xf9\x9e\tS\xf2\x19Y\xef\xa1\xd3\xf3e\xbd\xafF!\x9e)\xeb\xdd\x0f{\xda\xc6>)\xe7= e^v\xf4\xc2'J\x14@\x83[݅\xc1c\xf3\x1a␉\xfe_91\xb6\x97\xb5uF\xfex\xf1,\xfad\x1e\x89\x92\xf4\xe2/\x17\xdf\x1e\xfaσ\xf0A\x14\xf7q\xe7\x0f\x18G\xa0\xda\x1dh;-\xab\x9b\x05\xf7m\xb2\xf1Y\xf865\x13\xbeFb\x04V\x97%\x8f\xb0\xf8\xad\xca\x02\x03\xc5\xe7\xd2k\xa4g\x1c\x15]E\xe0$\x1d\x16\xa5\xfa \xb2\x9d\x92BV\xda{%,\xacw\x99;Q\x1e@Ƙ5\xba\xc2\xffJv\xb2\x8adb\x8f\xa0o\"#oz\xf2\x9d\xe4<\x1f\x84\x06C\xf7o/\xbbo\x8c\xf4\xa9z䑙]\x04\xd0\xe3\x0e\x04F\xd8Ŷ\x9d\x80\x1f\n\x02\xf8\x93\xf1\xc7\f\x16\x01$\x15\x11\x8c;Ϋ\xcb\t\xb4\xf9\x8e|.\x9d\xefi\xb6\xdd1\xeeSIK\xe6;9\x85\xaf\x9b\xa27`\x97\u038dv\x9f\xe5\xc8\xc2\x1f\x92\x9a7?!/\xc5#6\x91|wB\xca]bn\xef\xb3\xc3\xf3)Iusv\xcc/\x96@w\xfe\xb4\xb9$\xfcL\xa7\xc8\xcd\xc1\u038b\xa7ýb\x12\xdc뤾%&\xbc\x9d/s\xfd\x1c\x1e\x80\xe1\xf4\xb5ɤ\xb5I\x0f\xc1\xf8\xf8&\xd3\xd2\xe6$\xa3Mb,\x8d\xf5_-\xdd\xecՒ\xcc^7\xb5l\x94%F_\xceI\x1e\x8b\xd7j!\x93\n\x90\xbf\x16\xb3\x9d\x8a\x06\xa9:&\xe5I{\x9e\xcfG0,ჹ\xf5JvkQq\xc3J\x8e\xc1\xcd=ˣ\x0e\x00\xb3\x83C]T\xe2w\x89\xc71}y\x94\xcf_j\xae\xbd<\xb2\xbe\xa9&\x8f\xc09\xa1\xb1u՛y\xe6\xca\x13er\tVG\xd8\xd5\xe9\xabe\xf8\x9aF\v\xc7\xeex\xe2\x145M\x11s\xfbP1\\ZeP\x98\xa7ț\x9eU\xe9lc\xfc\xed\x1f\x15\xa8\x03\xc1\xe2.\xb5\xed\xd1\x1c\x8c\xf2\vS\xdb\xcdQ\x10\x15^l\r\xf9\xb4{\x86x\xb3\x94\xc9;\xe14\xe1\xf1x\xf0\x1b+#\x9a\x8d\x86\x15|v\x0f\x11\xedc\xe0s!\xeb\xaf#\x9fM\x19\xad\xa9'\x88^v\xdb1\x7f\xe31\xa9\xe9ӭ\xb1?\xe8d\xd0)'\x82҂\xf2\x93'\x80^j\x1b2\xb5\x11I\xb6\xbd\xd2N\xf8\xcc\v\xe0\xbd\xe0\x89\x9e\x978ɓ\x88\xa9\x94\x93;\xf3\xf0\xf4\n'u^\xf5\x84\xcek\x9d\xccI>\x91\x93\x94v\x92\x1c\x99MI\x1b\x99\x0e\x9e\x8e\x9f\xb4I8a\x93\x10V\x9d\x1ai\xc2I\x9ay'h\x12p\x98\xba4^\xf1\xa4\xcc+\x9e\x90y\xed\x931\x13L2\xf1z\xde\t\x98\x93\xdd\xfaR\xe5\xa0FC#\xa9\\8\xca\x7f){\x8d\xee@\x8eb\x02\xa14\x9dmձ_Q\\\xfbj\x98X\xf7t(\xc4g9\xad\xa5\xfd;\xf1\x9a\xc6\x1c\xe9\x1aw\xbe\x18\xaa\v\xe9h(\xa9\xc2\x02\xbb\xeb\x83K\xf9\x88\xaa\xca\x0f4\xdb\x1dA\xdfQM6R\x15Ԑ\x8b:Hv\xe5\x80ۿ/.\t\xf9(뼁v\xed\x18͊\x92\x1f쎁\\\xb4?8\x8d\x03\xa2\xdc\x16z\xbb\x95\x9ce\x11[*Z?\xc85\xee\x15t\xc0\xaaFY;\xac^چqS\nͮn\x9dƍ\xe4\\>\xce܋Ӓ\xfd'\x96\x97~\x86\xb7\xe6\xdd\xed\na\x04\xf6\xc0z\xd5u\x02S=\x9b5X5\xd9\xccsh\xed\xaf6\x1d\x88\xdd\\\xc0v\x05W\xc8]\xb1ޠ\xa6\xbd\xe8̤\x95.\xb7+7\x8e\xa1^,\xcfPq \x12\xb3N̎\xa9|YRe\x0e.\x99a\xd1\x19CP\x8bcޖA\xed\xd1/@\x1cEo\xa8;\x8cQ\xbcC\xd9\r\x8c\x1e\xe3\xee\x94q\f\x9f\xf0\x9b<\xdbw\xc6q\f[\x18K\xc4T\xe4\xe7hv\xd4ټXڗ\xcf\xfdU\xee\xe1}ԛ\xd5A\xcf\xddQ\xf3H\nS\x80\xe8*\xc3\x0efr\xae\x01\xab\xc6\xf6_=#')t\xed\xeb~\x9e⸺낈\xcc/\x94A\r\x9d\xc5\xe4\x13V)?\x90ۯ\xb8g\xaaE\x9b_\xa2~\xcf\x14\\W!`\x1a\x81\xe3?\xf8\xe9\xfc\xe9[\xdaHE\xb7\xf0\x8bt\x85\xa0\xa7\xc8\xdem\xdd)\x10\ueb5e\x90c\x19\x16M\xacJ\xac/I}\x04\xacɋ\xeeU\u07b5\xa3\x9cYK\xd8\x18~\n\xdd\xef\xef\x7fq\xb32\xac\x80\xcb\xf7\x95K\t\xb02Q\x83Eq\x98\xad\x83\xb4\xb6\xff\xdd\xc9G,P\x1b\xf7+\x86\xc2\xfe\xcdd\x14`B6\xa6\xe9͚RUrIsP7Rl\xd8vbv\xbfu\x1a\x1f\xa9\xd9\f\x7f\xf4\x93\xabuT\x80\x7f\xe68\xbd\xb5y8\a\xfe\x91q\xd0nX\t\x02\xf8\xb6\xffU-\x8f\xabb\xedl\xb8\x8d}Yw0\xa0\xe3ܴ\xd05\\\x82\xb2V\x94s\"W:\xf0\xea\xf0\xc4\x1b\x8a0a`\v\xfd\r݈\x04\xdew\n\x93\a>\x9f\x12G_\xe3_\xb5\xcc\xca\xd6Jsv\xa5\xdcD\x06>\x04\xa7u\xcd\xc3#3\xbe,\xd3y\xebh\x0em\x16\x86\n\xd8c\xc5\xf6\xe9\x12\xf6\xae\xb0\xbb\xbf\xf8\xc23r\xa5\xb0\b\xa6/\xfa\x8eE#O\xaab\xbf\xaeӁ\xea\xd4\"\xfd\xce\x18(J\x13\xd3\xd2ӂ\xe4\xa71\x80\xb5\x85#\r\xe5-~\xa6\xa1A\xccF\xd5\a\x91\x8d\xa5-\xf9uT8h\x83\x8c`\xa77\nir\xc2>\x99\x18D\x1eVz8\b3\x0f\x15\x9e\n>\xd7N\x1bZ\x9cT\x8f\xff\xa6\x0f\x06odQy+e\x8f\xd6c\xa7\xba!\x7fL,7\xe0ܗ\xb8=\xb1\xd0 '\xb0\aA\xac^s(\x0eW\n̈́\xe2\xcfO:\xdd\x104Ep\"D\xef\x9d!\xdeO\xa0\xf1~\x93\x1ft\r\x133\x11\xf1\xba\x8a>\x12\xfaf\xa3\xdb\xe7_[\xbb\x19\x96\x16\xc4i\xf6^T6g\x9au\xf5\xc2\xf3\x84\xdc\xcd\xddj\b\xdc)\"\xae\x7f\x9b\xc73\x97q\x7f\xba\xcf\x12i\xfd\xe9\xce\x12h\x11\x885\x8f\x9f\x7f\xee\xb8\xd4O+ٍ_:\x83#\v'\xb4(\xe7\xfe\x18]\x01Z\xd3m\xa8\xd5\xfdh\x8d\xf6-\bp\x8e-\x17\x06\x88\x00m\xce\\u+U\xbb%C3SQ\xdfAH\x1fm\xb5\xfaA\x13.cP\xf1\xbe\x0e\x16.\x82\n\xbb\x99\x99\x88z*\x99J\xd9\xfd|\xa8\x1bZܠ\r\x89\xd4i\xae\xee\x02ζ\xcc\xee\x12,\xe5\xb6T\xad\xe9\x16\x96\x99\xe4\x1cPZ\xf7\xc7\xf5\x92kݟl\xfb\x02TON\xedc\xbb\xad\x8fe9j\xbb\x10.u\xc9\xd4x9\x93a\n\x9a{\xd2z\x03\x92\xd8\U0006c34d\xc3B\xf4\x12\xb1\xfeH\xdbmê\xf3b\xd9{H\xfd\x1db\v\xbf\xa3\x8e\xf3cA\x7f\x97jA\n&\xec?T\xe4.\x14\x15>\x9e5\xfe\x9d\x94\x0fw\x11#\xb67\xf8\x9f\xeb\x86M\x90\x80\t7l<\x8e\xb8\x96\x95\x8f#\xd7\x06m< \x81u\xd7ϼQC\x98#\xfa\xa07\x9dA_\xe8\xcf\x1dH\x93\xaa\xc0\xf5<\x00\xeb.\\T\xc5\xf9aq\f\xf9\xe8R\xbc\x06v\xab.\xbd7\x03\x9a\xd3\xee\x03\x1d\x85XN\x14H]V\xa1-\xd0O\xd9/z4\x0f\x19\x93=\x1c\xffܴ\x1e£\x1bf\xcb\xdc\x1b\x98`\xc7\b<\xefV\x17/!\x98`\xfe[ۦ>\x19\xdfڸ\x85|\xa7A\xffV\xfcd\xf5\x92|\x82\xbe\xa3\xdf\x1d\x96\x86\x1cs\fpUE\x9a\xacĭ\x92[\x05\xba\xcftK\xf2w\xca\f\x13ۏR\xdd\xf2j\xcb\xc4\xe7\xe1\x83!c\x8do\xa92\xcc2\xad\x1bOl\xa0LP\xce\xfe\x19\x93O\xed\x97Ӏn\x067JK\x920\x8c\xa1\x17\xef\xc1ڪ\x83\xfb\xfb\xa8(,=^O\xb1;\x02M\xa6dcm\x1346E\xe8\xf6\x92|\x92\xd1\x05\xee\x13tX\x17\xa65\xad@\x9b%l6R\x19\x17\xaf].\t\xdb\x04'\x82\x95\x1d\xe89rW\n\x12\x16\v\xb4֩\x0f\x8d\x1aB\xb7\xafBm\x8a\x05\xc7\vzp\xb1\x19\x9ae\x95\xb5\x94\xae\xb4\xa1\xc5[\x0e\xd6\xf4\xd2\x00\xdd\xedӬm\xf2\xfe\x8c~\xa3s:\x8d\xc2]\xd5\xe7\xf1\x9a\xec\xcf\xe8.z1_\xd1y\xa7\xfcH\xf1\xa6ۓV\xed\xdf\xfd\xb7\x11g\x91\a{nwQ\xcb[\x14\x06\xfe\xaa\xfe\xa2\xa8V\xea\xfd\x88r:oI\vߓ\xff\xe5\x7f\x03\x00\x00\xff\xff\xe1\xa0\x1ak\x81\x7f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xccYI\x93b\xb9\x11\xbe\xf3+2b\x0es\xe9\aݶ\x0f\x0e.\x0e\x9a\xb2#:\\\xed\xaah\xca\xe5\xeb\b)\x01\rzҳ\x16h\xbc\xfcwGj\x81\xc7[\x1a\xaa\xed\x18\x8f.UOK*\xd7/3EUU\x13\xd6\xc8W\xb4N\x1a=\a\xd6H\xfc\xeaQӗ\x9b\xee\x7f\xef\xa6\xd2\xcc\x0e\x1f&{\xa9\xc5\x1c\x96\xc1yS\x7fAg\x82\xe5\xf8\x80\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d}\x02p\xa3\xbd5J\xa1\xad\xb6\xa8\xa7\xfb\xb0\xc6u\x90J\xa0\x8d\xc4\xcbՇ\xf7\xd3\x0f\xbf\x9b\xbe\x9f\x00hV\xe3\x1c\u058c\xefC㼱l\x8b\xca\xf0Drz@\x85\xd6L\xa5\x99\xb8\x069ݰ\xb5&4s\xb8,$\n\xf9\xf6\xc4\xf9\xc7Hl\x95\x88=fbq]I\xe7\xff<\xbe\xe7Q:\x1f\xf75*X\xa6\xc6؊[\xdc\xceX\xff\x97\xcb\xd5\x15\xac\x9dJ+Ro\x83bv\xe4\xf8\x04\xc0q\xd3\xe0\x1c\xe2\xe9\x86q\x14\x13\x80\xac\x9aH\xad\x02&DT6S\xcfVj\x8fviT\xa8\xf5\xf9.\x81\x8e[\xd9\xf8\xa8\xcc$\vda\xa0H\x03\xce3\x1f\x1c\xb8\xc0w\xc0\x1c,\x0eL*\xb6V8\xfb\xabf\xe5\xffH\x0f\xe0gg\xf43\xf3\xbb9Lөi\xb3c\xae\xac&\x1b=\xb7f\xfc\x89\x04p\xdeJ\xbd\x1db\xe9\x919\xffʔ\x14\x91\x93\x17Y#H\a~\x87\xa0\x98\xf3\xe0i\x82\xbe\x92\x86\x80T\x84P4\x04G\xe6\xf2=\x00\x87D%\xeah\x98Sջ\xeb\x8amb\x05^;T\x12\xff4\x93\xb9o\x91-\xfe=\xe5\x16\xcf$\x9dgusEw\xb1\xc51bW\xaax\xc0\r\vʷE%+\xa9\xb6_^\x8b\xd5 \x9f\x8at\xea\xeaƇ\xab\xb9t\xeb\xda\x18\x85,QI\xbb\x0e\x1f\x92\x17\xf2\x1d\xd6l\x9e7\x9b\x06\xf5\xe2\xf9\xd3\xeboWW\xd30\xe4H\x9d\xa0 ñ\x96mvh\x11^c\xfc%\xbb\xb9,ڙ&\x80Y\xff\x8c\xdc_\x8c\xd8XӠ\xf5\xb2\x04K\x1a-,j\xcdvx\xfaWu\xb5\x06@b\xa4S \b\x940\xf9U\x8e\x1f\x14Yr0\x1b\xf0;\xe9\xc0bcѡN0E\xd3Lg\x06\xa7\x1d\xd2+\xb4D\x86b;(AXv@\xeb\xc1\"7[-\xffq\xa6\xed\xc0\x9b\xec\xcc\x1e\x9d\x87\x18\xa1\x9a)rր\xef\x80iѡ\\\xb3\x13X\xa4;!\xe8\x16\xbdx\xc0u\xf9\xf8L\xd1 \xf5\xc6\xcca\xe7}\xe3\xe6\xb3\xd9V\xfa\x82\xd0\xdc\xd4u\xd0ҟf\x11l\xe5:xc\xddL\xe0\x01\xd5\xcc\xc9m\xc5,\xdfI\x8f\xdc\a\x8b3\xd6\xc8*\n\xa2\x13\xa4\xd6\xe2\a\x9b1\xdd]]\xdb\v\xe94\"\xa4\xbe\xc1<\x04\xaf\xc9e\x12\xa9$\xe2\xc5\n4E\xaa\xfb\xf2\xc7\xd5\v\x14N\x92\xa5\x92Q.[{z)\xf6!mJ\xbdA\x9b\xcem\xac\xa9#MԢ1R\xfb\xf8\xc1\x95D\xed\xc1\x85u-=\xb9\xc1\xdf\x03:O\xa6\xeb\x92]\xc6,\x06k\x84\xd0D\x90\xe8n\xf8\xa4a\xc9jTK\xe6\xf0\x17\xb6\x15Y\xc5Ud\x84\xbb\xac\xd5\xce\xcd\xdd\xcdI\xbd\xad\x85\x92SGL;\x88\x06\xab\x06\xf9U\xdc\tt\xd2Rdx\xe61FWGA\x19*Ɠr\x19\xc3 A\x83q\x8e\xce}6\x02\xbb+\x1d\x96\x17\xe7\x8dW<6hk\xe9bz\x85\x8d\xb1\xdd\xcc\xc3\xceH\xde\x1e\x05\xf1\xba\x06\a@\x1d\xea>#\x15|A&\x9e\xb4:\x8d,\xfd\xcdJ߿hĐ4\x12\x8b\xab\x93\xe6\xcfh\xa5\x117\x84\xff\xd8\xd9~V\xc1\xce\x1ca\x13\xfd_{u\"\xecr'\xcd\xfb\xa8]\xc6\xe2\xf9SA\xf0\x14[90\xb3\xae\xa6\xb0\xc8Am6\xf0\x1e\x84tTH\xb8H\xb4\xaf,\x1dT,4\xe6\xe0mx\x93\xf8\xdc\xe8\x8d\xdc\xf6\x85n\xd7Fc\x1es\x83tGs\xcbx\x13\xa1\x16yGc\xcdA\n\xb4\x15Ň\xdcH\x9e9\t6e\x90\x8dD%z\xd84\x1aeQ\x14\x8b\x82\x82\x9a\xa9\x1b6\\\x9e7\xc6J\x9aI\x9d<\xf8B b\x8d\xadsj\xd6\x1e\xb5\xc0n\xb6\x89ܘ\bh\x0e\x05\x1c\xa5\xdf%\xa4TCq\aߌ=\x1a{<\rMwx\x7f\xd9!\xedL\x89\x17\xc1!\xb7裷\xa1\"\xf7!W\x9a\x02|\x0e.bm\x17'ʈ\x05_9\xbd\xc7S_\xd1p˸\xb9\x14\xba\xcdr/{\x95A\xa5y\x11\xc4\xe2\x06-\xea^\xb5P\xc6@\x06\xa0\xb6\xc7j\xf4\x18\x93\x800\xdc\x11\xfesl\xbc\x9b\x99\x03ڃ\xc4\xe3\xech\xec^\xeamE\xe6\xa9r\xbc\xcdb33\xfb!\xfe\x19\xb9\xef\xe5\xe9\xe1i\x0e\v!\xc0\xf8\x1dZ\xb2\xf1&\xa8▭\xaa\xea]L\xde\xef H\xf1\x87\xefQ\xa2iR\x98ݡ\xc8U\f\x95\x13U\x87\x91'\xd2\xdb*\x99\xd0X\xa0\xfcK\x9eQg\xd3'`\x1a\xf2ڡ\xb2\xb6=\b\xc5(\xdd\f\xc1\xef\x1e\xfb\xc8\xfb\x8d\x98\x04\xf8Z]\xecTլ\xa9\xd2n\xe6M-\xf9\xa4+m\xac\xbdo\x84o\xa9\xf5\xa5\x16\x92Smx\x1dv\xa5\a\x12W-\xc1\x80\x1a\xbaM\xc2\x18\xd8\f\xab)\x89\x9bS\xed\r\x8e\x9f\xda{/\x9dcB\xbe\x9c>\x1dz*\xdb\x1ch\xa4\xf4\xcal_\xcf\x11o\xb8њ\x02\xdd\x1b`g\x14\xfd\xd1u\xd3\xc7\x1b\xc1g\x1d\xf8\x1e\a\x14\xdf\x13\xe5c\xdcXt\x9c\x8e\x11/\xc1a\xc4\xf5[l\xc0\xed\x88\xe0l\x89\xf6\x1e^\x96\v\xdax\xce\xc0\f\x96\vX\a-\x14\x16\x8e\x8e;\xd4Դ\xc8\xcdi\xf8.\x1a/\x8f\xab\xa2\xd5X\xbc䶣\xe8vX\x86\x94\x1e\xe6\xb0>\r\x94\x1bw\b\xd9X\xdcȯw\b\xf9\x1c7\x16\x857\xcc\xef@j'\x05\x02\x1bP\x7f\xaa\x03G\x04=\x97\x16O\x19s\xbe\xc3<\xdf\u0086\xc4\xce[\xe0\xa1\xe8\xf8F\xfc<\xe7mg-\x94\xef\x9c<\xae\xcḇ8\x1e\x94\xe8p~\xd3\xf8S*\xde\xf8@\x16\xbeb\xe6\xb5\x7f\xe2\x1bE`yY\x19\nf*9\x8c\xb5\xe8\x1a\xa3\x05\xb5l\xf7\x95\x80\x17\x96\xffw\x85\xe0\xb0Y\xabk\x94\xeb\xac\x15+\xdc\xd5\x05\xc5W\xa47\xf7A\xe9m\xad\xdde\x98\xb5\xa3\xfe\xf4\xd2\nud\xfcE:\xa0\xc1\x8a\xa6\xd5\x16Qg\xae!\xe8X\x18ƒa:\x99\f\x1cy\xa0&\x9cR\x98\x98\x93p6\x9e\xd4\xe6H\xa7[\xe4\"\x050:%|\xea\r\x99\x16\xb9+\xa7\xa5\x01\xcaG\xa9\x14\x15\x01\x16kCڢ\xb2֢:\x01s\xe4M\x87\xdfL\xdf\xff\xffZ.Ŝ\xa7\x0e\n\xc5\x17<\xc8\xfe\xd3\xd4}\xfa~\xecQ)\xf0p\x0e\x1a\xfa\xf8\xa9t\xeb3\x9b\xb7\xfd\x04\x1b\xa9\xa8\x98la\xc7\x1d\xe5\xc1\xc0\xc3\xea\xc7\xd5\xe3\x8f.\xf6\x10\xa8\xbd\x83#Y\xd0E\x96\xa8i0\xf9\x85$8OY\xe4\xb6\x03\x14{&/\x00e\xf4\x96*\xcf\xf4\\B%^\xf2'cA\xa0\xa7l\xa5\xb7\xc0wLo)6\x86@?r\x9c\xd9o3J\xee3\xea!R\x8f\xb8\xc7]\x16}\x91C=\xc1[\xac9\xfe\x8e}\xe6?\x9b\xf6\xf2\\\xdaQ\xfc\x18\xd8\x16St\x17K2'EW\xfe\xf2\xb6}\x19\xdf\xdf`\xf7\x1fοW=\xff\xd5S\x7f\xef\x89\xffW\xa1\x9c\x9a*ݛ\xe5\xf3\xe7\xb4+=x\xe6#\xc0\xd6&\xf8\x81\xec\xdfr\xf8\xc1\xa0\x8e\xbff\xbc\x85\xc7\xf8\x1bͭ\x02\x85\xf6\x14\x8b\xf0`m|\x14-\x8fu\x11*\x86\xf2\xd2\xfd\x10\xbc\xe8\xfc\x94\xd4^\xeb\xff\xd0t\x87\\\x83y\xba7\x99rmˮY\xc9홰>?u\xcf\xe1\x9f\xff\x9e\xfc'\x00\x00\xff\xff\xf3/:\xb2\x01\x1d\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xbcVMs\xdb6\x10\xbd\xebW\xecL\xaf%\x15O{\xe8\xf0\xd689x\xdaf4v&w\bX\x89\x88A\x00\xdd]\xc8u?\xfe{\a\x00)K\x14\xe5$\x97\xf0&`\xb1\xfb\xf0\xde\ue0da\xa6Y\xa9h?!\xb1\r\xbe\x03\x15-\xfe%\xe8\xf3/n\x1f\x7f\xe1ֆ\xf5\xe1f\xf5h\xbd\xe9\xe06\xb1\x84\xe1\x1e9$\xd2\xf8\x0ew\xd6[\xb1\xc1\xaf\x06\x14e\x94\xa8n\x05\xa0\xbc\x0f\xa2\xf22\xe7\x9f\x00:x\xa1\xe0\x1cR\xb3G\xdf>\xa6-n\x93u\x06\xa9$\x9fJ\x1f\u07b47?\xb7oV\x00^\r\u0601A\x87\x82[\xa5\x1fS$\xfc3!\v\xb7\atH\xa1\xb5a\xc5\x11uο\xa7\x90b\a/\x1b\xf5\xfcX\xbb\xe2~WR\xbd-\xa9\xeek\xaa\xb2\xeb,\xcbo\xd7\"~\xb7cTt\x89\x94[\x06T\x02\xd8\xfa}r\x8a\x16CV\x00\xacC\xc4\x0e>dXQi4+\x80\xf1\xda\x05f\x03ʘB\xa4r\x1b\xb2^\x90n\x83K\xc3D`\x03\x06Y\x93\x8dR\x88\xfa\xd8c\xb9\"\x84\x1dH\x8fPˁ\x04\xd8\xe2\x88\xc0\x94s\x00\x9f9\xf8\x8d\x92\xbe\x836\xf3\xd5\xd6\xd0\fd\f\xa8T\xbf\x9d/\xcbs\x06\xccB\xd6\xef\xafA`Q\x92x\x02Q\xea\xda\xe0\x81N\xf8=\aP\xe2\xdb\xd8+>\xaf\xfeP6\xaeU\xae1\x87\x9bʴ\xeeqP\xdd\x18\x1b\"\xfa_7w\x9f~z8[\x86s\xac\v҂eP\x13\xd2L\\e\r\x82G\b\x04C\xa0\x89Un\x8fI#\x85\x88$vj\xad\xfa\x9d\f\xcf\xc9\xea\f¿\xcd\xd9\x1e@F]O\x81\xc9S\x84\\H\x1c\x9b\x02\xcdx\xd1J\xaee \x8c\x84\x8c\xbe\xceU^V\x1e\xc2\xf63jig\xa9\x1f\x90r\x1a\xe0>$g\xf2\xf0\x1d\x90\x04\bu\xd8{\xfb\xf717\xe7{\xe7\xa2NI\xa1$\xb7\x9dW\x0e\x0e\xca%\xfc\x11\x947\xb3̃z\x06\xc2\\\x13\x92?\xc9W\x0e\xf0\x1c\xc7\x1f\x99D\xebw\xa1\x83^$r\xb7^\xef\xadL\x96\xa2\xc30$o\xe5y]\xdc\xc1n\x93\x04\xe2\xb5\xc1\x03\xba5\xdb}\xa3H\xf7VPK\"\\\xabh\x9br\x11_l\xa5\x1d\xcc\x0f4\x9a\x10\x9f\x95\xbd\xe8\x9e\xfa\x15\x17\xf8\x06y\xb2'\xd4\x1e\xa9\xa9\xea\x15_T\xc8K\x99\xba\xfb\xf7\x0f\x1faBR\x95\xaa\xa2\xbc\x84^\xf02\xe9\x93ٴ~\x87T\xcf\xed(\f%'z\x13\x83\xf5R~hg\xd1\vp\xda\x0eVx\xea\xd8,\xdd<\xedm\xb1\xdd\xec\x00)\x1a%h\xe6\x01w\x1enՀ\xeeV1~g\xad\xb2*\xdcd\x11\xbeJ\xad\xd3\xc7d\x1e\\\xe9=٘\x9e\x81+\xd2.\f\xffCD\x9d\xc5\xcd\xfc\xe6\xd3vgu\x1d\xab] x\xea\xad\xee\xa7\xe1\x9f\xd1t4\x8as\xfe\x96\x8d!\x7f/v;߹zy(\"[\xc2Y\xc36p\xe1ݯ\xf3RL\xf5\x1b\x99\xa9\x8e>r\xa3\x13Qi\xbe\xa3ϫ\xa5C_\xcb\x05\x12\x05\xbaX\x9d\x81z_\x82\xca?\x06e=\x83\xf2\xcf\xe3A\x90^\t> 10 + metadataCacheLimit := (cacheLimit / 5) >> 10 + return repo.ConnectOptions{ CachingOptions: content.CachingOptions{ - ContentCacheSizeBytes: maxDataCacheMB << 20, - MetadataCacheSizeBytes: maxMetadataCacheMB << 20, - MaxListCacheDuration: content.DurationSeconds(time.Duration(maxCacheDurationSecond) * time.Second), + // softLimit 80% + ContentCacheSizeBytes: (dataCacheLimit / 5 * 4) << 10, + MetadataCacheSizeBytes: (metadataCacheLimit / 5 * 4) << 10, + // hardLimit 100% + ContentCacheSizeLimitBytes: dataCacheLimit << 10, + MetadataCacheSizeLimitBytes: metadataCacheLimit << 10, + MaxListCacheDuration: content.DurationSeconds(time.Duration(maxCacheDurationSecond) * time.Second), }, ClientOptions: repo.ClientOptions{ Hostname: optionalHaveString(udmrepo.GenOptionOwnerDomain, repoOptions.GeneralOptions), diff --git a/pkg/repository/udmrepo/kopialib/backend/utils.go b/pkg/repository/udmrepo/kopialib/backend/utils.go index a740a0b7b..62ba4c322 100644 --- a/pkg/repository/udmrepo/kopialib/backend/utils.go +++ b/pkg/repository/udmrepo/kopialib/backend/utils.go @@ -98,6 +98,21 @@ func optionalHaveBase64(ctx context.Context, key string, flags map[string]string return nil } +func optionalHaveIntWithDefault(ctx context.Context, key string, flags map[string]string, defValue int64) int64 { + if value, exist := flags[key]; exist { + if value != "" { + ret, err := strconv.ParseInt(value, 10, 64) + if err == nil { + return ret + } + + backendLog()(ctx).Errorf("Ignore %s, value [%s] is invalid, err %v", key, value, err) + } + } + + return defValue +} + func backendLog() func(ctx context.Context) logging.Logger { return logging.Module("kopialib-bd") } diff --git a/pkg/repository/udmrepo/repo_options.go b/pkg/repository/udmrepo/repo_options.go index af54e0947..28eadfdb9 100644 --- a/pkg/repository/udmrepo/repo_options.go +++ b/pkg/repository/udmrepo/repo_options.go @@ -63,6 +63,8 @@ const ( StoreOptionGenRetentionPeriod = "retentionPeriod" StoreOptionGenReadOnly = "readOnly" + StoreOptionCacheLimit = "cacheLimitMB" + ThrottleOptionReadOps = "readOPS" ThrottleOptionWriteOps = "writeOPS" ThrottleOptionListOps = "listOPS" From 1a167f9ebfad34b61fdad28fd4554b7ed82058a4 Mon Sep 17 00:00:00 2001 From: Anshul Ahuja Date: Wed, 31 Jul 2024 20:23:39 +0530 Subject: [PATCH 28/69] Fail Delete Backup if BSL is not available (#8029) * Fail Delete Backup if BSL is not available Signed-off-by: Anshul Ahuja * linter Signed-off-by: Anshul Ahuja --------- Signed-off-by: Anshul Ahuja --- pkg/controller/backup_deletion_controller.go | 6 +++++- pkg/controller/backup_deletion_controller_test.go | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index a37f1f9e8..86612f871 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -248,7 +248,11 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque backupStore, err := r.backupStoreGetter.Get(location, pluginManager, log) if err != nil { - return ctrl.Result{}, errors.Wrap(err, "error getting the backup store") + _, patchErr := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { + r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed + r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("cannot delete backup because backup storage location %s is currently unavailable, error: %s", location.Name, err.Error())) + }) + return ctrl.Result{}, patchErr } actions, err := pluginManager.GetDeleteItemActions() diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index c82a90bca..e36876f87 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -125,8 +125,13 @@ func TestBackupDeletionControllerReconcile(t *testing.T) { td := setupBackupDeletionControllerTest(t, defaultTestDbr(), location, backup) td.controller.backupStoreGetter = &fakeErrorBackupStoreGetter{} _, err := td.controller.Reconcile(ctx, td.req) - assert.Error(t, err) - assert.True(t, strings.HasPrefix(err.Error(), "error getting the backup store")) + require.NoError(t, err) + res := &velerov1api.DeleteBackupRequest{} + err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) + require.NoError(t, err) + assert.Equal(t, "Processed", string(res.Status.Phase)) + assert.Len(t, res.Status.Errors, 1) + assert.True(t, strings.HasPrefix(res.Status.Errors[0], fmt.Sprintf("cannot delete backup because backup storage location %s is currently unavailable", location.Name))) }) t.Run("missing spec.backupName", func(t *testing.T) { From 545a0e2112865bbb09aebbb37b1bf23b08dd0076 Mon Sep 17 00:00:00 2001 From: Michael Fruchtman Date: Wed, 31 Jul 2024 14:22:04 -0700 Subject: [PATCH 29/69] Doc update Openshift IBM Cloud Updates the documentation for CSI snapshot data movement for OpenShift on IBM Cloud. The default hostpath /var/lib/kubelet/pods cannot find PersistentVolumeClaims with volumeMode: Block on host. The correct hostpath for OpenShift on IBM Cloud is /var/data/kubelet/pods. Signed-off-by: Michael Fruchtman --- .../docs/main/csi-snapshot-data-movement.md | 18 ++++++++++++++++++ .../docs/v1.13/csi-snapshot-data-movement.md | 18 ++++++++++++++++++ .../docs/v1.14/csi-snapshot-data-movement.md | 18 ++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/site/content/docs/main/csi-snapshot-data-movement.md b/site/content/docs/main/csi-snapshot-data-movement.md index 78967ccde..f17283416 100644 --- a/site/content/docs/main/csi-snapshot-data-movement.md +++ b/site/content/docs/main/csi-snapshot-data-movement.md @@ -121,6 +121,24 @@ oc annotate namespace openshift.io/node-selector="" oc create -n -f ds.yaml ``` +**OpenShift on IBM Cloud** + + +Update the host path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to +`/var/data/kubelet/pods`. + +```yaml +hostPath: + path: /var/lib/kubelet/pods +``` + +to + +```yaml +hostPath: + path: /var/data/kubet/pods +``` + **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** You need to enable the `Allow Privileged` option in your plan configuration so that Velero is able to mount the hostpath. diff --git a/site/content/docs/v1.13/csi-snapshot-data-movement.md b/site/content/docs/v1.13/csi-snapshot-data-movement.md index b6a03ad12..b9743f2dc 100644 --- a/site/content/docs/v1.13/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.13/csi-snapshot-data-movement.md @@ -121,6 +121,24 @@ oc annotate namespace openshift.io/node-selector="" oc create -n -f ds.yaml ``` +**OpenShift on IBM Cloud** + + +Update the host path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to +`/var/data/kubelet/pods`. + +```yaml +hostPath: + path: /var/lib/kubelet/pods +``` + +to + +```yaml +hostPath: + path: /var/data/kubet/pods +``` + **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** You need to enable the `Allow Privileged` option in your plan configuration so that Velero is able to mount the hostpath. diff --git a/site/content/docs/v1.14/csi-snapshot-data-movement.md b/site/content/docs/v1.14/csi-snapshot-data-movement.md index 78967ccde..f17283416 100644 --- a/site/content/docs/v1.14/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.14/csi-snapshot-data-movement.md @@ -121,6 +121,24 @@ oc annotate namespace openshift.io/node-selector="" oc create -n -f ds.yaml ``` +**OpenShift on IBM Cloud** + + +Update the host path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to +`/var/data/kubelet/pods`. + +```yaml +hostPath: + path: /var/lib/kubelet/pods +``` + +to + +```yaml +hostPath: + path: /var/data/kubet/pods +``` + **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** You need to enable the `Allow Privileged` option in your plan configuration so that Velero is able to mount the hostpath. From c1e3d6f40e3c373c32aed2bb29c55636bd214387 Mon Sep 17 00:00:00 2001 From: Michael Fruchtman Date: Wed, 31 Jul 2024 14:31:22 -0700 Subject: [PATCH 30/69] Add OpenShift on IBM Cloud to list Signed-off-by: Michael Fruchtman --- site/content/docs/main/csi-snapshot-data-movement.md | 2 +- site/content/docs/v1.13/csi-snapshot-data-movement.md | 2 +- site/content/docs/v1.14/csi-snapshot-data-movement.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/docs/main/csi-snapshot-data-movement.md b/site/content/docs/main/csi-snapshot-data-movement.md index f17283416..f586ba0e5 100644 --- a/site/content/docs/main/csi-snapshot-data-movement.md +++ b/site/content/docs/main/csi-snapshot-data-movement.md @@ -41,7 +41,7 @@ velero install --use-node-agent ### Configure Node Agent DaemonSet spec After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the node-agent DaemonSet spec. -The steps in this section are only needed if you are installing on RancherOS, Nutanix, OpenShift, VMware Tanzu Kubernetes Grid +The steps in this section are only needed if you are installing on RancherOS, Nutanix, OpenShift, OpenShift on IBM Cloud, VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. diff --git a/site/content/docs/v1.13/csi-snapshot-data-movement.md b/site/content/docs/v1.13/csi-snapshot-data-movement.md index b9743f2dc..7c005e168 100644 --- a/site/content/docs/v1.13/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.13/csi-snapshot-data-movement.md @@ -41,7 +41,7 @@ velero install --use-node-agent ### Configure Node Agent DaemonSet spec After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the node-agent DaemonSet spec. -The steps in this section are only needed if you are installing on RancherOS, Nutanix, OpenShift, VMware Tanzu Kubernetes Grid +The steps in this section are only needed if you are installing on RancherOS, Nutanix, OpenShift, OpenShift on IBM Cloud, VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. diff --git a/site/content/docs/v1.14/csi-snapshot-data-movement.md b/site/content/docs/v1.14/csi-snapshot-data-movement.md index f17283416..f586ba0e5 100644 --- a/site/content/docs/v1.14/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.14/csi-snapshot-data-movement.md @@ -41,7 +41,7 @@ velero install --use-node-agent ### Configure Node Agent DaemonSet spec After installation, some PaaS/CaaS platforms based on Kubernetes also require modifications the node-agent DaemonSet spec. -The steps in this section are only needed if you are installing on RancherOS, Nutanix, OpenShift, VMware Tanzu Kubernetes Grid +The steps in this section are only needed if you are installing on RancherOS, Nutanix, OpenShift, OpenShift on IBM Cloud, VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS), or Microsoft Azure. From 2fa71e41b2c9c1f9cfc7cc61f53d2ceca95ecb99 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Wed, 31 Jul 2024 15:45:33 -0700 Subject: [PATCH 31/69] Update docs for volume policy feature Signed-off-by: Shubham Pampattiwar --- site/content/docs/main/resource-filtering.md | 23 ++-- site/content/docs/v1.14/resource-filtering.md | 123 +++++++++--------- 2 files changed, 72 insertions(+), 74 deletions(-) diff --git a/site/content/docs/main/resource-filtering.md b/site/content/docs/main/resource-filtering.md index e80afce33..ce17b4370 100644 --- a/site/content/docs/main/resource-filtering.md +++ b/site/content/docs/main/resource-filtering.md @@ -223,7 +223,13 @@ Kubernetes namespace resources to exclude from the backup, formatted as resource ``` ## Resource policies -Velero provides resource policies to filter resources to do backup or restore. currently, it only supports skip backup volume by resource policies. +Velero provides resource policies to filter resources to do backup or restore. + +### Supported VolumePolicy actions +There are three actions supported via the VolumePolicy feature: +* skip: don't back up the action matching volume's data. +* snapshot: back up the action matching volume's data by the snapshot way. +* fs-backup: back up the action matching volumes' data by the fs-backup way. ### Creating resource policies @@ -243,15 +249,14 @@ Below is the two-step of using resource policies to skip backup of volume: This flag could also be combined with the other include and exclude filters above ### YAML template - -Velero only support volume resource policies currently, other kinds of resource policies could be extended in the future. The policies YAML config file would look like this: +The policies YAML config file would look like this: - Yaml template: ```yaml # currently only supports v1 version version: v1 volumePolicies: # each policy consists of a list of conditions and an action - # we could have lots of policies, but if the resource matched the first policy, the latters will be ignored + # we could have lots of policies, but if the resource matched the first policy, the latter will be ignored # each key in the object is one condition, and one policy will apply to resources that meet ALL conditions # NOTE: capacity or storageClass is suited for [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes), and pod [Volume](https://kubernetes.io/docs/concepts/storage/volumes) not support it. - conditions: @@ -278,7 +283,7 @@ Velero only support volume resource policies currently, other kinds of resource nfs: server: 192.168.200.90 action: - type: skip + type: fs-backup - conditions: # nfs could be empty which matches any nfs volume source nfs: {} @@ -288,7 +293,7 @@ Velero only support volume resource policies currently, other kinds of resource # csi could be empty which matches any csi volume source csi: {} action: - type: skip + type: snapshot - conditions: volumeTypes: - emptyDir @@ -367,12 +372,6 @@ Velero supported conditions and format listed below: * The filesystem volume backup opt-in/opt-out way has the third priority. * The `backup.Spec.SnapshotVolumes` has the fourth priority. -### Supported VolumePolicy actions -By now, there are three supporting action types: -* skip: don't back up the action matching volume's data. -* snapshot: back up the action matching volume's data by the snapshot way. -* fs-backup: back up the action matching volumes' data by the fs-backup way. - #### Support for `fs-backup` and `snapshot` actions via volume policy feature - Starting from velero 1.14, the resource policy/volume policy feature has been extended to support more actions like `fs-backup` and `snapshot`. - This feature only extends the action aspect of volume policy and not criteria aspect, the criteria components as described above remain the same. diff --git a/site/content/docs/v1.14/resource-filtering.md b/site/content/docs/v1.14/resource-filtering.md index e80afce33..9072d2420 100644 --- a/site/content/docs/v1.14/resource-filtering.md +++ b/site/content/docs/v1.14/resource-filtering.md @@ -63,11 +63,11 @@ Includes cluster-scoped resources. Cannot work with `--include-cluster-scoped-re * `nil` ("auto" or not supplied): - - Cluster-scoped resources are included when backing up or restoring all namespaces. Default: `true`. + - Cluster-scoped resources are included when backing up or restoring all namespaces. Default: `true`. - - Cluster-scoped resources are not included when namespace filtering is used. Default: `false`. + - Cluster-scoped resources are not included when namespace filtering is used. Default: `false`. - * Some related cluster-scoped resources may still be backed/restored up if triggered by a custom action (for example, PVC->PV) unless `--include-cluster-resources=false`. + * Some related cluster-scoped resources may still be backed/restored up if triggered by a custom action (for example, PVC->PV) unless `--include-cluster-resources=false`. * Backup entire cluster including cluster-scoped resources. @@ -223,7 +223,13 @@ Kubernetes namespace resources to exclude from the backup, formatted as resource ``` ## Resource policies -Velero provides resource policies to filter resources to do backup or restore. currently, it only supports skip backup volume by resource policies. +Velero provides resource policies to filter resources to do backup or restore. + +### Supported VolumePolicy actions +There are three actions supported via the VolumePolicy feature: +* skip: don't back up the action matching volume's data. +* snapshot: back up the action matching volume's data by the snapshot way. +* fs-backup: back up the action matching volumes' data by the fs-backup way. ### Creating resource policies @@ -243,15 +249,14 @@ Below is the two-step of using resource policies to skip backup of volume: This flag could also be combined with the other include and exclude filters above ### YAML template - -Velero only support volume resource policies currently, other kinds of resource policies could be extended in the future. The policies YAML config file would look like this: +The policies YAML config file would look like this: - Yaml template: ```yaml # currently only supports v1 version version: v1 volumePolicies: # each policy consists of a list of conditions and an action - # we could have lots of policies, but if the resource matched the first policy, the latters will be ignored + # we could have lots of policies, but if the resource matched the first policy, the latter will be ignored # each key in the object is one condition, and one policy will apply to resources that meet ALL conditions # NOTE: capacity or storageClass is suited for [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes), and pod [Volume](https://kubernetes.io/docs/concepts/storage/volumes) not support it. - conditions: @@ -278,7 +283,7 @@ Velero only support volume resource policies currently, other kinds of resource nfs: server: 192.168.200.90 action: - type: skip + type: fs-backup - conditions: # nfs could be empty which matches any nfs volume source nfs: {} @@ -288,7 +293,7 @@ Velero only support volume resource policies currently, other kinds of resource # csi could be empty which matches any csi volume source csi: {} action: - type: skip + type: snapshot - conditions: volumeTypes: - emptyDir @@ -303,10 +308,10 @@ Velero only support volume resource policies currently, other kinds of resource Currently, Velero supports the volume attributes listed below: - capacity: matching volumes have the capacity that falls within this `capacity` range. The capacity value should include the lower value and upper value concatenated by commas, the unit of each value in capacity could be `Ti`, `Gi`, `Mi`, `Ki` etc, which is a standard storage unit in Kubernetes. And it has several combinations below: - - "0,5Gi" or "0Gi,5Gi" which means capacity or size matches from 0 to 5Gi, including value 0 and value 5Gi - - ",5Gi" which is equal to "0,5Gi" - - "5Gi," which means capacity or size matches larger than 5Gi, including value 5Gi - - "5Gi" which is not supported and will be failed in validating the configuration + - "0,5Gi" or "0Gi,5Gi" which means capacity or size matches from 0 to 5Gi, including value 0 and value 5Gi + - ",5Gi" which is equal to "0,5Gi" + - "5Gi," which means capacity or size matches larger than 5Gi, including value 5Gi + - "5Gi" which is not supported and will be failed in validating the configuration - storageClass: matching volumes those with specified `storageClass`, such as `gp2`, `ebs-sc` in eks - volume sources: matching volumes that used specified volume sources. Currently we support nfs or csi backend volume source @@ -342,7 +347,7 @@ Velero supported conditions and format listed below: server: 192.168.200.90 path: /mnt/nfs ``` - For volume provisioned by [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes) support all above attributes, but for pod [Volume](https://kubernetes.io/docs/concepts/storage/volumes) only support filtered by volume source. + For volume provisioned by [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes) support all above attributes, but for pod [Volume](https://kubernetes.io/docs/concepts/storage/volumes) only support filtered by volume source. - volume types @@ -355,7 +360,7 @@ Velero supported conditions and format listed below: - configmap - cinder ``` - Volume types could be found in [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes) and pod [Volume](https://kubernetes.io/docs/concepts/storage/volumes) + Volume types could be found in [Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes) and pod [Volume](https://kubernetes.io/docs/concepts/storage/volumes) ### Resource policies rules - Velero already has lots of include or exclude filters. the resource policies are the final filters after others include or exclude filters in one backup processing workflow. So if use a defined similar filter like the opt-in approach to backup one pod volume but skip backup of the same pod volume in resource policies, as resource policies are the final filters that are applied, the volume will not be backed up. @@ -367,43 +372,37 @@ Velero supported conditions and format listed below: * The filesystem volume backup opt-in/opt-out way has the third priority. * The `backup.Spec.SnapshotVolumes` has the fourth priority. -### Supported VolumePolicy actions -By now, there are three supporting action types: -* skip: don't back up the action matching volume's data. -* snapshot: back up the action matching volume's data by the snapshot way. -* fs-backup: back up the action matching volumes' data by the fs-backup way. - #### Support for `fs-backup` and `snapshot` actions via volume policy feature - Starting from velero 1.14, the resource policy/volume policy feature has been extended to support more actions like `fs-backup` and `snapshot`. - This feature only extends the action aspect of volume policy and not criteria aspect, the criteria components as described above remain the same. -- When we are using the volume policy approach for backing up the volumes then the volume policy criteria and action need to be specific and explicit, -there is no default behaviour, if a volume matches fs-backup action then fs-backup method will be used for that volume and similarly if the volume matches -the criteria for snapshot action then the snapshot workflow will be used for the volume backup. +- When we are using the volume policy approach for backing up the volumes then the volume policy criteria and action need to be specific and explicit, + there is no default behaviour, if a volume matches fs-backup action then fs-backup method will be used for that volume and similarly if the volume matches + the criteria for snapshot action then the snapshot workflow will be used for the volume backup. - Another thing to note is that the volume policy workflow uses the legacy opt-in/opt-out approach as a fallback option. For instance, the user specifies -a volume policy but for a particular volume included in the backup there are no actions(fs-backup/snapshot) matching in the volume policy for that volume, -in such a scenario the legacy approach will be used for backing up the particular volume. Considering everything, the recommendation would be to use only one -of the approaches to backup volumes - volume policy approach or the opt-in/opt-out legacy approach, and not mix them for clarity. + a volume policy but for a particular volume included in the backup there are no actions(fs-backup/snapshot) matching in the volume policy for that volume, + in such a scenario the legacy approach will be used for backing up the particular volume. Considering everything, the recommendation would be to use only one + of the approaches to backup volumes - volume policy approach or the opt-in/opt-out legacy approach, and not mix them for clarity. - Snapshot action can either be a native snapshot or a csi snapshot or csi snapshot datamover, as is the case with the current flow where velero itself makes the decision based on the backup CR's existing options. - The `snapshot` action via Volume Policy has higher priority if there is a `snapshot` action matching for a particular volume, this volume would be backed up via snapshot irrespective of the value of `backup.Spec.SnapshotVolumes`. - If for a particular volume there is no `snapshot` matching action then the volume will be backed up via snapshot given that `backup.Spec.SnapshotVolumes` is not explicitly set to false. - Let's see some examples on how to use the volume policy feature for `fs-backup` and `snapshot` action purposes: -We will use a simple application example in which there is an application pod which has 2 volumes: +We will use a simple application example in which there is an application pod which has 2 volumes: - Volume 1 has associated Persistent Volume Claim 1 and Persistent Volume 1 which uses storage class `gp2-csi` - Volume 2 has associated Persistent Volume Claim 2 and Persistent Volume 2 which uses storage class `gp3-csi` Now lets go through some example uses-cases and their outcomes: -***Example 1: User wants to use `fs-backup` action for backing up the volumes having storage class as `gp2-csi`*** +***Example 1: User wants to use `fs-backup` action for backing up the volumes having storage class as `gp2-csi`*** 1. User specifies the volume policy as follows: ```yaml version: v1 volumePolicies: -- conditions: - storageClass: - - gp2-csi - action: - type: fs-backup + - conditions: + storageClass: + - gp2-csi + action: + type: fs-backup ``` 2. User creates a backup using this volume policy @@ -414,11 +413,11 @@ volumePolicies: ```yaml version: v1 volumePolicies: -- conditions: - storageClass: - - gp2-csi - action: - type: snapshot + - conditions: + storageClass: + - gp2-csi + action: + type: snapshot ``` 2. User creates a backup using this volume policy 3. The outcome would be that velero would perform `snapshot` operation ***only*** on `Volume 1` as ***only*** `Volume 1` satisfies the criteria for `snapshot` action. @@ -428,16 +427,16 @@ volumePolicies: ```yaml version: v1 volumePolicies: -- conditions: - storageClass: - - gp2-csi - action: - type: snapshot -- conditions: - storageClass: - - gp3-csi - action: - type: fs-backup + - conditions: + storageClass: + - gp2-csi + action: + type: snapshot + - conditions: + storageClass: + - gp3-csi + action: + type: fs-backup ``` 2. User creates a backup using this volume policy 3. The outcome would be that velero would perform `snapshot` operation ***only*** on `Volume 1` as ***only*** `Volume 1` satisfies the criteria for `snapshot` action. Also, velero would perform `fs-backup` operation ***only*** on `Volume 2` as ***only*** `Volume 2` satisfies the criteria for `fs-backup` action. @@ -447,27 +446,27 @@ volumePolicies: ```yaml version: v1 volumePolicies: -- conditions: - storageClass: - - gp3-csi - action: - type: snapshot + - conditions: + storageClass: + - gp3-csi + action: + type: snapshot ``` 2. User creates a backup using this volume policy -3. The outcome would be that velero would perform `snapshot` operation for `Volume 2` as it matches the action criteria and velero would also perform the `fs-backup` operation for `Volume-1` via the legacy annotations based fallback approach as there is no matching action for `Volume-1` +3. The outcome would be that velero would perform `snapshot` operation for `Volume 2` as it matches the action criteria and velero would also perform the `fs-backup` operation for `Volume-1` via the legacy annotations based fallback approach as there is no matching action for `Volume-1` ***Example 5: User wants to use `fs-backup` action for backing up the volumes having storage class as `gp2-csi` and at the same time also specifies `defaultVolumesToFSBackup: true` (fallback option for no action matching volumes)*** 1. User specifies the volume policy as follows and specifies `defaultVolumesToFSBackup: true`: ```yaml version: v1 volumePolicies: -- conditions: - storageClass: - - gp2-csi - action: - type: fs-backup + - conditions: + storageClass: + - gp2-csi + action: + type: fs-backup ``` 2. User creates a backup using this volume policy 3. The outcome would be that velero would perform `fs-backup` operation on both the volumes - - `fs-backup` on `Volume 1` because `Volume 1` satisfies the criteria for `fs-backup` action. - - Also, for Volume 2 as no matching action was found so legacy approach will be used as a fallback option for this volume (`fs-backup` operation will be done as `defaultVolumesToFSBackup: true` is specified by the user). + - `fs-backup` on `Volume 1` because `Volume 1` satisfies the criteria for `fs-backup` action. + - Also, for Volume 2 as no matching action was found so legacy approach will be used as a fallback option for this volume (`fs-backup` operation will be done as `defaultVolumesToFSBackup: true` is specified by the user). From 92b9e59fd5acd40e7c49801934dfc841bb9f6c55 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Wed, 31 Jul 2024 22:15:15 -0400 Subject: [PATCH 32/69] Unused parameters Signed-off-by: Tiger Kaovilai --- pkg/controller/backup_repository_controller_test.go | 12 ++++++------ pkg/controller/backup_sync_controller_test.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/controller/backup_repository_controller_test.go b/pkg/controller/backup_repository_controller_test.go index 519f3cb02..92ef5e335 100644 --- a/pkg/controller/backup_repository_controller_test.go +++ b/pkg/controller/backup_repository_controller_test.go @@ -33,7 +33,7 @@ import ( const testMaintenanceFrequency = 10 * time.Minute -func mockBackupRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *BackupRepoReconciler { +func mockBackupRepoReconciler(t *testing.T, mockOn string, arg interface{}, ret interface{}) *BackupRepoReconciler { mgr := &repomokes.Manager{} if mockOn != "" { mgr.On(mockOn, arg).Return(ret) @@ -61,7 +61,7 @@ func mockBackupRepositoryCR() *velerov1api.BackupRepository { func TestPatchBackupRepository(t *testing.T) { rr := mockBackupRepositoryCR() - reconciler := mockBackupRepoReconciler(t, rr, "", nil, nil) + reconciler := mockBackupRepoReconciler(t, "", nil, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) err = reconciler.patchBackupRepository(context.Background(), rr, repoReady()) @@ -77,7 +77,7 @@ func TestCheckNotReadyRepo(t *testing.T) { rr.Spec.BackupStorageLocation = "default" rr.Spec.ResticIdentifier = "fake-identifier" rr.Spec.VolumeNamespace = "volume-ns-1" - reconciler := mockBackupRepoReconciler(t, rr, "PrepareRepo", rr, nil) + reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) locations := &velerov1api.BackupStorageLocation{ @@ -100,7 +100,7 @@ func TestCheckNotReadyRepo(t *testing.T) { func TestRunMaintenanceIfDue(t *testing.T) { rr := mockBackupRepositoryCR() - reconciler := mockBackupRepoReconciler(t, rr, "PruneRepo", rr, nil) + reconciler := mockBackupRepoReconciler(t, "PruneRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) lastTm := rr.Status.LastMaintenanceTime @@ -118,7 +118,7 @@ func TestRunMaintenanceIfDue(t *testing.T) { func TestInitializeRepo(t *testing.T) { rr := mockBackupRepositoryCR() rr.Spec.BackupStorageLocation = "default" - reconciler := mockBackupRepoReconciler(t, rr, "PrepareRepo", rr, nil) + reconciler := mockBackupRepoReconciler(t, "PrepareRepo", rr, nil) err := reconciler.Client.Create(context.TODO(), rr) assert.NoError(t, err) locations := &velerov1api.BackupStorageLocation{ @@ -189,7 +189,7 @@ func TestBackupRepoReconcile(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - reconciler := mockBackupRepoReconciler(t, test.repo, "", test.repo, nil) + reconciler := mockBackupRepoReconciler(t, "", test.repo, nil) err := reconciler.Client.Create(context.TODO(), test.repo) assert.NoError(t, err) _, err = reconciler.Reconcile(context.TODO(), ctrl.Request{NamespacedName: types.NamespacedName{Namespace: test.repo.Namespace, Name: test.repo.Name}}) diff --git a/pkg/controller/backup_sync_controller_test.go b/pkg/controller/backup_sync_controller_test.go index 536d0078b..1bba44134 100644 --- a/pkg/controller/backup_sync_controller_test.go +++ b/pkg/controller/backup_sync_controller_test.go @@ -144,7 +144,7 @@ func defaultLocationWithLongerLocationName(namespace string) *velerov1api.Backup } } -func numBackups(c ctrlClient.WithWatch, ns string) (int, error) { +func numBackups(c ctrlClient.WithWatch) (int, error) { var existingK8SBackups velerov1api.BackupList err := c.List(context.TODO(), &existingK8SBackups, &ctrlClient.ListOptions{}) if err != nil { @@ -692,7 +692,7 @@ var _ = Describe("Backup Sync Reconciler", func() { } r.deleteOrphanedBackups(ctx, bslName, test.cloudBackups, velerotest.NewLogger()) - numBackups, err := numBackups(client, r.namespace) + numBackups, err := numBackups(client) Expect(err).ShouldNot(HaveOccurred()) fmt.Println("") From ad6104b90a354fbd0ce2a7a95852621aba78165b Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Wed, 31 Jul 2024 22:16:11 -0400 Subject: [PATCH 33/69] unused write to field Spec Signed-off-by: Tiger Kaovilai --- pkg/controller/restore_controller_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/controller/restore_controller_test.go b/pkg/controller/restore_controller_test.go index fe86d4c09..09fd12d10 100644 --- a/pkg/controller/restore_controller_test.go +++ b/pkg/controller/restore_controller_test.go @@ -613,12 +613,6 @@ func TestRestoreReconcile(t *testing.T) { }, } - if test.restore.Spec.ScheduleName != "" && test.backup != nil { - expected.Spec = SpecPatch{ - BackupName: test.backup.Name, - } - } - if test.expectedStartTime != nil { expected.Status.StartTimestamp = test.expectedStartTime } From 6d0d1aaccc0dbf4fdf3cc7a3b86d08b787cf3ba2 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Wed, 31 Jul 2024 22:18:42 -0400 Subject: [PATCH 34/69] tautological condition: non-nil != nil https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilness#cond:~:text=p%20%3A%3D%20%26v%0A...%0Aif%20p%20!%3D%20nil%20%7B%20//%20tautological%20condition%0A%7D Signed-off-by: Tiger Kaovilai --- pkg/controller/restore_finalizer_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/restore_finalizer_controller.go b/pkg/controller/restore_finalizer_controller.go index a75ea199a..433f197a9 100644 --- a/pkg/controller/restore_finalizer_controller.go +++ b/pkg/controller/restore_finalizer_controller.go @@ -311,7 +311,7 @@ func (ctx *finalizerContext) patchDynamicPVWithVolumeInfo() (errs results.Result // We are handling a common but specific scenario where a PVC is in a pending state and uses a storage class with // VolumeBindingMode set to WaitForFirstConsumer. In this case, the PV patch step is skipped to avoid // failures due to the PVC not being bound, which could cause a timeout and result in a failed restore. - if pvc != nil && pvc.Status.Phase == v1.ClaimPending { + if pvc.Status.Phase == v1.ClaimPending { // check if storage class used has VolumeBindingMode as WaitForFirstConsumer scName := *pvc.Spec.StorageClassName sc := &storagev1api.StorageClass{} From 514ba56ca1765c147437e430abd9691e70263e2c Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 1 Aug 2024 13:07:22 +0800 Subject: [PATCH 35/69] data mover ms new controller Signed-off-by: Lyndon-Li --- pkg/cmd/cli/datamover/restore.go | 3 +-- pkg/controller/data_download_controller.go | 4 ++-- pkg/controller/data_upload_controller.go | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pkg/cmd/cli/datamover/restore.go b/pkg/cmd/cli/datamover/restore.go index fbd92fa18..fb46abd40 100644 --- a/pkg/cmd/cli/datamover/restore.go +++ b/pkg/cmd/cli/datamover/restore.go @@ -198,8 +198,7 @@ func (s *dataMoverRestore) run() { } }() - // TODOOO: call s.runDataPath() - time.Sleep(time.Duration(1<<63 - 1)) + s.runDataPath() } func (s *dataMoverRestore) runDataPath() { diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index bf3c8e7df..abb1da2ae 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -336,7 +336,7 @@ func (r *DataDownloadReconciler) runCancelableDataPath(ctx context.Context, asyn return r.errorOut(ctx, dd, err, "error to initialize asyncBR", log) } - log.Infof("async restore init for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) + log.Infof("async restore init for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) if err := asyncBR.StartRestore(dd.Spec.SnapshotID, datapath.AccessPoint{ ByPath: res.ByPod.VolumeName, @@ -344,7 +344,7 @@ func (r *DataDownloadReconciler) runCancelableDataPath(ctx context.Context, asyn return r.errorOut(ctx, dd, err, fmt.Sprintf("error starting async restore for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName), log) } - log.Info("Async restore started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) + log.Infof("Async restore started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) return ctrl.Result{}, nil } diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 068945870..7cc7ee41c 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -347,7 +347,7 @@ func (r *DataUploadReconciler) runCancelableDataUpload(ctx context.Context, asyn return r.errorOut(ctx, du, err, "error to initialize asyncBR", log) } - log.Infof("async backup init for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) + log.Infof("async backup init for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) if err := asyncBR.StartBackup(datapath.AccessPoint{ ByPath: res.ByPod.VolumeName, @@ -355,7 +355,7 @@ func (r *DataUploadReconciler) runCancelableDataUpload(ctx context.Context, asyn return r.errorOut(ctx, du, err, fmt.Sprintf("error starting async backup for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName), log) } - log.Info("Async backup started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) + log.Infof("Async backup started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) return ctrl.Result{}, nil } From 903458b61b7831f2b0609b6a8e6e9e92e2ae29a6 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 1 Aug 2024 14:58:19 +0800 Subject: [PATCH 36/69] data mover ms new controller Signed-off-by: Lyndon-Li --- changelogs/unreleased/8074-Lyndon-Li | 1 + pkg/controller/data_download_controller.go | 12 +++++++++--- pkg/controller/data_upload_controller.go | 12 +++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/8074-Lyndon-Li diff --git a/changelogs/unreleased/8074-Lyndon-Li b/changelogs/unreleased/8074-Lyndon-Li new file mode 100644 index 000000000..ea7acad68 --- /dev/null +++ b/changelogs/unreleased/8074-Lyndon-Li @@ -0,0 +1 @@ +Data mover micro service DUCR/DDCR controller refactor according to design #7576 \ No newline at end of file diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index abb1da2ae..f0c0c1728 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -349,7 +349,9 @@ func (r *DataDownloadReconciler) runCancelableDataPath(ctx context.Context, asyn } func (r *DataDownloadReconciler) OnDataDownloadCompleted(ctx context.Context, namespace string, ddName string, result datapath.Result) { - defer r.closeDataPath(ctx, ddName) + defer func() { + go r.closeDataPath(ctx, ddName) + }() log := r.logger.WithField("datadownload", ddName) log.Info("Async fs restore data path completed") @@ -382,7 +384,9 @@ func (r *DataDownloadReconciler) OnDataDownloadCompleted(ctx context.Context, na } func (r *DataDownloadReconciler) OnDataDownloadFailed(ctx context.Context, namespace string, ddName string, err error) { - defer r.closeDataPath(ctx, ddName) + defer func() { + go r.closeDataPath(ctx, ddName) + }() log := r.logger.WithField("datadownload", ddName) @@ -399,7 +403,9 @@ func (r *DataDownloadReconciler) OnDataDownloadFailed(ctx context.Context, names } func (r *DataDownloadReconciler) OnDataDownloadCancelled(ctx context.Context, namespace string, ddName string) { - defer r.closeDataPath(ctx, ddName) + defer func() { + go r.closeDataPath(ctx, ddName) + }() log := r.logger.WithField("datadownload", ddName) diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 7cc7ee41c..91413a8cc 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -360,7 +360,9 @@ func (r *DataUploadReconciler) runCancelableDataUpload(ctx context.Context, asyn } func (r *DataUploadReconciler) OnDataUploadCompleted(ctx context.Context, namespace string, duName string, result datapath.Result) { - defer r.closeDataPath(ctx, duName) + defer func() { + go r.closeDataPath(ctx, duName) + }() log := r.logger.WithField("dataupload", duName) @@ -404,7 +406,9 @@ func (r *DataUploadReconciler) OnDataUploadCompleted(ctx context.Context, namesp } func (r *DataUploadReconciler) OnDataUploadFailed(ctx context.Context, namespace, duName string, err error) { - defer r.closeDataPath(ctx, duName) + defer func() { + go r.closeDataPath(ctx, duName) + }() log := r.logger.WithField("dataupload", duName) @@ -421,7 +425,9 @@ func (r *DataUploadReconciler) OnDataUploadFailed(ctx context.Context, namespace } func (r *DataUploadReconciler) OnDataUploadCancelled(ctx context.Context, namespace string, duName string) { - defer r.closeDataPath(ctx, duName) + defer func() { + go r.closeDataPath(ctx, duName) + }() log := r.logger.WithField("dataupload", duName) From 49a7fe74a97613098db3f15a6e427c94a1deab6a Mon Sep 17 00:00:00 2001 From: Michael Fruchtman Date: Thu, 1 Aug 2024 09:32:21 -0700 Subject: [PATCH 37/69] s/kubet/kubelet Signed-off-by: Michael Fruchtman --- site/content/docs/main/csi-snapshot-data-movement.md | 2 +- site/content/docs/v1.13/csi-snapshot-data-movement.md | 2 +- site/content/docs/v1.14/csi-snapshot-data-movement.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/docs/main/csi-snapshot-data-movement.md b/site/content/docs/main/csi-snapshot-data-movement.md index f586ba0e5..5912391da 100644 --- a/site/content/docs/main/csi-snapshot-data-movement.md +++ b/site/content/docs/main/csi-snapshot-data-movement.md @@ -136,7 +136,7 @@ to ```yaml hostPath: - path: /var/data/kubet/pods + path: /var/data/kubelet/pods ``` **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** diff --git a/site/content/docs/v1.13/csi-snapshot-data-movement.md b/site/content/docs/v1.13/csi-snapshot-data-movement.md index 7c005e168..cc45b24f9 100644 --- a/site/content/docs/v1.13/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.13/csi-snapshot-data-movement.md @@ -136,7 +136,7 @@ to ```yaml hostPath: - path: /var/data/kubet/pods + path: /var/data/kubelet/pods ``` **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** diff --git a/site/content/docs/v1.14/csi-snapshot-data-movement.md b/site/content/docs/v1.14/csi-snapshot-data-movement.md index f586ba0e5..5912391da 100644 --- a/site/content/docs/v1.14/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.14/csi-snapshot-data-movement.md @@ -136,7 +136,7 @@ to ```yaml hostPath: - path: /var/data/kubet/pods + path: /var/data/kubelet/pods ``` **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** From dc38a2a879cd625bfc3e8f17b6a5d11f0df69dbc Mon Sep 17 00:00:00 2001 From: Gareth Anderson Date: Mon, 5 Aug 2024 04:16:34 +0000 Subject: [PATCH 38/69] Updated IBM COS documentation Added option checksumAlgorith, this stops 403 errors as per https://github.com/vmware-tanzu/velero/issues/7543 Added plugins line as velero install failed without this option in version 1.14.0 Removed the volumesnapshotlocation as it does not exist in 1.14.0 Signed-off-by: Gareth Anderson --- changelogs/unreleased/8082-gjanders | 1 + site/content/docs/main/contributions/ibm-config.md | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) create mode 100644 changelogs/unreleased/8082-gjanders diff --git a/changelogs/unreleased/8082-gjanders b/changelogs/unreleased/8082-gjanders new file mode 100644 index 000000000..3b5327464 --- /dev/null +++ b/changelogs/unreleased/8082-gjanders @@ -0,0 +1 @@ +Updates to IBM COS documentation to match current version diff --git a/site/content/docs/main/contributions/ibm-config.md b/site/content/docs/main/contributions/ibm-config.md index 332d0a570..8d53a2531 100644 --- a/site/content/docs/main/contributions/ibm-config.md +++ b/site/content/docs/main/contributions/ibm-config.md @@ -65,8 +65,9 @@ velero install \ --provider aws \ --bucket \ --secret-file ./credentials-velero \ + --plugins velero/velero-plugin-for-aws:v1.10.0\ --use-volume-snapshots=false \ - --backup-location-config region=,s3ForcePathStyle="true",s3Url= + --backup-location-config region=,s3ForcePathStyle="true",s3Url=,checksumAlgorithm="" ``` Velero does not have a volume snapshot plugin for IBM Cloud, so creating volume snapshots is disabled. @@ -75,12 +76,6 @@ Additionally, you can specify `--use-node-agent` to enable [File System Backup][ (Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/node-agent pods. -Once the installation is complete, remove the default `VolumeSnapshotLocation` that was created by `velero install`, since it's specific to AWS and won't work for IBM Cloud: - -```bash -kubectl -n velero delete volumesnapshotlocation.velero.io default -``` - For more complex installation needs, use either the Helm chart, or add `--dry-run -o yaml` options for generating the YAML representation for the installation. ## Installing the nginx example (optional) From 75210c7f4a5b93fbc119ccc6ac1b5b85c77c01fa Mon Sep 17 00:00:00 2001 From: Gareth Anderson Date: Mon, 5 Aug 2024 09:08:00 +0000 Subject: [PATCH 39/69] Re-adding this doc line as requested by @blackpiglet Signed-off-by: Gareth Anderson --- site/content/docs/main/contributions/ibm-config.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/content/docs/main/contributions/ibm-config.md b/site/content/docs/main/contributions/ibm-config.md index 8d53a2531..464f53c82 100644 --- a/site/content/docs/main/contributions/ibm-config.md +++ b/site/content/docs/main/contributions/ibm-config.md @@ -76,6 +76,12 @@ Additionally, you can specify `--use-node-agent` to enable [File System Backup][ (Optional) Specify [CPU and memory resource requests and limits][15] for the Velero/node-agent pods. +Once the installation is complete, remove the default `VolumeSnapshotLocation` that was created by `velero install`, since it's specific to AWS and won't work for IBM Cloud: + +```bash +kubectl -n velero delete volumesnapshotlocation.velero.io default +``` + For more complex installation needs, use either the Helm chart, or add `--dry-run -o yaml` options for generating the YAML representation for the installation. ## Installing the nginx example (optional) From a523d10802a00122104c4f050dc7101e40647f3c Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 5 Aug 2024 19:40:27 +0800 Subject: [PATCH 40/69] data mover ms node agent resume Signed-off-by: Lyndon-Li --- changelogs/unreleased/8085-Lyndon-Li | 1 + pkg/builder/data_download_builder.go | 25 ++ pkg/builder/data_upload_builder.go | 7 + pkg/cmd/cli/nodeagent/server.go | 43 +-- pkg/cmd/server/server.go | 20 +- pkg/controller/data_download_controller.go | 247 ++++++------ .../data_download_controller_test.go | 361 +++++++++++------- pkg/controller/data_upload_controller.go | 253 ++++++------ pkg/controller/data_upload_controller_test.go | 358 ++++++++++------- 9 files changed, 806 insertions(+), 509 deletions(-) create mode 100644 changelogs/unreleased/8085-Lyndon-Li diff --git a/changelogs/unreleased/8085-Lyndon-Li b/changelogs/unreleased/8085-Lyndon-Li new file mode 100644 index 000000000..f063cdfc1 --- /dev/null +++ b/changelogs/unreleased/8085-Lyndon-Li @@ -0,0 +1 @@ +According to design #7576, after node-agent restarts, if a DU/DD is in InProgress status, re-capture the data mover ms pod and continue the execution \ No newline at end of file diff --git a/pkg/builder/data_download_builder.go b/pkg/builder/data_download_builder.go index 9a85c7905..09bd498a0 100644 --- a/pkg/builder/data_download_builder.go +++ b/pkg/builder/data_download_builder.go @@ -19,6 +19,7 @@ package builder import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" ) @@ -116,3 +117,27 @@ func (d *DataDownloadBuilder) StartTimestamp(startTime *metav1.Time) *DataDownlo d.object.Status.StartTimestamp = startTime return d } + +// CompletionTimestamp sets the DataDownload's StartTimestamp. +func (d *DataDownloadBuilder) CompletionTimestamp(completionTimestamp *metav1.Time) *DataDownloadBuilder { + d.object.Status.CompletionTimestamp = completionTimestamp + return d +} + +// Labels sets the DataDownload's Labels. +func (d *DataDownloadBuilder) Labels(labels map[string]string) *DataDownloadBuilder { + d.object.Labels = labels + return d +} + +// Labels sets the DataDownload's Progress. +func (d *DataDownloadBuilder) Progress(progress shared.DataMoveOperationProgress) *DataDownloadBuilder { + d.object.Status.Progress = progress + return d +} + +// Node sets the DataDownload's Node. +func (d *DataDownloadBuilder) Node(node string) *DataDownloadBuilder { + d.object.Status.Node = node + return d +} diff --git a/pkg/builder/data_upload_builder.go b/pkg/builder/data_upload_builder.go index 7ff33dcb0..0bc28b860 100644 --- a/pkg/builder/data_upload_builder.go +++ b/pkg/builder/data_upload_builder.go @@ -133,7 +133,14 @@ func (d *DataUploadBuilder) Labels(labels map[string]string) *DataUploadBuilder return d } +// Labels sets the DataUpload's Progress. func (d *DataUploadBuilder) Progress(progress shared.DataMoveOperationProgress) *DataUploadBuilder { d.object.Status.Progress = progress return d } + +// Node sets the DataUpload's Node. +func (d *DataUploadBuilder) Node(node string) *DataUploadBuilder { + d.object.Status.Node = node + return d +} diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 61dd0b006..b6e364523 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -292,18 +292,28 @@ func (s *nodeAgentServer) run() { if s.dataPathConfigs != nil && len(s.dataPathConfigs.LoadAffinity) > 0 { loadAffinity = s.dataPathConfigs.LoadAffinity[0] } - dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) - s.attemptDataUploadResume(dataUploadReconciler) + dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataUploadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data upload controller") } - dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.kubeClient, s.dataPathMgr, repoEnsurer, credentialGetter, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) - s.attemptDataDownloadResume(dataDownloadReconciler) + dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, repoEnsurer, credentialGetter, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataDownloadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data download controller") } + go func() { + s.mgr.GetCache().WaitForCacheSync(s.ctx) + + if err := dataUploadReconciler.AttemptDataUploadResume(s.ctx, s.mgr.GetClient(), s.logger.WithField("node", s.nodeName), s.namespace); err != nil { + s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data upload resume") + } + + if err := dataDownloadReconciler.AttemptDataDownloadResume(s.ctx, s.mgr.GetClient(), s.logger.WithField("node", s.nodeName), s.namespace); err != nil { + s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data download resume") + } + }() + s.logger.Info("Controllers starting...") if err := s.mgr.Start(ctrl.SetupSignalHandler()); err != nil { @@ -373,31 +383,6 @@ func (s *nodeAgentServer) markInProgressCRsFailed() { s.markInProgressPVRsFailed(client) } -func (s *nodeAgentServer) attemptDataUploadResume(r *controller.DataUploadReconciler) { - // the function is called before starting the controller manager, the embedded client isn't ready to use, so create a new one here - client, err := ctrlclient.New(s.mgr.GetConfig(), ctrlclient.Options{Scheme: s.mgr.GetScheme()}) - if err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to create client") - return - } - if err := r.AttemptDataUploadResume(s.ctx, client, s.logger.WithField("node", s.nodeName), s.namespace); err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data upload resume") - } -} - -func (s *nodeAgentServer) attemptDataDownloadResume(r *controller.DataDownloadReconciler) { - // the function is called before starting the controller manager, the embedded client isn't ready to use, so create a new one here - client, err := ctrlclient.New(s.mgr.GetConfig(), ctrlclient.Options{Scheme: s.mgr.GetScheme()}) - if err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to create client") - return - } - - if err := r.AttemptDataDownloadResume(s.ctx, client, s.logger.WithField("node", s.nodeName), s.namespace); err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data download resume") - } -} - func (s *nodeAgentServer) markInProgressPVBsFailed(client ctrlclient.Client) { pvbs := &velerov1api.PodVolumeBackupList{} if err := client.List(s.ctx, pvbs, &ctrlclient.ListOptions{Namespace: s.namespace}); err != nil { diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index d8938ca56..5d1c2b76b 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -1148,9 +1148,15 @@ func markDataUploadsCancel(ctx context.Context, client ctrlclient.Client, backup du.Status.Phase == velerov2alpha1api.DataUploadPhaseNew || du.Status.Phase == "" { err := controller.UpdateDataUploadWithRetry(ctx, client, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, log.WithField("dataupload", du.Name), - func(dataUpload *velerov2alpha1api.DataUpload) { + func(dataUpload *velerov2alpha1api.DataUpload) bool { + if dataUpload.Spec.Cancel { + return false + } + dataUpload.Spec.Cancel = true - dataUpload.Status.Message = fmt.Sprintf("found a dataupload with status %q during the velero server starting, mark it as cancel", du.Status.Phase) + dataUpload.Status.Message = fmt.Sprintf("Dataupload is in status %q during the velero server starting, mark it as cancel", du.Status.Phase) + + return true }) if err != nil { @@ -1183,9 +1189,15 @@ func markDataDownloadsCancel(ctx context.Context, client ctrlclient.Client, rest dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseNew || dd.Status.Phase == "" { err := controller.UpdateDataDownloadWithRetry(ctx, client, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, log.WithField("datadownload", dd.Name), - func(dataDownload *velerov2alpha1api.DataDownload) { + func(dataDownload *velerov2alpha1api.DataDownload) bool { + if dataDownload.Spec.Cancel { + return false + } + dataDownload.Spec.Cancel = true - dataDownload.Status.Message = fmt.Sprintf("found a datadownload with status %q during the velero server starting, mark it as cancel", dd.Status.Phase) + dataDownload.Status.Message = fmt.Sprintf("Datadownload is in status %q during the velero server starting, mark it as cancel", dd.Status.Phase) + + return true }) if err != nil { diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index c8e8cca50..c4becaa52 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -56,6 +57,7 @@ import ( type DataDownloadReconciler struct { client client.Client kubeClient kubernetes.Interface + mgr manager.Manager logger logrus.FieldLogger credentialGetter *credentials.CredentialGetter fileSystem filesystem.Interface @@ -68,11 +70,12 @@ type DataDownloadReconciler struct { metrics *metrics.ServerMetrics } -func NewDataDownloadReconciler(client client.Client, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, +func NewDataDownloadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, repoEnsurer *repository.Ensurer, credentialGetter *credentials.CredentialGetter, nodeName string, preparingTimeout time.Duration, logger logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataDownloadReconciler { return &DataDownloadReconciler{ client: client, kubeClient: kubeClient, + mgr: mgr, logger: logger.WithField("controller", "DataDownload"), credentialGetter: credentialGetter, fileSystem: filesystem.NewFileSystem(), @@ -137,9 +140,17 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request } else if controllerutil.ContainsFinalizer(dd, DataUploadDownloadFinalizer) && !dd.Spec.Cancel && !isDataDownloadInFinalState(dd) { // when delete cr we need to clear up internal resources created by Velero, here we use the cancel mechanism // to help clear up resources instead of clear them directly in case of some conflict with Expose action - if err := UpdateDataDownloadWithRetry(ctx, r.client, req.NamespacedName, log, func(dataDownload *velerov2alpha1api.DataDownload) { + log.Warnf("Cancel dd under phase %s because it is being deleted", dd.Status.Phase) + + if err := UpdateDataDownloadWithRetry(ctx, r.client, req.NamespacedName, log, func(dataDownload *velerov2alpha1api.DataDownload) bool { + if dataDownload.Spec.Cancel { + return false + } + dataDownload.Spec.Cancel = true - dataDownload.Status.Message = fmt.Sprintf("found a datadownload %s/%s is being deleted, mark it as cancel", dd.Namespace, dd.Name) + dataDownload.Status.Message = "Cancel datadownload because it is being deleted" + + return true }); err != nil { log.Errorf("failed to set cancel flag with error %s for %s/%s", err.Error(), dd.Namespace, dd.Name) return ctrl.Result{}, err @@ -552,9 +563,15 @@ func (r *DataDownloadReconciler) findSnapshotRestoreForPod(ctx context.Context, } } else if unrecoverable, reason := kube.IsPodUnrecoverable(pod, log); unrecoverable { err := UpdateDataDownloadWithRetry(context.Background(), r.client, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, r.logger.WithField("datadownlad", dd.Name), - func(dataDownload *velerov2alpha1api.DataDownload) { + func(dataDownload *velerov2alpha1api.DataDownload) bool { + if dataDownload.Spec.Cancel { + return false + } + dataDownload.Spec.Cancel = true - dataDownload.Status.Message = fmt.Sprintf("datadownload mark as cancel to failed early for exposing pod %s/%s is in abnormal status for %s", pod.Namespace, pod.Name, reason) + dataDownload.Status.Message = fmt.Sprintf("Cancel datadownload because the exposing pod %s/%s is in abnormal status for reason %s", pod.Namespace, pod.Name, reason) + + return true }) if err != nil { @@ -575,75 +592,6 @@ func (r *DataDownloadReconciler) findSnapshotRestoreForPod(ctx context.Context, return []reconcile.Request{request} } -func (r *DataDownloadReconciler) FindDataDownloads(ctx context.Context, cli client.Client, ns string) ([]*velerov2alpha1api.DataDownload, error) { - pods := &v1.PodList{} - var dataDownloads []*velerov2alpha1api.DataDownload - if err := cli.List(ctx, pods, &client.ListOptions{Namespace: ns}); err != nil { - r.logger.WithError(errors.WithStack(err)).Error("failed to list pods on current node") - return nil, errors.Wrapf(err, "failed to list pods on current node") - } - - for _, pod := range pods.Items { - if pod.Spec.NodeName != r.nodeName { - r.logger.Debugf("Pod %s related data download will not handled by %s nodes", pod.GetName(), r.nodeName) - continue - } - dd, err := findDataDownloadByPod(cli, pod) - if err != nil { - r.logger.WithError(errors.WithStack(err)).Error("failed to get dataDownload by pod") - continue - } else if dd != nil { - dataDownloads = append(dataDownloads, dd) - } - } - return dataDownloads, nil -} - -func (r *DataDownloadReconciler) findAcceptDataDownloadsByNodeLabel(ctx context.Context, cli client.Client, ns string) ([]velerov2alpha1api.DataDownload, error) { - dataDownloads := &velerov2alpha1api.DataDownloadList{} - if err := cli.List(ctx, dataDownloads, &client.ListOptions{Namespace: ns}); err != nil { - r.logger.WithError(errors.WithStack(err)).Error("failed to list datauploads") - return nil, errors.Wrapf(err, "failed to list datauploads") - } - - var result []velerov2alpha1api.DataDownload - for _, dd := range dataDownloads.Items { - if dd.Status.Phase != velerov2alpha1api.DataDownloadPhaseAccepted { - continue - } - if dd.Labels[acceptNodeLabelKey] == r.nodeName { - result = append(result, dd) - } - } - return result, nil -} - -// CancelAcceptedDataDownload will cancel the accepted data download -func (r *DataDownloadReconciler) CancelAcceptedDataDownload(ctx context.Context, cli client.Client, ns string) { - r.logger.Infof("Canceling accepted data for node %s", r.nodeName) - dataDownloads, err := r.findAcceptDataDownloadsByNodeLabel(ctx, cli, ns) - if err != nil { - r.logger.WithError(err).Error("failed to find data downloads") - return - } - - for _, dd := range dataDownloads { - if dd.Spec.Cancel { - continue - } - err = UpdateDataDownloadWithRetry(ctx, cli, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, - r.logger.WithField("dataupload", dd.Name), func(dataDownload *velerov2alpha1api.DataDownload) { - dataDownload.Spec.Cancel = true - dataDownload.Status.Message = fmt.Sprintf("found a datadownload with status %q during the node-agent starting, mark it as cancel", dd.Status.Phase) - }) - - r.logger.Warn(dd.Status.Message) - if err != nil { - r.logger.WithError(err).Errorf("failed to set cancel flag with error %s", err.Error()) - } - } -} - func (r *DataDownloadReconciler) prepareDataDownload(ssb *velerov2alpha1api.DataDownload) { ssb.Status.Phase = velerov2alpha1api.DataDownloadPhasePrepared ssb.Status.Node = r.nodeName @@ -795,56 +743,139 @@ func isDataDownloadInFinalState(dd *velerov2alpha1api.DataDownload) bool { dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseCompleted } -func UpdateDataDownloadWithRetry(ctx context.Context, client client.Client, namespacedName types.NamespacedName, log *logrus.Entry, updateFunc func(dataDownload *velerov2alpha1api.DataDownload)) error { - return wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { +func UpdateDataDownloadWithRetry(ctx context.Context, client client.Client, namespacedName types.NamespacedName, log *logrus.Entry, updateFunc func(*velerov2alpha1api.DataDownload) bool) error { + return wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (bool, error) { dd := &velerov2alpha1api.DataDownload{} if err := client.Get(ctx, namespacedName, dd); err != nil { return false, errors.Wrap(err, "getting DataDownload") } - updateFunc(dd) - updateErr := client.Update(ctx, dd) - if updateErr != nil { - if apierrors.IsConflict(updateErr) { - log.Warnf("failed to update datadownload for %s/%s and will retry it", dd.Namespace, dd.Name) - return false, nil + if updateFunc(dd) { + err := client.Update(ctx, dd) + if err != nil { + if apierrors.IsConflict(err) { + log.Warnf("failed to update datadownload for %s/%s and will retry it", dd.Namespace, dd.Name) + return false, nil + } else { + return false, errors.Wrapf(err, "error updating datadownload %s/%s", dd.Namespace, dd.Name) + } } - log.Errorf("failed to update datadownload with error %s for %s/%s", updateErr.Error(), dd.Namespace, dd.Name) - return false, err } return true, nil }) } -func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, cli client.Client, logger *logrus.Entry, ns string) error { - if dataDownloads, err := r.FindDataDownloads(ctx, cli, ns); err != nil { - return errors.Wrapf(err, "failed to find data downloads") - } else { - for i := range dataDownloads { - dd := dataDownloads[i] - if dd.Status.Phase == velerov2alpha1api.DataDownloadPhasePrepared { - // keep doing nothing let controller re-download the data - // the Prepared CR could be still handled by datadownload controller after node-agent restart - logger.WithField("datadownload", dd.GetName()).Debug("find a datadownload with status prepared") - } else if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseInProgress { - err = UpdateDataDownloadWithRetry(ctx, cli, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, logger.WithField("datadownload", dd.Name), - func(dataDownload *velerov2alpha1api.DataDownload) { - dataDownload.Spec.Cancel = true - dataDownload.Status.Message = fmt.Sprintf("found a datadownload with status %q during the node-agent starting, mark it as cancel", dd.Status.Phase) - }) +var funcResumeCancellableDataRestore = (*DataDownloadReconciler).resumeCancellableDataPath - if err != nil { - logger.WithError(errors.WithStack(err)).Errorf("failed to mark datadownload %q into canceled", dd.GetName()) - continue - } - logger.WithField("datadownload", dd.GetName()).Debug("mark datadownload into canceled") +func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, cli client.Client, logger *logrus.Entry, ns string) error { + dataDownloads := &velerov2alpha1api.DataDownloadList{} + if err := cli.List(ctx, dataDownloads, &client.ListOptions{Namespace: ns}); err != nil { + r.logger.WithError(errors.WithStack(err)).Error("failed to list datadownloads") + return errors.Wrapf(err, "error to list datadownloads") + } + + for i := range dataDownloads.Items { + dd := &dataDownloads.Items[i] + if dd.Status.Phase == velerov2alpha1api.DataDownloadPhasePrepared { + // keep doing nothing let controller re-download the data + // the Prepared CR could be still handled by datadownload controller after node-agent restart + logger.WithField("datadownload", dd.GetName()).Debug("find a datadownload with status prepared") + } else if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseInProgress { + if dd.Status.Node != r.nodeName { + logger.WithField("dd", dd.Name).WithField("current node", r.nodeName).Infof("DD should be resumed by another node %s", dd.Status.Node) + continue + } + + err := funcResumeCancellableDataRestore(r, ctx, dd, logger) + if err == nil { + continue + } + + logger.WithField("datadownload", dd.GetName()).WithError(err).Warn("Failed to resume data path for dd, have to cancel it") + + resumeErr := err + err = UpdateDataDownloadWithRetry(ctx, cli, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, logger.WithField("datadownload", dd.Name), + func(dataDownload *velerov2alpha1api.DataDownload) bool { + if dataDownload.Spec.Cancel { + return false + } + + dataDownload.Spec.Cancel = true + dataDownload.Status.Message = fmt.Sprintf("Resume InProgress datadownload failed with error %v, mark it as cancel", resumeErr) + + return true + }) + if err != nil { + logger.WithError(errors.WithStack(err)).WithError(errors.WithStack(err)).Error("Failed to trigger dataupload cancel") + } + } else if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseAccepted { + r.logger.WithField("datadownload", dd.GetName()).Warn("Cancel dd under Accepted phase") + + err := UpdateDataDownloadWithRetry(ctx, cli, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, + r.logger.WithField("datadownload", dd.Name), func(dataDownload *velerov2alpha1api.DataDownload) bool { + if dataDownload.Spec.Cancel { + return false + } + + dataDownload.Spec.Cancel = true + dataDownload.Status.Message = "Datadownload is in Accepted status during the node-agent starting, mark it as cancel" + + return true + }) + if err != nil { + r.logger.WithField("datadownload", dd.GetName()).WithError(err).Errorf("Failed to trigger dataupload cancel") } } } - //If the data download is in Accepted status, the expoded PVC may be not created - // so we need to mark the data download as canceled for it may not be recoverable - r.CancelAcceptedDataDownload(ctx, cli, ns) + return nil +} + +func (r *DataDownloadReconciler) resumeCancellableDataPath(ctx context.Context, dd *velerov2alpha1api.DataDownload, log logrus.FieldLogger) error { + log.Info("Resume cancelable dataDownload") + + res, err := r.restoreExposer.GetExposed(ctx, getDataDownloadOwnerObject(dd), r.client, r.nodeName, dd.Spec.OperationTimeout.Duration) + if err != nil { + return errors.Wrapf(err, "error to get exposed volume for dd %s", dd.Name) + } + + if res == nil { + return errors.Errorf("expose info missed for dd %s", dd.Name) + } + + callbacks := datapath.Callbacks{ + OnCompleted: r.OnDataDownloadCompleted, + OnFailed: r.OnDataDownloadFailed, + OnCancelled: r.OnDataDownloadCancelled, + OnProgress: r.OnDataDownloadProgress, + } + + asyncBR, err := r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, r.kubeClient, r.mgr, datapath.TaskTypeBackup, dd.Name, dd.Namespace, res.ByPod.HostingPod.Name, res.ByPod.HostingContainer, dd.Name, callbacks, true, log) + if err != nil { + return errors.Wrapf(err, "error to create asyncBR watcher for dd %s", dd.Name) + } + + resumeComplete := false + defer func() { + if !resumeComplete { + r.closeDataPath(ctx, dd.Name) + } + }() + + if err := asyncBR.Init(ctx, nil); err != nil { + return errors.Wrapf(err, "error to init asyncBR watcher for dd %s", dd.Name) + } + + if err := asyncBR.StartRestore(dd.Spec.SnapshotID, datapath.AccessPoint{ + ByPath: res.ByPod.VolumeName, + }, nil); err != nil { + return errors.Wrapf(err, "error to resume asyncBR watche for dd %s", dd.Name) + } + + resumeComplete = true + + log.Infof("asyncBR is resumed for dd %s", dd.Name) + return nil } diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index 7c7c5dbef..356e7f49a 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -33,10 +33,12 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" clientgofake "k8s.io/client-go/kubernetes/fake" ctrl "sigs.k8s.io/controller-runtime" kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -149,7 +151,7 @@ func initDataDownloadReconcilerWithError(objects []runtime.Object, needError ... dataPathMgr := datapath.NewManager(1) - return NewDataDownloadReconciler(fakeClient, fakeKubeClient, dataPathMgr, nil, &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil + return NewDataDownloadReconciler(fakeClient, nil, fakeKubeClient, dataPathMgr, nil, &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } func TestDataDownloadReconcile(t *testing.T) { @@ -869,12 +871,11 @@ func TestUpdateDataDownloadWithRetry(t *testing.T) { testCases := []struct { Name string needErrs []bool + noChange bool ExpectErr bool }{ { - Name: "SuccessOnFirstAttempt", - needErrs: []bool{false, false, false, false}, - ExpectErr: false, + Name: "SuccessOnFirstAttempt", }, { Name: "Error get", @@ -886,6 +887,11 @@ func TestUpdateDataDownloadWithRetry(t *testing.T) { needErrs: []bool{false, false, true, false, false}, ExpectErr: true, }, + { + Name: "no change", + noChange: true, + needErrs: []bool{false, false, true, false, false}, + }, { Name: "Conflict with error timeout", needErrs: []bool{false, false, false, false, true}, @@ -901,8 +907,14 @@ func TestUpdateDataDownloadWithRetry(t *testing.T) { require.NoError(t, err) err = r.client.Create(ctx, dataDownloadBuilder().Result()) require.NoError(t, err) - updateFunc := func(dataDownload *velerov2alpha1api.DataDownload) { + updateFunc := func(dataDownload *velerov2alpha1api.DataDownload) bool { + if tc.noChange { + return false + } + dataDownload.Spec.Cancel = true + + return true } err = UpdateDataDownloadWithRetry(ctx, r.client, namespacedName, velerotest.NewLogger().WithField("name", tc.Name), updateFunc) if tc.ExpectErr { @@ -914,136 +926,115 @@ func TestUpdateDataDownloadWithRetry(t *testing.T) { } } -func TestFindDataDownloads(t *testing.T) { - tests := []struct { - name string - pod corev1.Pod - du *velerov2alpha1api.DataDownload - expectedUploads []velerov2alpha1api.DataDownload - expectedError bool - }{ - // Test case 1: Pod with matching nodeName and DataDownload label - { - name: "MatchingPod", - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: "pod-1", - Labels: map[string]string{ - velerov1api.DataDownloadLabel: dataDownloadName, - }, - }, - Spec: corev1.PodSpec{ - NodeName: "node-1", - }, - }, - du: dataDownloadBuilder().Result(), - expectedUploads: []velerov2alpha1api.DataDownload{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: dataDownloadName, - }, - }, - }, - expectedError: false, - }, - // Test case 2: Pod with non-matching nodeName - { - name: "NonMatchingNodePod", - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: "pod-2", - Labels: map[string]string{ - velerov1api.DataDownloadLabel: dataDownloadName, - }, - }, - Spec: corev1.PodSpec{ - NodeName: "node-2", - }, - }, - du: dataDownloadBuilder().Result(), - expectedUploads: []velerov2alpha1api.DataDownload{}, - expectedError: false, - }, - } +type ddResumeTestHelper struct { + resumeErr error + getExposeErr error + exposeResult *exposer.ExposeResult + asyncBR datapath.AsyncBR +} - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - r, err := initDataDownloadReconcilerWithError(nil) - require.NoError(t, err) - r.nodeName = "node-1" - err = r.client.Create(ctx, test.du) - require.NoError(t, err) - err = r.client.Create(ctx, &test.pod) - require.NoError(t, err) - uploads, err := r.FindDataDownloads(context.Background(), r.client, "velero") +func (dt *ddResumeTestHelper) resumeCancellableDataPath(_ *DataUploadReconciler, _ context.Context, _ *velerov2alpha1api.DataUpload, _ logrus.FieldLogger) error { + return dt.resumeErr +} - if test.expectedError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, len(test.expectedUploads), len(uploads)) - } - }) - } +func (dt *ddResumeTestHelper) Expose(context.Context, corev1.ObjectReference, string, string, map[string]string, time.Duration) error { + return nil +} + +func (dt *ddResumeTestHelper) GetExposed(context.Context, corev1.ObjectReference, kbclient.Client, string, time.Duration) (*exposer.ExposeResult, error) { + return dt.exposeResult, dt.getExposeErr +} + +func (dt *ddResumeTestHelper) PeekExposed(context.Context, corev1.ObjectReference) error { + return nil +} + +func (dt *ddResumeTestHelper) RebindVolume(context.Context, corev1.ObjectReference, string, string, time.Duration) error { + return nil +} + +func (dt *ddResumeTestHelper) CleanUp(context.Context, corev1.ObjectReference) {} + +func (dt *ddResumeTestHelper) newMicroServiceBRWatcher(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, string, string, string, string, + datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { + return dt.asyncBR } func TestAttemptDataDownloadResume(t *testing.T) { tests := []struct { - name string - dataUploads []velerov2alpha1api.DataDownload - du *velerov2alpha1api.DataDownload - pod *corev1.Pod - needErrs []bool - acceptedDataDownloads []string - prepareddDataDownloads []string - cancelledDataDownloads []string - expectedError bool + name string + dataUploads []velerov2alpha1api.DataDownload + dd *velerov2alpha1api.DataDownload + needErrs []bool + resumeErr error + acceptedDataDownloads []string + prepareddDataDownloads []string + cancelledDataDownloads []string + inProgressDataDownloads []string + expectedError string }{ - // Test case 1: Process Accepted DataDownload { - name: "AcceptedDataDownload", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataDownloadName).Volumes(&corev1.Volume{Name: dataDownloadName}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataDownloadLabel: dataDownloadName, + name: "accepted DataDownload with no dd label", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Result(), + cancelledDataDownloads: []string{dataDownloadName}, + acceptedDataDownloads: []string{dataDownloadName}, + }, + { + name: "accepted DataDownload in the current node", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Labels(map[string]string{acceptNodeLabelKey: "node-1"}).Result(), + cancelledDataDownloads: []string{dataDownloadName}, + acceptedDataDownloads: []string{dataDownloadName}, + }, + { + name: "accepted DataDownload with dd label but is canceled", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Cancel(true).Labels(map[string]string{ + acceptNodeLabelKey: "node-1", }).Result(), - du: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Result(), + acceptedDataDownloads: []string{dataDownloadName}, + cancelledDataDownloads: []string{dataDownloadName}, + }, + { + name: "accepted DataDownload with dd label but cancel fail", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Labels(map[string]string{ + acceptNodeLabelKey: "node-1", + }).Result(), + needErrs: []bool{false, false, true, false, false, false}, acceptedDataDownloads: []string{dataDownloadName}, - expectedError: false, }, - // Test case 2: Cancel an Accepted DataDownload { - name: "CancelAcceptedDataDownload", - du: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Result(), - }, - // Test case 3: Process Accepted Prepared DataDownload - { - name: "PreparedDataDownload", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataDownloadName).Volumes(&corev1.Volume{Name: dataDownloadName}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataDownloadLabel: dataDownloadName, - }).Result(), - du: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), + name: "prepared DataDownload", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), prepareddDataDownloads: []string{dataDownloadName}, }, - // Test case 4: Process Accepted InProgress DataDownload { - name: "InProgressDataDownload", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataDownloadName).Volumes(&corev1.Volume{Name: dataDownloadName}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataDownloadLabel: dataDownloadName, - }).Result(), - du: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), - prepareddDataDownloads: []string{dataDownloadName}, + name: "InProgress DataDownload, not the current node", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseInProgress).Result(), + inProgressDataDownloads: []string{dataDownloadName}, }, - // Test case 5: get resume error { - name: "ResumeError", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataDownloadName).Volumes(&corev1.Volume{Name: dataDownloadName}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataDownloadLabel: dataDownloadName, - }).Result(), + name: "InProgress DataDownload, no resume error", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseInProgress).Node("node-1").Result(), + inProgressDataDownloads: []string{dataDownloadName}, + }, + { + name: "InProgress DataDownload, resume error, cancel error", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseInProgress).Node("node-1").Result(), + resumeErr: errors.New("fake-resume-error"), + needErrs: []bool{false, false, true, false, false, false}, + inProgressDataDownloads: []string{dataDownloadName}, + }, + { + name: "InProgress DataDownload, resume error, cancel succeed", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseInProgress).Node("node-1").Result(), + resumeErr: errors.New("fake-resume-error"), + cancelledDataDownloads: []string{dataDownloadName}, + inProgressDataDownloads: []string{dataDownloadName}, + }, + { + name: "Error", needErrs: []bool{false, false, false, false, false, true}, - du: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), - expectedError: true, + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), + expectedError: "error to list datadownloads: List error", }, } @@ -1054,30 +1045,31 @@ func TestAttemptDataDownloadResume(t *testing.T) { r.nodeName = "node-1" require.NoError(t, err) defer func() { - r.client.Delete(ctx, test.du, &kbclient.DeleteOptions{}) - if test.pod != nil { - r.client.Delete(ctx, test.pod, &kbclient.DeleteOptions{}) - } + r.client.Delete(ctx, test.dd, &kbclient.DeleteOptions{}) }() - assert.NoError(t, r.client.Create(ctx, test.du)) - if test.pod != nil { - assert.NoError(t, r.client.Create(ctx, test.pod)) - } - // Run the test - err = r.AttemptDataDownloadResume(ctx, r.client, r.logger.WithField("name", test.name), test.du.Namespace) + assert.NoError(t, r.client.Create(ctx, test.dd)) - if test.expectedError { - assert.Error(t, err) + dt := &duResumeTestHelper{ + resumeErr: test.resumeErr, + } + + funcResumeCancellableDataBackup = dt.resumeCancellableDataPath + + // Run the test + err = r.AttemptDataDownloadResume(ctx, r.client, r.logger.WithField("name", test.name), test.dd.Namespace) + + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) // Verify DataDownload marked as Canceled for _, duName := range test.cancelledDataDownloads { - dataUpload := &velerov2alpha1api.DataDownload{} - err := r.client.Get(context.Background(), types.NamespacedName{Namespace: "velero", Name: duName}, dataUpload) + dataDownload := &velerov2alpha1api.DataDownload{} + err := r.client.Get(context.Background(), types.NamespacedName{Namespace: "velero", Name: duName}, dataDownload) require.NoError(t, err) - assert.Equal(t, velerov2alpha1api.DataDownloadPhaseCanceled, dataUpload.Status.Phase) + assert.True(t, dataDownload.Spec.Cancel) } // Verify DataDownload marked as Accepted for _, duName := range test.acceptedDataDownloads { @@ -1097,3 +1089,108 @@ func TestAttemptDataDownloadResume(t *testing.T) { }) } } + +func TestResumeCancellableRestore(t *testing.T) { + tests := []struct { + name string + dataDownloads []velerov2alpha1api.DataDownload + dd *velerov2alpha1api.DataDownload + getExposeErr error + exposeResult *exposer.ExposeResult + createWatcherErr error + initWatcherErr error + startWatcherErr error + mockInit bool + mockStart bool + mockClose bool + expectedError string + }{ + { + name: "get expose failed", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseInProgress).Result(), + getExposeErr: errors.New("fake-expose-error"), + expectedError: fmt.Sprintf("error to get exposed volume for dd %s: fake-expose-error", dataDownloadName), + }, + { + name: "no expose", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Node("node-1").Result(), + expectedError: fmt.Sprintf("expose info missed for dd %s", dataDownloadName), + }, + { + name: "watcher init error", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Node("node-1").Result(), + exposeResult: &exposer.ExposeResult{ + ByPod: exposer.ExposeByPod{ + HostingPod: &corev1.Pod{}, + }, + }, + mockInit: true, + mockClose: true, + initWatcherErr: errors.New("fake-init-watcher-error"), + expectedError: fmt.Sprintf("error to init asyncBR watcher for dd %s: fake-init-watcher-error", dataDownloadName), + }, + { + name: "start watcher error", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Node("node-1").Result(), + exposeResult: &exposer.ExposeResult{ + ByPod: exposer.ExposeByPod{ + HostingPod: &corev1.Pod{}, + }, + }, + mockInit: true, + mockStart: true, + mockClose: true, + startWatcherErr: errors.New("fake-start-watcher-error"), + expectedError: fmt.Sprintf("error to resume asyncBR watche for dd %s: fake-start-watcher-error", dataDownloadName), + }, + { + name: "succeed", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhaseAccepted).Node("node-1").Result(), + exposeResult: &exposer.ExposeResult{ + ByPod: exposer.ExposeByPod{ + HostingPod: &corev1.Pod{}, + }, + }, + mockInit: true, + mockStart: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ctx := context.TODO() + r, err := initDataDownloadReconciler(nil, false) + r.nodeName = "node-1" + require.NoError(t, err) + + mockAsyncBR := datapathmockes.NewAsyncBR(t) + + if test.mockInit { + mockAsyncBR.On("Init", mock.Anything, mock.Anything).Return(test.initWatcherErr) + } + + if test.mockStart { + mockAsyncBR.On("StartRestore", mock.Anything, mock.Anything, mock.Anything).Return(test.startWatcherErr) + } + + if test.mockClose { + mockAsyncBR.On("Close", mock.Anything).Return() + } + + dt := &ddResumeTestHelper{ + getExposeErr: test.getExposeErr, + exposeResult: test.exposeResult, + asyncBR: mockAsyncBR, + } + + r.restoreExposer = dt + + datapath.MicroServiceBRWatcherCreator = dt.newMicroServiceBRWatcher + + err = r.resumeCancellableDataPath(ctx, test.dd, velerotest.NewLogger()) + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) + } + }) + } +} diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 4781f04f4..d9025f668 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" @@ -67,6 +68,7 @@ type DataUploadReconciler struct { client client.Client kubeClient kubernetes.Interface csiSnapshotClient snapshotter.SnapshotV1Interface + mgr manager.Manager repoEnsurer *repository.Ensurer Clock clocks.WithTickerAndDelayedExecution credentialGetter *credentials.CredentialGetter @@ -80,11 +82,12 @@ type DataUploadReconciler struct { metrics *metrics.ServerMetrics } -func NewDataUploadReconciler(client client.Client, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, +func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, repoEnsurer *repository.Ensurer, clock clocks.WithTickerAndDelayedExecution, cred *credentials.CredentialGetter, nodeName string, fs filesystem.Interface, preparingTimeout time.Duration, log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { return &DataUploadReconciler{ client: client, + mgr: mgr, kubeClient: kubeClient, csiSnapshotClient: csiSnapshotClient, Clock: clock, @@ -150,9 +153,17 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) } else if controllerutil.ContainsFinalizer(du, DataUploadDownloadFinalizer) && !du.Spec.Cancel && !isDataUploadInFinalState(du) { // when delete cr we need to clear up internal resources created by Velero, here we use the cancel mechanism // to help clear up resources instead of clear them directly in case of some conflict with Expose action - if err := UpdateDataUploadWithRetry(ctx, r.client, req.NamespacedName, log, func(dataUpload *velerov2alpha1api.DataUpload) { + log.Warnf("Cancel du under phase %s because it is being deleted", du.Status.Phase) + + if err := UpdateDataUploadWithRetry(ctx, r.client, req.NamespacedName, log, func(dataUpload *velerov2alpha1api.DataUpload) bool { + if dataUpload.Spec.Cancel { + return false + } + dataUpload.Spec.Cancel = true - dataUpload.Status.Message = fmt.Sprintf("found a dataupload %s/%s is being deleted, mark it as cancel", du.Namespace, du.Name) + dataUpload.Status.Message = "Cancel dataupload because it is being deleted" + + return true }); err != nil { log.Errorf("failed to set cancel flag with error %s for %s/%s", err.Error(), du.Namespace, du.Name) return ctrl.Result{}, err @@ -599,9 +610,15 @@ func (r *DataUploadReconciler) findDataUploadForPod(ctx context.Context, podObj } } else if unrecoverable, reason := kube.IsPodUnrecoverable(pod, log); unrecoverable { // let the abnormal backup pod failed early err := UpdateDataUploadWithRetry(context.Background(), r.client, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, r.logger.WithField("dataupload", du.Name), - func(dataUpload *velerov2alpha1api.DataUpload) { + func(dataUpload *velerov2alpha1api.DataUpload) bool { + if dataUpload.Spec.Cancel { + return false + } + dataUpload.Spec.Cancel = true - dataUpload.Status.Message = fmt.Sprintf("dataupload mark as cancel to failed early for exposing pod %s/%s is in abnormal status for reason %s", pod.Namespace, pod.Name, reason) + dataUpload.Status.Message = fmt.Sprintf("Cancel dataupload because the exposing pod %s/%s is in abnormal status for reason %s", pod.Namespace, pod.Name, reason) + + return true }) if err != nil { @@ -622,75 +639,6 @@ func (r *DataUploadReconciler) findDataUploadForPod(ctx context.Context, podObj return []reconcile.Request{request} } -func (r *DataUploadReconciler) FindDataUploadsByPod(ctx context.Context, cli client.Client, ns string) ([]velerov2alpha1api.DataUpload, error) { - pods := &corev1.PodList{} - var dataUploads []velerov2alpha1api.DataUpload - if err := cli.List(ctx, pods, &client.ListOptions{Namespace: ns}); err != nil { - r.logger.WithError(errors.WithStack(err)).Error("failed to list pods on current node") - return nil, errors.Wrapf(err, "failed to list pods on current node") - } - - for _, pod := range pods.Items { - if pod.Spec.NodeName != r.nodeName { - r.logger.Debugf("Pod %s related data upload will not handled by %s nodes", pod.GetName(), r.nodeName) - continue - } - du, err := findDataUploadByPod(cli, pod) - if err != nil { - r.logger.WithError(errors.WithStack(err)).Error("failed to get dataUpload by pod") - continue - } else if du != nil { - dataUploads = append(dataUploads, *du) - } - } - return dataUploads, nil -} - -func (r *DataUploadReconciler) findAcceptDataUploadsByNodeLabel(ctx context.Context, cli client.Client, ns string) ([]velerov2alpha1api.DataUpload, error) { - dataUploads := &velerov2alpha1api.DataUploadList{} - if err := cli.List(ctx, dataUploads, &client.ListOptions{Namespace: ns}); err != nil { - r.logger.WithError(errors.WithStack(err)).Error("failed to list datauploads") - return nil, errors.Wrapf(err, "failed to list datauploads") - } - - var result []velerov2alpha1api.DataUpload - for _, du := range dataUploads.Items { - if du.Status.Phase != velerov2alpha1api.DataUploadPhaseAccepted { - continue - } - if du.Labels[acceptNodeLabelKey] == r.nodeName { - result = append(result, du) - } - } - return result, nil -} - -func (r *DataUploadReconciler) CancelAcceptedDataupload(ctx context.Context, cli client.Client, ns string) { - r.logger.Infof("Reset accepted dataupload for node %s", r.nodeName) - dataUploads, err := r.findAcceptDataUploadsByNodeLabel(ctx, cli, ns) - if err != nil { - r.logger.WithError(err).Error("failed to find dataupload") - return - } - - for _, du := range dataUploads { - if du.Spec.Cancel { - continue - } - err = UpdateDataUploadWithRetry(ctx, cli, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, r.logger.WithField("dataupload", du.Name), - func(dataUpload *velerov2alpha1api.DataUpload) { - dataUpload.Spec.Cancel = true - dataUpload.Status.Message = fmt.Sprintf("found a dataupload with status %q during the node-agent starting, mark it as cancel", du.Status.Phase) - }) - - r.logger.WithField("dataupload", du.GetName()).Warn(du.Status.Message) - if err != nil { - r.logger.WithError(errors.WithStack(err)).Errorf("failed to mark dataupload %q cancel", du.GetName()) - continue - } - } -} - func (r *DataUploadReconciler) prepareDataUpload(du *velerov2alpha1api.DataUpload) { du.Status.Phase = velerov2alpha1api.DataUploadPhasePrepared du.Status.Node = r.nodeName @@ -902,54 +850,145 @@ func isDataUploadInFinalState(du *velerov2alpha1api.DataUpload) bool { du.Status.Phase == velerov2alpha1api.DataUploadPhaseCompleted } -func UpdateDataUploadWithRetry(ctx context.Context, client client.Client, namespacedName types.NamespacedName, log *logrus.Entry, updateFunc func(dataUpload *velerov2alpha1api.DataUpload)) error { - return wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (done bool, err error) { +func UpdateDataUploadWithRetry(ctx context.Context, client client.Client, namespacedName types.NamespacedName, log *logrus.Entry, updateFunc func(*velerov2alpha1api.DataUpload) bool) error { + return wait.PollUntilContextCancel(ctx, time.Second, true, func(ctx context.Context) (bool, error) { du := &velerov2alpha1api.DataUpload{} if err := client.Get(ctx, namespacedName, du); err != nil { return false, errors.Wrap(err, "getting DataUpload") } - updateFunc(du) - updateErr := client.Update(ctx, du) - if updateErr != nil { - if apierrors.IsConflict(updateErr) { - log.Warnf("failed to update dataupload for %s/%s and will retry it", du.Namespace, du.Name) - return false, nil + if updateFunc(du) { + err := client.Update(ctx, du) + if err != nil { + if apierrors.IsConflict(err) { + log.Warnf("failed to update dataupload for %s/%s and will retry it", du.Namespace, du.Name) + return false, nil + } else { + return false, errors.Wrapf(err, "error updating dataupload with error %s/%s", du.Namespace, du.Name) + } } - log.Errorf("failed to update dataupload with error %s for %s/%s", updateErr.Error(), du.Namespace, du.Name) - return false, err } + return true, nil }) } -func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, cli client.Client, logger *logrus.Entry, ns string) error { - if dataUploads, err := r.FindDataUploadsByPod(ctx, cli, ns); err != nil { - return errors.Wrap(err, "failed to find data uploads") - } else { - for _, du := range dataUploads { - if du.Status.Phase == velerov2alpha1api.DataUploadPhasePrepared { - // keep doing nothing let controller re-download the data - // the Prepared CR could be still handled by dataupload controller after node-agent restart - logger.WithField("dataupload", du.GetName()).Debug("find a dataupload with status prepared") - } else if du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress { - err = UpdateDataUploadWithRetry(ctx, cli, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, logger.WithField("dataupload", du.Name), - func(dataUpload *velerov2alpha1api.DataUpload) { - dataUpload.Spec.Cancel = true - dataUpload.Status.Message = fmt.Sprintf("found a dataupload with status %q during the node-agent starting, mark it as cancel", du.Status.Phase) - }) +var funcResumeCancellableDataBackup = (*DataUploadReconciler).resumeCancellableDataPath - if err != nil { - logger.WithError(errors.WithStack(err)).Errorf("failed to mark dataupload %q into canceled", du.GetName()) - continue - } - logger.WithField("dataupload", du.GetName()).Debug("mark dataupload into canceled") +func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, cli client.Client, logger *logrus.Entry, ns string) error { + dataUploads := &velerov2alpha1api.DataUploadList{} + if err := cli.List(ctx, dataUploads, &client.ListOptions{Namespace: ns}); err != nil { + r.logger.WithError(errors.WithStack(err)).Error("failed to list datauploads") + return errors.Wrapf(err, "error to list datauploads") + } + + for i := range dataUploads.Items { + du := &dataUploads.Items[i] + if du.Status.Phase == velerov2alpha1api.DataUploadPhasePrepared { + // keep doing nothing let controller re-download the data + // the Prepared CR could be still handled by dataupload controller after node-agent restart + logger.WithField("dataupload", du.GetName()).Debug("find a dataupload with status prepared") + } else if du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress { + if du.Status.Node != r.nodeName { + logger.WithField("du", du.Name).WithField("current node", r.nodeName).Infof("DU should be resumed by another node %s", du.Status.Node) + continue + } + + err := funcResumeCancellableDataBackup(r, ctx, du, logger) + if err == nil { + continue + } + + logger.WithField("dataupload", du.GetName()).WithError(err).Warn("Failed to resume data path for du, have to cancel it") + + resumeErr := err + err = UpdateDataUploadWithRetry(ctx, cli, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, logger.WithField("dataupload", du.Name), + func(dataUpload *velerov2alpha1api.DataUpload) bool { + if dataUpload.Spec.Cancel { + return false + } + + dataUpload.Spec.Cancel = true + dataUpload.Status.Message = fmt.Sprintf("Resume InProgress dataupload failed with error %v, mark it as cancel", resumeErr) + + return true + }) + if err != nil { + logger.WithField("dataupload", du.GetName()).WithError(errors.WithStack(err)).Error("Failed to trigger dataupload cancel") + } + } else if du.Status.Phase == velerov2alpha1api.DataUploadPhaseAccepted { + r.logger.WithField("dataupload", du.GetName()).Warn("Cancel du under Accepted phase") + + err := UpdateDataUploadWithRetry(ctx, cli, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, r.logger.WithField("dataupload", du.Name), + func(dataUpload *velerov2alpha1api.DataUpload) bool { + if dataUpload.Spec.Cancel { + return false + } + + dataUpload.Spec.Cancel = true + dataUpload.Status.Message = "Dataupload is in Accepted status during the node-agent starting, mark it as cancel" + + return true + }) + if err != nil { + r.logger.WithField("dataupload", du.GetName()).WithError(errors.WithStack(err)).Error("Failed to trigger dataupload cancel") } } } - //If the data upload is in Accepted status, the volume snapshot may be deleted and the exposed pod may not be created - // so we need to mark the data upload as canceled for it may not be recoverable - r.CancelAcceptedDataupload(ctx, cli, ns) + return nil +} + +func (r *DataUploadReconciler) resumeCancellableDataPath(ctx context.Context, du *velerov2alpha1api.DataUpload, log logrus.FieldLogger) error { + log.Info("Resume cancelable dataUpload") + + ep, ok := r.snapshotExposerList[du.Spec.SnapshotType] + if !ok { + return errors.Errorf("error to find exposer for du %s", du.Name) + } + + waitExposePara := r.setupWaitExposePara(du) + res, err := ep.GetExposed(ctx, getOwnerObject(du), du.Spec.OperationTimeout.Duration, waitExposePara) + if err != nil { + return errors.Wrapf(err, "error to get exposed snapshot for du %s", du.Name) + } + + if res == nil { + return errors.Errorf("expose info missed for du %s", du.Name) + } + + callbacks := datapath.Callbacks{ + OnCompleted: r.OnDataUploadCompleted, + OnFailed: r.OnDataUploadFailed, + OnCancelled: r.OnDataUploadCancelled, + OnProgress: r.OnDataUploadProgress, + } + + asyncBR, err := r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, r.kubeClient, r.mgr, datapath.TaskTypeBackup, du.Name, du.Namespace, res.ByPod.HostingPod.Name, res.ByPod.HostingContainer, du.Name, callbacks, true, log) + if err != nil { + return errors.Wrapf(err, "error to create asyncBR watcher for du %s", du.Name) + } + + resumeComplete := false + defer func() { + if !resumeComplete { + r.closeDataPath(ctx, du.Name) + } + }() + + if err := asyncBR.Init(ctx, nil); err != nil { + return errors.Wrapf(err, "error to init asyncBR watcher for du %s", du.Name) + } + + if err := asyncBR.StartBackup(datapath.AccessPoint{ + ByPath: res.ByPod.VolumeName, + }, du.Spec.DataMoverConfig, nil); err != nil { + return errors.Wrapf(err, "error to resume asyncBR watche for du %s", du.Name) + } + + resumeComplete = true + + log.Infof("asyncBR is resumed for du %s", du.Name) + return nil } diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index 2bca1d5b9..0584065b1 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -27,6 +27,7 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -35,6 +36,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" clientgofake "k8s.io/client-go/kubernetes/fake" "k8s.io/utils/clock" testclocks "k8s.io/utils/clock/testing" @@ -42,6 +44,7 @@ import ( kbclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/vmware-tanzu/velero/internal/credentials" @@ -49,6 +52,7 @@ import ( velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/datapath" + datapathmocks "github.com/vmware-tanzu/velero/pkg/datapath/mocks" "github.com/vmware-tanzu/velero/pkg/exposer" "github.com/vmware-tanzu/velero/pkg/metrics" velerotest "github.com/vmware-tanzu/velero/pkg/test" @@ -241,7 +245,7 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci if err != nil { return nil, err } - return NewDataUploadReconciler(fakeClient, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, nil, + return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, nil, testclocks.NewFakeClock(now), &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", fakeFS, time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } @@ -944,12 +948,11 @@ func TestUpdateDataUploadWithRetry(t *testing.T) { testCases := []struct { Name string needErrs []bool + noChange bool ExpectErr bool }{ { - Name: "SuccessOnFirstAttempt", - needErrs: []bool{false, false, false, false}, - ExpectErr: false, + Name: "SuccessOnFirstAttempt", }, { Name: "Error get", @@ -961,6 +964,11 @@ func TestUpdateDataUploadWithRetry(t *testing.T) { needErrs: []bool{false, false, true, false, false}, ExpectErr: true, }, + { + Name: "no change", + noChange: true, + needErrs: []bool{false, false, true, false, false}, + }, { Name: "Conflict with error timeout", needErrs: []bool{false, false, false, false, true}, @@ -976,8 +984,13 @@ func TestUpdateDataUploadWithRetry(t *testing.T) { require.NoError(t, err) err = r.client.Create(ctx, dataUploadBuilder().Result()) require.NoError(t, err) - updateFunc := func(dataDownload *velerov2alpha1api.DataUpload) { + updateFunc := func(dataDownload *velerov2alpha1api.DataUpload) bool { + if tc.noChange { + return false + } + dataDownload.Spec.Cancel = true + return true } err = UpdateDataUploadWithRetry(ctx, r.client, namespacedName, velerotest.NewLogger().WithField("name", tc.Name), updateFunc) if tc.ExpectErr { @@ -989,135 +1002,107 @@ func TestUpdateDataUploadWithRetry(t *testing.T) { } } -func TestFindDataUploads(t *testing.T) { - tests := []struct { - name string - pod corev1.Pod - du *velerov2alpha1api.DataUpload - expectedUploads []velerov2alpha1api.DataUpload - expectedError bool - }{ - // Test case 1: Pod with matching nodeName and DataUpload label - { - name: "MatchingPod", - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: "pod-1", - Labels: map[string]string{ - velerov1api.DataUploadLabel: dataUploadName, - }, - }, - Spec: corev1.PodSpec{ - NodeName: "node-1", - }, - }, - du: dataUploadBuilder().Result(), - expectedUploads: []velerov2alpha1api.DataUpload{ - { - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: dataUploadName, - }, - }, - }, - expectedError: false, - }, - // Test case 2: Pod with non-matching nodeName - { - name: "NonMatchingNodePod", - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "velero", - Name: "pod-2", - Labels: map[string]string{ - velerov1api.DataUploadLabel: dataUploadName, - }, - }, - Spec: corev1.PodSpec{ - NodeName: "node-2", - }, - }, - du: dataUploadBuilder().Result(), - expectedUploads: []velerov2alpha1api.DataUpload{}, - expectedError: false, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - r, err := initDataUploaderReconcilerWithError() - require.NoError(t, err) - r.nodeName = "node-1" - err = r.client.Create(ctx, test.du) - require.NoError(t, err) - err = r.client.Create(ctx, &test.pod) - require.NoError(t, err) - uploads, err := r.FindDataUploadsByPod(context.Background(), r.client, "velero") - - if test.expectedError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.Equal(t, len(test.expectedUploads), len(uploads)) - } - }) - } +type duResumeTestHelper struct { + resumeErr error + getExposeErr error + exposeResult *exposer.ExposeResult + asyncBR datapath.AsyncBR } + +func (dt *duResumeTestHelper) resumeCancellableDataPath(_ *DataUploadReconciler, _ context.Context, _ *velerov2alpha1api.DataUpload, _ logrus.FieldLogger) error { + return dt.resumeErr +} + +func (dt *duResumeTestHelper) Expose(context.Context, corev1.ObjectReference, interface{}) error { + return nil +} + +func (dt *duResumeTestHelper) GetExposed(context.Context, corev1.ObjectReference, time.Duration, interface{}) (*exposer.ExposeResult, error) { + return dt.exposeResult, dt.getExposeErr +} + +func (dt *duResumeTestHelper) PeekExposed(context.Context, corev1.ObjectReference) error { + return nil +} + +func (dt *duResumeTestHelper) CleanUp(context.Context, corev1.ObjectReference, string, string) {} + +func (dt *duResumeTestHelper) newMicroServiceBRWatcher(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, string, string, string, string, + datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { + return dt.asyncBR +} + func TestAttemptDataUploadResume(t *testing.T) { tests := []struct { - name string - dataUploads []velerov2alpha1api.DataUpload - du *velerov2alpha1api.DataUpload - pod *corev1.Pod - needErrs []bool - acceptedDataUploads []string - prepareddDataUploads []string - cancelledDataUploads []string - expectedError bool + name string + dataUploads []velerov2alpha1api.DataUpload + du *velerov2alpha1api.DataUpload + needErrs []bool + acceptedDataUploads []string + prepareddDataUploads []string + cancelledDataUploads []string + inProgressDataUploads []string + resumeErr error + expectedError string }{ - // Test case 1: Process Accepted DataUpload { - name: "AcceptedDataUpload", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataUploadLabel: dataUploadName, - }).Result(), - du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(), + name: "accepted DataUpload in other node", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(), + cancelledDataUploads: []string{dataUploadName}, + acceptedDataUploads: []string{dataUploadName}, + }, + { + name: "accepted DataUpload in the current node", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Labels(map[string]string{acceptNodeLabelKey: "node-1"}).Result(), + cancelledDataUploads: []string{dataUploadName}, + acceptedDataUploads: []string{dataUploadName}, + }, + { + name: "accepted DataUpload in the current node but canceled", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Labels(map[string]string{acceptNodeLabelKey: "node-1"}).Cancel(true).Result(), + cancelledDataUploads: []string{dataUploadName}, + acceptedDataUploads: []string{dataUploadName}, + }, + { + name: "accepted DataUpload in the current node but update error", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Labels(map[string]string{acceptNodeLabelKey: "node-1"}).Result(), + needErrs: []bool{false, false, true, false, false, false}, acceptedDataUploads: []string{dataUploadName}, - expectedError: false, }, - // Test case 2: Cancel an Accepted DataUpload { - name: "CancelAcceptedDataUpload", - du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(), - }, - // Test case 3: Process Accepted Prepared DataUpload - { - name: "PreparedDataUpload", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataUploadLabel: dataUploadName, - }).Result(), + name: "prepared DataUpload", du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), prepareddDataUploads: []string{dataUploadName}, }, - // Test case 4: Process Accepted InProgress DataUpload { - name: "InProgressDataUpload", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataUploadLabel: dataUploadName, - }).Result(), - du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), - prepareddDataUploads: []string{dataUploadName}, + name: "InProgress DataUpload, not the current node", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(), + inProgressDataUploads: []string{dataUploadName}, }, - // Test case 5: get resume error { - name: "ResumeError", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).NodeName("node-1").Labels(map[string]string{ - velerov1api.DataUploadLabel: dataUploadName, - }).Result(), + name: "InProgress DataUpload, resume error and update error", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Node("node-1").Result(), + needErrs: []bool{false, false, true, false, false, false}, + resumeErr: errors.New("fake-resume-error"), + inProgressDataUploads: []string{dataUploadName}, + }, + { + name: "InProgress DataUpload, resume error and update succeed", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Node("node-1").Result(), + resumeErr: errors.New("fake-resume-error"), + cancelledDataUploads: []string{dataUploadName}, + inProgressDataUploads: []string{dataUploadName}, + }, + { + name: "InProgress DataUpload and resume succeed", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Node("node-1").Result(), + inProgressDataUploads: []string{dataUploadName}, + }, + { + name: "Error", needErrs: []bool{false, false, false, false, false, true}, du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), - expectedError: true, + expectedError: "error to list datauploads: List error", }, } @@ -1127,22 +1112,20 @@ func TestAttemptDataUploadResume(t *testing.T) { r, err := initDataUploaderReconciler(test.needErrs...) r.nodeName = "node-1" require.NoError(t, err) - defer func() { - r.client.Delete(ctx, test.du, &kbclient.DeleteOptions{}) - if test.pod != nil { - r.client.Delete(ctx, test.pod, &kbclient.DeleteOptions{}) - } - }() assert.NoError(t, r.client.Create(ctx, test.du)) - if test.pod != nil { - assert.NoError(t, r.client.Create(ctx, test.pod)) + + dt := &duResumeTestHelper{ + resumeErr: test.resumeErr, } + + funcResumeCancellableDataBackup = dt.resumeCancellableDataPath + // Run the test err = r.AttemptDataUploadResume(ctx, r.client, r.logger.WithField("name", test.name), test.du.Namespace) - if test.expectedError { - assert.Error(t, err) + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) } else { assert.NoError(t, err) @@ -1151,7 +1134,7 @@ func TestAttemptDataUploadResume(t *testing.T) { dataUpload := &velerov2alpha1api.DataUpload{} err := r.client.Get(context.Background(), types.NamespacedName{Namespace: "velero", Name: duName}, dataUpload) require.NoError(t, err) - assert.Equal(t, velerov2alpha1api.DataUploadPhaseCanceled, dataUpload.Status.Phase) + assert.True(t, dataUpload.Spec.Cancel) } // Verify DataUploads marked as Accepted for _, duName := range test.acceptedDataUploads { @@ -1167,6 +1150,123 @@ func TestAttemptDataUploadResume(t *testing.T) { require.NoError(t, err) assert.Equal(t, velerov2alpha1api.DataUploadPhasePrepared, dataUpload.Status.Phase) } + // Verify DataUploads marked as InProgress + for _, duName := range test.inProgressDataUploads { + dataUpload := &velerov2alpha1api.DataUpload{} + err := r.client.Get(context.Background(), types.NamespacedName{Namespace: "velero", Name: duName}, dataUpload) + require.NoError(t, err) + assert.Equal(t, velerov2alpha1api.DataUploadPhaseInProgress, dataUpload.Status.Phase) + } + } + }) + } +} + +func TestResumeCancellableBackup(t *testing.T) { + tests := []struct { + name string + dataUploads []velerov2alpha1api.DataUpload + du *velerov2alpha1api.DataUpload + getExposeErr error + exposeResult *exposer.ExposeResult + createWatcherErr error + initWatcherErr error + startWatcherErr error + mockInit bool + mockStart bool + mockClose bool + expectedError string + }{ + { + name: "not find exposer", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType("").Result(), + expectedError: fmt.Sprintf("error to find exposer for du %s", dataUploadName), + }, + { + name: "get expose failed", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(velerov2alpha1api.SnapshotTypeCSI).Result(), + getExposeErr: errors.New("fake-expose-error"), + expectedError: fmt.Sprintf("error to get exposed snapshot for du %s: fake-expose-error", dataUploadName), + }, + { + name: "no expose", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Node("node-1").Result(), + expectedError: fmt.Sprintf("expose info missed for du %s", dataUploadName), + }, + { + name: "watcher init error", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Node("node-1").Result(), + exposeResult: &exposer.ExposeResult{ + ByPod: exposer.ExposeByPod{ + HostingPod: &corev1.Pod{}, + }, + }, + mockInit: true, + mockClose: true, + initWatcherErr: errors.New("fake-init-watcher-error"), + expectedError: fmt.Sprintf("error to init asyncBR watcher for du %s: fake-init-watcher-error", dataUploadName), + }, + { + name: "start watcher error", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Node("node-1").Result(), + exposeResult: &exposer.ExposeResult{ + ByPod: exposer.ExposeByPod{ + HostingPod: &corev1.Pod{}, + }, + }, + mockInit: true, + mockStart: true, + mockClose: true, + startWatcherErr: errors.New("fake-start-watcher-error"), + expectedError: fmt.Sprintf("error to resume asyncBR watche for du %s: fake-start-watcher-error", dataUploadName), + }, + { + name: "succeed", + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Node("node-1").Result(), + exposeResult: &exposer.ExposeResult{ + ByPod: exposer.ExposeByPod{ + HostingPod: &corev1.Pod{}, + }, + }, + mockInit: true, + mockStart: true, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + ctx := context.TODO() + r, err := initDataUploaderReconciler() + r.nodeName = "node-1" + require.NoError(t, err) + + mockAsyncBR := datapathmocks.NewAsyncBR(t) + + if test.mockInit { + mockAsyncBR.On("Init", mock.Anything, mock.Anything).Return(test.initWatcherErr) + } + + if test.mockStart { + mockAsyncBR.On("StartBackup", mock.Anything, mock.Anything, mock.Anything).Return(test.startWatcherErr) + } + + if test.mockClose { + mockAsyncBR.On("Close", mock.Anything).Return() + } + + dt := &duResumeTestHelper{ + getExposeErr: test.getExposeErr, + exposeResult: test.exposeResult, + asyncBR: mockAsyncBR, + } + + r.snapshotExposerList[velerov2alpha1api.SnapshotTypeCSI] = dt + + datapath.MicroServiceBRWatcherCreator = dt.newMicroServiceBRWatcher + + err = r.resumeCancellableDataPath(ctx, test.du, velerotest.NewLogger()) + if test.expectedError != "" { + assert.EqualError(t, err, test.expectedError) } }) } From 5c88c897a55c65057f3d580666b9e99f5b3d2788 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 6 Aug 2024 16:52:04 +0800 Subject: [PATCH 41/69] Patch dbr's status when error happens This commit makes sure the dbr's status is "Processed" when an error happens before the actual deletion is started fixes #7812 Signed-off-by: Daniel Jiang --- changelogs/unreleased/8086-reasonerjt | 1 + pkg/controller/backup_deletion_controller.go | 58 +++++++++---------- .../backup_deletion_controller_test.go | 8 +-- 3 files changed, 32 insertions(+), 35 deletions(-) create mode 100644 changelogs/unreleased/8086-reasonerjt diff --git a/changelogs/unreleased/8086-reasonerjt b/changelogs/unreleased/8086-reasonerjt new file mode 100644 index 000000000..1a369efff --- /dev/null +++ b/changelogs/unreleased/8086-reasonerjt @@ -0,0 +1 @@ +Patch dbr's status when error happens \ No newline at end of file diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 86612f871..f3a7f32b5 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -146,10 +146,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Make sure we have the backup name if dbr.Spec.BackupName == "" { - _, err := r.patchDeleteBackupRequest(ctx, dbr, func(res *velerov1api.DeleteBackupRequest) { - res.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - res.Status.Errors = []string{"spec.backupName is required"} - }) + err := r.patchDeleteBackupRequestWithError(ctx, dbr, errors.New("spec.backupName is required")) return ctrl.Result{}, err } @@ -163,10 +160,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Don't allow deleting an in-progress backup if r.backupTracker.Contains(dbr.Namespace, dbr.Spec.BackupName) { - _, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { - r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - r.Status.Errors = []string{"backup is still in progress"} - }) + err := r.patchDeleteBackupRequestWithError(ctx, dbr, errors.New("backup is still in progress")) return ctrl.Result{}, err } @@ -177,10 +171,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque Name: dbr.Spec.BackupName, }, backup); apierrors.IsNotFound(err) { // Couldn't find backup - update status to Processed and record the not-found error - _, err = r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { - r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - r.Status.Errors = []string{"backup not found"} - }) + err = r.patchDeleteBackupRequestWithError(ctx, dbr, errors.New("backup not found")) return ctrl.Result{}, err } else if err != nil { return ctrl.Result{}, errors.Wrap(err, "error getting backup") @@ -193,20 +184,14 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque Name: backup.Spec.StorageLocation, }, location); err != nil { if apierrors.IsNotFound(err) { - _, err := r.patchDeleteBackupRequest(ctx, dbr, 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)) - }) + err := r.patchDeleteBackupRequestWithError(ctx, dbr, fmt.Errorf("backup storage location %s not found", backup.Spec.StorageLocation)) return ctrl.Result{}, err } return ctrl.Result{}, errors.Wrap(err, "error getting backup storage location") } if location.Spec.AccessMode == velerov1api.BackupStorageLocationAccessModeReadOnly { - _, err := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { - r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("cannot delete backup because backup storage location %s is currently in read-only mode", location.Name)) - }) + err := r.patchDeleteBackupRequestWithError(ctx, dbr, fmt.Errorf("cannot delete backup because backup storage location %s is currently in read-only mode", location.Name)) return ctrl.Result{}, err } @@ -236,8 +221,9 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque b.Status.Phase = velerov1api.BackupPhaseDeleting }) if err != nil { - log.WithError(errors.WithStack(err)).Error("Error setting backup phase to deleting") - return ctrl.Result{}, err + log.WithError(err).Error("Error setting backup phase to deleting") + err2 := r.patchDeleteBackupRequestWithError(ctx, dbr, errors.Wrap(err, "error setting backup phase to deleting")) + return ctrl.Result{}, err2 } backupScheduleName := backup.GetLabels()[velerov1api.ScheduleNameLabel] @@ -248,17 +234,17 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque backupStore, err := r.backupStoreGetter.Get(location, pluginManager, log) if err != nil { - _, patchErr := r.patchDeleteBackupRequest(ctx, dbr, func(r *velerov1api.DeleteBackupRequest) { - r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed - r.Status.Errors = append(r.Status.Errors, fmt.Sprintf("cannot delete backup because backup storage location %s is currently unavailable, error: %s", location.Name, err.Error())) - }) - return ctrl.Result{}, patchErr + log.WithError(err).Error("Error getting the backup store") + err2 := r.patchDeleteBackupRequestWithError(ctx, dbr, errors.Wrap(err, "error getting the backup store")) + return ctrl.Result{}, err2 } actions, err := pluginManager.GetDeleteItemActions() log.Debugf("%d actions before invoking actions", len(actions)) if err != nil { - return ctrl.Result{}, errors.Wrap(err, "error getting delete item actions") + log.WithError(err).Error("Error getting delete item actions") + err2 := r.patchDeleteBackupRequestWithError(ctx, dbr, errors.New("error getting delete item actions")) + return ctrl.Result{}, err2 } // don't defer CleanupClients here, since it was already called above. @@ -270,7 +256,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque log.WithError(err).Errorf("Unable to download tarball for backup %s, skipping associated DeleteItemAction plugins", backup.Name) } else { defer closeAndRemoveFile(backupFile, r.logger) - ctx := &delete.Context{ + deleteCtx := &delete.Context{ Backup: backup, BackupReader: backupFile, Actions: actions, @@ -281,9 +267,11 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque // Optimization: wrap in a gofunc? Would be useful for large backups with lots of objects. // but what do we do with the error returned? We can't just swallow it as that may lead to dangling resources. - err = delete.InvokeDeleteActions(ctx) + err = delete.InvokeDeleteActions(deleteCtx) if err != nil { - return ctrl.Result{}, errors.Wrap(err, "error invoking delete item actions") + log.WithError(err).Error("Error invoking delete item actions") + err2 := r.patchDeleteBackupRequestWithError(ctx, dbr, errors.New("error invoking delete item actions")) + return ctrl.Result{}, err2 } } } @@ -593,6 +581,14 @@ func (r *backupDeletionReconciler) patchDeleteBackupRequest(ctx context.Context, return req, nil } +func (r *backupDeletionReconciler) patchDeleteBackupRequestWithError(ctx context.Context, req *velerov1api.DeleteBackupRequest, err error) error { + _, err = r.patchDeleteBackupRequest(ctx, req, func(r *velerov1api.DeleteBackupRequest) { + r.Status.Phase = velerov1api.DeleteBackupRequestPhaseProcessed + r.Status.Errors = []string{err.Error()} + }) + return err +} + func (r *backupDeletionReconciler) patchBackup(ctx context.Context, backup *velerov1api.Backup, mutate func(*velerov1api.Backup)) (*velerov1api.Backup, error) { //TODO: The patchHelper can't be used here because the `backup/xxx/status` does not exist, until the backup resource is refactored diff --git a/pkg/controller/backup_deletion_controller_test.go b/pkg/controller/backup_deletion_controller_test.go index e36876f87..c1c5b7018 100644 --- a/pkg/controller/backup_deletion_controller_test.go +++ b/pkg/controller/backup_deletion_controller_test.go @@ -122,16 +122,16 @@ func TestBackupDeletionControllerReconcile(t *testing.T) { }, }, } - td := setupBackupDeletionControllerTest(t, defaultTestDbr(), location, backup) + dbr := defaultTestDbr() + td := setupBackupDeletionControllerTest(t, dbr, location, backup) td.controller.backupStoreGetter = &fakeErrorBackupStoreGetter{} _, err := td.controller.Reconcile(ctx, td.req) require.NoError(t, err) res := &velerov1api.DeleteBackupRequest{} - err = td.fakeClient.Get(ctx, td.req.NamespacedName, res) - require.NoError(t, err) + td.fakeClient.Get(ctx, td.req.NamespacedName, res) assert.Equal(t, "Processed", string(res.Status.Phase)) assert.Len(t, res.Status.Errors, 1) - assert.True(t, strings.HasPrefix(res.Status.Errors[0], fmt.Sprintf("cannot delete backup because backup storage location %s is currently unavailable", location.Name))) + assert.True(t, strings.HasPrefix(res.Status.Errors[0], "error getting the backup store")) }) t.Run("missing spec.backupName", func(t *testing.T) { From 64d8014b871951e39c59370230a60a0bae1c7f55 Mon Sep 17 00:00:00 2001 From: Michael Steven Fruchtman <40075929+msfrucht@users.noreply.github.com> Date: Tue, 6 Aug 2024 06:05:45 -0700 Subject: [PATCH 42/69] Correcting OpenShift on IBM Documentation Error (#8077) * Correcting Openshift on IBM Documentation Error I have to admit to some significant error and embarrassment regarding the documentation update about Openshift on IBM Cloud pull request https://github.com/vmware-tanzu/velero/pull/8069. I will correct my error before it gets any further. Just exposing /var/data/kubelet/pods is incorrect and host path /var/lib/kubelet/pods should remain unchanged. The errors with the defaults during csi snapshot data movement were: data path backup failed: Failed to run kopia backup: unable to get local block device entry: resolveSymlink: lstat /var/data/: no such file or directory I suspected this was the same as RancherOS and Nutanix. It is not. The original tested changes changed both /var/lib/kubelet/{pods,plugins} to /var/data/kubelet/{pods,plugins}. The published changes only result in the error: ``` status: completionTimestamp: '2024-08-02T17:12:29Z' message: >- data path backup failed: Failed to run kopia backup: unable to get local block device entry: resolveSymlink: lstat /var/data/kubelet/plugins: no such file or directory node: 10.240.0.5 phase: Failed progress: {} startTimestamp: '2024-08-02T17:12:11Z' ``` After making continued modifications to the daemonset the correct configuration was: ``` volumeMounts: - name: host-pods mountPath: /host_pods mountPropagation: HostToContainer - name: host-plugins mountPath: /var/data/kubelet/plugins mountPropagation: HostToContainer ``` ``` volumes: - name: host-pods hostPath: path: /var/lib/kubelet/pods type: '' - name: host-plugins hostPath: path: /var/data/kubelet/plugins type: '' ``` Only the changes to the plugin path were required. The plugin path changes were required to both the mount path and the host path. Regardless of whether /var/lib/kubelet/pods or /var/data/kubelet/pods host path, backups and restore succeeded provided the plugin path was modified. ``` volumeMounts: - name: host-pods mountPath: /host_pods mountPropagation: HostToContainer - name: host-plugins mountPath: /var/data/kubelet/plugins mountPropagation: HostToContainer ``` ``` volumes: - name: host-pods hostPath: path: /var/data/kubelet/pods type: '' - name: host-plugins hostPath: path: /var/data/kubelet/plugins type: '' ``` After getting on-host access was able to confirm. Pods are at /var/lib/kubelet/pods. ``` ls /var/lib/kubelet/pods 07c0be63-335d-4cfb-b39f-816bc2fb32cd 51f31b3e-4710-4ef0-8626-5f1a78a624b2 a4802fd3-3b62-45a4-8f21-974880b6f92a cccb35c9-b4f9-4ca9-a697-736ae64f09ad 0a5d4366-7fa1-4525-9e45-a43a362b8542 558b0643-0661-4d4a-b03e-aac60c6ad710 a4b106fb-5b7b-48e5-828a-ea7b41ba0e59 ce1290e1-4330-4df6-8166-14784bcce930 ``` On host the volumes are in /var/data/kubelet/plugins. ``` ls /var/data/kubelet/plugins/kubernetes.io/csi/openshift-storage.cephfs.csi.ceph.com/231e04896c4f528efb95d23a3c153db9fc4a7206b7320f74443f30de7228dba5/globalmount/velero/backups/backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c/ backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-csi-volumesnapshotclasses.json.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-resource-list.json.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-csi-volumesnapshotcontents.json.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-results.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-csi-volumesnapshots.json.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-volumesnapshots.json.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-itemoperations.json.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c.tar.gz backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-logs.gz velero-backup.json backup-resources-41d84d16-47a7-4ea8-a9cb-6348d01bcb2c-podvolumebackups.json.gz ``` With the volume config changed to expose /var/data/kubelet/plugins for the plugin hostPath, the DataUploads and DataDownloads succeed for both Filesystem and Block mode PVCs. ``` status: completionTimestamp: '2024-08-02T17:23:33Z' node: 10.240.0.5 path: >- /host_pods/7fcb9d56-7885-437c-acd3-67db6b1ee8ae/volumeDevices/kubernetes.io~csi/pvc-47b91f56-db8c-44bf-9ecc-737170561b4b phase: Completed progress: bytesDone: 5368709120 totalBytes: 5368709120 snapshotID: 8faae36b3592fee4efbfad024f26033e startTimestamp: '2024-08-02T17:21:22Z' ``` ``` status: completionTimestamp: '2024-08-02T18:42:19Z' node: 10.240.0.5 phase: Completed progress: bytesDone: 5368709120 totalBytes: 5368709120 startTimestamp: '2024-08-02T18:41:00Z' ``` My apologies for the error. Signed-off-by: Michael Fruchtman * Add context to plugins mountPath Signed-off-by: Michael Fruchtman --------- Signed-off-by: Michael Fruchtman --- .../docs/main/csi-snapshot-data-movement.md | 22 +++++++++++++---- .../docs/v1.13/csi-snapshot-data-movement.md | 22 +++++++++++++---- .../docs/v1.14/csi-snapshot-data-movement.md | 24 +++++++++++++++---- 3 files changed, 55 insertions(+), 13 deletions(-) diff --git a/site/content/docs/main/csi-snapshot-data-movement.md b/site/content/docs/main/csi-snapshot-data-movement.md index 5912391da..60d72aecf 100644 --- a/site/content/docs/main/csi-snapshot-data-movement.md +++ b/site/content/docs/main/csi-snapshot-data-movement.md @@ -124,19 +124,33 @@ oc create -n -f ds.yaml **OpenShift on IBM Cloud** -Update the host path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to -`/var/data/kubelet/pods`. +Update the host path and mount path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/plugins` to +`/var/data/kubelet/plugins`. ```yaml hostPath: - path: /var/lib/kubelet/pods + path: /var/lib/kubelet/plugins ``` to ```yaml hostPath: - path: /var/data/kubelet/pods + path: /var/data/kubelet/plugins +``` + +and + +```yaml +- name: host-plugins + mountPath: /var/lib/kubelet/plugins +``` + +to + +```yaml +- name: host-plugins + mountPath: /var/data/kubelet/plugins ``` **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** diff --git a/site/content/docs/v1.13/csi-snapshot-data-movement.md b/site/content/docs/v1.13/csi-snapshot-data-movement.md index cc45b24f9..57523b6b7 100644 --- a/site/content/docs/v1.13/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.13/csi-snapshot-data-movement.md @@ -124,19 +124,33 @@ oc create -n -f ds.yaml **OpenShift on IBM Cloud** -Update the host path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to -`/var/data/kubelet/pods`. +Update the host path and mount path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/plugins` to +`/var/data/kubelet/plugins`. ```yaml hostPath: - path: /var/lib/kubelet/pods + path: /var/lib/kubelet/plugins ``` to ```yaml hostPath: - path: /var/data/kubelet/pods + path: /var/data/kubelet/plugins +``` + +and + +```yaml +- name: host-plugins + mountPath: /var/lib/kubelet/plugins +``` + +to + +```yaml +- name: host-plugins + mountPath: /var/data/kubelet/plugins ``` **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** diff --git a/site/content/docs/v1.14/csi-snapshot-data-movement.md b/site/content/docs/v1.14/csi-snapshot-data-movement.md index 5912391da..266ac5289 100644 --- a/site/content/docs/v1.14/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.14/csi-snapshot-data-movement.md @@ -1,4 +1,4 @@ ---- +`--- title: "CSI Snapshot Data Movement" layout: docs --- @@ -124,19 +124,33 @@ oc create -n -f ds.yaml **OpenShift on IBM Cloud** -Update the host path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/pods` to -`/var/data/kubelet/pods`. +Update the host path and mount path for volumes in the node-agent DaemonSet in the Velero namespace from `/var/lib/kubelet/plugins` to +`/var/data/kubelet/plugins`. ```yaml hostPath: - path: /var/lib/kubelet/pods + path: /var/lib/kubelet/plugins ``` to ```yaml hostPath: - path: /var/data/kubelet/pods + path: /var/data/kubelet/plugins +``` + +and + +```yaml +- name: host-plugins + mountPath: /var/lib/kubelet/plugins +``` + +to + +```yaml +- name: host-plugins + mountPath: /var/data/kubelet/plugins ``` **VMware Tanzu Kubernetes Grid Integrated Edition (formerly VMware Enterprise PKS)** From be4aabccd9db95c6c7c74b62c062170fa8eb6cca Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Tue, 6 Aug 2024 19:05:59 -0400 Subject: [PATCH 43/69] Fix v1.14 site header for csi-snapshot-data-movement Signed-off-by: Tiger Kaovilai --- site/content/docs/v1.14/csi-snapshot-data-movement.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/docs/v1.14/csi-snapshot-data-movement.md b/site/content/docs/v1.14/csi-snapshot-data-movement.md index 266ac5289..60d72aecf 100644 --- a/site/content/docs/v1.14/csi-snapshot-data-movement.md +++ b/site/content/docs/v1.14/csi-snapshot-data-movement.md @@ -1,4 +1,4 @@ -`--- +--- title: "CSI Snapshot Data Movement" layout: docs --- From 82d9fe4d4dec542c1a8aea31bb6a20fb050c01cf Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 31 Jul 2024 17:39:29 +0800 Subject: [PATCH 44/69] backup repo config Signed-off-by: Lyndon-Li --- changelogs/unreleased/8093-Lyndon-Li | 1 + design/backup-repo-config.md | 53 +++------------ .../backup_repository_controller.go | 2 +- pkg/repository/provider/unified_repo_test.go | 10 +-- .../udmrepo/kopialib/backend/common.go | 4 +- .../udmrepo/kopialib/backend/common_test.go | 8 ++- .../udmrepo/kopialib/backend/utils_test.go | 65 +++++++++++++++++++ 7 files changed, 89 insertions(+), 54 deletions(-) create mode 100644 changelogs/unreleased/8093-Lyndon-Li diff --git a/changelogs/unreleased/8093-Lyndon-Li b/changelogs/unreleased/8093-Lyndon-Li new file mode 100644 index 000000000..a43c47e09 --- /dev/null +++ b/changelogs/unreleased/8093-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #7620, add backup repository configuration implementation and support cacheLimit configuration for Kopia repo \ No newline at end of file diff --git a/design/backup-repo-config.md b/design/backup-repo-config.md index 9e4b168c8..f6a25e8ec 100644 --- a/design/backup-repo-config.md +++ b/design/backup-repo-config.md @@ -86,18 +86,6 @@ For any reason, if the configMap doesn't effect, nothing is specified to the bac The BackupRepository configMap supports backup repository type specific configurations, even though users can only specify one configMap. So in the configMap struct, multiple entries are supported, indexed by the backup repository type. During the backup repository creation, the configMap is searched by the repository type. -Below are the struct for the configMap: -``` golang -type RepoConfig struct { - CacheLimitMB int `json:"cacheLimitMB,omitempty"` - EnableCompression int `json:"enableCompression,omitempty"` -} - -type RepoConfigs struct { - Configs map[string]RepoConfig `json:"configs"` -} -``` - ### Configurations With the above mechanisms, any kind of configuration could be added. Here list the configurations defined at present: @@ -105,23 +93,7 @@ With the above mechanisms, any kind of configuration could be added. Here list t ```enableCompression```: specifies to enable/disable compression for a backup repsotiory. Most of the backup repositories support the data compression feature, if it is not supported by a backup repository, this parameter is ignored. Most of the backup repositories support to dynamically enable/disable compression, so this parameter is defined to be used whenever creating a write connection to the backup repository, if the dynamically changing is not supported, this parameter will be hornored only when initializing the backup repository. For Kopia repository, this parameter is supported and can be dynamically modified. ### Sample -Below is an example of the BackupRepository configMap with the configurations: -json format: -```json -{ - "configs": { - "repo-type-1": { - "cacheLimitMB": 2048, - "enableCompression": true - }, - "repo-type-2": { - "cacheLimitMB": 1024, - "enableCompression": false - } - } -} -``` -yaml format: +Below is an example of the BackupRepository configMap with the configurations: ```yaml apiVersion: v1 kind: ConfigMap @@ -129,25 +101,20 @@ metadata: name: namespace: velero data: - configs: | + : | { - "repo-type-1": { - "cacheLimitMB": 2048, - "enableCompression": true - }, - "repo-type-2": { - "cacheLimitMB": 1024, - "enableCompression": false - } - } + "cacheLimitMB": 2048, + "enableCompression": true + } + : | + { + "cacheLimitMB": 1, + "enableCompression": false + } ``` To create the configMap, users need to save something like the above sample to a file and then run below commands: ``` -kubectl create cm -n velero --from-file= -``` -Or -``` kubectl apply -f ``` diff --git a/pkg/controller/backup_repository_controller.go b/pkg/controller/backup_repository_controller.go index 56da435e5..0bc457a17 100644 --- a/pkg/controller/backup_repository_controller.go +++ b/pkg/controller/backup_repository_controller.go @@ -246,7 +246,7 @@ func (r *BackupRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 config, err := getBackupRepositoryConfig(ctx, r, r.backukpRepoConfig, r.namespace, req.Name, req.Spec.RepositoryType, log) if err != nil { - log.WithError(err).Warnf("Failed to get repo config from %s for repo %s, repo config is ignored", r.backukpRepoConfig, req.Name) + log.WithError(err).Warn("Failed to get repo config, repo config is ignored") } else if config != nil { log.Infof("Init repo with config %v", config) } diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index 0d3b41378..a5063bbbf 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -452,11 +452,11 @@ func TestGetStorageVariables(t *testing.T) { udmrepo.StoreOptionCacheLimit: "1000", }, expected: map[string]string{ - "fspath": "fake-path", - "bucket": "", - "prefix": "fake-prefix/fake-repo-type/", - "region": "", - "cacheLimit": "1000", + "fspath": "fake-path", + "bucket": "", + "prefix": "fake-prefix/fake-repo-type/", + "region": "", + "cacheLimitMB": "1000", }, }, } diff --git a/pkg/repository/udmrepo/kopialib/backend/common.go b/pkg/repository/udmrepo/kopialib/backend/common.go index 6e5facdec..646811da9 100644 --- a/pkg/repository/udmrepo/kopialib/backend/common.go +++ b/pkg/repository/udmrepo/kopialib/backend/common.go @@ -33,7 +33,7 @@ import ( ) const ( - defaultCacheLimitMB = 2000 + defaultCacheLimitMB = 5000 maxCacheDurationSecond = 30 ) @@ -68,7 +68,7 @@ func SetupNewRepositoryOptions(ctx context.Context, flags map[string]string) rep func SetupConnectOptions(ctx context.Context, repoOptions udmrepo.RepoOptions) repo.ConnectOptions { cacheLimit := optionalHaveIntWithDefault(ctx, udmrepo.StoreOptionCacheLimit, repoOptions.StorageOptions, defaultCacheLimitMB) << 20 - // 80% for data cache and 20% for metadata cahce and align to KB + // 80% for data cache and 20% for metadata cache and align to KB dataCacheLimit := (cacheLimit / 5 * 4) >> 10 metadataCacheLimit := (cacheLimit / 5) >> 10 diff --git a/pkg/repository/udmrepo/kopialib/backend/common_test.go b/pkg/repository/udmrepo/kopialib/backend/common_test.go index 8ec90f069..c5c070716 100644 --- a/pkg/repository/udmrepo/kopialib/backend/common_test.go +++ b/pkg/repository/udmrepo/kopialib/backend/common_test.go @@ -111,9 +111,11 @@ func TestSetupNewRepositoryOptions(t *testing.T) { func TestSetupConnectOptions(t *testing.T) { defaultCacheOption := content.CachingOptions{ - ContentCacheSizeBytes: 2000 << 20, - MetadataCacheSizeBytes: 2000 << 20, - MaxListCacheDuration: content.DurationSeconds(time.Duration(30) * time.Second), + ContentCacheSizeBytes: 3200 << 20, + MetadataCacheSizeBytes: 800 << 20, + ContentCacheSizeLimitBytes: 4000 << 20, + MetadataCacheSizeLimitBytes: 1000 << 20, + MaxListCacheDuration: content.DurationSeconds(time.Duration(30) * time.Second), } testCases := []struct { diff --git a/pkg/repository/udmrepo/kopialib/backend/utils_test.go b/pkg/repository/udmrepo/kopialib/backend/utils_test.go index 0eb238196..6f9049f41 100644 --- a/pkg/repository/udmrepo/kopialib/backend/utils_test.go +++ b/pkg/repository/udmrepo/kopialib/backend/utils_test.go @@ -90,3 +90,68 @@ func TestOptionalHaveBool(t *testing.T) { }) } } + +func TestOptionalHaveIntWithDefault(t *testing.T) { + var expectMsg string + testCases := []struct { + name string + key string + flags map[string]string + defaultValue int64 + logger *storagemocks.Core + retFuncCheck func(mock.Arguments) + expectMsg string + retValue int64 + }{ + { + name: "key not exist", + key: "fake-key", + flags: map[string]string{}, + defaultValue: 2000, + retValue: 2000, + }, + { + name: "value valid", + key: "fake-key", + flags: map[string]string{ + "fake-key": "1000", + }, + retValue: 1000, + }, + { + name: "value invalid", + key: "fake-key", + flags: map[string]string{ + "fake-key": "fake-value", + }, + logger: new(storagemocks.Core), + retFuncCheck: func(args mock.Arguments) { + ent := args[0].(zapcore.Entry) + if ent.Level == zapcore.ErrorLevel { + expectMsg = ent.Message + } + }, + expectMsg: "Ignore fake-key, value [fake-value] is invalid, err strconv.ParseInt: parsing \"fake-value\": invalid syntax", + defaultValue: 2000, + retValue: 2000, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if tc.logger != nil { + tc.logger.On("Enabled", mock.Anything).Return(true) + tc.logger.On("Check", mock.Anything, mock.Anything).Run(tc.retFuncCheck).Return(&zapcore.CheckedEntry{}) + } + + ctx := logging.WithLogger(context.Background(), func(module string) logging.Logger { + return zap.New(tc.logger).Sugar() + }) + + retValue := optionalHaveIntWithDefault(ctx, tc.key, tc.flags, tc.defaultValue) + + require.Equal(t, retValue, tc.retValue) + require.Equal(t, tc.expectMsg, expectMsg) + }) + } +} From 2c7047a30499af981e4687d97d1c30b112fe4b90 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 7 Aug 2024 17:23:15 +0800 Subject: [PATCH 45/69] Merge branch 'main' into data-mover-ms-node-agent-resume Signed-off-by: Lyndon-Li --- pkg/builder/data_download_builder.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/builder/data_download_builder.go b/pkg/builder/data_download_builder.go index 92c5b1336..1eb8e6441 100644 --- a/pkg/builder/data_download_builder.go +++ b/pkg/builder/data_download_builder.go @@ -130,12 +130,6 @@ func (d *DataDownloadBuilder) CompletionTimestamp(completionTimestamp *metav1.Ti return d } -// Labels sets the DataDownload's Labels. -func (d *DataDownloadBuilder) Labels(labels map[string]string) *DataDownloadBuilder { - d.object.Labels = labels - return d -} - // Labels sets the DataDownload's Progress. func (d *DataDownloadBuilder) Progress(progress shared.DataMoveOperationProgress) *DataDownloadBuilder { d.object.Status.Progress = progress From fefb4b858c796630428fb204a60b051f53fb1de6 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 7 Aug 2024 17:06:48 +0800 Subject: [PATCH 46/69] issue 8072: restic deprecation - warning messages Signed-off-by: Lyndon-Li --- changelogs/unreleased/8096-Lyndon-Li | 1 + pkg/cmd/cli/install/install.go | 4 +- pkg/cmd/server/server.go | 4 +- pkg/cmd/server/server_test.go | 7 +++ pkg/podvolume/backupper.go | 22 ++++++-- pkg/podvolume/backupper_test.go | 54 ++++++++++++++------ pkg/uploader/types.go | 11 ++-- pkg/uploader/types_test.go | 27 +++++++--- site/content/docs/main/file-system-backup.md | 35 +++++++++++++ 9 files changed, 134 insertions(+), 31 deletions(-) create mode 100644 changelogs/unreleased/8096-Lyndon-Li diff --git a/changelogs/unreleased/8096-Lyndon-Li b/changelogs/unreleased/8096-Lyndon-Li new file mode 100644 index 000000000..9c0e2dd0d --- /dev/null +++ b/changelogs/unreleased/8096-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #8072, add the warning messages for restic deprecation \ No newline at end of file diff --git a/pkg/cmd/cli/install/install.go b/pkg/cmd/cli/install/install.go index c51cee658..b73438e78 100644 --- a/pkg/cmd/cli/install/install.go +++ b/pkg/cmd/cli/install/install.go @@ -364,8 +364,10 @@ func (o *Options) Validate(c *cobra.Command, args []string, f client.Factory) er return err } - if err := uploader.ValidateUploaderType(o.UploaderType); err != nil { + if msg, err := uploader.ValidateUploaderType(o.UploaderType); err != nil { return err + } else if msg != "" { + fmt.Printf("⚠️ %s\n", msg) } // If we're only installing CRDs, we can skip the rest of the validation. diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index d8938ca56..df965ed42 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -288,8 +288,10 @@ type server struct { } func newServer(f client.Factory, config serverConfig, logger *logrus.Logger) (*server, error) { - if err := uploader.ValidateUploaderType(config.uploaderType); err != nil { + if msg, err := uploader.ValidateUploaderType(config.uploaderType); err != nil { return nil, err + } else if msg != "" { + logger.Warn(msg) } if config.clientQPS < 0.0 { diff --git a/pkg/cmd/server/server_test.go b/pkg/cmd/server/server_test.go index 4d222b776..6e5995586 100644 --- a/pkg/cmd/server/server_test.go +++ b/pkg/cmd/server/server_test.go @@ -203,6 +203,13 @@ func Test_newServer(t *testing.T) { }, logger) assert.Error(t, err) + // invalid clientQPS Restic uploader + _, err = newServer(factory, serverConfig{ + uploaderType: uploader.ResticType, + clientQPS: -1, + }, logger) + assert.Error(t, err) + // invalid clientBurst factory.On("SetClientQPS", mock.Anything).Return() _, err = newServer(factory, serverConfig{ diff --git a/pkg/podvolume/backupper.go b/pkg/podvolume/backupper.go index 8d06cb222..5576f4e40 100644 --- a/pkg/podvolume/backupper.go +++ b/pkg/podvolume/backupper.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/nodeagent" "github.com/vmware-tanzu/velero/pkg/repository" + "github.com/vmware-tanzu/velero/pkg/uploader" uploaderutil "github.com/vmware-tanzu/velero/pkg/uploader/util" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -163,10 +164,13 @@ func (b *backupper) getMatchAction(resPolicies *resourcepolicies.Policies, pvc * return nil, errors.Errorf("failed to check resource policies for empty volume") } +var funcGetRepositoryType = getRepositoryType + func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api.Pod, volumesToBackup []string, resPolicies *resourcepolicies.Policies, log logrus.FieldLogger) ([]*velerov1api.PodVolumeBackup, *PVCBackupSummary, []error) { if len(volumesToBackup) == 0 { return nil, nil, nil } + log.Infof("pod %s/%s has volumes to backup: %v", pod.Namespace, pod.Name, volumesToBackup) var ( @@ -189,6 +193,13 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. } } + if msg, err := uploader.ValidateUploaderType(b.uploaderType); err != nil { + skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log) + return nil, pvcSummary, []error{err} + } else if msg != "" { + log.Warn(msg) + } + if err := kube.IsPodRunning(pod); err != nil { skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log) return nil, pvcSummary, nil @@ -196,18 +207,21 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. err := nodeagent.IsRunningInNode(b.ctx, backup.Namespace, pod.Spec.NodeName, b.crClient) if err != nil { - return nil, nil, []error{err} + skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log) + return nil, pvcSummary, []error{err} } - repositoryType := getRepositoryType(b.uploaderType) + repositoryType := funcGetRepositoryType(b.uploaderType) if repositoryType == "" { err := errors.Errorf("empty repository type, uploader %s", b.uploaderType) - return nil, nil, []error{err} + skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log) + return nil, pvcSummary, []error{err} } repo, err := b.repoEnsurer.EnsureRepo(b.ctx, backup.Namespace, pod.Namespace, backup.Spec.StorageLocation, repositoryType) if err != nil { - return nil, nil, []error{err} + skipAllPodVolumes(pod, volumesToBackup, err, pvcSummary, log) + return nil, pvcSummary, []error{err} } // get a single non-exclusive lock since we'll wait for all individual diff --git a/pkg/podvolume/backupper_test.go b/pkg/podvolume/backupper_test.go index 06cec20de..16d4ce286 100644 --- a/pkg/podvolume/backupper_test.go +++ b/pkg/podvolume/backupper_test.go @@ -309,22 +309,38 @@ func TestBackupPodVolumes(t *testing.T) { corev1api.AddToScheme(scheme) tests := []struct { - name string - bsl string - uploaderType string - volumes []string - sourcePod *corev1api.Pod - kubeClientObj []runtime.Object - ctlClientObj []runtime.Object - veleroClientObj []runtime.Object - veleroReactors []reactor - runtimeScheme *runtime.Scheme - pvbs int - errs []string + name string + bsl string + uploaderType string + volumes []string + sourcePod *corev1api.Pod + kubeClientObj []runtime.Object + ctlClientObj []runtime.Object + veleroClientObj []runtime.Object + veleroReactors []reactor + runtimeScheme *runtime.Scheme + pvbs int + mockGetRepositoryType bool + errs []string }{ { name: "empty volume list", }, + { + name: "wrong uploader type", + volumes: []string{ + "fake-volume-1", + "fake-volume-2", + }, + sourcePod: createPodObj(true, false, false, 2), + kubeClientObj: []runtime.Object{ + createNodeAgentPodObj(true), + }, + uploaderType: "fake-uploader-type", + errs: []string{ + "invalid uploader type 'fake-uploader-type', valid upload types are: 'restic', 'kopia'", + }, + }, { name: "pod is not running", volumes: []string{ @@ -348,7 +364,8 @@ func TestBackupPodVolumes(t *testing.T) { "fake-volume-1", "fake-volume-2", }, - sourcePod: createPodObj(true, false, false, 2), + sourcePod: createPodObj(true, false, false, 2), + uploaderType: "kopia", errs: []string{ "daemonset pod not found in running state in node fake-node-name", }, @@ -363,9 +380,10 @@ func TestBackupPodVolumes(t *testing.T) { kubeClientObj: []runtime.Object{ createNodeAgentPodObj(true), }, - uploaderType: "fake-uploader-type", + uploaderType: "kopia", + mockGetRepositoryType: true, errs: []string{ - "empty repository type, uploader fake-uploader-type", + "empty repository type, uploader kopia", }, }, { @@ -542,6 +560,12 @@ func TestBackupPodVolumes(t *testing.T) { require.NoError(t, err) + if test.mockGetRepositoryType { + funcGetRepositoryType = func(string) string { return "" } + } else { + funcGetRepositoryType = getRepositoryType + } + pvbs, _, errs := bp.BackupPodVolumes(backupObj, test.sourcePod, test.volumes, nil, velerotest.NewLogger()) if errs == nil { diff --git a/pkg/uploader/types.go b/pkg/uploader/types.go index 02106e266..fb79f7c9f 100644 --- a/pkg/uploader/types.go +++ b/pkg/uploader/types.go @@ -39,12 +39,17 @@ const ( // ValidateUploaderType validates if the input param is a valid uploader type. // It will return an error if it's invalid. -func ValidateUploaderType(t string) error { +func ValidateUploaderType(t string) (string, error) { t = strings.TrimSpace(t) if t != ResticType && t != KopiaType { - return fmt.Errorf("invalid uploader type '%s', valid upload types are: '%s', '%s'", t, ResticType, KopiaType) + return "", fmt.Errorf("invalid uploader type '%s', valid upload types are: '%s', '%s'", t, ResticType, KopiaType) } - return nil + + if t == ResticType { + return fmt.Sprintf("Uploader '%s' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero", t), nil + } + + return "", nil } type SnapshotInfo struct { diff --git a/pkg/uploader/types_test.go b/pkg/uploader/types_test.go index 492051bf2..e92f20c79 100644 --- a/pkg/uploader/types_test.go +++ b/pkg/uploader/types_test.go @@ -1,34 +1,47 @@ package uploader -import "testing" +import ( + "testing" + + "github.com/stretchr/testify/assert" +) func TestValidateUploaderType(t *testing.T) { tests := []struct { name string input string - wantErr bool + wantErr string + wantMsg string }{ { "'restic' is a valid type", "restic", - false, + "", + "Uploader 'restic' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero", }, { "' kopia ' is a valid type (space will be trimmed)", " kopia ", - false, + "", + "", }, { "'anything_else' is invalid", "anything_else", - true, + "invalid uploader type 'anything_else', valid upload types are: 'restic', 'kopia'", + "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := ValidateUploaderType(tt.input); (err != nil) != tt.wantErr { - t.Errorf("ValidateUploaderType(), input = '%s' error = %v, wantErr %v", tt.input, err, tt.wantErr) + msg, err := ValidateUploaderType(tt.input) + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + assert.NoError(t, err) } + + assert.Equal(t, tt.wantMsg, msg) }) } } diff --git a/site/content/docs/main/file-system-backup.md b/site/content/docs/main/file-system-backup.md index 1e4917eb4..5dac5e3c5 100644 --- a/site/content/docs/main/file-system-backup.md +++ b/site/content/docs/main/file-system-backup.md @@ -28,6 +28,7 @@ Cons: - It access the file system from the mounted hostpath directory, so Velero Node Agent pods need to run as root user and even under privileged mode in some environments. **NOTE:** hostPath volumes are not supported, but the [local volume type][5] is supported. +**NOTE:** restic is under the deprecation process by following [Velero Deprecation Policy][17], for more details, see the Restic Deprecation section. ## Setup File System Backup @@ -643,6 +644,39 @@ If you want to constraint the CPU/memory usage, you need to [customize the resou During the restore, the repository may also cache data/metadata so as to reduce the network footprint and speed up the restore. The repository uses its own policy to store and clean up the cache. For Kopia repository, the cache is stored in the node-agent pod's root file system and the cleanup is triggered for the data/metadata that are older than 10 minutes (not configurable at present). So you should prepare enough disk space, otherwise, the node-agent pod may be evicted due to running out of the ephemeral storage. +## Restic Deprecation + +According to the [Velero Deprecation Policy][17], restic path is being deprecated starting from v1.15, specifically: +- For 1.15 and 1.16, if restic path is used by a backup, the backup still creates and succeeds but you will see warnings +- For 1.17 and 1.18, backups with restic path are disabled, but you are still allowed to restore from your previous restic backups +- From 1.19, both backups and restores with restic path will be disabled, you are not able to use 1.19 or higher to restore your restic backup data + +For 1.15 and 1.16, you will see below warnings if `--uploader-type=restic` is used in Velero installation: +In the output of installation: +``` +⚠️ Uploader 'restic' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero +``` +In Velero server log: +``` +level=warning msg="Uploader 'restic' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero +``` +In the output of `velero backup describe` command for a backup with fs-backup: +``` + Namespaces: + : resource: /pods name: message: /Uploader 'restic' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero +``` + +And you will see below warnings you upgrade from v1.9 or lower to 1.15 or 1.16: +In Velero server log: +``` +level=warning msg="Uploader 'restic' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero +``` +In the output of `velero backup describe` command for a backup with fs-backup: +``` + Namespaces: + : resource: /pods name: message: /Uploader 'restic' is deprecated, don't use it for new backups, otherwise the backups won't be available for restore when this functionality is removed in a future version of Velero +``` + [1]: https://github.com/restic/restic [2]: https://github.com/kopia/kopia @@ -660,3 +694,4 @@ For Kopia repository, the cache is stored in the node-agent pod's root file syst [14]: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ [15]: customize-installation.md#customize-resource-requests-and-limits [16]: performance-guidance.md +[17]: https://github.com/vmware-tanzu/velero/blob/main/GOVERNANCE.md#deprecation-policy From 4dea3a48e8a4cc56debe634d62b758ae5b2c92ce Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 9 Aug 2024 14:34:48 +0800 Subject: [PATCH 47/69] data mover ms smoking test Signed-off-by: Lyndon-Li --- pkg/cmd/cli/datamover/backup.go | 1 + pkg/cmd/cli/datamover/restore.go | 1 + pkg/controller/data_download_controller.go | 72 ++++---- .../data_download_controller_test.go | 49 +++++- pkg/controller/data_upload_controller.go | 75 +++++---- pkg/controller/data_upload_controller_test.go | 156 ++++++++++-------- pkg/datamover/backup_micro_service.go | 14 +- pkg/datamover/backup_micro_service_test.go | 6 +- pkg/datamover/restore_micro_service.go | 10 +- pkg/datamover/restore_micro_service_test.go | 6 +- pkg/datapath/file_system.go | 46 +++++- pkg/datapath/file_system_test.go | 2 + pkg/datapath/micro_service_watcher.go | 106 ++++++------ pkg/datapath/micro_service_watcher_test.go | 2 +- 14 files changed, 330 insertions(+), 216 deletions(-) diff --git a/pkg/cmd/cli/datamover/backup.go b/pkg/cmd/cli/datamover/backup.go index 35f483d92..ca600faab 100644 --- a/pkg/cmd/cli/datamover/backup.go +++ b/pkg/cmd/cli/datamover/backup.go @@ -233,6 +233,7 @@ func (s *dataMoverBackup) runDataPath() { result, err := dpService.RunCancelableDataPath(s.ctx) if err != nil { + dpService.Shutdown() s.cancelFunc() funcExitWithMessage(s.logger, false, "Failed to run data path service for DataUpload %s: %v", s.config.duName, err) return diff --git a/pkg/cmd/cli/datamover/restore.go b/pkg/cmd/cli/datamover/restore.go index fb46abd40..fc74a64f1 100644 --- a/pkg/cmd/cli/datamover/restore.go +++ b/pkg/cmd/cli/datamover/restore.go @@ -223,6 +223,7 @@ func (s *dataMoverRestore) runDataPath() { result, err := dpService.RunCancelableDataPath(s.ctx) if err != nil { s.cancelFunc() + dpService.Shutdown() funcExitWithMessage(s.logger, false, "Failed to run data path service for DataDownload %s: %v", s.config.ddName, err) return } diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index f0c0c1728..12365e03c 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -217,9 +217,9 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request } else if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseAccepted { if dd.Spec.Cancel { log.Debugf("Data download is been canceled %s in Phase %s", dd.GetName(), dd.Status.Phase) - r.TryCancelDataDownload(ctx, dd, "") + r.tryCancelAcceptedDataDownload(ctx, dd, "") } else if peekErr := r.restoreExposer.PeekExposed(ctx, getDataDownloadOwnerObject(dd)); peekErr != nil { - r.TryCancelDataDownload(ctx, dd, fmt.Sprintf("found a dataupload %s/%s with expose error: %s. mark it as cancel", dd.Namespace, dd.Name, peekErr)) + r.tryCancelAcceptedDataDownload(ctx, dd, fmt.Sprintf("found a dataupload %s/%s with expose error: %s. mark it as cancel", dd.Namespace, dd.Name, peekErr)) log.Errorf("Cancel dd %s/%s because of expose error %s", dd.Namespace, dd.Name, peekErr) } else if dd.Status.StartTimestamp != nil { if time.Since(dd.Status.StartTimestamp.Time) >= r.preparingTimeout { @@ -272,23 +272,35 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request return r.errorOut(ctx, dd, err, "error to create data path", log) } } + + if err := r.initCancelableDataPath(ctx, asyncBR, result, log); err != nil { + log.WithError(err).Warnf("Failed to init cancelable data path for %s, will close and retry", dd.Name) + + r.closeDataPath(ctx, dd.Name) + return r.errorOut(ctx, dd, err, "error initializing data path", log) + } + // Update status to InProgress original := dd.DeepCopy() dd.Status.Phase = velerov2alpha1api.DataDownloadPhaseInProgress dd.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} if err := r.client.Patch(ctx, dd, client.MergeFrom(original)); err != nil { - log.WithError(err).Error("Unable to update status to in progress") - return ctrl.Result{}, err + log.WithError(err).Warnf("Failed to update datadownload %s to InProgress, will close data path and retry", dd.Name) + + r.closeDataPath(ctx, dd.Name) + return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil } log.Info("Data download is marked as in progress") - reconcileResult, err := r.runCancelableDataPath(ctx, asyncBR, dd, result, log) - if err != nil { - log.Errorf("Failed to run cancelable data path for %s with err %v", dd.Name, err) + if err := r.startCancelableDataPath(asyncBR, dd, result, log); err != nil { + log.WithError(err).Errorf("Failed to start cancelable data path for %s", dd.Name) + r.closeDataPath(ctx, dd.Name) + return r.errorOut(ctx, dd, err, "error starting data path", log) } - return reconcileResult, err + + return ctrl.Result{}, nil } else if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseInProgress { log.Info("Data download is in progress") if dd.Spec.Cancel { @@ -331,27 +343,33 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request } } -func (r *DataDownloadReconciler) runCancelableDataPath(ctx context.Context, asyncBR datapath.AsyncBR, dd *velerov2alpha1api.DataDownload, res *exposer.ExposeResult, log logrus.FieldLogger) (reconcile.Result, error) { +func (r *DataDownloadReconciler) initCancelableDataPath(ctx context.Context, asyncBR datapath.AsyncBR, res *exposer.ExposeResult, log logrus.FieldLogger) error { + log.Info("Init cancelable dataDownload") + if err := asyncBR.Init(ctx, nil); err != nil { - return r.errorOut(ctx, dd, err, "error to initialize asyncBR", log) + return errors.Wrap(err, "error initializing asyncBR") } log.Infof("async restore init for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) + return nil +} + +func (r *DataDownloadReconciler) startCancelableDataPath(asyncBR datapath.AsyncBR, dd *velerov2alpha1api.DataDownload, res *exposer.ExposeResult, log logrus.FieldLogger) error { + log.Info("Start cancelable dataDownload") + if err := asyncBR.StartRestore(dd.Spec.SnapshotID, datapath.AccessPoint{ ByPath: res.ByPod.VolumeName, }, dd.Spec.DataMoverConfig); err != nil { - return r.errorOut(ctx, dd, err, fmt.Sprintf("error starting async restore for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName), log) + return errors.Wrapf(err, "error starting async restore for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) } - log.Infof("Async restore started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) - return ctrl.Result{}, nil + log.Info("Async restore started for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) + return nil } func (r *DataDownloadReconciler) OnDataDownloadCompleted(ctx context.Context, namespace string, ddName string, result datapath.Result) { - defer func() { - go r.closeDataPath(ctx, ddName) - }() + defer r.dataPathMgr.RemoveAsyncBR(ddName) log := r.logger.WithField("datadownload", ddName) log.Info("Async fs restore data path completed") @@ -384,9 +402,7 @@ func (r *DataDownloadReconciler) OnDataDownloadCompleted(ctx context.Context, na } func (r *DataDownloadReconciler) OnDataDownloadFailed(ctx context.Context, namespace string, ddName string, err error) { - defer func() { - go r.closeDataPath(ctx, ddName) - }() + defer r.dataPathMgr.RemoveAsyncBR(ddName) log := r.logger.WithField("datadownload", ddName) @@ -396,16 +412,12 @@ func (r *DataDownloadReconciler) OnDataDownloadFailed(ctx context.Context, names if getErr := r.client.Get(ctx, types.NamespacedName{Name: ddName, Namespace: namespace}, &dd); getErr != nil { log.WithError(getErr).Warn("Failed to get data download on failure") } else { - if _, errOut := r.errorOut(ctx, &dd, err, "data path restore failed", log); err != nil { - log.WithError(err).Warnf("Failed to patch data download with err %v", errOut) - } + r.errorOut(ctx, &dd, err, "data path restore failed", log) } } func (r *DataDownloadReconciler) OnDataDownloadCancelled(ctx context.Context, namespace string, ddName string) { - defer func() { - go r.closeDataPath(ctx, ddName) - }() + defer r.dataPathMgr.RemoveAsyncBR(ddName) log := r.logger.WithField("datadownload", ddName) @@ -432,9 +444,9 @@ func (r *DataDownloadReconciler) OnDataDownloadCancelled(ctx context.Context, na } } -func (r *DataDownloadReconciler) TryCancelDataDownload(ctx context.Context, dd *velerov2alpha1api.DataDownload, message string) { +func (r *DataDownloadReconciler) tryCancelAcceptedDataDownload(ctx context.Context, dd *velerov2alpha1api.DataDownload, message string) { log := r.logger.WithField("datadownload", dd.Name) - log.Warn("Async fs backup data path canceled") + log.Warn("Accepted data download is canceled") succeeded, err := r.exclusiveUpdateDataDownload(ctx, dd, func(dataDownload *velerov2alpha1api.DataDownload) { dataDownload.Status.Phase = velerov2alpha1api.DataDownloadPhaseCanceled @@ -442,7 +454,10 @@ func (r *DataDownloadReconciler) TryCancelDataDownload(ctx context.Context, dd * dataDownload.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} } dataDownload.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} - dataDownload.Status.Message = message + + if message != "" { + dataDownload.Status.Message = message + } }) if err != nil { @@ -456,7 +471,6 @@ func (r *DataDownloadReconciler) TryCancelDataDownload(ctx context.Context, dd * // success update r.metrics.RegisterDataDownloadCancel(r.nodeName) r.restoreExposer.CleanUp(ctx, getDataDownloadOwnerObject(dd)) - r.closeDataPath(ctx, dd.Name) } func (r *DataDownloadReconciler) OnDataDownloadProgress(ctx context.Context, namespace string, ddName string, progress *uploader.Progress) { diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index c6c2697f7..385870426 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -192,6 +192,10 @@ func TestDataDownloadReconcile(t *testing.T) { isFSBRRestoreErr bool notNilExpose bool notMockCleanUp bool + mockInit bool + mockInitErr error + mockStart bool + mockStartErr error mockCancel bool mockClose bool expected *velerov2alpha1api.DataDownload @@ -264,13 +268,36 @@ func TestDataDownloadReconcile(t *testing.T) { expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, }, { - name: "Unable to update status to in progress for data download", + name: "data path init error", dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), targetPVC: builder.ForPersistentVolumeClaim("test-ns", "test-pvc").Result(), - needErrs: []bool{false, false, false, true}, + mockInit: true, + mockInitErr: errors.New("fake-data-path-init-error"), + mockClose: true, notNilExpose: true, - notMockCleanUp: true, - expectedStatusMsg: "Patch error", + expectedStatusMsg: "error initializing asyncBR: fake-data-path-init-error", + }, + { + name: "Unable to update status to in progress for data download", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), + targetPVC: builder.ForPersistentVolumeClaim("test-ns", "test-pvc").Result(), + needErrs: []bool{false, false, false, true}, + mockInit: true, + mockClose: true, + notNilExpose: true, + notMockCleanUp: true, + expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, + }, + { + name: "data path start error", + dd: dataDownloadBuilder().Phase(velerov2alpha1api.DataDownloadPhasePrepared).Result(), + targetPVC: builder.ForPersistentVolumeClaim("test-ns", "test-pvc").Result(), + mockInit: true, + mockStart: true, + mockStartErr: errors.New("fake-data-path-start-error"), + mockClose: true, + notNilExpose: true, + expectedStatusMsg: "error starting async restore for pod test-name, volume test-pvc: fake-data-path-start-error", }, { name: "accept DataDownload error", @@ -399,6 +426,14 @@ func TestDataDownloadReconcile(t *testing.T) { datapath.MicroServiceBRWatcherCreator = func(kbclient.Client, kubernetes.Interface, manager.Manager, string, string, string, string, string, string, datapath.Callbacks, logrus.FieldLogger) datapath.AsyncBR { asyncBR := datapathmockes.NewAsyncBR(t) + if test.mockInit { + asyncBR.On("Init", mock.Anything, mock.Anything).Return(test.mockInitErr) + } + + if test.mockStart { + asyncBR.On("StartRestore", mock.Anything, mock.Anything, mock.Anything).Return(test.mockStartErr) + } + if test.mockCancel { asyncBR.On("Cancel").Return() } @@ -488,6 +523,10 @@ func TestDataDownloadReconcile(t *testing.T) { assert.True(t, true, apierrors.IsNotFound(err)) } + if !test.needCreateFSBR { + assert.Nil(t, r.dataPathMgr.GetAsyncBR(test.dd.Name)) + } + t.Logf("%s: \n %v \n", test.name, dd) }) } @@ -845,7 +884,7 @@ func TestTryCancelDataDownload(t *testing.T) { err = r.client.Create(ctx, test.dd) require.NoError(t, err) - r.TryCancelDataDownload(ctx, test.dd, "") + r.tryCancelAcceptedDataDownload(ctx, test.dd, "") if test.expectedErr == "" { assert.NoError(t, err) diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 91413a8cc..b10f1f636 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -230,9 +230,9 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) // we don't want to update CR into cancel status forcely as it may conflict with CR update in Expose action // we could retry when the CR requeue in periodcally log.Debugf("Data upload is been canceled %s in Phase %s", du.GetName(), du.Status.Phase) - r.TryCancelDataUpload(ctx, du, "") + r.tryCancelAcceptedDataUpload(ctx, du, "") } else if peekErr := ep.PeekExposed(ctx, getOwnerObject(du)); peekErr != nil { - r.TryCancelDataUpload(ctx, du, fmt.Sprintf("found a dataupload %s/%s with expose error: %s. mark it as cancel", du.Namespace, du.Name, peekErr)) + r.tryCancelAcceptedDataUpload(ctx, du, fmt.Sprintf("found a dataupload %s/%s with expose error: %s. mark it as cancel", du.Namespace, du.Name, peekErr)) log.Errorf("Cancel du %s/%s because of expose error %s", du.Namespace, du.Name, peekErr) } else if du.Status.StartTimestamp != nil { if time.Since(du.Status.StartTimestamp.Time) >= r.preparingTimeout { @@ -283,21 +283,35 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) return r.errorOut(ctx, du, err, "error to create data path", log) } } + + if err := r.initCancelableDataPath(ctx, asyncBR, res, log); err != nil { + log.WithError(err).Warnf("Failed to init cancelable data path for %s, will close and retry", du.Name) + + r.closeDataPath(ctx, du.Name) + return r.errorOut(ctx, du, err, "error initializing data path", log) + } + // Update status to InProgress original := du.DeepCopy() du.Status.Phase = velerov2alpha1api.DataUploadPhaseInProgress du.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} if err := r.client.Patch(ctx, du, client.MergeFrom(original)); err != nil { - return r.errorOut(ctx, du, err, "error updating dataupload status", log) + log.WithError(err).Warnf("Failed to update dataupload %s to InProgress, will data path close and retry", du.Name) + + r.closeDataPath(ctx, du.Name) + return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil } log.Info("Data upload is marked as in progress") - result, err := r.runCancelableDataUpload(ctx, asyncBR, du, res, log) - if err != nil { - log.Errorf("Failed to run cancelable data path for %s with err %v", du.Name, err) + + if err := r.startCancelableDataPath(asyncBR, du, res, log); err != nil { + log.WithError(err).Errorf("Failed to start cancelable data path for %s", du.Name) r.closeDataPath(ctx, du.Name) + + return r.errorOut(ctx, du, err, "error starting data path", log) } - return result, err + + return ctrl.Result{}, nil } else if du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress { log.Info("Data upload is in progress") if du.Spec.Cancel { @@ -340,29 +354,33 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) } } -func (r *DataUploadReconciler) runCancelableDataUpload(ctx context.Context, asyncBR datapath.AsyncBR, du *velerov2alpha1api.DataUpload, res *exposer.ExposeResult, log logrus.FieldLogger) (reconcile.Result, error) { - log.Info("Run cancelable dataUpload") +func (r *DataUploadReconciler) initCancelableDataPath(ctx context.Context, asyncBR datapath.AsyncBR, res *exposer.ExposeResult, log logrus.FieldLogger) error { + log.Info("Init cancelable dataUpload") if err := asyncBR.Init(ctx, nil); err != nil { - return r.errorOut(ctx, du, err, "error to initialize asyncBR", log) + return errors.Wrap(err, "error initializing asyncBR") } log.Infof("async backup init for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) + return nil +} + +func (r *DataUploadReconciler) startCancelableDataPath(asyncBR datapath.AsyncBR, du *velerov2alpha1api.DataUpload, res *exposer.ExposeResult, log logrus.FieldLogger) error { + log.Info("Start cancelable dataUpload") + if err := asyncBR.StartBackup(datapath.AccessPoint{ ByPath: res.ByPod.VolumeName, }, du.Spec.DataMoverConfig, nil); err != nil { - return r.errorOut(ctx, du, err, fmt.Sprintf("error starting async backup for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName), log) + return errors.Wrapf(err, "error starting async backup for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) } - log.Infof("Async backup started for pod %s, volume %s", res.ByPod.HostingPod, res.ByPod.VolumeName) - return ctrl.Result{}, nil + log.Info("Async backup started for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) + return nil } func (r *DataUploadReconciler) OnDataUploadCompleted(ctx context.Context, namespace string, duName string, result datapath.Result) { - defer func() { - go r.closeDataPath(ctx, duName) - }() + defer r.dataPathMgr.RemoveAsyncBR(duName) log := r.logger.WithField("dataupload", duName) @@ -406,9 +424,7 @@ func (r *DataUploadReconciler) OnDataUploadCompleted(ctx context.Context, namesp } func (r *DataUploadReconciler) OnDataUploadFailed(ctx context.Context, namespace, duName string, err error) { - defer func() { - go r.closeDataPath(ctx, duName) - }() + defer r.dataPathMgr.RemoveAsyncBR(duName) log := r.logger.WithField("dataupload", duName) @@ -418,16 +434,12 @@ func (r *DataUploadReconciler) OnDataUploadFailed(ctx context.Context, namespace if getErr := r.client.Get(ctx, types.NamespacedName{Name: duName, Namespace: namespace}, &du); getErr != nil { log.WithError(getErr).Warn("Failed to get dataupload on failure") } else { - if _, errOut := r.errorOut(ctx, &du, err, "data path backup failed", log); err != nil { - log.WithError(err).Warnf("Failed to patch dataupload with err %v", errOut) - } + r.errorOut(ctx, &du, err, "data path backup failed", log) } } func (r *DataUploadReconciler) OnDataUploadCancelled(ctx context.Context, namespace string, duName string) { - defer func() { - go r.closeDataPath(ctx, duName) - }() + defer r.dataPathMgr.RemoveAsyncBR(duName) log := r.logger.WithField("dataupload", duName) @@ -453,17 +465,19 @@ func (r *DataUploadReconciler) OnDataUploadCancelled(ctx context.Context, namesp } } -// TryCancelDataUpload clear up resources only when update success -func (r *DataUploadReconciler) TryCancelDataUpload(ctx context.Context, du *velerov2alpha1api.DataUpload, message string) { +func (r *DataUploadReconciler) tryCancelAcceptedDataUpload(ctx context.Context, du *velerov2alpha1api.DataUpload, message string) { log := r.logger.WithField("dataupload", du.Name) - log.Warn("Async fs backup data path canceled") + log.Warn("Accepted data upload is canceled") succeeded, err := r.exclusiveUpdateDataUpload(ctx, du, func(dataUpload *velerov2alpha1api.DataUpload) { dataUpload.Status.Phase = velerov2alpha1api.DataUploadPhaseCanceled if dataUpload.Status.StartTimestamp.IsZero() { dataUpload.Status.StartTimestamp = &metav1.Time{Time: r.Clock.Now()} } dataUpload.Status.CompletionTimestamp = &metav1.Time{Time: r.Clock.Now()} - dataUpload.Status.Message = message + + if message != "" { + dataUpload.Status.Message = message + } }) if err != nil { @@ -478,7 +492,6 @@ func (r *DataUploadReconciler) TryCancelDataUpload(ctx context.Context, du *vele r.metrics.RegisterDataUploadCancel(r.nodeName) // cleans up any objects generated during the snapshot expose r.cleanUp(ctx, du, log) - r.closeDataPath(ctx, du.Name) } func (r *DataUploadReconciler) cleanUp(ctx context.Context, du *velerov2alpha1api.DataUpload, log *logrus.Entry) { @@ -692,7 +705,7 @@ func (r *DataUploadReconciler) errorOut(ctx context.Context, du *velerov2alpha1a } se.CleanUp(ctx, getOwnerObject(du), volumeSnapshotName, du.Spec.SourceNamespace) } else { - err = errors.Wrapf(err, "failed to clean up exposed snapshot with could not find %s snapshot exposer", du.Spec.SnapshotType) + log.Warnf("failed to clean up exposed snapshot could not find %s snapshot exposer", du.Spec.SnapshotType) } return ctrl.Result{}, r.updateStatusToFailed(ctx, du, err, msg, log) diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index ee73372b1..ea7603b90 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -306,20 +306,16 @@ type fakeDataUploadFSBR struct { du *velerov2alpha1api.DataUpload kubeClient kbclient.Client clock clock.WithTickerAndDelayedExecution + initErr error + startErr error } func (f *fakeDataUploadFSBR) Init(ctx context.Context, param interface{}) error { - return nil + return f.initErr } func (f *fakeDataUploadFSBR) StartBackup(source datapath.AccessPoint, uploaderConfigs map[string]string, param interface{}) error { - du := f.du - original := f.du.DeepCopy() - du.Status.Phase = velerov2alpha1api.DataUploadPhaseCompleted - du.Status.CompletionTimestamp = &metav1.Time{Time: f.clock.Now()} - f.kubeClient.Patch(context.Background(), du, kbclient.MergeFrom(original)) - - return nil + return f.startErr } func (f *fakeDataUploadFSBR) StartRestore(snapshotID string, target datapath.AccessPoint, uploaderConfigs map[string]string) error { @@ -348,27 +344,24 @@ func TestReconcile(t *testing.T) { needErrs []bool peekErr error notCreateFSBR bool + fsBRInitErr error + fsBRStartErr error }{ { - name: "Dataupload is not initialized", - du: builder.ForDataUpload("unknown-ns", "unknown-name").Result(), - expectedProcessed: false, - expected: nil, - expectedRequeue: ctrl.Result{}, + name: "Dataupload is not initialized", + du: builder.ForDataUpload("unknown-ns", "unknown-name").Result(), + expectedRequeue: ctrl.Result{}, }, { - name: "Error get Dataupload", - du: builder.ForDataUpload(velerov1api.DefaultNamespace, "unknown-name").Result(), - expectedProcessed: false, - expected: nil, - expectedRequeue: ctrl.Result{}, - expectedErrMsg: "getting DataUpload: Get error", - needErrs: []bool{true, false, false, false}, + name: "Error get Dataupload", + du: builder.ForDataUpload(velerov1api.DefaultNamespace, "unknown-name").Result(), + expectedRequeue: ctrl.Result{}, + expectedErrMsg: "getting DataUpload: Get error", + needErrs: []bool{true, false, false, false}, }, { - name: "Unsupported data mover type", - du: dataUploadBuilder().DataMover("unknown type").Result(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase("").Result(), - expectedRequeue: ctrl.Result{}, + name: "Unsupported data mover type", + du: dataUploadBuilder().DataMover("unknown type").Result(), + expected: dataUploadBuilder().Phase("").Result(), + expectedRequeue: ctrl.Result{}, }, { name: "Unknown type of snapshot exposer is not initialized", du: dataUploadBuilder().SnapshotType("unknown type").Result(), @@ -377,13 +370,12 @@ func TestReconcile(t *testing.T) { expectedRequeue: ctrl.Result{}, expectedErrMsg: "unknown type type of snapshot exposer is not exist", }, { - name: "Dataupload should be accepted", - du: dataUploadBuilder().Result(), - pod: builder.ForPod("fake-ns", dataUploadName).Volumes(&corev1.Volume{Name: "test-pvc"}).Result(), - pvc: builder.ForPersistentVolumeClaim("fake-ns", "test-pvc").Result(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(), - expectedRequeue: ctrl.Result{}, + name: "Dataupload should be accepted", + du: dataUploadBuilder().Result(), + pod: builder.ForPod("fake-ns", dataUploadName).Volumes(&corev1.Volume{Name: "test-pvc"}).Result(), + pvc: builder.ForPersistentVolumeClaim("fake-ns", "test-pvc").Result(), + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseAccepted).Result(), + expectedRequeue: ctrl.Result{}, }, { name: "Dataupload should fail to get PVC information", @@ -395,34 +387,31 @@ func TestReconcile(t *testing.T) { expectedErrMsg: "failed to get PVC", }, { - name: "Dataupload should be prepared", - du: dataUploadBuilder().SnapshotType(fakeSnapshotType).Result(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), - expectedRequeue: ctrl.Result{}, - }, { - name: "Dataupload prepared should be completed", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), - du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), - expectedProcessed: true, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCompleted).Result(), - expectedRequeue: ctrl.Result{}, + name: "Dataupload should be prepared", + du: dataUploadBuilder().SnapshotType(fakeSnapshotType).Result(), + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), + expectedRequeue: ctrl.Result{}, }, { - name: "Dataupload with not enabled cancel", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), - du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(false).Result(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(), - expectedRequeue: ctrl.Result{}, + name: "Dataupload prepared should be completed", + pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(), + expectedRequeue: ctrl.Result{}, }, { - name: "Dataupload should be cancel", - pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), - du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(true).Result(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceling).Result(), - expectedRequeue: ctrl.Result{}, + name: "Dataupload with not enabled cancel", + pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(false).Result(), + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(), + expectedRequeue: ctrl.Result{}, + }, + { + name: "Dataupload should be cancel", + pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).SnapshotType(fakeSnapshotType).Cancel(true).Result(), + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseCanceling).Result(), + expectedRequeue: ctrl.Result{}, }, { name: "Dataupload should be cancel with match node", @@ -445,19 +434,43 @@ func TestReconcile(t *testing.T) { du.Status.Node = "different_node" return du }(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(), - expectedRequeue: ctrl.Result{}, - notCreateFSBR: true, + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseInProgress).Result(), + expectedRequeue: ctrl.Result{}, + notCreateFSBR: true, }, { - name: "runCancelableDataUpload is concurrent limited", - dataMgr: datapath.NewManager(0), + name: "runCancelableDataUpload is concurrent limited", + dataMgr: datapath.NewManager(0), + pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), + expectedRequeue: ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, + }, + { + name: "data path init error", pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), - expectedProcessed: false, - expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Result(), - expectedRequeue: ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, + fsBRInitErr: errors.New("fake-data-path-init-error"), + expectedProcessed: true, + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).SnapshotType(fakeSnapshotType).Result(), + expectedErrMsg: "error initializing asyncBR: fake-data-path-init-error", + }, + { + name: "Unable to update status to in progress for data download", + pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), + needErrs: []bool{false, false, false, true}, + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), + expectedRequeue: ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, + }, + { + name: "data path start error", + pod: builder.ForPod(velerov1api.DefaultNamespace, dataUploadName).Volumes(&corev1.Volume{Name: "dataupload-1"}).Result(), + du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Result(), + fsBRStartErr: errors.New("fake-data-path-start-error"), + expectedProcessed: true, + expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhaseFailed).SnapshotType(fakeSnapshotType).Result(), + expectedErrMsg: "error starting async backup for pod dataupload-1, volume dataupload-1: fake-data-path-start-error", }, { name: "prepare timeout", @@ -480,7 +493,6 @@ func TestReconcile(t *testing.T) { du.DeletionTimestamp = &metav1.Time{Time: time.Now()} return du }(), - expectedProcessed: false, checkFunc: func(du velerov2alpha1api.DataUpload) bool { return du.Spec.Cancel }, @@ -496,7 +508,6 @@ func TestReconcile(t *testing.T) { du.DeletionTimestamp = &metav1.Time{Time: time.Now()} return du }(), - expectedProcessed: false, checkFunc: func(du velerov2alpha1api.DataUpload) bool { return !controllerutil.ContainsFinalizer(&du, DataUploadDownloadFinalizer) }, @@ -555,12 +566,16 @@ func TestReconcile(t *testing.T) { du: test.du, kubeClient: r.client, clock: r.Clock, + initErr: test.fsBRInitErr, + startErr: test.fsBRStartErr, } } } + testCreateFsBR := false if test.du.Status.Phase == velerov2alpha1api.DataUploadPhaseInProgress && !test.notCreateFSBR { if fsBR := r.dataPathMgr.GetAsyncBR(test.du.Name); fsBR == nil { + testCreateFsBR = true _, err := r.dataPathMgr.CreateMicroServiceBRWatcher(ctx, r.client, nil, nil, datapath.TaskTypeBackup, test.du.Name, velerov1api.DefaultNamespace, "", "", "", datapath.Callbacks{OnCancelled: r.OnDataUploadCancelled}, false, velerotest.NewLogger()) require.NoError(t, err) } @@ -605,6 +620,11 @@ func TestReconcile(t *testing.T) { if test.checkFunc != nil { assert.True(t, test.checkFunc(du)) } + + if !testCreateFsBR && du.Status.Phase != velerov2alpha1api.DataUploadPhaseInProgress { + assert.Nil(t, r.dataPathMgr.GetAsyncBR(test.du.Name)) + } + }) } } @@ -926,7 +946,7 @@ func TestTryCancelDataUpload(t *testing.T) { err = r.client.Create(ctx, test.dd) require.NoError(t, err) - r.TryCancelDataUpload(ctx, test.dd, "") + r.tryCancelAcceptedDataUpload(ctx, test.dd, "") if test.expectedErr == "" { assert.NoError(t, err) diff --git a/pkg/datamover/backup_micro_service.go b/pkg/datamover/backup_micro_service.go index cedacc0ce..fa48b3523 100644 --- a/pkg/datamover/backup_micro_service.go +++ b/pkg/datamover/backup_micro_service.go @@ -127,15 +127,13 @@ func (r *BackupMicroService) Init() error { return err } -var waitControllerTimeout time.Duration = time.Minute * 2 - func (r *BackupMicroService) RunCancelableDataPath(ctx context.Context) (string, error) { log := r.logger.WithFields(logrus.Fields{ "dataupload": r.dataUploadName, }) du := &velerov2alpha1api.DataUpload{} - err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, waitControllerTimeout, true, func(ctx context.Context) (bool, error) { + err := wait.PollUntilContextCancel(ctx, 500*time.Millisecond, true, func(ctx context.Context) (bool, error) { err := r.client.Get(ctx, types.NamespacedName{ Namespace: r.namespace, Name: r.dataUploadName, @@ -241,8 +239,6 @@ func (r *BackupMicroService) Shutdown() { var funcMarshal = json.Marshal func (r *BackupMicroService) OnDataUploadCompleted(ctx context.Context, namespace string, duName string, result datapath.Result) { - defer r.closeDataPath(ctx, duName) - log := r.logger.WithField("dataupload", duName) backupBytes, err := funcMarshal(result.Backup) @@ -262,8 +258,6 @@ func (r *BackupMicroService) OnDataUploadCompleted(ctx context.Context, namespac } func (r *BackupMicroService) OnDataUploadFailed(ctx context.Context, namespace string, duName string, err error) { - defer r.closeDataPath(ctx, duName) - log := r.logger.WithField("dataupload", duName) log.WithError(err).Error("Async fs backup data path failed") @@ -274,8 +268,6 @@ func (r *BackupMicroService) OnDataUploadFailed(ctx context.Context, namespace s } func (r *BackupMicroService) OnDataUploadCancelled(ctx context.Context, namespace string, duName string) { - defer r.closeDataPath(ctx, duName) - log := r.logger.WithField("dataupload", duName) log.Warn("Async fs backup data path canceled") @@ -296,8 +288,6 @@ func (r *BackupMicroService) OnDataUploadProgress(ctx context.Context, namespace return } - log.Infof("Sending event for progress %v (%s)", progress, string(progressBytes)) - r.eventRecorder.Event(r.dataUpload, false, datapath.EventReasonProgress, string(progressBytes)) } @@ -313,7 +303,7 @@ func (r *BackupMicroService) closeDataPath(ctx context.Context, duName string) { func (r *BackupMicroService) cancelDataUpload(du *velerov2alpha1api.DataUpload) { r.logger.WithField("DataUpload", du.Name).Info("Data upload is being canceled") - r.eventRecorder.Event(du, false, "Canceling", "Canceling for data upload %s", du.Name) + r.eventRecorder.Event(du, false, datapath.EventReasonCancelling, "Canceling for data upload %s", du.Name) fsBackup := r.dataPathMgr.GetAsyncBR(du.Name) if fsBackup == nil { diff --git a/pkg/datamover/backup_micro_service_test.go b/pkg/datamover/backup_micro_service_test.go index 9db38d4c0..cca428ee8 100644 --- a/pkg/datamover/backup_micro_service_test.go +++ b/pkg/datamover/backup_micro_service_test.go @@ -301,12 +301,12 @@ func TestRunCancelableDataPath(t *testing.T) { }{ { name: "no du", - ctx: context.Background(), + ctx: ctxTimeout, expectedErr: "error waiting for du: context deadline exceeded", }, { name: "du not in in-progress", - ctx: context.Background(), + ctx: ctxTimeout, kubeClientObj: []runtime.Object{du}, expectedErr: "error waiting for du: context deadline exceeded", }, @@ -412,8 +412,6 @@ func TestRunCancelableDataPath(t *testing.T) { return fsBR } - waitControllerTimeout = time.Second - if test.result != nil { go func() { time.Sleep(time.Millisecond * 500) diff --git a/pkg/datamover/restore_micro_service.go b/pkg/datamover/restore_micro_service.go index 508469701..d0a4c6f50 100644 --- a/pkg/datamover/restore_micro_service.go +++ b/pkg/datamover/restore_micro_service.go @@ -122,7 +122,7 @@ func (r *RestoreMicroService) RunCancelableDataPath(ctx context.Context) (string }) dd := &velerov2alpha1api.DataDownload{} - err := wait.PollUntilContextTimeout(ctx, 500*time.Millisecond, waitControllerTimeout, true, func(ctx context.Context) (bool, error) { + err := wait.PollUntilContextCancel(ctx, 500*time.Millisecond, true, func(ctx context.Context) (bool, error) { err := r.client.Get(ctx, types.NamespacedName{ Namespace: r.namespace, Name: r.dataDownloadName, @@ -214,8 +214,6 @@ func (r *RestoreMicroService) Shutdown() { } func (r *RestoreMicroService) OnDataDownloadCompleted(ctx context.Context, namespace string, ddName string, result datapath.Result) { - defer r.closeDataPath(ctx, ddName) - log := r.logger.WithField("datadownload", ddName) restoreBytes, err := funcMarshal(result.Restore) @@ -235,8 +233,6 @@ func (r *RestoreMicroService) OnDataDownloadCompleted(ctx context.Context, names } func (r *RestoreMicroService) OnDataDownloadFailed(ctx context.Context, namespace string, ddName string, err error) { - defer r.closeDataPath(ctx, ddName) - log := r.logger.WithField("datadownload", ddName) log.WithError(err).Error("Async fs restore data path failed") @@ -247,8 +243,6 @@ func (r *RestoreMicroService) OnDataDownloadFailed(ctx context.Context, namespac } func (r *RestoreMicroService) OnDataDownloadCancelled(ctx context.Context, namespace string, ddName string) { - defer r.closeDataPath(ctx, ddName) - log := r.logger.WithField("datadownload", ddName) log.Warn("Async fs restore data path canceled") @@ -284,7 +278,7 @@ func (r *RestoreMicroService) closeDataPath(ctx context.Context, ddName string) func (r *RestoreMicroService) cancelDataDownload(dd *velerov2alpha1api.DataDownload) { r.logger.WithField("DataDownload", dd.Name).Info("Data download is being canceled") - r.eventRecorder.Event(dd, false, "Canceling", "Canceling for data download %s", dd.Name) + r.eventRecorder.Event(dd, false, datapath.EventReasonCancelling, "Canceling for data download %s", dd.Name) fsBackup := r.dataPathMgr.GetAsyncBR(dd.Name) if fsBackup == nil { diff --git a/pkg/datamover/restore_micro_service_test.go b/pkg/datamover/restore_micro_service_test.go index e3ef8701d..8a3ed61e1 100644 --- a/pkg/datamover/restore_micro_service_test.go +++ b/pkg/datamover/restore_micro_service_test.go @@ -254,12 +254,12 @@ func TestRunCancelableRestore(t *testing.T) { }{ { name: "no dd", - ctx: context.Background(), + ctx: ctxTimeout, expectedErr: "error waiting for dd: context deadline exceeded", }, { name: "dd not in in-progress", - ctx: context.Background(), + ctx: ctxTimeout, kubeClientObj: []runtime.Object{dd}, expectedErr: "error waiting for dd: context deadline exceeded", }, @@ -365,8 +365,6 @@ func TestRunCancelableRestore(t *testing.T) { return fsBR } - waitControllerTimeout = time.Second - if test.result != nil { go func() { time.Sleep(time.Millisecond * 500) diff --git a/pkg/datapath/file_system.go b/pkg/datapath/file_system.go index 762c91b18..5d3b54f28 100644 --- a/pkg/datapath/file_system.go +++ b/pkg/datapath/file_system.go @@ -18,6 +18,7 @@ package datapath import ( "context" + "sync" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -66,6 +67,8 @@ type fileSystemBR struct { callbacks Callbacks jobName string requestorType string + wgDataPath sync.WaitGroup + dataPathLock sync.Mutex } func newFileSystemBR(jobName string, requestorType string, client client.Client, namespace string, callbacks Callbacks, log logrus.FieldLogger) AsyncBR { @@ -75,6 +78,7 @@ func newFileSystemBR(jobName string, requestorType string, client client.Client, client: client, namespace: namespace, callbacks: callbacks, + wgDataPath: sync.WaitGroup{}, log: log, } @@ -134,6 +138,23 @@ func (fs *fileSystemBR) Init(ctx context.Context, param interface{}) error { } func (fs *fileSystemBR) Close(ctx context.Context) { + if fs.cancel != nil { + fs.cancel() + } + + fs.log.WithField("user", fs.jobName).Info("Closing FileSystemBR") + + fs.wgDataPath.Wait() + + fs.close(ctx) + + fs.log.WithField("user", fs.jobName).Info("FileSystemBR is closed") +} + +func (fs *fileSystemBR) close(ctx context.Context) { + fs.dataPathLock.Lock() + defer fs.dataPathLock.Unlock() + if fs.uploaderProv != nil { if err := fs.uploaderProv.Close(ctx); err != nil { fs.log.Errorf("failed to close uploader provider with error %v", err) @@ -141,13 +162,6 @@ func (fs *fileSystemBR) Close(ctx context.Context) { fs.uploaderProv = nil } - - if fs.cancel != nil { - fs.cancel() - fs.cancel = nil - } - - fs.log.WithField("user", fs.jobName).Info("FileSystemBR is closed") } func (fs *fileSystemBR) StartBackup(source AccessPoint, uploaderConfig map[string]string, param interface{}) error { @@ -155,9 +169,18 @@ func (fs *fileSystemBR) StartBackup(source AccessPoint, uploaderConfig map[strin return errors.New("file system data path is not initialized") } + fs.wgDataPath.Add(1) + backupParam := param.(*FSBRStartParam) go func() { + fs.log.Info("Start data path backup") + + defer func() { + fs.close(context.Background()) + fs.wgDataPath.Done() + }() + snapshotID, emptySnapshot, err := fs.uploaderProv.RunBackup(fs.ctx, source.ByPath, backupParam.RealSource, backupParam.Tags, backupParam.ForceFull, backupParam.ParentSnapshot, source.VolMode, uploaderConfig, fs) @@ -182,7 +205,16 @@ func (fs *fileSystemBR) StartRestore(snapshotID string, target AccessPoint, uplo return errors.New("file system data path is not initialized") } + fs.wgDataPath.Add(1) + go func() { + fs.log.Info("Start data path restore") + + defer func() { + fs.close(context.Background()) + fs.wgDataPath.Done() + }() + err := fs.uploaderProv.RunRestore(fs.ctx, snapshotID, target.ByPath, target.VolMode, uploaderConfigs, fs) if err == provider.ErrorCanceled { diff --git a/pkg/datapath/file_system_test.go b/pkg/datapath/file_system_test.go index 85c6df08d..fab33df1c 100644 --- a/pkg/datapath/file_system_test.go +++ b/pkg/datapath/file_system_test.go @@ -96,6 +96,7 @@ func TestAsyncBackup(t *testing.T) { fs := newFileSystemBR("job-1", "test", nil, "velero", Callbacks{}, velerotest.NewLogger()).(*fileSystemBR) mockProvider := providerMock.NewProvider(t) mockProvider.On("RunBackup", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(test.result.Backup.SnapshotID, test.result.Backup.EmptySnapshot, test.err) + mockProvider.On("Close", mock.Anything).Return(nil) fs.uploaderProv = mockProvider fs.initialized = true fs.callbacks = test.callbacks @@ -179,6 +180,7 @@ func TestAsyncRestore(t *testing.T) { fs := newFileSystemBR("job-1", "test", nil, "velero", Callbacks{}, velerotest.NewLogger()).(*fileSystemBR) mockProvider := providerMock.NewProvider(t) mockProvider.On("RunRestore", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(test.err) + mockProvider.On("Close", mock.Anything).Return(nil) fs.uploaderProv = mockProvider fs.initialized = true fs.callbacks = test.callbacks diff --git a/pkg/datapath/micro_service_watcher.go b/pkg/datapath/micro_service_watcher.go index a5826459a..8a129bde8 100644 --- a/pkg/datapath/micro_service_watcher.go +++ b/pkg/datapath/micro_service_watcher.go @@ -46,11 +46,12 @@ const ( ErrCancelled = "data path is canceled" - EventReasonStarted = "Data-Path-Started" - EventReasonCompleted = "Data-Path-Completed" - EventReasonFailed = "Data-Path-Failed" - EventReasonCancelled = "Data-Path-Canceled" - EventReasonProgress = "Data-Path-Progress" + EventReasonStarted = "Data-Path-Started" + EventReasonCompleted = "Data-Path-Completed" + EventReasonFailed = "Data-Path-Failed" + EventReasonCancelled = "Data-Path-Canceled" + EventReasonProgress = "Data-Path-Progress" + EventReasonCancelling = "Data-Path-Canceling" ) type microServiceBRWatcher struct { @@ -76,6 +77,7 @@ type microServiceBRWatcher struct { podInformer ctrlcache.Informer eventHandler cache.ResourceEventHandlerRegistration podHandler cache.ResourceEventHandlerRegistration + watcherLock sync.Mutex } func newMicroServiceBRWatcher(client client.Client, kubeClient kubernetes.Interface, mgr manager.Manager, taskType string, taskName string, namespace string, @@ -121,8 +123,6 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er return } - ms.log.Infof("Pushed adding event %s/%s, message %s for object %v", evt.Namespace, evt.Name, evt.Message, evt.InvolvedObject) - ms.eventCh <- evt }, UpdateFunc: func(_, obj interface{}) { @@ -131,8 +131,6 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er return } - ms.log.Infof("Pushed updating event %s/%s, message %s for object %v", evt.Namespace, evt.Name, evt.Message, evt.InvolvedObject) - ms.eventCh <- evt }, }, @@ -177,12 +175,9 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er } }() - ms.log.WithFields( - logrus.Fields{ - "taskType": ms.taskType, - "taskName": ms.taskName, - "thisPod": ms.thisPod, - }).Info("MicroServiceBR is initialized") + if err := ms.reEnsureThisPod(ctx); err != nil { + return err + } ms.eventInformer = eventInformer ms.podInformer = podInformer @@ -191,63 +186,73 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er ms.ctx, ms.cancel = context.WithCancel(ctx) + ms.log.WithFields( + logrus.Fields{ + "taskType": ms.taskType, + "taskName": ms.taskName, + "thisPod": ms.thisPod, + }).Info("MicroServiceBR is initialized") + succeeded = true return nil + } func (ms *microServiceBRWatcher) Close(ctx context.Context) { if ms.cancel != nil { ms.cancel() - ms.cancel = nil } ms.log.WithField("taskType", ms.taskType).WithField("taskName", ms.taskName).Info("Closing MicroServiceBR") ms.wgWatcher.Wait() - if ms.eventInformer != nil && ms.eventHandler != nil { - if err := ms.eventInformer.RemoveEventHandler(ms.eventHandler); err != nil { - ms.log.WithError(err).Warn("Failed to remove event handler") - } - } - - if ms.podInformer != nil && ms.podHandler != nil { - if err := ms.podInformer.RemoveEventHandler(ms.podHandler); err != nil { - ms.log.WithError(err).Warn("Failed to remove pod handler") - } - } + ms.close() ms.log.WithField("taskType", ms.taskType).WithField("taskName", ms.taskName).Info("MicroServiceBR is closed") } -func (ms *microServiceBRWatcher) StartBackup(source AccessPoint, uploaderConfig map[string]string, param interface{}) error { - ms.log.Infof("Start watching backup ms for source %v", source) +func (ms *microServiceBRWatcher) close() { + ms.watcherLock.Lock() + defer ms.watcherLock.Unlock() - if err := ms.reEnsureThisPod(); err != nil { - return err + if ms.eventHandler != nil { + if err := ms.eventInformer.RemoveEventHandler(ms.eventHandler); err != nil { + ms.log.WithError(err).Warn("Failed to remove event handler") + } + + ms.eventHandler = nil } + if ms.podHandler != nil { + if err := ms.podInformer.RemoveEventHandler(ms.podHandler); err != nil { + ms.log.WithError(err).Warn("Failed to remove pod handler") + } + + ms.podHandler = nil + } +} + +func (ms *microServiceBRWatcher) StartBackup(source AccessPoint, uploaderConfig map[string]string, param interface{}) error { + ms.log.Infof("Start watching backup ms for source %v", source.ByPath) + ms.startWatch() return nil } func (ms *microServiceBRWatcher) StartRestore(snapshotID string, target AccessPoint, uploaderConfigs map[string]string) error { - ms.log.Infof("Start watching restore ms to target %v, from snapshot %s", target, snapshotID) - - if err := ms.reEnsureThisPod(); err != nil { - return err - } + ms.log.Infof("Start watching restore ms to target %s, from snapshot %s", target.ByPath, snapshotID) ms.startWatch() return nil } -func (ms *microServiceBRWatcher) reEnsureThisPod() error { +func (ms *microServiceBRWatcher) reEnsureThisPod(ctx context.Context) error { thisPod := &v1.Pod{} - if err := ms.client.Get(ms.ctx, types.NamespacedName{ + if err := ms.client.Get(ctx, types.NamespacedName{ Namespace: ms.namespace, Name: ms.thisPod, }, thisPod); err != nil { @@ -275,6 +280,11 @@ func (ms *microServiceBRWatcher) startWatch() { go func() { ms.log.Info("Start watching data path pod") + defer func() { + ms.close() + ms.wgWatcher.Done() + }() + var lastPod *v1.Pod watchLoop: @@ -291,14 +301,16 @@ func (ms *microServiceBRWatcher) startWatch() { } if lastPod == nil { - ms.log.Warn("Data path pod watch loop is canceled") - ms.wgWatcher.Done() + ms.log.Warn("Watch loop is cancelled on waiting data path pod") return } epilogLoop: for !ms.startedFromEvent || !ms.terminatedFromEvent { select { + case <-ms.ctx.Done(): + ms.log.Warn("Watch loop is cancelled on waiting final event") + return case <-time.After(eventWaitTimeout): break epilogLoop case evt := <-ms.eventCh: @@ -339,8 +351,6 @@ func (ms *microServiceBRWatcher) startWatch() { } logger.Info("Complete callback on data path pod termination") - - ms.wgWatcher.Done() }() } @@ -348,20 +358,22 @@ func (ms *microServiceBRWatcher) onEvent(evt *v1.Event) { switch evt.Reason { case EventReasonStarted: ms.startedFromEvent = true - ms.log.Infof("Received data path start message %s", evt.Message) + ms.log.Infof("Received data path start message: %s", evt.Message) case EventReasonProgress: ms.callbacks.OnProgress(ms.ctx, ms.namespace, ms.taskName, funcGetProgressFromMessage(evt.Message, ms.log)) case EventReasonCompleted: - ms.log.Infof("Received data path completed message %v", funcGetResultFromMessage(ms.taskType, evt.Message, ms.log)) + ms.log.Infof("Received data path completed message: %v", funcGetResultFromMessage(ms.taskType, evt.Message, ms.log)) ms.terminatedFromEvent = true case EventReasonCancelled: - ms.log.Infof("Received data path canceled message %s", evt.Message) + ms.log.Infof("Received data path canceled message: %s", evt.Message) ms.terminatedFromEvent = true case EventReasonFailed: - ms.log.Infof("Received data path failed message %s", evt.Message) + ms.log.Infof("Received data path failed message: %s", evt.Message) ms.terminatedFromEvent = true + case EventReasonCancelling: + ms.log.Infof("Received data path canceling message: %s", evt.Message) default: - ms.log.Debugf("Received event for data mover %s.[reason %s, message %s]", ms.taskName, evt.Reason, evt.Message) + ms.log.Infof("Received event for data path %s,reason: %s, message: %s", ms.taskName, evt.Reason, evt.Message) } } diff --git a/pkg/datapath/micro_service_watcher_test.go b/pkg/datapath/micro_service_watcher_test.go index f10f6b331..f926363e0 100644 --- a/pkg/datapath/micro_service_watcher_test.go +++ b/pkg/datapath/micro_service_watcher_test.go @@ -102,7 +102,7 @@ func TestReEnsureThisPod(t *testing.T) { log: velerotest.NewLogger(), } - err := ms.reEnsureThisPod() + err := ms.reEnsureThisPod(context.Background()) if test.expectErr != "" { assert.EqualError(t, err, test.expectErr) } else { From 8ca7cae66268852a335eeb1f1b96e59fe2fcb80d Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Fri, 9 Aug 2024 16:23:30 +0800 Subject: [PATCH 48/69] Add information about restore status to the doc fixes #6237 Signed-off-by: Daniel Jiang --- site/content/docs/main/restore-reference.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/site/content/docs/main/restore-reference.md b/site/content/docs/main/restore-reference.md index a7ec86e07..012258ff3 100644 --- a/site/content/docs/main/restore-reference.md +++ b/site/content/docs/main/restore-reference.md @@ -275,6 +275,12 @@ You can also configure the existing resource policy in a [Restore](api-types/res * Update of a resource only applies to the Kubernetes resource data such as its spec. It may not work as expected for certain resource types such as PVCs and Pods. In case of PVCs for example, data in the PV is not restored or overwritten in any way. * `update` existing resource policy works in a best-effort way, which means when restore's `--existing-resource-policy` is set to `update`, Velero will try to update the resource if the resource already exists, if the update fails, Velero will fall back to the default non-destructive way in the restore, and just logs a warning without failing the restore. +## Restore "status" field of objects + +By default, Velero will remove the `status` field of an object before it's restored. This is because the value `status` field is typically set by the controller during reconciliation. However, some custom resources are designed to store environment specific information in the `status` field, and it is important to preserve such information during restore. + +You can use `--status-exclude-resources` and `--status-exclude-resources` flags to select the resources whose `status` field will be restored by Velero. If there are resources selected via these flags, velero will trigger another API call to update the restored object to restore `status` field after it's created. + ## Write Sparse files If using fs-restore or CSI snapshot data movements, it's supported to write sparse files during restore by the below command: ```bash From 1228b41851ae99b8d0bc49d3bbdf198df7c7c4ea Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Fri, 26 Jul 2024 19:05:34 -0400 Subject: [PATCH 49/69] Internal ItemBlockAction plugins This PR implements the internal ItemBlockAction plugins needed for pod, PVC, and SA. Signed-off-by: Scott Seago --- changelogs/unreleased/8054-sseago | 1 + pkg/backup/actions/backup_pv_action.go | 10 +- pkg/backup/actions/pod_action.go | 31 +- pkg/backup/actions/service_account_action.go | 61 +- .../actions/service_account_action_test.go | 53 +- pkg/cmd/server/plugin/plugin.go | 51 +- pkg/itemblock/actions/pod_action.go | 63 ++ pkg/itemblock/actions/pod_action_test.go | 148 +++++ pkg/itemblock/actions/pvc_action.go | 113 ++++ pkg/itemblock/actions/pvc_action_test.go | 167 +++++ .../actions/service_account_action.go | 73 +++ .../actions/service_account_action_test.go | 609 ++++++++++++++++++ pkg/util/actionhelpers/pod_helper.go | 53 ++ pkg/util/actionhelpers/pvc_helper.go | 34 + .../actions => util/actionhelpers}/rbac.go | 56 +- .../actionhelpers/service_account_helper.go | 84 +++ 16 files changed, 1460 insertions(+), 147 deletions(-) create mode 100644 changelogs/unreleased/8054-sseago create mode 100644 pkg/itemblock/actions/pod_action.go create mode 100644 pkg/itemblock/actions/pod_action_test.go create mode 100644 pkg/itemblock/actions/pvc_action.go create mode 100644 pkg/itemblock/actions/pvc_action_test.go create mode 100644 pkg/itemblock/actions/service_account_action.go create mode 100644 pkg/itemblock/actions/service_account_action_test.go create mode 100644 pkg/util/actionhelpers/pod_helper.go create mode 100644 pkg/util/actionhelpers/pvc_helper.go rename pkg/{backup/actions => util/actionhelpers}/rbac.go (71%) create mode 100644 pkg/util/actionhelpers/service_account_helper.go diff --git a/changelogs/unreleased/8054-sseago b/changelogs/unreleased/8054-sseago new file mode 100644 index 000000000..0060d7276 --- /dev/null +++ b/changelogs/unreleased/8054-sseago @@ -0,0 +1 @@ +Internal ItemBlockAction plugins diff --git a/pkg/backup/actions/backup_pv_action.go b/pkg/backup/actions/backup_pv_action.go index f5a65555c..4b8a44ef6 100644 --- a/pkg/backup/actions/backup_pv_action.go +++ b/pkg/backup/actions/backup_pv_action.go @@ -26,8 +26,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" ) // PVCAction inspects a PersistentVolumeClaim for the PersistentVolume @@ -51,7 +51,7 @@ func (a *PVCAction) AppliesTo() (velero.ResourceSelector, error) { func (a *PVCAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runtime.Unstructured, []velero.ResourceIdentifier, error) { a.log.Info("Executing PVCAction") - var pvc corev1api.PersistentVolumeClaim + pvc := new(corev1api.PersistentVolumeClaim) if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), &pvc); err != nil { return nil, nil, errors.Wrap(err, "unable to convert unstructured item to persistent volume claim") } @@ -60,10 +60,6 @@ func (a *PVCAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti return item, nil, nil } - pv := velero.ResourceIdentifier{ - GroupResource: kuberesource.PersistentVolumes, - Name: pvc.Spec.VolumeName, - } // remove dataSource if exists from prior restored CSI volumes if pvc.Spec.DataSource != nil { pvc.Spec.DataSource = nil @@ -94,5 +90,5 @@ func (a *PVCAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti return nil, nil, errors.Wrap(err, "unable to convert pvc to unstructured item") } - return &unstructured.Unstructured{Object: pvcMap}, []velero.ResourceIdentifier{pv}, nil + return &unstructured.Unstructured{Object: pvcMap}, actionhelpers.RelatedItemsForPVC(pvc, a.log), nil } diff --git a/pkg/backup/actions/pod_action.go b/pkg/backup/actions/pod_action.go index ce6b1ade8..8ed5e3b44 100644 --- a/pkg/backup/actions/pod_action.go +++ b/pkg/backup/actions/pod_action.go @@ -23,8 +23,8 @@ import ( "k8s.io/apimachinery/pkg/runtime" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" ) // PodAction implements ItemAction. @@ -55,32 +55,5 @@ func (a *PodAction) Execute(item runtime.Unstructured, backup *v1.Backup) (runti if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil { return nil, nil, errors.WithStack(err) } - - var additionalItems []velero.ResourceIdentifier - if pod.Spec.PriorityClassName != "" { - a.log.Infof("Adding priorityclass %s to additionalItems", pod.Spec.PriorityClassName) - additionalItems = append(additionalItems, velero.ResourceIdentifier{ - GroupResource: kuberesource.PriorityClasses, - Name: pod.Spec.PriorityClassName, - }) - } - - if len(pod.Spec.Volumes) == 0 { - a.log.Info("pod has no volumes") - return item, additionalItems, nil - } - - for _, volume := range pod.Spec.Volumes { - if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName != "" { - a.log.Infof("Adding pvc %s to additionalItems", volume.PersistentVolumeClaim.ClaimName) - - additionalItems = append(additionalItems, velero.ResourceIdentifier{ - GroupResource: kuberesource.PersistentVolumeClaims, - Namespace: pod.Namespace, - Name: volume.PersistentVolumeClaim.ClaimName, - }) - } - } - - return item, additionalItems, nil + return item, actionhelpers.RelatedItemsForPod(pod, a.log), nil } diff --git a/pkg/backup/actions/service_account_action.go b/pkg/backup/actions/service_account_action.go index c7a3649c4..b563f7a03 100644 --- a/pkg/backup/actions/service_account_action.go +++ b/pkg/backup/actions/service_account_action.go @@ -19,40 +19,24 @@ package actions import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" - rbac "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/sets" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" - "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" ) // ServiceAccountAction implements ItemAction. type ServiceAccountAction struct { log logrus.FieldLogger - clusterRoleBindings []ClusterRoleBinding + clusterRoleBindings []actionhelpers.ClusterRoleBinding } // NewServiceAccountAction creates a new ItemAction for service accounts. -func NewServiceAccountAction(logger logrus.FieldLogger, clusterRoleBindingListers map[string]ClusterRoleBindingLister, discoveryHelper velerodiscovery.Helper) (*ServiceAccountAction, error) { - // Look up the supported RBAC version - var supportedAPI metav1.GroupVersionForDiscovery - for _, ag := range discoveryHelper.APIGroups() { - if ag.Name == rbac.GroupName { - supportedAPI = ag.PreferredVersion - break - } - } - - crbLister := clusterRoleBindingListers[supportedAPI.Version] - - // This should be safe because the List call will return a 0-item slice - // if there's no matching API version. - crbs, err := crbLister.List() +func NewServiceAccountAction(logger logrus.FieldLogger, clusterRoleBindingListers map[string]actionhelpers.ClusterRoleBindingLister, discoveryHelper velerodiscovery.Helper) (*ServiceAccountAction, error) { + crbs, err := actionhelpers.ClusterRoleBindingsForAction(clusterRoleBindingListers, discoveryHelper) if err != nil { return nil, err } @@ -82,40 +66,5 @@ func (a *ServiceAccountAction) Execute(item runtime.Unstructured, backup *v1.Bac return nil, nil, errors.WithStack(err) } - var ( - namespace = objectMeta.GetNamespace() - name = objectMeta.GetName() - bindings = sets.NewString() - roles = sets.NewString() - ) - - for _, crb := range a.clusterRoleBindings { - for _, s := range crb.ServiceAccountSubjects(namespace) { - if s == name { - a.log.Infof("Adding clusterrole %s and clusterrolebinding %s to additionalItems since serviceaccount %s/%s is a subject", - crb.RoleRefName(), crb.Name(), namespace, name) - - bindings.Insert(crb.Name()) - roles.Insert(crb.RoleRefName()) - break - } - } - } - - var additionalItems []velero.ResourceIdentifier - for binding := range bindings { - additionalItems = append(additionalItems, velero.ResourceIdentifier{ - GroupResource: kuberesource.ClusterRoleBindings, - Name: binding, - }) - } - - for role := range roles { - additionalItems = append(additionalItems, velero.ResourceIdentifier{ - GroupResource: kuberesource.ClusterRoles, - Name: role, - }) - } - - return item, additionalItems, nil + return item, actionhelpers.RelatedItemsForServiceAccount(objectMeta, a.clusterRoleBindings, a.log), nil } diff --git a/pkg/backup/actions/service_account_action_test.go b/pkg/backup/actions/service_account_action_test.go index 5ef441e6f..1ea578ce0 100644 --- a/pkg/backup/actions/service_account_action_test.go +++ b/pkg/backup/actions/service_account_action_test.go @@ -31,21 +31,22 @@ import ( "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" ) -func newV1ClusterRoleBindingList(rbacCRBList []rbac.ClusterRoleBinding) []ClusterRoleBinding { - var crbs []ClusterRoleBinding +func newV1ClusterRoleBindingList(rbacCRBList []rbac.ClusterRoleBinding) []actionhelpers.ClusterRoleBinding { + var crbs []actionhelpers.ClusterRoleBinding for _, c := range rbacCRBList { - crbs = append(crbs, v1ClusterRoleBinding{crb: c}) + crbs = append(crbs, actionhelpers.V1ClusterRoleBinding{Crb: c}) } return crbs } -func newV1beta1ClusterRoleBindingList(rbacCRBList []rbacbeta.ClusterRoleBinding) []ClusterRoleBinding { - var crbs []ClusterRoleBinding +func newV1beta1ClusterRoleBindingList(rbacCRBList []rbacbeta.ClusterRoleBinding) []actionhelpers.ClusterRoleBinding { + var crbs []actionhelpers.ClusterRoleBinding for _, c := range rbacCRBList { - crbs = append(crbs, v1beta1ClusterRoleBinding{crb: c}) + crbs = append(crbs, actionhelpers.V1beta1ClusterRoleBinding{Crb: c}) } return crbs @@ -55,10 +56,10 @@ type FakeV1ClusterRoleBindingLister struct { v1crbs []rbac.ClusterRoleBinding } -func (f FakeV1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { - var crbs []ClusterRoleBinding +func (f FakeV1ClusterRoleBindingLister) List() ([]actionhelpers.ClusterRoleBinding, error) { + var crbs []actionhelpers.ClusterRoleBinding for _, c := range f.v1crbs { - crbs = append(crbs, v1ClusterRoleBinding{crb: c}) + crbs = append(crbs, actionhelpers.V1ClusterRoleBinding{Crb: c}) } return crbs, nil } @@ -67,10 +68,10 @@ type FakeV1beta1ClusterRoleBindingLister struct { v1beta1crbs []rbacbeta.ClusterRoleBinding } -func (f FakeV1beta1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { - var crbs []ClusterRoleBinding +func (f FakeV1beta1ClusterRoleBindingLister) List() ([]actionhelpers.ClusterRoleBinding, error) { + var crbs []actionhelpers.ClusterRoleBinding for _, c := range f.v1beta1crbs { - crbs = append(crbs, v1beta1ClusterRoleBinding{crb: c}) + crbs = append(crbs, actionhelpers.V1beta1ClusterRoleBinding{Crb: c}) } return crbs, nil } @@ -93,21 +94,21 @@ func TestNewServiceAccountAction(t *testing.T) { tests := []struct { name string version string - expectedCRBs []ClusterRoleBinding + expectedCRBs []actionhelpers.ClusterRoleBinding }{ { name: "rbac v1 API instantiates an saAction", version: rbac.SchemeGroupVersion.Version, - expectedCRBs: []ClusterRoleBinding{ - v1ClusterRoleBinding{ - crb: rbac.ClusterRoleBinding{ + expectedCRBs: []actionhelpers.ClusterRoleBinding{ + actionhelpers.V1ClusterRoleBinding{ + Crb: rbac.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "v1crb-1", }, }, }, - v1ClusterRoleBinding{ - crb: rbac.ClusterRoleBinding{ + actionhelpers.V1ClusterRoleBinding{ + Crb: rbac.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "v1crb-2", }, @@ -118,16 +119,16 @@ func TestNewServiceAccountAction(t *testing.T) { { name: "rbac v1beta1 API instantiates an saAction", version: rbacbeta.SchemeGroupVersion.Version, - expectedCRBs: []ClusterRoleBinding{ - v1beta1ClusterRoleBinding{ - crb: rbacbeta.ClusterRoleBinding{ + expectedCRBs: []actionhelpers.ClusterRoleBinding{ + actionhelpers.V1beta1ClusterRoleBinding{ + Crb: rbacbeta.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "v1beta1crb-1", }, }, }, - v1beta1ClusterRoleBinding{ - crb: rbacbeta.ClusterRoleBinding{ + actionhelpers.V1beta1ClusterRoleBinding{ + Crb: rbacbeta.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: "v1beta1crb-2", }, @@ -138,7 +139,7 @@ func TestNewServiceAccountAction(t *testing.T) { { name: "no RBAC API instantiates an saAction with empty slice", version: "", - expectedCRBs: []ClusterRoleBinding{}, + expectedCRBs: []actionhelpers.ClusterRoleBinding{}, }, } // Set up all of our fakes outside the test loop @@ -171,10 +172,10 @@ func TestNewServiceAccountAction(t *testing.T) { }, } - clusterRoleBindingListers := map[string]ClusterRoleBindingLister{ + clusterRoleBindingListers := map[string]actionhelpers.ClusterRoleBindingLister{ rbac.SchemeGroupVersion.Version: FakeV1ClusterRoleBindingLister{v1crbs: v1crbs}, rbacbeta.SchemeGroupVersion.Version: FakeV1beta1ClusterRoleBindingLister{v1beta1crbs: v1beta1crbs}, - "": noopClusterRoleBindingLister{}, + "": actionhelpers.NoopClusterRoleBindingLister{}, } for _, test := range tests { diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go index 0f729f3ac..265d52607 100644 --- a/pkg/cmd/server/plugin/plugin.go +++ b/pkg/cmd/server/plugin/plugin.go @@ -30,10 +30,12 @@ import ( "github.com/vmware-tanzu/velero/pkg/client" velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" "github.com/vmware-tanzu/velero/pkg/features" + iba "github.com/vmware-tanzu/velero/pkg/itemblock/actions" veleroplugin "github.com/vmware-tanzu/velero/pkg/plugin/framework" plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" ria "github.com/vmware-tanzu/velero/pkg/restore/actions" csiria "github.com/vmware-tanzu/velero/pkg/restore/actions/csi" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" ) func NewCommand(f client.Factory) *cobra.Command { @@ -171,6 +173,18 @@ func NewCommand(f client.Factory) *cobra.Command { RegisterRestoreItemActionV2( "velero.io/csi-volumesnapshotclass-restorer", newVolumeSnapshotClassRestoreItemAction, + ). + RegisterItemBlockAction( + "velero.io/pvc", + newPVCItemBlockAction(f), + ). + RegisterItemBlockAction( + "velero.io/pod", + newPodItemBlockAction, + ). + RegisterItemBlockAction( + "velero.io/service-account", + newServiceAccountItemBlockAction(f), ) if !features.IsEnabled(velerov1api.APIGroupVersionsFeatureFlag) { @@ -211,7 +225,7 @@ func newServiceAccountBackupItemAction(f client.Factory) plugincommon.HandlerIni action, err := bia.NewServiceAccountAction( logger, - bia.NewClusterRoleBindingListerMap(clientset), + actionhelpers.NewClusterRoleBindingListerMap(clientset), discoveryHelper) if err != nil { return nil, err @@ -431,3 +445,38 @@ func newVolumeSnapshotContentRestoreItemAction(logger logrus.FieldLogger) (inter func newVolumeSnapshotClassRestoreItemAction(logger logrus.FieldLogger) (interface{}, error) { return csiria.NewVolumeSnapshotClassRestoreItemAction(logger) } + +// ItemBlockAction plugins + +func newPVCItemBlockAction(f client.Factory) plugincommon.HandlerInitializer { + return iba.NewPVCAction(f) +} + +func newPodItemBlockAction(logger logrus.FieldLogger) (interface{}, error) { + return iba.NewPodAction(logger), nil +} + +func newServiceAccountItemBlockAction(f client.Factory) plugincommon.HandlerInitializer { + return func(logger logrus.FieldLogger) (interface{}, error) { + // TODO(ncdc): consider a k8s style WantsKubernetesClientSet initialization approach + clientset, err := f.KubeClient() + if err != nil { + return nil, err + } + + discoveryHelper, err := velerodiscovery.NewHelper(clientset.Discovery(), logger) + if err != nil { + return nil, err + } + + action, err := iba.NewServiceAccountAction( + logger, + actionhelpers.NewClusterRoleBindingListerMap(clientset), + discoveryHelper) + if err != nil { + return nil, err + } + + return action, nil + } +} diff --git a/pkg/itemblock/actions/pod_action.go b/pkg/itemblock/actions/pod_action.go new file mode 100644 index 000000000..2596e78a2 --- /dev/null +++ b/pkg/itemblock/actions/pod_action.go @@ -0,0 +1,63 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actions + +import ( + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + corev1api "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" +) + +// PodAction implements ItemBlockAction. +type PodAction struct { + log logrus.FieldLogger +} + +// NewPodAction creates a new ItemBlockAction for pods. +func NewPodAction(logger logrus.FieldLogger) *PodAction { + return &PodAction{log: logger} +} + +// AppliesTo returns a ResourceSelector that applies only to pods. +func (a *PodAction) AppliesTo() (velero.ResourceSelector, error) { + return velero.ResourceSelector{ + IncludedResources: []string{"pods"}, + }, nil +} + +// GetRelatedItems scans the pod's spec.volumes for persistentVolumeClaim volumes and returns a +// ResourceIdentifier list containing references to all of the persistentVolumeClaim volumes used by +// the pod. This ensures that when a pod is backed up, all referenced PVCs are backed up along with the pod. +func (a *PodAction) GetRelatedItems(item runtime.Unstructured, backup *v1.Backup) ([]velero.ResourceIdentifier, error) { + a.log.Info("Executing pod ItemBlockAction") + defer a.log.Info("Done executing pod ItemBlockAction") + + pod := new(corev1api.Pod) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), pod); err != nil { + return nil, errors.WithStack(err) + } + return actionhelpers.RelatedItemsForPod(pod, a.log), nil +} + +func (a *PodAction) Name() string { + return "PodItemBlockAction" +} diff --git a/pkg/itemblock/actions/pod_action_test.go b/pkg/itemblock/actions/pod_action_test.go new file mode 100644 index 000000000..645feeee2 --- /dev/null +++ b/pkg/itemblock/actions/pod_action_test.go @@ -0,0 +1,148 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actions + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestPodActionAppliesTo(t *testing.T) { + a := NewPodAction(velerotest.NewLogger()) + + actual, err := a.AppliesTo() + require.NoError(t, err) + + expected := velero.ResourceSelector{ + IncludedResources: []string{"pods"}, + } + assert.Equal(t, expected, actual) +} + +func TestPodActionGetRelatedItems(t *testing.T) { + tests := []struct { + name string + pod runtime.Unstructured + expected []velero.ResourceIdentifier + }{ + { + name: "no spec.volumes", + pod: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "namespace": "foo", + "name": "bar" + } + } + `), + }, + { + name: "persistentVolumeClaim without claimName", + pod: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "namespace": "foo", + "name": "bar" + }, + "spec": { + "volumes": [ + { + "persistentVolumeClaim": {} + } + ] + } + } + `), + }, + { + name: "full test, mix of volume types", + pod: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "namespace": "foo", + "name": "bar" + }, + "spec": { + "volumes": [ + { + "persistentVolumeClaim": {} + }, + { + "emptyDir": {} + }, + { + "persistentVolumeClaim": {"claimName": "claim1"} + }, + { + "emptyDir": {} + }, + { + "persistentVolumeClaim": {"claimName": "claim2"} + } + ] + } + } + `), + expected: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PersistentVolumeClaims, Namespace: "foo", Name: "claim1"}, + {GroupResource: kuberesource.PersistentVolumeClaims, Namespace: "foo", Name: "claim2"}, + }, + }, + { + name: "test priority class", + pod: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "namespace": "foo", + "name": "bar" + }, + "spec": { + "priorityClassName": "testPriorityClass" + } + } + `), + expected: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PriorityClasses, Name: "testPriorityClass"}, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + a := NewPodAction(velerotest.NewLogger()) + + relatedItems, err := a.GetRelatedItems(test.pod, nil) + require.NoError(t, err) + assert.Equal(t, test.expected, relatedItems) + }) + } +} diff --git a/pkg/itemblock/actions/pvc_action.go b/pkg/itemblock/actions/pvc_action.go new file mode 100644 index 000000000..4ec99d03b --- /dev/null +++ b/pkg/itemblock/actions/pvc_action.go @@ -0,0 +1,113 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actions + +import ( + "context" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + corev1api "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/runtime" + crclient "sigs.k8s.io/controller-runtime/pkg/client" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/client" + "github.com/vmware-tanzu/velero/pkg/kuberesource" + plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" + "github.com/vmware-tanzu/velero/pkg/util/kube" +) + +// PVCAction inspects a PersistentVolumeClaim for the PersistentVolume +// that it references and backs it up +type PVCAction struct { + log logrus.FieldLogger + crClient crclient.Client +} + +func NewPVCAction(f client.Factory) plugincommon.HandlerInitializer { + return func(logger logrus.FieldLogger) (interface{}, error) { + crClient, err := f.KubebuilderClient() + if err != nil { + return nil, errors.WithStack(err) + } + + return &PVCAction{ + log: logger, + crClient: crClient, + }, nil + } +} + +func (a *PVCAction) AppliesTo() (velero.ResourceSelector, error) { + return velero.ResourceSelector{ + IncludedResources: []string{"persistentvolumeclaims"}, + }, nil +} + +func (a *PVCAction) GetRelatedItems(item runtime.Unstructured, backup *v1.Backup) ([]velero.ResourceIdentifier, error) { + a.log.Info("Executing PVC ItemBlockAction") + defer a.log.Info("Done executing PVC ItemBlockAction") + + pvc := new(corev1api.PersistentVolumeClaim) + if err := runtime.DefaultUnstructuredConverter.FromUnstructured(item.UnstructuredContent(), &pvc); err != nil { + return nil, errors.Wrap(err, "unable to convert unstructured item to persistent volume claim") + } + + if pvc.Status.Phase != corev1api.ClaimBound || pvc.Spec.VolumeName == "" { + return nil, nil + } + // returns the PV for the PVC (shared with BIA additionalItems) + relatedItems := actionhelpers.RelatedItemsForPVC(pvc, a.log) + + // Adds pods mounting this PVC to ensure that multiple pods mounting the same RWX + // volume get backed up together. + pods := new(corev1api.PodList) + err := a.crClient.List(context.Background(), pods, crclient.InNamespace(pvc.Namespace)) + if err != nil { + return nil, errors.Wrap(err, "failed to list pods") + } + + for i := range pods.Items { + for _, volume := range pods.Items[i].Spec.Volumes { + if volume.VolumeSource.PersistentVolumeClaim == nil { + continue + } + if volume.PersistentVolumeClaim.ClaimName == pvc.Name { + if kube.IsPodRunning(&pods.Items[i]) != nil { + a.log.Infof("Related pod %s is not running, not adding to ItemBlock for PVC %s", pods.Items[i].Name, pvc.Name) + } else { + a.log.Infof("Adding related Pod %s to PVC %s", pods.Items[i].Name, pvc.Name) + relatedItems = append(relatedItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.Pods, + Namespace: pods.Items[i].Namespace, + Name: pods.Items[i].Name, + }) + } + break + } + } + } + + return relatedItems, nil +} + +func (a *PVCAction) Name() string { + return "PodItemBlockAction" +} diff --git a/pkg/itemblock/actions/pvc_action_test.go b/pkg/itemblock/actions/pvc_action_test.go new file mode 100644 index 000000000..c485dcd80 --- /dev/null +++ b/pkg/itemblock/actions/pvc_action_test.go @@ -0,0 +1,167 @@ +/* +Copyright 2017 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 actions + +import ( + "context" + "fmt" + "testing" + + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1api "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" + factorymocks "github.com/vmware-tanzu/velero/pkg/client/mocks" + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestBackupPVAction(t *testing.T) { + tests := []struct { + name string + pvc *corev1api.PersistentVolumeClaim + pods []*corev1api.Pod + expectedErr error + expectedRelated []velero.ResourceIdentifier + }{ + { + name: "Test no volumeName", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").Phase(corev1api.ClaimBound).Result(), + expectedErr: nil, + expectedRelated: nil, + }, + { + name: "Test empty volumeName", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("").Phase(corev1api.ClaimBound).Result(), + expectedErr: nil, + expectedRelated: nil, + }, + { + name: "Test no status phase", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Result(), + expectedErr: nil, + expectedRelated: nil, + }, + { + name: "Test pending status phase", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimPending).Result(), + expectedErr: nil, + expectedRelated: nil, + }, + { + name: "Test lost status phase", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimLost).Result(), + expectedErr: nil, + expectedRelated: nil, + }, + { + name: "Test with volume", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(), + expectedErr: nil, + expectedRelated: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PersistentVolumes, Name: "testPV"}, + }, + }, + { + name: "Test with volume and one running pod", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(), + pods: []*corev1api.Pod{ + builder.ForPod("velero", "testPod1").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(), + }, + expectedErr: nil, + expectedRelated: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PersistentVolumes, Name: "testPV"}, + {GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod1"}, + }, + }, + { + name: "Test with volume and multiple running pods", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(), + pods: []*corev1api.Pod{ + builder.ForPod("velero", "testPod1").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(), + builder.ForPod("velero", "testPod2").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(), + builder.ForPod("velero", "testPod3").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(), + }, + expectedErr: nil, + expectedRelated: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PersistentVolumes, Name: "testPV"}, + {GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod1"}, + {GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod2"}, + {GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod3"}, + }, + }, + { + name: "Test with volume and multiple running pods, some not running", + pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").Phase(corev1api.ClaimBound).Result(), + pods: []*corev1api.Pod{ + builder.ForPod("velero", "testPod1").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodSucceeded).Result(), + builder.ForPod("velero", "testPod2").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).NodeName("velero").Phase(corev1api.PodRunning).Result(), + builder.ForPod("velero", "testPod3").Volumes(builder.ForVolume("testPVC").PersistentVolumeClaimSource("testPVC").Result()).Phase(corev1api.PodRunning).Result(), + }, + expectedErr: nil, + expectedRelated: []velero.ResourceIdentifier{ + {GroupResource: kuberesource.PersistentVolumes, Name: "testPV"}, + {GroupResource: kuberesource.Pods, Namespace: "velero", Name: "testPod2"}, + }, + }, + } + + backup := &v1.Backup{} + logger := logrus.New() + + f := &factorymocks.Factory{} + f.On("KubebuilderClient").Return(nil, fmt.Errorf("")) + plugin := NewPVCAction(f) + _, err := plugin(logger) + require.Error(t, err) + + for _, tc := range tests { + t.Run(tc.name, func(*testing.T) { + crClient := velerotest.NewFakeControllerRuntimeClient(t) + f := &factorymocks.Factory{} + f.On("KubebuilderClient").Return(crClient, nil) + plugin := NewPVCAction(f) + i, err := plugin(logger) + require.NoError(t, err) + a := i.(*PVCAction) + + if tc.pvc != nil { + require.NoError(t, crClient.Create(context.Background(), tc.pvc)) + } + for _, pod := range tc.pods { + require.NoError(t, crClient.Create(context.Background(), pod)) + } + + pvcMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&tc.pvc) + require.NoError(t, err) + + relatedItems, err := a.GetRelatedItems(&unstructured.Unstructured{Object: pvcMap}, backup) + if tc.expectedErr != nil { + require.EqualError(t, err, tc.expectedErr.Error()) + } else { + require.NoError(t, err) + } + assert.Equal(t, tc.expectedRelated, relatedItems) + }) + } +} diff --git a/pkg/itemblock/actions/service_account_action.go b/pkg/itemblock/actions/service_account_action.go new file mode 100644 index 000000000..91cdbbe59 --- /dev/null +++ b/pkg/itemblock/actions/service_account_action.go @@ -0,0 +1,73 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actions + +import ( + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + + v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" +) + +// ServiceAccountAction implements ItemBlockAction. +type ServiceAccountAction struct { + log logrus.FieldLogger + clusterRoleBindings []actionhelpers.ClusterRoleBinding +} + +// NewServiceAccountAction creates a new ItemBlockAction for service accounts. +func NewServiceAccountAction(logger logrus.FieldLogger, clusterRoleBindingListers map[string]actionhelpers.ClusterRoleBindingLister, discoveryHelper velerodiscovery.Helper) (*ServiceAccountAction, error) { + crbs, err := actionhelpers.ClusterRoleBindingsForAction(clusterRoleBindingListers, discoveryHelper) + if err != nil { + return nil, err + } + + return &ServiceAccountAction{ + log: logger, + clusterRoleBindings: crbs, + }, nil +} + +// AppliesTo returns a ResourceSelector that applies only to service accounts. +func (a *ServiceAccountAction) AppliesTo() (velero.ResourceSelector, error) { + return velero.ResourceSelector{ + IncludedResources: []string{"serviceaccounts"}, + }, nil +} + +// GetRelatedItems checks for any ClusterRoleBindings that have this service account as a subject, and +// returns the ClusterRoleBinding and associated ClusterRole. +func (a *ServiceAccountAction) GetRelatedItems(item runtime.Unstructured, backup *v1.Backup) ([]velero.ResourceIdentifier, error) { + a.log.Info("Running ServiceAccount ItemBlockAction") + defer a.log.Info("Done running ServiceAccount ItemBlockAction") + + objectMeta, err := meta.Accessor(item) + if err != nil { + return nil, errors.WithStack(err) + } + + return actionhelpers.RelatedItemsForServiceAccount(objectMeta, a.clusterRoleBindings, a.log), nil +} + +func (a *ServiceAccountAction) Name() string { + return "ServiceAccountItemBlockAction" +} diff --git a/pkg/itemblock/actions/service_account_action_test.go b/pkg/itemblock/actions/service_account_action_test.go new file mode 100644 index 000000000..52c39b0bc --- /dev/null +++ b/pkg/itemblock/actions/service_account_action_test.go @@ -0,0 +1,609 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actions + +import ( + "fmt" + "sort" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + rbac "k8s.io/api/rbac/v1" + rbacbeta "k8s.io/api/rbac/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" + velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/util/actionhelpers" +) + +func newV1ClusterRoleBindingList(rbacCRBList []rbac.ClusterRoleBinding) []actionhelpers.ClusterRoleBinding { + var crbs []actionhelpers.ClusterRoleBinding + for _, c := range rbacCRBList { + crbs = append(crbs, actionhelpers.V1ClusterRoleBinding{Crb: c}) + } + + return crbs +} + +func newV1beta1ClusterRoleBindingList(rbacCRBList []rbacbeta.ClusterRoleBinding) []actionhelpers.ClusterRoleBinding { + var crbs []actionhelpers.ClusterRoleBinding + for _, c := range rbacCRBList { + crbs = append(crbs, actionhelpers.V1beta1ClusterRoleBinding{Crb: c}) + } + + return crbs +} + +type FakeV1ClusterRoleBindingLister struct { + v1crbs []rbac.ClusterRoleBinding +} + +func (f FakeV1ClusterRoleBindingLister) List() ([]actionhelpers.ClusterRoleBinding, error) { + var crbs []actionhelpers.ClusterRoleBinding + for _, c := range f.v1crbs { + crbs = append(crbs, actionhelpers.V1ClusterRoleBinding{Crb: c}) + } + return crbs, nil +} + +type FakeV1beta1ClusterRoleBindingLister struct { + v1beta1crbs []rbacbeta.ClusterRoleBinding +} + +func (f FakeV1beta1ClusterRoleBindingLister) List() ([]actionhelpers.ClusterRoleBinding, error) { + var crbs []actionhelpers.ClusterRoleBinding + for _, c := range f.v1beta1crbs { + crbs = append(crbs, actionhelpers.V1beta1ClusterRoleBinding{Crb: c}) + } + return crbs, nil +} + +func TestServiceAccountActionAppliesTo(t *testing.T) { + // Instantiating the struct directly since using + // NewServiceAccountAction requires a full Kubernetes clientset + a := &ServiceAccountAction{} + + actual, err := a.AppliesTo() + require.NoError(t, err) + + expected := velero.ResourceSelector{ + IncludedResources: []string{"serviceaccounts"}, + } + assert.Equal(t, expected, actual) +} + +func TestNewServiceAccountAction(t *testing.T) { + tests := []struct { + name string + version string + expectedCRBs []actionhelpers.ClusterRoleBinding + }{ + { + name: "rbac v1 API instantiates an saAction", + version: rbac.SchemeGroupVersion.Version, + expectedCRBs: []actionhelpers.ClusterRoleBinding{ + actionhelpers.V1ClusterRoleBinding{ + Crb: rbac.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "v1crb-1", + }, + }, + }, + actionhelpers.V1ClusterRoleBinding{ + Crb: rbac.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "v1crb-2", + }, + }, + }, + }, + }, + { + name: "rbac v1beta1 API instantiates an saAction", + version: rbacbeta.SchemeGroupVersion.Version, + expectedCRBs: []actionhelpers.ClusterRoleBinding{ + actionhelpers.V1beta1ClusterRoleBinding{ + Crb: rbacbeta.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "v1beta1crb-1", + }, + }, + }, + actionhelpers.V1beta1ClusterRoleBinding{ + Crb: rbacbeta.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "v1beta1crb-2", + }, + }, + }, + }, + }, + { + name: "no RBAC API instantiates an saAction with empty slice", + version: "", + expectedCRBs: []actionhelpers.ClusterRoleBinding{}, + }, + } + // Set up all of our fakes outside the test loop + discoveryHelper := velerotest.FakeDiscoveryHelper{} + logger := velerotest.NewLogger() + + v1crbs := []rbac.ClusterRoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "v1crb-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "v1crb-2", + }, + }, + } + + v1beta1crbs := []rbacbeta.ClusterRoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "v1beta1crb-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "v1beta1crb-2", + }, + }, + } + + clusterRoleBindingListers := map[string]actionhelpers.ClusterRoleBindingLister{ + rbac.SchemeGroupVersion.Version: FakeV1ClusterRoleBindingLister{v1crbs: v1crbs}, + rbacbeta.SchemeGroupVersion.Version: FakeV1beta1ClusterRoleBindingLister{v1beta1crbs: v1beta1crbs}, + "": actionhelpers.NoopClusterRoleBindingLister{}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // We only care about the preferred version, nothing else in the list + discoveryHelper.APIGroupsList = []metav1.APIGroup{ + { + Name: rbac.GroupName, + PreferredVersion: metav1.GroupVersionForDiscovery{ + Version: test.version, + }, + }, + } + action, err := NewServiceAccountAction(logger, clusterRoleBindingListers, &discoveryHelper) + require.NoError(t, err) + assert.Equal(t, test.expectedCRBs, action.clusterRoleBindings) + }) + } +} + +func TestServiceAccountActionExecute(t *testing.T) { + tests := []struct { + name string + serviceAccount runtime.Unstructured + crbs []rbac.ClusterRoleBinding + expectedAdditionalItems []velero.ResourceIdentifier + }{ + { + name: "no crbs", + serviceAccount: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": { + "namespace": "velero", + "name": "velero" + } + } + `), + crbs: nil, + expectedAdditionalItems: nil, + }, + { + name: "no matching crbs", + serviceAccount: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": { + "namespace": "velero", + "name": "velero" + } + } + `), + crbs: []rbac.ClusterRoleBinding{ + { + Subjects: []rbac.Subject{ + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + { + Kind: "non-matching-kind", + Namespace: "velero", + Name: "velero", + }, + { + Kind: rbac.ServiceAccountKind, + Namespace: "non-matching-ns", + Name: "velero", + }, + { + Kind: rbac.ServiceAccountKind, + Namespace: "velero", + Name: "non-matching-name", + }, + }, + RoleRef: rbac.RoleRef{ + Name: "role", + }, + }, + }, + expectedAdditionalItems: nil, + }, + { + name: "some matching crbs", + serviceAccount: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": { + "namespace": "velero", + "name": "velero" + } + } + `), + crbs: []rbac.ClusterRoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-1", + }, + Subjects: []rbac.Subject{ + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + }, + RoleRef: rbac.RoleRef{ + Name: "role-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-2", + }, + Subjects: []rbac.Subject{ + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + { + Kind: rbac.ServiceAccountKind, + Namespace: "velero", + Name: "velero", + }, + }, + RoleRef: rbac.RoleRef{ + Name: "role-2", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-3", + }, + Subjects: []rbac.Subject{ + { + Kind: rbac.ServiceAccountKind, + Namespace: "velero", + Name: "velero", + }, + }, + RoleRef: rbac.RoleRef{ + Name: "role-3", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-4", + }, + Subjects: []rbac.Subject{ + { + Kind: rbac.ServiceAccountKind, + Namespace: "velero", + Name: "velero", + }, + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + }, + RoleRef: rbac.RoleRef{ + Name: "role-4", + }, + }, + }, + expectedAdditionalItems: []velero.ResourceIdentifier{ + { + GroupResource: kuberesource.ClusterRoleBindings, + Name: "crb-2", + }, + { + GroupResource: kuberesource.ClusterRoleBindings, + Name: "crb-3", + }, + { + GroupResource: kuberesource.ClusterRoleBindings, + Name: "crb-4", + }, + { + GroupResource: kuberesource.ClusterRoles, + Name: "role-2", + }, + { + GroupResource: kuberesource.ClusterRoles, + Name: "role-3", + }, + { + GroupResource: kuberesource.ClusterRoles, + Name: "role-4", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Create the action struct directly so we don't need to mock a clientset + action := &ServiceAccountAction{ + log: velerotest.NewLogger(), + clusterRoleBindings: newV1ClusterRoleBindingList(test.crbs), + } + + additional, err := action.GetRelatedItems(test.serviceAccount, nil) + + assert.NoError(t, err) + + // ensure slices are ordered for valid comparison + sort.Slice(test.expectedAdditionalItems, func(i, j int) bool { + return fmt.Sprintf("%s.%s", test.expectedAdditionalItems[i].GroupResource.String(), test.expectedAdditionalItems[i].Name) < + fmt.Sprintf("%s.%s", test.expectedAdditionalItems[j].GroupResource.String(), test.expectedAdditionalItems[j].Name) + }) + + sort.Slice(additional, func(i, j int) bool { + return fmt.Sprintf("%s.%s", additional[i].GroupResource.String(), additional[i].Name) < + fmt.Sprintf("%s.%s", additional[j].GroupResource.String(), additional[j].Name) + }) + + assert.Equal(t, test.expectedAdditionalItems, additional) + }) + } +} + +func TestServiceAccountActionExecuteOnBeta1(t *testing.T) { + tests := []struct { + name string + serviceAccount runtime.Unstructured + crbs []rbacbeta.ClusterRoleBinding + expectedAdditionalItems []velero.ResourceIdentifier + }{ + { + name: "no crbs", + serviceAccount: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": { + "namespace": "velero", + "name": "velero" + } + } + `), + crbs: nil, + expectedAdditionalItems: nil, + }, + { + name: "no matching crbs", + serviceAccount: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": { + "namespace": "velero", + "name": "velero" + } + } + `), + crbs: []rbacbeta.ClusterRoleBinding{ + { + Subjects: []rbacbeta.Subject{ + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + { + Kind: "non-matching-kind", + Namespace: "velero", + Name: "velero", + }, + { + Kind: rbacbeta.ServiceAccountKind, + Namespace: "non-matching-ns", + Name: "velero", + }, + { + Kind: rbacbeta.ServiceAccountKind, + Namespace: "velero", + Name: "non-matching-name", + }, + }, + RoleRef: rbacbeta.RoleRef{ + Name: "role", + }, + }, + }, + expectedAdditionalItems: nil, + }, + { + name: "some matching crbs", + serviceAccount: velerotest.UnstructuredOrDie(` + { + "apiVersion": "v1", + "kind": "ServiceAccount", + "metadata": { + "namespace": "velero", + "name": "velero" + } + } + `), + crbs: []rbacbeta.ClusterRoleBinding{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-1", + }, + Subjects: []rbacbeta.Subject{ + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + }, + RoleRef: rbacbeta.RoleRef{ + Name: "role-1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-2", + }, + Subjects: []rbacbeta.Subject{ + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + { + Kind: rbacbeta.ServiceAccountKind, + Namespace: "velero", + Name: "velero", + }, + }, + RoleRef: rbacbeta.RoleRef{ + Name: "role-2", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-3", + }, + Subjects: []rbacbeta.Subject{ + { + Kind: rbacbeta.ServiceAccountKind, + Namespace: "velero", + Name: "velero", + }, + }, + RoleRef: rbacbeta.RoleRef{ + Name: "role-3", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "crb-4", + }, + Subjects: []rbacbeta.Subject{ + { + Kind: rbacbeta.ServiceAccountKind, + Namespace: "velero", + Name: "velero", + }, + { + Kind: "non-matching-kind", + Namespace: "non-matching-ns", + Name: "non-matching-name", + }, + }, + RoleRef: rbacbeta.RoleRef{ + Name: "role-4", + }, + }, + }, + expectedAdditionalItems: []velero.ResourceIdentifier{ + { + GroupResource: kuberesource.ClusterRoleBindings, + Name: "crb-2", + }, + { + GroupResource: kuberesource.ClusterRoleBindings, + Name: "crb-3", + }, + { + GroupResource: kuberesource.ClusterRoleBindings, + Name: "crb-4", + }, + { + GroupResource: kuberesource.ClusterRoles, + Name: "role-2", + }, + { + GroupResource: kuberesource.ClusterRoles, + Name: "role-3", + }, + { + GroupResource: kuberesource.ClusterRoles, + Name: "role-4", + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + // Create the action struct directly so we don't need to mock a clientset + action := &ServiceAccountAction{ + log: velerotest.NewLogger(), + clusterRoleBindings: newV1beta1ClusterRoleBindingList(test.crbs), + } + + additional, err := action.GetRelatedItems(test.serviceAccount, nil) + + assert.NoError(t, err) + + // ensure slices are ordered for valid comparison + sort.Slice(test.expectedAdditionalItems, func(i, j int) bool { + return fmt.Sprintf("%s.%s", test.expectedAdditionalItems[i].GroupResource.String(), test.expectedAdditionalItems[i].Name) < + fmt.Sprintf("%s.%s", test.expectedAdditionalItems[j].GroupResource.String(), test.expectedAdditionalItems[j].Name) + }) + + sort.Slice(additional, func(i, j int) bool { + return fmt.Sprintf("%s.%s", additional[i].GroupResource.String(), additional[i].Name) < + fmt.Sprintf("%s.%s", additional[j].GroupResource.String(), additional[j].Name) + }) + + assert.Equal(t, test.expectedAdditionalItems, additional) + }) + } +} diff --git a/pkg/util/actionhelpers/pod_helper.go b/pkg/util/actionhelpers/pod_helper.go new file mode 100644 index 000000000..72c42936c --- /dev/null +++ b/pkg/util/actionhelpers/pod_helper.go @@ -0,0 +1,53 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actionhelpers + +import ( + "github.com/sirupsen/logrus" + corev1api "k8s.io/api/core/v1" + + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +func RelatedItemsForPod(pod *corev1api.Pod, log logrus.FieldLogger) []velero.ResourceIdentifier { + var additionalItems []velero.ResourceIdentifier + if pod.Spec.PriorityClassName != "" { + log.Infof("Adding priorityclass %s to additionalItems", pod.Spec.PriorityClassName) + additionalItems = append(additionalItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.PriorityClasses, + Name: pod.Spec.PriorityClassName, + }) + } + + if len(pod.Spec.Volumes) == 0 { + log.Info("pod has no volumes") + } + + for _, volume := range pod.Spec.Volumes { + if volume.PersistentVolumeClaim != nil && volume.PersistentVolumeClaim.ClaimName != "" { + log.Infof("Adding pvc %s to additionalItems", volume.PersistentVolumeClaim.ClaimName) + + additionalItems = append(additionalItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.PersistentVolumeClaims, + Namespace: pod.Namespace, + Name: volume.PersistentVolumeClaim.ClaimName, + }) + } + } + return additionalItems +} diff --git a/pkg/util/actionhelpers/pvc_helper.go b/pkg/util/actionhelpers/pvc_helper.go new file mode 100644 index 000000000..f6a72eeaa --- /dev/null +++ b/pkg/util/actionhelpers/pvc_helper.go @@ -0,0 +1,34 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actionhelpers + +import ( + "github.com/sirupsen/logrus" + corev1api "k8s.io/api/core/v1" + + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +func RelatedItemsForPVC(pvc *corev1api.PersistentVolumeClaim, log logrus.FieldLogger) []velero.ResourceIdentifier { + return []velero.ResourceIdentifier{ + { + GroupResource: kuberesource.PersistentVolumes, + Name: pvc.Spec.VolumeName, + }, + } +} diff --git a/pkg/backup/actions/rbac.go b/pkg/util/actionhelpers/rbac.go similarity index 71% rename from pkg/backup/actions/rbac.go rename to pkg/util/actionhelpers/rbac.go index 5da936ef7..b763ec858 100644 --- a/pkg/backup/actions/rbac.go +++ b/pkg/util/actionhelpers/rbac.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package actions +package actionhelpers import ( "context" @@ -35,42 +35,42 @@ type ClusterRoleBindingLister interface { } // noopClusterRoleBindingLister exists to handle clusters where RBAC is disabled. -type noopClusterRoleBindingLister struct { +type NoopClusterRoleBindingLister struct { } -func (noop noopClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { +func (noop NoopClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { return []ClusterRoleBinding{}, nil } -type v1ClusterRoleBindingLister struct { +type V1ClusterRoleBindingLister struct { client rbacclient.ClusterRoleBindingInterface } -func (v1 v1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { +func (v1 V1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { crbList, err := v1.client.List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, errors.WithStack(err) } var crbs []ClusterRoleBinding for _, crb := range crbList.Items { - crbs = append(crbs, v1ClusterRoleBinding{crb: crb}) + crbs = append(crbs, V1ClusterRoleBinding{Crb: crb}) } return crbs, nil } -type v1beta1ClusterRoleBindingLister struct { +type V1beta1ClusterRoleBindingLister struct { client rbacbetaclient.ClusterRoleBindingInterface } -func (v1beta1 v1beta1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { +func (v1beta1 V1beta1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, error) { crbList, err := v1beta1.client.List(context.TODO(), metav1.ListOptions{}) if err != nil { return nil, errors.WithStack(err) } var crbs []ClusterRoleBinding for _, crb := range crbList.Items { - crbs = append(crbs, v1beta1ClusterRoleBinding{crb: crb}) + crbs = append(crbs, V1beta1ClusterRoleBinding{Crb: crb}) } return crbs, nil @@ -81,9 +81,9 @@ func (v1beta1 v1beta1ClusterRoleBindingLister) List() ([]ClusterRoleBinding, err // Necessary so that callers to the ClusterRoleBindingLister interfaces don't need the kubernetes.Interface. func NewClusterRoleBindingListerMap(clientset kubernetes.Interface) map[string]ClusterRoleBindingLister { return map[string]ClusterRoleBindingLister{ - rbac.SchemeGroupVersion.Version: v1ClusterRoleBindingLister{client: clientset.RbacV1().ClusterRoleBindings()}, - rbacbeta.SchemeGroupVersion.Version: v1beta1ClusterRoleBindingLister{client: clientset.RbacV1beta1().ClusterRoleBindings()}, - "": noopClusterRoleBindingLister{}, + rbac.SchemeGroupVersion.Version: V1ClusterRoleBindingLister{client: clientset.RbacV1().ClusterRoleBindings()}, + rbacbeta.SchemeGroupVersion.Version: V1beta1ClusterRoleBindingLister{client: clientset.RbacV1beta1().ClusterRoleBindings()}, + "": NoopClusterRoleBindingLister{}, } } @@ -97,21 +97,21 @@ type ClusterRoleBinding interface { RoleRefName() string } -type v1ClusterRoleBinding struct { - crb rbac.ClusterRoleBinding +type V1ClusterRoleBinding struct { + Crb rbac.ClusterRoleBinding } -func (c v1ClusterRoleBinding) Name() string { - return c.crb.Name +func (c V1ClusterRoleBinding) Name() string { + return c.Crb.Name } -func (c v1ClusterRoleBinding) RoleRefName() string { - return c.crb.RoleRef.Name +func (c V1ClusterRoleBinding) RoleRefName() string { + return c.Crb.RoleRef.Name } -func (c v1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string { +func (c V1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string { var saSubjects []string - for _, s := range c.crb.Subjects { + for _, s := range c.Crb.Subjects { if s.Kind == rbac.ServiceAccountKind && s.Namespace == namespace { saSubjects = append(saSubjects, s.Name) } @@ -119,21 +119,21 @@ func (c v1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string return saSubjects } -type v1beta1ClusterRoleBinding struct { - crb rbacbeta.ClusterRoleBinding +type V1beta1ClusterRoleBinding struct { + Crb rbacbeta.ClusterRoleBinding } -func (c v1beta1ClusterRoleBinding) Name() string { - return c.crb.Name +func (c V1beta1ClusterRoleBinding) Name() string { + return c.Crb.Name } -func (c v1beta1ClusterRoleBinding) RoleRefName() string { - return c.crb.RoleRef.Name +func (c V1beta1ClusterRoleBinding) RoleRefName() string { + return c.Crb.RoleRef.Name } -func (c v1beta1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string { +func (c V1beta1ClusterRoleBinding) ServiceAccountSubjects(namespace string) []string { var saSubjects []string - for _, s := range c.crb.Subjects { + for _, s := range c.Crb.Subjects { if s.Kind == rbac.ServiceAccountKind && s.Namespace == namespace { saSubjects = append(saSubjects, s.Name) } diff --git a/pkg/util/actionhelpers/service_account_helper.go b/pkg/util/actionhelpers/service_account_helper.go new file mode 100644 index 000000000..7c388c4da --- /dev/null +++ b/pkg/util/actionhelpers/service_account_helper.go @@ -0,0 +1,84 @@ +/* +Copyright the Velero contributors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package actionhelpers + +import ( + "github.com/sirupsen/logrus" + rbac "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + + velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" + "github.com/vmware-tanzu/velero/pkg/kuberesource" + "github.com/vmware-tanzu/velero/pkg/plugin/velero" +) + +func ClusterRoleBindingsForAction(clusterRoleBindingListers map[string]ClusterRoleBindingLister, discoveryHelper velerodiscovery.Helper) ([]ClusterRoleBinding, error) { + // Look up the supported RBAC version + var supportedAPI metav1.GroupVersionForDiscovery + for _, ag := range discoveryHelper.APIGroups() { + if ag.Name == rbac.GroupName { + supportedAPI = ag.PreferredVersion + break + } + } + + crbLister := clusterRoleBindingListers[supportedAPI.Version] + + // This should be safe because the List call will return a 0-item slice + // if there's no matching API version. + return crbLister.List() +} + +func RelatedItemsForServiceAccount(objectMeta metav1.Object, clusterRoleBindings []ClusterRoleBinding, log logrus.FieldLogger) []velero.ResourceIdentifier { + var ( + namespace = objectMeta.GetNamespace() + name = objectMeta.GetName() + bindings = sets.NewString() + roles = sets.NewString() + ) + + for _, crb := range clusterRoleBindings { + for _, s := range crb.ServiceAccountSubjects(namespace) { + if s == name { + log.Infof("Adding clusterrole %s and clusterrolebinding %s to relatedItems since serviceaccount %s/%s is a subject", + crb.RoleRefName(), crb.Name(), namespace, name) + + bindings.Insert(crb.Name()) + roles.Insert(crb.RoleRefName()) + break + } + } + } + + var relatedItems []velero.ResourceIdentifier + for binding := range bindings { + relatedItems = append(relatedItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.ClusterRoleBindings, + Name: binding, + }) + } + + for role := range roles { + relatedItems = append(relatedItems, velero.ResourceIdentifier{ + GroupResource: kuberesource.ClusterRoles, + Name: role, + }) + } + + return relatedItems +} From 04db3ba767c37a942a60be48d2dd25296974206f Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 8 Aug 2024 13:06:47 +0800 Subject: [PATCH 50/69] data mover ms node agent resume Signed-off-by: Lyndon-Li --- pkg/builder/data_download_builder.go | 2 +- pkg/builder/data_upload_builder.go | 2 +- pkg/cmd/cli/nodeagent/server.go | 2 ++ pkg/controller/data_download_controller.go | 2 +- pkg/controller/data_download_controller_test.go | 2 +- pkg/controller/data_upload_controller.go | 2 +- pkg/controller/data_upload_controller_test.go | 2 +- 7 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/builder/data_download_builder.go b/pkg/builder/data_download_builder.go index 1eb8e6441..e0ed2ba6d 100644 --- a/pkg/builder/data_download_builder.go +++ b/pkg/builder/data_download_builder.go @@ -130,7 +130,7 @@ func (d *DataDownloadBuilder) CompletionTimestamp(completionTimestamp *metav1.Ti return d } -// Labels sets the DataDownload's Progress. +// Progress sets the DataDownload's Progress. func (d *DataDownloadBuilder) Progress(progress shared.DataMoveOperationProgress) *DataDownloadBuilder { d.object.Status.Progress = progress return d diff --git a/pkg/builder/data_upload_builder.go b/pkg/builder/data_upload_builder.go index 0bc28b860..465f6b94e 100644 --- a/pkg/builder/data_upload_builder.go +++ b/pkg/builder/data_upload_builder.go @@ -133,7 +133,7 @@ func (d *DataUploadBuilder) Labels(labels map[string]string) *DataUploadBuilder return d } -// Labels sets the DataUpload's Progress. +// Progress sets the DataUpload's Progress. func (d *DataUploadBuilder) Progress(progress shared.DataMoveOperationProgress) *DataUploadBuilder { d.object.Status.Progress = progress return d diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 09b93d706..181afbf69 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -312,6 +312,8 @@ func (s *nodeAgentServer) run() { if err := dataDownloadReconciler.AttemptDataDownloadResume(s.ctx, s.mgr.GetClient(), s.logger.WithField("node", s.nodeName), s.namespace); err != nil { s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data download resume") } + + s.logger.Info("Attempt complete to resume dataUploads and dataDownloads") }() s.logger.Info("Controllers starting...") diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index 73626c016..ba5ad3753 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -864,7 +864,7 @@ func (r *DataDownloadReconciler) resumeCancellableDataPath(ctx context.Context, if err := asyncBR.StartRestore(dd.Spec.SnapshotID, datapath.AccessPoint{ ByPath: res.ByPod.VolumeName, }, nil); err != nil { - return errors.Wrapf(err, "error to resume asyncBR watche for dd %s", dd.Name) + return errors.Wrapf(err, "error to resume asyncBR watcher for dd %s", dd.Name) } resumeComplete = true diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index 224ff0988..7e8a1f691 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -1135,7 +1135,7 @@ func TestResumeCancellableRestore(t *testing.T) { mockStart: true, mockClose: true, startWatcherErr: errors.New("fake-start-watcher-error"), - expectedError: fmt.Sprintf("error to resume asyncBR watche for dd %s: fake-start-watcher-error", dataDownloadName), + expectedError: fmt.Sprintf("error to resume asyncBR watcher for dd %s: fake-start-watcher-error", dataDownloadName), }, { name: "succeed", diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 11003856d..4660e5b2f 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -968,7 +968,7 @@ func (r *DataUploadReconciler) resumeCancellableDataPath(ctx context.Context, du if err := asyncBR.StartBackup(datapath.AccessPoint{ ByPath: res.ByPod.VolumeName, }, du.Spec.DataMoverConfig, nil); err != nil { - return errors.Wrapf(err, "error to resume asyncBR watche for du %s", du.Name) + return errors.Wrapf(err, "error to resume asyncBR watcher for du %s", du.Name) } resumeComplete = true diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index 05b61f401..4024a714d 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -1218,7 +1218,7 @@ func TestResumeCancellableBackup(t *testing.T) { mockStart: true, mockClose: true, startWatcherErr: errors.New("fake-start-watcher-error"), - expectedError: fmt.Sprintf("error to resume asyncBR watche for du %s: fake-start-watcher-error", dataUploadName), + expectedError: fmt.Sprintf("error to resume asyncBR watcher for du %s: fake-start-watcher-error", dataUploadName), }, { name: "succeed", From 3aabfc3414adc7740e4367cf0b7d5f4a60f25bc6 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Mon, 12 Aug 2024 13:28:19 -0700 Subject: [PATCH 51/69] Minor fixes to Dockerfile and docs Signed-off-by: Shubham Pampattiwar --- Dockerfile | 4 ++-- site/content/docs/main/build-from-source.md | 2 +- site/content/docs/v1.14/build-from-source.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0d461642e..2c70dd456 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ # limitations under the License. # Velero binary build section -FROM --platform=$BUILDPLATFORM golang:1.22-bookworm as velero-builder +FROM --platform=$BUILDPLATFORM golang:1.22-bookworm AS velero-builder ARG GOPROXY ARG BIN @@ -47,7 +47,7 @@ RUN mkdir -p /output/usr/bin && \ go clean -modcache -cache # Restic binary build section -FROM --platform=$BUILDPLATFORM golang:1.22-bookworm as restic-builder +FROM --platform=$BUILDPLATFORM golang:1.22-bookworm AS restic-builder ARG BIN ARG TARGETOS diff --git a/site/content/docs/main/build-from-source.md b/site/content/docs/main/build-from-source.md index 083cdc202..798800036 100644 --- a/site/content/docs/main/build-from-source.md +++ b/site/content/docs/main/build-from-source.md @@ -96,7 +96,7 @@ Optionally, set the `$VERSION` environment variable to change the image tag or ` ```bash make container ``` -_Note: To build build container images for both `velero` and `velero-restore-helper`, run: `make all-containers`_ +_Note: To build container images for both `velero` and `velero-restore-helper`, run: `make all-containers`_ ### Publishing container images to a registry diff --git a/site/content/docs/v1.14/build-from-source.md b/site/content/docs/v1.14/build-from-source.md index 083cdc202..798800036 100644 --- a/site/content/docs/v1.14/build-from-source.md +++ b/site/content/docs/v1.14/build-from-source.md @@ -96,7 +96,7 @@ Optionally, set the `$VERSION` environment variable to change the image tag or ` ```bash make container ``` -_Note: To build build container images for both `velero` and `velero-restore-helper`, run: `make all-containers`_ +_Note: To build container images for both `velero` and `velero-restore-helper`, run: `make all-containers`_ ### Publishing container images to a registry From 8eac3606d9adb63822efaa3225b2e399b758b7de Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Mon, 12 Aug 2024 16:55:36 -0700 Subject: [PATCH 52/69] Add support for backup PVC configuration Signed-off-by: Shubham Pampattiwar add changelog file Signed-off-by: Shubham Pampattiwar make update Signed-off-by: Shubham Pampattiwar pass backupPVCConfig to exposer as part of csi params Signed-off-by: Shubham Pampattiwar --- .../unreleased/8109-shubham-pampattiwar | 1 + pkg/cmd/cli/nodeagent/server.go | 8 +- pkg/controller/data_upload_controller.go | 5 +- pkg/controller/data_upload_controller_test.go | 4 +- pkg/exposer/csi_snapshot.go | 25 ++- pkg/exposer/csi_snapshot_test.go | 147 ++++++++++++++++++ pkg/nodeagent/node_agent.go | 11 ++ 7 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 changelogs/unreleased/8109-shubham-pampattiwar diff --git a/changelogs/unreleased/8109-shubham-pampattiwar b/changelogs/unreleased/8109-shubham-pampattiwar new file mode 100644 index 000000000..db84fc0c6 --- /dev/null +++ b/changelogs/unreleased/8109-shubham-pampattiwar @@ -0,0 +1 @@ +Add support for backup PVC configuration diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 181afbf69..935e97250 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -292,7 +292,13 @@ func (s *nodeAgentServer) run() { if s.dataPathConfigs != nil && len(s.dataPathConfigs.LoadAffinity) > 0 { loadAffinity = s.dataPathConfigs.LoadAffinity[0] } - dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + + var backupPVCConfig map[string]nodeagent.BackupPVC + if s.dataPathConfigs != nil && s.dataPathConfigs.BackupPVCConfig != nil { + backupPVCConfig = s.dataPathConfigs.BackupPVCConfig + } + + dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, backupPVCConfig, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataUploadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data upload controller") } diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 4660e5b2f..520e48795 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -78,12 +78,13 @@ type DataUploadReconciler struct { snapshotExposerList map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer dataPathMgr *datapath.Manager loadAffinity *nodeagent.LoadAffinity + backupPVCConfig map[string]nodeagent.BackupPVC preparingTimeout time.Duration metrics *metrics.ServerMetrics } func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, - dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, repoEnsurer *repository.Ensurer, clock clocks.WithTickerAndDelayedExecution, + dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, backupPVCConfig map[string]nodeagent.BackupPVC, repoEnsurer *repository.Ensurer, clock clocks.WithTickerAndDelayedExecution, cred *credentials.CredentialGetter, nodeName string, fs filesystem.Interface, preparingTimeout time.Duration, log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { return &DataUploadReconciler{ client: client, @@ -99,6 +100,7 @@ func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClie snapshotExposerList: map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer{velerov2alpha1api.SnapshotTypeCSI: exposer.NewCSISnapshotExposer(kubeClient, csiSnapshotClient, log)}, dataPathMgr: dataPathMgr, loadAffinity: loadAffinity, + backupPVCConfig: backupPVCConfig, preparingTimeout: preparingTimeout, metrics: metrics, } @@ -788,6 +790,7 @@ func (r *DataUploadReconciler) setupExposeParam(du *velerov2alpha1api.DataUpload ExposeTimeout: r.preparingTimeout, VolumeSize: pvc.Spec.Resources.Requests[corev1.ResourceStorage], Affinity: r.loadAffinity, + BackupPVCConfig: r.backupPVCConfig, }, nil } return nil, nil diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index 4024a714d..2a8d55010 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -22,6 +22,8 @@ import ( "testing" "time" + "github.com/vmware-tanzu/velero/pkg/nodeagent" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1" snapshotFake "github.com/kubernetes-csi/external-snapshotter/client/v7/clientset/versioned/fake" "github.com/pkg/errors" @@ -245,7 +247,7 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci if err != nil { return nil, err } - return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, nil, + return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, map[string]nodeagent.BackupPVC{}, nil, testclocks.NewFakeClock(now), &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", fakeFS, time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } diff --git a/pkg/exposer/csi_snapshot.go b/pkg/exposer/csi_snapshot.go index 28c29e197..cd0450ad5 100644 --- a/pkg/exposer/csi_snapshot.go +++ b/pkg/exposer/csi_snapshot.go @@ -67,6 +67,9 @@ type CSISnapshotExposeParam struct { // Affinity specifies the node affinity of the backup pod Affinity *nodeagent.LoadAffinity + + // BackupPVCConfig is the config for backupPVC (intermediate PVC) of snapshot data movement + BackupPVCConfig map[string]nodeagent.BackupPVC } // CSISnapshotExposeWaitParam define the input param for WaitExposed of CSI snapshots @@ -163,7 +166,17 @@ func (e *csiSnapshotExposer) Expose(ctx context.Context, ownerObject corev1.Obje curLog.WithField("vs name", volumeSnapshot.Name).Warnf("The snapshot doesn't contain a valid restore size, use source volume's size %v", volumeSize) } - backupPVC, err := e.createBackupPVC(ctx, ownerObject, backupVS.Name, csiExposeParam.StorageClass, csiExposeParam.AccessMode, volumeSize) + // check if there is a mapping for source pvc storage class in backupPVC config + // if the mapping exists then use the values(storage class, readOnly accessMode) + // for backupPVC (intermediate PVC in snapshot data movement) object creation + backupPVCStorageClass := csiExposeParam.StorageClass + backupPVCReadOnly := false + if value, exists := csiExposeParam.BackupPVCConfig[csiExposeParam.StorageClass]; exists { + backupPVCStorageClass = value.StorageClass + backupPVCReadOnly = value.ReadOnly + } + + backupPVC, err := e.createBackupPVC(ctx, ownerObject, backupVS.Name, backupPVCStorageClass, csiExposeParam.AccessMode, volumeSize, backupPVCReadOnly) if err != nil { return errors.Wrap(err, "error to create backup pvc") } @@ -347,7 +360,7 @@ func (e *csiSnapshotExposer) createBackupVSC(ctx context.Context, ownerObject co return e.csiSnapshotClient.VolumeSnapshotContents().Create(ctx, vsc, metav1.CreateOptions{}) } -func (e *csiSnapshotExposer) createBackupPVC(ctx context.Context, ownerObject corev1.ObjectReference, backupVS, storageClass, accessMode string, resource resource.Quantity) (*corev1.PersistentVolumeClaim, error) { +func (e *csiSnapshotExposer) createBackupPVC(ctx context.Context, ownerObject corev1.ObjectReference, backupVS, storageClass, accessMode string, resource resource.Quantity, readOnly bool) (*corev1.PersistentVolumeClaim, error) { backupPVCName := ownerObject.Name volumeMode, err := getVolumeModeByAccessMode(accessMode) @@ -355,6 +368,12 @@ func (e *csiSnapshotExposer) createBackupPVC(ctx context.Context, ownerObject co return nil, err } + pvcAccessMode := corev1.ReadWriteOnce + + if readOnly { + pvcAccessMode = corev1.ReadOnlyMany + } + dataSource := &corev1.TypedLocalObjectReference{ APIGroup: &snapshotv1api.SchemeGroupVersion.Group, Kind: "VolumeSnapshot", @@ -377,7 +396,7 @@ func (e *csiSnapshotExposer) createBackupPVC(ctx context.Context, ownerObject co }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, + pvcAccessMode, }, StorageClassName: &storageClass, VolumeMode: &volumeMode, diff --git a/pkg/exposer/csi_snapshot_test.go b/pkg/exposer/csi_snapshot_test.go index e11102294..82044dbb8 100644 --- a/pkg/exposer/csi_snapshot_test.go +++ b/pkg/exposer/csi_snapshot_test.go @@ -18,10 +18,13 @@ package exposer import ( "context" + "fmt" "reflect" "testing" "time" + "k8s.io/utils/pointer" + snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v7/apis/volumesnapshot/v1" snapshotFake "github.com/kubernetes-csi/external-snapshotter/client/v7/clientset/versioned/fake" "github.com/pkg/errors" @@ -821,3 +824,147 @@ func TestToSystemAffinity(t *testing.T) { }) } } + +func Test_csiSnapshotExposer_createBackupPVC(t *testing.T) { + backup := &velerov1.Backup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: velerov1.SchemeGroupVersion.String(), + Kind: "Backup", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1.DefaultNamespace, + Name: "fake-backup", + UID: "fake-uid", + }, + } + + dataSource := &corev1.TypedLocalObjectReference{ + APIGroup: &snapshotv1api.SchemeGroupVersion.Group, + Kind: "VolumeSnapshot", + Name: "fake-snapshot", + } + volumeMode := corev1.PersistentVolumeFilesystem + + backupPVC := corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1.DefaultNamespace, + Name: "fake-backup", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: backup.APIVersion, + Kind: backup.Kind, + Name: backup.Name, + UID: backup.UID, + Controller: pointer.BoolPtr(true), + }, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadWriteOnce, + }, + VolumeMode: &volumeMode, + DataSource: dataSource, + DataSourceRef: nil, + StorageClassName: pointer.String("fake-storage-class"), + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + } + + backupPVCReadOnly := corev1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: velerov1.DefaultNamespace, + Name: "fake-backup", + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: backup.APIVersion, + Kind: backup.Kind, + Name: backup.Name, + UID: backup.UID, + Controller: pointer.BoolPtr(true), + }, + }, + }, + Spec: corev1.PersistentVolumeClaimSpec{ + AccessModes: []corev1.PersistentVolumeAccessMode{ + corev1.ReadOnlyMany, + }, + VolumeMode: &volumeMode, + DataSource: dataSource, + DataSourceRef: nil, + StorageClassName: pointer.String("fake-storage-class"), + Resources: corev1.VolumeResourceRequirements{ + Requests: corev1.ResourceList{ + corev1.ResourceStorage: resource.MustParse("1Gi"), + }, + }, + }, + } + + tests := []struct { + name string + ownerBackup *velerov1.Backup + backupVS string + storageClass string + accessMode string + resource resource.Quantity + readOnly bool + kubeClientObj []runtime.Object + snapshotClientObj []runtime.Object + want *corev1.PersistentVolumeClaim + wantErr assert.ErrorAssertionFunc + }{ + { + name: "backupPVC gets created successfully with parameters from source PVC", + ownerBackup: backup, + backupVS: "fake-snapshot", + storageClass: "fake-storage-class", + accessMode: AccessModeFileSystem, + resource: resource.MustParse("1Gi"), + readOnly: false, + want: &backupPVC, + wantErr: assert.NoError, + }, + { + name: "backupPVC gets created successfully with parameters from source PVC but accessMode from backupPVC Config as read only", + ownerBackup: backup, + backupVS: "fake-snapshot", + storageClass: "fake-storage-class", + accessMode: AccessModeFileSystem, + resource: resource.MustParse("1Gi"), + readOnly: true, + want: &backupPVCReadOnly, + wantErr: assert.NoError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fakeKubeClient := fake.NewSimpleClientset(tt.kubeClientObj...) + fakeSnapshotClient := snapshotFake.NewSimpleClientset(tt.snapshotClientObj...) + e := &csiSnapshotExposer{ + kubeClient: fakeKubeClient, + csiSnapshotClient: fakeSnapshotClient.SnapshotV1(), + log: velerotest.NewLogger(), + } + var ownerObject corev1.ObjectReference + if tt.ownerBackup != nil { + ownerObject = corev1.ObjectReference{ + Kind: tt.ownerBackup.Kind, + Namespace: tt.ownerBackup.Namespace, + Name: tt.ownerBackup.Name, + UID: tt.ownerBackup.UID, + APIVersion: tt.ownerBackup.APIVersion, + } + } + got, err := e.createBackupPVC(context.Background(), ownerObject, tt.backupVS, tt.storageClass, tt.accessMode, tt.resource, tt.readOnly) + if !tt.wantErr(t, err, fmt.Sprintf("createBackupPVC(%v, %v, %v, %v, %v, %v)", ownerObject, tt.backupVS, tt.storageClass, tt.accessMode, tt.resource, tt.readOnly)) { + return + } + assert.Equalf(t, tt.want, got, "createBackupPVC(%v, %v, %v, %v, %v, %v)", ownerObject, tt.backupVS, tt.storageClass, tt.accessMode, tt.resource, tt.readOnly) + }) + } +} diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index e3597e27a..483b29565 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -63,12 +63,23 @@ type RuledConfigs struct { Number int `json:"number"` } +type BackupPVC struct { + // StorageClass is the name of storage class to be used by the backupPVC + StorageClass string `json:"storageClass,omitempty"` + + // ReadOnly sets the backupPVC's access mode as read only + ReadOnly bool `json:"readOnly,omitempty"` +} + type Configs struct { // LoadConcurrency is the config for data path load concurrency per node. LoadConcurrency *LoadConcurrency `json:"loadConcurrency,omitempty"` // LoadAffinity is the config for data path load affinity. LoadAffinity []*LoadAffinity `json:"loadAffinity,omitempty"` + + // BackupPVCConfig is the config for backupPVC (intermediate PVC) of snapshot data movement + BackupPVCConfig map[string]BackupPVC `json:"backupPVC,omitempty"` } // IsRunning checks if the node agent daemonset is running properly. If not, return the error found From 4ffc6d17b2207b02aba3a56102dfd2fcfc580251 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 14 Aug 2024 10:32:04 +0800 Subject: [PATCH 53/69] Delete the pkg/generated directory. Signed-off-by: Xun Jiang --- changelogs/unreleased/8114-blackpiglet | 1 + .../clientset/versioned/clientset.go | 111 -------- pkg/generated/clientset/versioned/doc.go | 20 -- .../versioned/fake/clientset_generated.go | 92 ------- pkg/generated/clientset/versioned/fake/doc.go | 20 -- .../clientset/versioned/fake/register.go | 58 ---- .../clientset/versioned/mocks/Interface.go | 79 ------ .../clientset/versioned/scheme/doc.go | 20 -- .../clientset/versioned/scheme/register.go | 58 ---- .../versioned/typed/velero/v1/backup.go | 195 -------------- .../typed/velero/v1/backuprepository.go | 195 -------------- .../typed/velero/v1/backupstoragelocation.go | 195 -------------- .../typed/velero/v1/deletebackuprequest.go | 195 -------------- .../versioned/typed/velero/v1/doc.go | 20 -- .../typed/velero/v1/downloadrequest.go | 195 -------------- .../versioned/typed/velero/v1/fake/doc.go | 20 -- .../typed/velero/v1/fake/fake_backup.go | 142 ---------- .../velero/v1/fake/fake_backuprepository.go | 142 ---------- .../v1/fake/fake_backupstoragelocation.go | 142 ---------- .../v1/fake/fake_deletebackuprequest.go | 142 ---------- .../velero/v1/fake/fake_downloadrequest.go | 142 ---------- .../velero/v1/fake/fake_podvolumebackup.go | 142 ---------- .../velero/v1/fake/fake_podvolumerestore.go | 142 ---------- .../typed/velero/v1/fake/fake_restore.go | 142 ---------- .../typed/velero/v1/fake/fake_schedule.go | 142 ---------- .../v1/fake/fake_serverstatusrequest.go | 142 ---------- .../velero/v1/fake/fake_velero_client.go | 80 ------ .../v1/fake/fake_volumesnapshotlocation.go | 142 ---------- .../typed/velero/v1/generated_expansion.go | 41 --- .../typed/velero/v1/mocks/BackupInterface.go | 252 ------------------ .../typed/velero/v1/mocks/BackupsGetter.go | 43 --- .../v1/mocks/DeleteBackupRequestInterface.go | 252 ------------------ .../v1/mocks/DeleteBackupRequestsGetter.go | 43 --- .../v1/mocks/PodVolumeBackupInterface.go | 252 ------------------ .../velero/v1/mocks/ScheduleInterface.go | 252 ------------------ .../typed/velero/v1/mocks/SchedulesGetter.go | 43 --- .../velero/v1/mocks/VeleroV1Interface.go | 221 --------------- .../mocks/VolumeSnapshotLocationInterface.go | 252 ------------------ .../v1/mocks/VolumeSnapshotLocationsGetter.go | 43 --- .../typed/velero/v1/podvolumebackup.go | 195 -------------- .../typed/velero/v1/podvolumerestore.go | 195 -------------- .../versioned/typed/velero/v1/restore.go | 195 -------------- .../versioned/typed/velero/v1/schedule.go | 195 -------------- .../typed/velero/v1/serverstatusrequest.go | 195 -------------- .../typed/velero/v1/velero_client.go | 139 ---------- .../typed/velero/v1/volumesnapshotlocation.go | 195 -------------- .../typed/velero/v2alpha1/datadownload.go | 195 -------------- .../typed/velero/v2alpha1/dataupload.go | 195 -------------- .../versioned/typed/velero/v2alpha1/doc.go | 20 -- .../typed/velero/v2alpha1/fake/doc.go | 20 -- .../velero/v2alpha1/fake/fake_datadownload.go | 142 ---------- .../velero/v2alpha1/fake/fake_dataupload.go | 142 ---------- .../v2alpha1/fake/fake_velero_client.go | 44 --- .../velero/v2alpha1/generated_expansion.go | 23 -- .../typed/velero/v2alpha1/velero_client.go | 94 ------- .../informers/externalversions/factory.go | 180 ------------- .../informers/externalversions/generic.go | 89 ------- .../internalinterfaces/factory_interfaces.go | 40 --- .../externalversions/velero/interface.go | 54 ---- .../externalversions/velero/v1/backup.go | 90 ------- .../velero/v1/backuprepository.go | 90 ------- .../velero/v1/backupstoragelocation.go | 90 ------- .../velero/v1/deletebackuprequest.go | 90 ------- .../velero/v1/downloadrequest.go | 90 ------- .../externalversions/velero/v1/interface.go | 115 -------- .../velero/v1/podvolumebackup.go | 90 ------- .../velero/v1/podvolumerestore.go | 90 ------- .../externalversions/velero/v1/restore.go | 90 ------- .../externalversions/velero/v1/schedule.go | 90 ------- .../velero/v1/serverstatusrequest.go | 90 ------- .../velero/v1/volumesnapshotlocation.go | 90 ------- .../velero/v2alpha1/datadownload.go | 90 ------- .../velero/v2alpha1/dataupload.go | 90 ------- .../velero/v2alpha1/interface.go | 52 ---- pkg/generated/listers/velero/v1/backup.go | 99 ------- .../listers/velero/v1/backuprepository.go | 99 ------- .../velero/v1/backupstoragelocation.go | 99 ------- .../listers/velero/v1/deletebackuprequest.go | 99 ------- .../listers/velero/v1/downloadrequest.go | 99 ------- .../listers/velero/v1/expansion_generated.go | 107 -------- .../listers/velero/v1/podvolumebackup.go | 99 ------- .../listers/velero/v1/podvolumerestore.go | 99 ------- pkg/generated/listers/velero/v1/restore.go | 99 ------- pkg/generated/listers/velero/v1/schedule.go | 99 ------- .../listers/velero/v1/serverstatusrequest.go | 99 ------- .../velero/v1/volumesnapshotlocation.go | 99 ------- .../listers/velero/v2alpha1/datadownload.go | 99 ------- .../listers/velero/v2alpha1/dataupload.go | 99 ------- .../velero/v2alpha1/expansion_generated.go | 35 --- 89 files changed, 1 insertion(+), 10122 deletions(-) create mode 100644 changelogs/unreleased/8114-blackpiglet delete mode 100644 pkg/generated/clientset/versioned/clientset.go delete mode 100644 pkg/generated/clientset/versioned/doc.go delete mode 100644 pkg/generated/clientset/versioned/fake/clientset_generated.go delete mode 100644 pkg/generated/clientset/versioned/fake/doc.go delete mode 100644 pkg/generated/clientset/versioned/fake/register.go delete mode 100644 pkg/generated/clientset/versioned/mocks/Interface.go delete mode 100644 pkg/generated/clientset/versioned/scheme/doc.go delete mode 100644 pkg/generated/clientset/versioned/scheme/register.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/backup.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/deletebackuprequest.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/doc.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/downloadrequest.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/doc.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backup.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_deletebackuprequest.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_downloadrequest.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumebackup.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumerestore.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_restore.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_schedule.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_serverstatusrequest.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_volumesnapshotlocation.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupInterface.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupsGetter.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestInterface.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestsGetter.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/PodVolumeBackupInterface.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/ScheduleInterface.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/SchedulesGetter.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/VeleroV1Interface.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationInterface.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationsGetter.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/podvolumebackup.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/podvolumerestore.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/restore.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/schedule.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/serverstatusrequest.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v1/volumesnapshotlocation.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go delete mode 100644 pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.go delete mode 100644 pkg/generated/informers/externalversions/factory.go delete mode 100644 pkg/generated/informers/externalversions/generic.go delete mode 100644 pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go delete mode 100644 pkg/generated/informers/externalversions/velero/interface.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/backup.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/backuprepository.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/deletebackuprequest.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/downloadrequest.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/interface.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/podvolumebackup.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/podvolumerestore.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/restore.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/schedule.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/serverstatusrequest.go delete mode 100644 pkg/generated/informers/externalversions/velero/v1/volumesnapshotlocation.go delete mode 100644 pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go delete mode 100644 pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go delete mode 100644 pkg/generated/informers/externalversions/velero/v2alpha1/interface.go delete mode 100644 pkg/generated/listers/velero/v1/backup.go delete mode 100644 pkg/generated/listers/velero/v1/backuprepository.go delete mode 100644 pkg/generated/listers/velero/v1/backupstoragelocation.go delete mode 100644 pkg/generated/listers/velero/v1/deletebackuprequest.go delete mode 100644 pkg/generated/listers/velero/v1/downloadrequest.go delete mode 100644 pkg/generated/listers/velero/v1/expansion_generated.go delete mode 100644 pkg/generated/listers/velero/v1/podvolumebackup.go delete mode 100644 pkg/generated/listers/velero/v1/podvolumerestore.go delete mode 100644 pkg/generated/listers/velero/v1/restore.go delete mode 100644 pkg/generated/listers/velero/v1/schedule.go delete mode 100644 pkg/generated/listers/velero/v1/serverstatusrequest.go delete mode 100644 pkg/generated/listers/velero/v1/volumesnapshotlocation.go delete mode 100644 pkg/generated/listers/velero/v2alpha1/datadownload.go delete mode 100644 pkg/generated/listers/velero/v2alpha1/dataupload.go delete mode 100644 pkg/generated/listers/velero/v2alpha1/expansion_generated.go diff --git a/changelogs/unreleased/8114-blackpiglet b/changelogs/unreleased/8114-blackpiglet new file mode 100644 index 000000000..d068ff437 --- /dev/null +++ b/changelogs/unreleased/8114-blackpiglet @@ -0,0 +1 @@ +Delete generated k8s client and informer. \ No newline at end of file diff --git a/pkg/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go deleted file mode 100644 index 881dee994..000000000 --- a/pkg/generated/clientset/versioned/clientset.go +++ /dev/null @@ -1,111 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package versioned - -import ( - "fmt" - - velerov1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" - discovery "k8s.io/client-go/discovery" - rest "k8s.io/client-go/rest" - flowcontrol "k8s.io/client-go/util/flowcontrol" -) - -type Interface interface { - Discovery() discovery.DiscoveryInterface - VeleroV1() velerov1.VeleroV1Interface - VeleroV2alpha1() velerov2alpha1.VeleroV2alpha1Interface -} - -// Clientset contains the clients for groups. Each group has exactly one -// version included in a Clientset. -type Clientset struct { - *discovery.DiscoveryClient - veleroV1 *velerov1.VeleroV1Client - veleroV2alpha1 *velerov2alpha1.VeleroV2alpha1Client -} - -// VeleroV1 retrieves the VeleroV1Client -func (c *Clientset) VeleroV1() velerov1.VeleroV1Interface { - return c.veleroV1 -} - -// VeleroV2alpha1 retrieves the VeleroV2alpha1Client -func (c *Clientset) VeleroV2alpha1() velerov2alpha1.VeleroV2alpha1Interface { - return c.veleroV2alpha1 -} - -// Discovery retrieves the DiscoveryClient -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - if c == nil { - return nil - } - return c.DiscoveryClient -} - -// NewForConfig creates a new Clientset for the given config. -// If config's RateLimiter is not set and QPS and Burst are acceptable, -// NewForConfig will generate a rate-limiter in configShallowCopy. -func NewForConfig(c *rest.Config) (*Clientset, error) { - configShallowCopy := *c - if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { - if configShallowCopy.Burst <= 0 { - return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") - } - configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) - } - var cs Clientset - var err error - cs.veleroV1, err = velerov1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - cs.veleroV2alpha1, err = velerov2alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - - cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - return &cs, nil -} - -// NewForConfigOrDie creates a new Clientset for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *Clientset { - var cs Clientset - cs.veleroV1 = velerov1.NewForConfigOrDie(c) - cs.veleroV2alpha1 = velerov2alpha1.NewForConfigOrDie(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) - return &cs -} - -// New creates a new Clientset for the given RESTClient. -func New(c rest.Interface) *Clientset { - var cs Clientset - cs.veleroV1 = velerov1.New(c) - cs.veleroV2alpha1 = velerov2alpha1.New(c) - - cs.DiscoveryClient = discovery.NewDiscoveryClient(c) - return &cs -} diff --git a/pkg/generated/clientset/versioned/doc.go b/pkg/generated/clientset/versioned/doc.go deleted file mode 100644 index 95ffaaafa..000000000 --- a/pkg/generated/clientset/versioned/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated clientset. -package versioned diff --git a/pkg/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go deleted file mode 100644 index d514b27c1..000000000 --- a/pkg/generated/clientset/versioned/fake/clientset_generated.go +++ /dev/null @@ -1,92 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - velerov1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - fakevelerov1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1/fake" - velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" - fakevelerov2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/discovery" - fakediscovery "k8s.io/client-go/discovery/fake" - "k8s.io/client-go/testing" -) - -// NewSimpleClientset returns a clientset that will respond with the provided objects. -// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement -// for a real clientset and is mostly useful in simple unit tests. -func NewSimpleClientset(objects ...runtime.Object) *Clientset { - o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) - for _, obj := range objects { - if err := o.Add(obj); err != nil { - panic(err) - } - } - - cs := &Clientset{tracker: o} - cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} - cs.AddReactor("*", "*", testing.ObjectReaction(o)) - cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { - gvr := action.GetResource() - ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) - if err != nil { - return false, nil, err - } - return true, watch, nil - }) - - return cs -} - -// Clientset implements clientset.Interface. Meant to be embedded into a -// struct to get a default implementation. This makes faking out just the method -// you want to test easier. -type Clientset struct { - testing.Fake - discovery *fakediscovery.FakeDiscovery - tracker testing.ObjectTracker -} - -func (c *Clientset) Discovery() discovery.DiscoveryInterface { - return c.discovery -} - -func (c *Clientset) Tracker() testing.ObjectTracker { - return c.tracker -} - -var ( - _ clientset.Interface = &Clientset{} - _ testing.FakeClient = &Clientset{} -) - -// VeleroV1 retrieves the VeleroV1Client -func (c *Clientset) VeleroV1() velerov1.VeleroV1Interface { - return &fakevelerov1.FakeVeleroV1{Fake: &c.Fake} -} - -// VeleroV2alpha1 retrieves the VeleroV2alpha1Client -func (c *Clientset) VeleroV2alpha1() velerov2alpha1.VeleroV2alpha1Interface { - return &fakevelerov2alpha1.FakeVeleroV2alpha1{Fake: &c.Fake} -} diff --git a/pkg/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go deleted file mode 100644 index 1403ef8c7..000000000 --- a/pkg/generated/clientset/versioned/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated fake clientset. -package fake diff --git a/pkg/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go deleted file mode 100644 index 8e9316a47..000000000 --- a/pkg/generated/clientset/versioned/fake/register.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var scheme = runtime.NewScheme() -var codecs = serializer.NewCodecFactory(scheme) - -var localSchemeBuilder = runtime.SchemeBuilder{ - velerov1.AddToScheme, - velerov2alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(scheme)) -} diff --git a/pkg/generated/clientset/versioned/mocks/Interface.go b/pkg/generated/clientset/versioned/mocks/Interface.go deleted file mode 100644 index 4544cbc4d..000000000 --- a/pkg/generated/clientset/versioned/mocks/Interface.go +++ /dev/null @@ -1,79 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - discovery "k8s.io/client-go/discovery" - - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - - v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" -) - -// Interface is an autogenerated mock type for the Interface type -type Interface struct { - mock.Mock -} - -// Discovery provides a mock function with given fields: -func (_m *Interface) Discovery() discovery.DiscoveryInterface { - ret := _m.Called() - - var r0 discovery.DiscoveryInterface - if rf, ok := ret.Get(0).(func() discovery.DiscoveryInterface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(discovery.DiscoveryInterface) - } - } - - return r0 -} - -// VeleroV1 provides a mock function with given fields: -func (_m *Interface) VeleroV1() v1.VeleroV1Interface { - ret := _m.Called() - - var r0 v1.VeleroV1Interface - if rf, ok := ret.Get(0).(func() v1.VeleroV1Interface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.VeleroV1Interface) - } - } - - return r0 -} - -// VeleroV2alpha1 provides a mock function with given fields: -func (_m *Interface) VeleroV2alpha1() v2alpha1.VeleroV2alpha1Interface { - ret := _m.Called() - - var r0 v2alpha1.VeleroV2alpha1Interface - if rf, ok := ret.Get(0).(func() v2alpha1.VeleroV2alpha1Interface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v2alpha1.VeleroV2alpha1Interface) - } - } - - return r0 -} - -// NewInterface creates a new instance of Interface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *Interface { - mock := &Interface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go deleted file mode 100644 index 927fc4f47..000000000 --- a/pkg/generated/clientset/versioned/scheme/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package contains the scheme of the automatically generated clientset. -package scheme diff --git a/pkg/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go deleted file mode 100644 index 12654733e..000000000 --- a/pkg/generated/clientset/versioned/scheme/register.go +++ /dev/null @@ -1,58 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package scheme - -import ( - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" -) - -var Scheme = runtime.NewScheme() -var Codecs = serializer.NewCodecFactory(Scheme) -var ParameterCodec = runtime.NewParameterCodec(Scheme) -var localSchemeBuilder = runtime.SchemeBuilder{ - velerov1.AddToScheme, - velerov2alpha1.AddToScheme, -} - -// AddToScheme adds all types of this clientset into the given scheme. This allows composition -// of clientsets, like in: -// -// import ( -// "k8s.io/client-go/kubernetes" -// clientsetscheme "k8s.io/client-go/kubernetes/scheme" -// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" -// ) -// -// kclientset, _ := kubernetes.NewForConfig(c) -// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) -// -// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types -// correctly. -var AddToScheme = localSchemeBuilder.AddToScheme - -func init() { - v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) - utilruntime.Must(AddToScheme(Scheme)) -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/backup.go b/pkg/generated/clientset/versioned/typed/velero/v1/backup.go deleted file mode 100644 index 420bfc5c9..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/backup.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// BackupsGetter has a method to return a BackupInterface. -// A group's client should implement this interface. -type BackupsGetter interface { - Backups(namespace string) BackupInterface -} - -// BackupInterface has methods to work with Backup resources. -type BackupInterface interface { - Create(ctx context.Context, backup *v1.Backup, opts metav1.CreateOptions) (*v1.Backup, error) - Update(ctx context.Context, backup *v1.Backup, opts metav1.UpdateOptions) (*v1.Backup, error) - UpdateStatus(ctx context.Context, backup *v1.Backup, opts metav1.UpdateOptions) (*v1.Backup, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Backup, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.BackupList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Backup, err error) - BackupExpansion -} - -// backups implements BackupInterface -type backups struct { - client rest.Interface - ns string -} - -// newBackups returns a Backups -func newBackups(c *VeleroV1Client, namespace string) *backups { - return &backups{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the backup, and returns the corresponding backup object, and an error if there is any. -func (c *backups) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Backup, err error) { - result = &v1.Backup{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backups"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Backups that match those selectors. -func (c *backups) List(ctx context.Context, opts metav1.ListOptions) (result *v1.BackupList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.BackupList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backups"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested backups. -func (c *backups) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("backups"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a backup and creates it. Returns the server's representation of the backup, and an error, if there is any. -func (c *backups) Create(ctx context.Context, backup *v1.Backup, opts metav1.CreateOptions) (result *v1.Backup, err error) { - result = &v1.Backup{} - err = c.client.Post(). - Namespace(c.ns). - Resource("backups"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backup). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a backup and updates it. Returns the server's representation of the backup, and an error, if there is any. -func (c *backups) Update(ctx context.Context, backup *v1.Backup, opts metav1.UpdateOptions) (result *v1.Backup, err error) { - result = &v1.Backup{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backups"). - Name(backup.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backup). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *backups) UpdateStatus(ctx context.Context, backup *v1.Backup, opts metav1.UpdateOptions) (result *v1.Backup, err error) { - result = &v1.Backup{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backups"). - Name(backup.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backup). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the backup and deletes it. Returns an error if one occurs. -func (c *backups) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("backups"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *backups) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("backups"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched backup. -func (c *backups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Backup, err error) { - result = &v1.Backup{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("backups"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go deleted file mode 100644 index 7ecef6dcf..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// BackupRepositoriesGetter has a method to return a BackupRepositoryInterface. -// A group's client should implement this interface. -type BackupRepositoriesGetter interface { - BackupRepositories(namespace string) BackupRepositoryInterface -} - -// BackupRepositoryInterface has methods to work with BackupRepository resources. -type BackupRepositoryInterface interface { - Create(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.CreateOptions) (*v1.BackupRepository, error) - Update(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (*v1.BackupRepository, error) - UpdateStatus(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (*v1.BackupRepository, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.BackupRepository, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.BackupRepositoryList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupRepository, err error) - BackupRepositoryExpansion -} - -// backupRepositories implements BackupRepositoryInterface -type backupRepositories struct { - client rest.Interface - ns string -} - -// newBackupRepositories returns a BackupRepositories -func newBackupRepositories(c *VeleroV1Client, namespace string) *backupRepositories { - return &backupRepositories{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the backupRepository, and returns the corresponding backupRepository object, and an error if there is any. -func (c *backupRepositories) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.BackupRepository, err error) { - result = &v1.BackupRepository{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backuprepositories"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of BackupRepositories that match those selectors. -func (c *backupRepositories) List(ctx context.Context, opts metav1.ListOptions) (result *v1.BackupRepositoryList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.BackupRepositoryList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backuprepositories"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested backupRepositories. -func (c *backupRepositories) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("backuprepositories"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a backupRepository and creates it. Returns the server's representation of the backupRepository, and an error, if there is any. -func (c *backupRepositories) Create(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.CreateOptions) (result *v1.BackupRepository, err error) { - result = &v1.BackupRepository{} - err = c.client.Post(). - Namespace(c.ns). - Resource("backuprepositories"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backupRepository). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a backupRepository and updates it. Returns the server's representation of the backupRepository, and an error, if there is any. -func (c *backupRepositories) Update(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (result *v1.BackupRepository, err error) { - result = &v1.BackupRepository{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backuprepositories"). - Name(backupRepository.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backupRepository). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *backupRepositories) UpdateStatus(ctx context.Context, backupRepository *v1.BackupRepository, opts metav1.UpdateOptions) (result *v1.BackupRepository, err error) { - result = &v1.BackupRepository{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backuprepositories"). - Name(backupRepository.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backupRepository). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the backupRepository and deletes it. Returns an error if one occurs. -func (c *backupRepositories) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("backuprepositories"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *backupRepositories) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("backuprepositories"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched backupRepository. -func (c *backupRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupRepository, err error) { - result = &v1.BackupRepository{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("backuprepositories"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go deleted file mode 100644 index 352c08ad2..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/backupstoragelocation.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// BackupStorageLocationsGetter has a method to return a BackupStorageLocationInterface. -// A group's client should implement this interface. -type BackupStorageLocationsGetter interface { - BackupStorageLocations(namespace string) BackupStorageLocationInterface -} - -// BackupStorageLocationInterface has methods to work with BackupStorageLocation resources. -type BackupStorageLocationInterface interface { - Create(ctx context.Context, backupStorageLocation *v1.BackupStorageLocation, opts metav1.CreateOptions) (*v1.BackupStorageLocation, error) - Update(ctx context.Context, backupStorageLocation *v1.BackupStorageLocation, opts metav1.UpdateOptions) (*v1.BackupStorageLocation, error) - UpdateStatus(ctx context.Context, backupStorageLocation *v1.BackupStorageLocation, opts metav1.UpdateOptions) (*v1.BackupStorageLocation, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.BackupStorageLocation, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.BackupStorageLocationList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupStorageLocation, err error) - BackupStorageLocationExpansion -} - -// backupStorageLocations implements BackupStorageLocationInterface -type backupStorageLocations struct { - client rest.Interface - ns string -} - -// newBackupStorageLocations returns a BackupStorageLocations -func newBackupStorageLocations(c *VeleroV1Client, namespace string) *backupStorageLocations { - return &backupStorageLocations{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the backupStorageLocation, and returns the corresponding backupStorageLocation object, and an error if there is any. -func (c *backupStorageLocations) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of BackupStorageLocations that match those selectors. -func (c *backupStorageLocations) List(ctx context.Context, opts metav1.ListOptions) (result *v1.BackupStorageLocationList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.BackupStorageLocationList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested backupStorageLocations. -func (c *backupStorageLocations) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a backupStorageLocation and creates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *backupStorageLocations) Create(ctx context.Context, backupStorageLocation *v1.BackupStorageLocation, opts metav1.CreateOptions) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Post(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backupStorageLocation). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a backupStorageLocation and updates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *backupStorageLocations) Update(ctx context.Context, backupStorageLocation *v1.BackupStorageLocation, opts metav1.UpdateOptions) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(backupStorageLocation.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backupStorageLocation). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *backupStorageLocations) UpdateStatus(ctx context.Context, backupStorageLocation *v1.BackupStorageLocation, opts metav1.UpdateOptions) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Put(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(backupStorageLocation.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(backupStorageLocation). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the backupStorageLocation and deletes it. Returns an error if one occurs. -func (c *backupStorageLocations) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *backupStorageLocations) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("backupstoragelocations"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched backupStorageLocation. -func (c *backupStorageLocations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupStorageLocation, err error) { - result = &v1.BackupStorageLocation{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("backupstoragelocations"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/deletebackuprequest.go b/pkg/generated/clientset/versioned/typed/velero/v1/deletebackuprequest.go deleted file mode 100644 index e713e4df9..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/deletebackuprequest.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// DeleteBackupRequestsGetter has a method to return a DeleteBackupRequestInterface. -// A group's client should implement this interface. -type DeleteBackupRequestsGetter interface { - DeleteBackupRequests(namespace string) DeleteBackupRequestInterface -} - -// DeleteBackupRequestInterface has methods to work with DeleteBackupRequest resources. -type DeleteBackupRequestInterface interface { - Create(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.CreateOptions) (*v1.DeleteBackupRequest, error) - Update(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.UpdateOptions) (*v1.DeleteBackupRequest, error) - UpdateStatus(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.UpdateOptions) (*v1.DeleteBackupRequest, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.DeleteBackupRequest, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.DeleteBackupRequestList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.DeleteBackupRequest, err error) - DeleteBackupRequestExpansion -} - -// deleteBackupRequests implements DeleteBackupRequestInterface -type deleteBackupRequests struct { - client rest.Interface - ns string -} - -// newDeleteBackupRequests returns a DeleteBackupRequests -func newDeleteBackupRequests(c *VeleroV1Client, namespace string) *deleteBackupRequests { - return &deleteBackupRequests{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the deleteBackupRequest, and returns the corresponding deleteBackupRequest object, and an error if there is any. -func (c *deleteBackupRequests) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.DeleteBackupRequest, err error) { - result = &v1.DeleteBackupRequest{} - err = c.client.Get(). - Namespace(c.ns). - Resource("deletebackuprequests"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of DeleteBackupRequests that match those selectors. -func (c *deleteBackupRequests) List(ctx context.Context, opts metav1.ListOptions) (result *v1.DeleteBackupRequestList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.DeleteBackupRequestList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("deletebackuprequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested deleteBackupRequests. -func (c *deleteBackupRequests) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("deletebackuprequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a deleteBackupRequest and creates it. Returns the server's representation of the deleteBackupRequest, and an error, if there is any. -func (c *deleteBackupRequests) Create(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.CreateOptions) (result *v1.DeleteBackupRequest, err error) { - result = &v1.DeleteBackupRequest{} - err = c.client.Post(). - Namespace(c.ns). - Resource("deletebackuprequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(deleteBackupRequest). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a deleteBackupRequest and updates it. Returns the server's representation of the deleteBackupRequest, and an error, if there is any. -func (c *deleteBackupRequests) Update(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.UpdateOptions) (result *v1.DeleteBackupRequest, err error) { - result = &v1.DeleteBackupRequest{} - err = c.client.Put(). - Namespace(c.ns). - Resource("deletebackuprequests"). - Name(deleteBackupRequest.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(deleteBackupRequest). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *deleteBackupRequests) UpdateStatus(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.UpdateOptions) (result *v1.DeleteBackupRequest, err error) { - result = &v1.DeleteBackupRequest{} - err = c.client.Put(). - Namespace(c.ns). - Resource("deletebackuprequests"). - Name(deleteBackupRequest.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(deleteBackupRequest). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the deleteBackupRequest and deletes it. Returns an error if one occurs. -func (c *deleteBackupRequests) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("deletebackuprequests"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *deleteBackupRequests) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("deletebackuprequests"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched deleteBackupRequest. -func (c *deleteBackupRequests) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.DeleteBackupRequest, err error) { - result = &v1.DeleteBackupRequest{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("deletebackuprequests"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/doc.go b/pkg/generated/clientset/versioned/typed/velero/v1/doc.go deleted file mode 100644 index d2243753c..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1 diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/downloadrequest.go b/pkg/generated/clientset/versioned/typed/velero/v1/downloadrequest.go deleted file mode 100644 index 68e5011f7..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/downloadrequest.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// DownloadRequestsGetter has a method to return a DownloadRequestInterface. -// A group's client should implement this interface. -type DownloadRequestsGetter interface { - DownloadRequests(namespace string) DownloadRequestInterface -} - -// DownloadRequestInterface has methods to work with DownloadRequest resources. -type DownloadRequestInterface interface { - Create(ctx context.Context, downloadRequest *v1.DownloadRequest, opts metav1.CreateOptions) (*v1.DownloadRequest, error) - Update(ctx context.Context, downloadRequest *v1.DownloadRequest, opts metav1.UpdateOptions) (*v1.DownloadRequest, error) - UpdateStatus(ctx context.Context, downloadRequest *v1.DownloadRequest, opts metav1.UpdateOptions) (*v1.DownloadRequest, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.DownloadRequest, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.DownloadRequestList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.DownloadRequest, err error) - DownloadRequestExpansion -} - -// downloadRequests implements DownloadRequestInterface -type downloadRequests struct { - client rest.Interface - ns string -} - -// newDownloadRequests returns a DownloadRequests -func newDownloadRequests(c *VeleroV1Client, namespace string) *downloadRequests { - return &downloadRequests{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the downloadRequest, and returns the corresponding downloadRequest object, and an error if there is any. -func (c *downloadRequests) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.DownloadRequest, err error) { - result = &v1.DownloadRequest{} - err = c.client.Get(). - Namespace(c.ns). - Resource("downloadrequests"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of DownloadRequests that match those selectors. -func (c *downloadRequests) List(ctx context.Context, opts metav1.ListOptions) (result *v1.DownloadRequestList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.DownloadRequestList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("downloadrequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested downloadRequests. -func (c *downloadRequests) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("downloadrequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a downloadRequest and creates it. Returns the server's representation of the downloadRequest, and an error, if there is any. -func (c *downloadRequests) Create(ctx context.Context, downloadRequest *v1.DownloadRequest, opts metav1.CreateOptions) (result *v1.DownloadRequest, err error) { - result = &v1.DownloadRequest{} - err = c.client.Post(). - Namespace(c.ns). - Resource("downloadrequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(downloadRequest). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a downloadRequest and updates it. Returns the server's representation of the downloadRequest, and an error, if there is any. -func (c *downloadRequests) Update(ctx context.Context, downloadRequest *v1.DownloadRequest, opts metav1.UpdateOptions) (result *v1.DownloadRequest, err error) { - result = &v1.DownloadRequest{} - err = c.client.Put(). - Namespace(c.ns). - Resource("downloadrequests"). - Name(downloadRequest.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(downloadRequest). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *downloadRequests) UpdateStatus(ctx context.Context, downloadRequest *v1.DownloadRequest, opts metav1.UpdateOptions) (result *v1.DownloadRequest, err error) { - result = &v1.DownloadRequest{} - err = c.client.Put(). - Namespace(c.ns). - Resource("downloadrequests"). - Name(downloadRequest.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(downloadRequest). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the downloadRequest and deletes it. Returns an error if one occurs. -func (c *downloadRequests) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("downloadrequests"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *downloadRequests) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("downloadrequests"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched downloadRequest. -func (c *downloadRequests) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.DownloadRequest, err error) { - result = &v1.DownloadRequest{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("downloadrequests"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/doc.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/doc.go deleted file mode 100644 index de930591e..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backup.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backup.go deleted file mode 100644 index 4045f4bb5..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backup.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeBackups implements BackupInterface -type FakeBackups struct { - Fake *FakeVeleroV1 - ns string -} - -var backupsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backups"} - -var backupsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "Backup"} - -// Get takes name of the backup, and returns the corresponding backup object, and an error if there is any. -func (c *FakeBackups) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.Backup, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(backupsResource, c.ns, name), &velerov1.Backup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Backup), err -} - -// List takes label and field selectors, and returns the list of Backups that match those selectors. -func (c *FakeBackups) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.BackupList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(backupsResource, backupsKind, c.ns, opts), &velerov1.BackupList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.BackupList{ListMeta: obj.(*velerov1.BackupList).ListMeta} - for _, item := range obj.(*velerov1.BackupList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested backups. -func (c *FakeBackups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(backupsResource, c.ns, opts)) - -} - -// Create takes the representation of a backup and creates it. Returns the server's representation of the backup, and an error, if there is any. -func (c *FakeBackups) Create(ctx context.Context, backup *velerov1.Backup, opts v1.CreateOptions) (result *velerov1.Backup, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(backupsResource, c.ns, backup), &velerov1.Backup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Backup), err -} - -// Update takes the representation of a backup and updates it. Returns the server's representation of the backup, and an error, if there is any. -func (c *FakeBackups) Update(ctx context.Context, backup *velerov1.Backup, opts v1.UpdateOptions) (result *velerov1.Backup, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(backupsResource, c.ns, backup), &velerov1.Backup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Backup), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeBackups) UpdateStatus(ctx context.Context, backup *velerov1.Backup, opts v1.UpdateOptions) (*velerov1.Backup, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(backupsResource, "status", c.ns, backup), &velerov1.Backup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Backup), err -} - -// Delete takes name of the backup and deletes it. Returns an error if one occurs. -func (c *FakeBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(backupsResource, c.ns, name), &velerov1.Backup{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeBackups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(backupsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.BackupList{}) - return err -} - -// Patch applies the patch and returns the patched backup. -func (c *FakeBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.Backup, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(backupsResource, c.ns, name, pt, data, subresources...), &velerov1.Backup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Backup), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go deleted file mode 100644 index ef9d6b41c..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeBackupRepositories implements BackupRepositoryInterface -type FakeBackupRepositories struct { - Fake *FakeVeleroV1 - ns string -} - -var backuprepositoriesResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backuprepositories"} - -var backuprepositoriesKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupRepository"} - -// Get takes name of the backupRepository, and returns the corresponding backupRepository object, and an error if there is any. -func (c *FakeBackupRepositories) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.BackupRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(backuprepositoriesResource, c.ns, name), &velerov1.BackupRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupRepository), err -} - -// List takes label and field selectors, and returns the list of BackupRepositories that match those selectors. -func (c *FakeBackupRepositories) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.BackupRepositoryList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(backuprepositoriesResource, backuprepositoriesKind, c.ns, opts), &velerov1.BackupRepositoryList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.BackupRepositoryList{ListMeta: obj.(*velerov1.BackupRepositoryList).ListMeta} - for _, item := range obj.(*velerov1.BackupRepositoryList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested backupRepositories. -func (c *FakeBackupRepositories) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(backuprepositoriesResource, c.ns, opts)) - -} - -// Create takes the representation of a backupRepository and creates it. Returns the server's representation of the backupRepository, and an error, if there is any. -func (c *FakeBackupRepositories) Create(ctx context.Context, backupRepository *velerov1.BackupRepository, opts v1.CreateOptions) (result *velerov1.BackupRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(backuprepositoriesResource, c.ns, backupRepository), &velerov1.BackupRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupRepository), err -} - -// Update takes the representation of a backupRepository and updates it. Returns the server's representation of the backupRepository, and an error, if there is any. -func (c *FakeBackupRepositories) Update(ctx context.Context, backupRepository *velerov1.BackupRepository, opts v1.UpdateOptions) (result *velerov1.BackupRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(backuprepositoriesResource, c.ns, backupRepository), &velerov1.BackupRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupRepository), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeBackupRepositories) UpdateStatus(ctx context.Context, backupRepository *velerov1.BackupRepository, opts v1.UpdateOptions) (*velerov1.BackupRepository, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(backuprepositoriesResource, "status", c.ns, backupRepository), &velerov1.BackupRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupRepository), err -} - -// Delete takes name of the backupRepository and deletes it. Returns an error if one occurs. -func (c *FakeBackupRepositories) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(backuprepositoriesResource, c.ns, name), &velerov1.BackupRepository{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeBackupRepositories) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(backuprepositoriesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.BackupRepositoryList{}) - return err -} - -// Patch applies the patch and returns the patched backupRepository. -func (c *FakeBackupRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.BackupRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(backuprepositoriesResource, c.ns, name, pt, data, subresources...), &velerov1.BackupRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupRepository), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go deleted file mode 100644 index 4ad942d05..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backupstoragelocation.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeBackupStorageLocations implements BackupStorageLocationInterface -type FakeBackupStorageLocations struct { - Fake *FakeVeleroV1 - ns string -} - -var backupstoragelocationsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "backupstoragelocations"} - -var backupstoragelocationsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "BackupStorageLocation"} - -// Get takes name of the backupStorageLocation, and returns the corresponding backupStorageLocation object, and an error if there is any. -func (c *FakeBackupStorageLocations) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(backupstoragelocationsResource, c.ns, name), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// List takes label and field selectors, and returns the list of BackupStorageLocations that match those selectors. -func (c *FakeBackupStorageLocations) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.BackupStorageLocationList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(backupstoragelocationsResource, backupstoragelocationsKind, c.ns, opts), &velerov1.BackupStorageLocationList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.BackupStorageLocationList{ListMeta: obj.(*velerov1.BackupStorageLocationList).ListMeta} - for _, item := range obj.(*velerov1.BackupStorageLocationList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested backupStorageLocations. -func (c *FakeBackupStorageLocations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(backupstoragelocationsResource, c.ns, opts)) - -} - -// Create takes the representation of a backupStorageLocation and creates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *FakeBackupStorageLocations) Create(ctx context.Context, backupStorageLocation *velerov1.BackupStorageLocation, opts v1.CreateOptions) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(backupstoragelocationsResource, c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// Update takes the representation of a backupStorageLocation and updates it. Returns the server's representation of the backupStorageLocation, and an error, if there is any. -func (c *FakeBackupStorageLocations) Update(ctx context.Context, backupStorageLocation *velerov1.BackupStorageLocation, opts v1.UpdateOptions) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(backupstoragelocationsResource, c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeBackupStorageLocations) UpdateStatus(ctx context.Context, backupStorageLocation *velerov1.BackupStorageLocation, opts v1.UpdateOptions) (*velerov1.BackupStorageLocation, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(backupstoragelocationsResource, "status", c.ns, backupStorageLocation), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} - -// Delete takes name of the backupStorageLocation and deletes it. Returns an error if one occurs. -func (c *FakeBackupStorageLocations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(backupstoragelocationsResource, c.ns, name), &velerov1.BackupStorageLocation{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeBackupStorageLocations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(backupstoragelocationsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.BackupStorageLocationList{}) - return err -} - -// Patch applies the patch and returns the patched backupStorageLocation. -func (c *FakeBackupStorageLocations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.BackupStorageLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(backupstoragelocationsResource, c.ns, name, pt, data, subresources...), &velerov1.BackupStorageLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.BackupStorageLocation), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_deletebackuprequest.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_deletebackuprequest.go deleted file mode 100644 index 50a8a466d..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_deletebackuprequest.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeDeleteBackupRequests implements DeleteBackupRequestInterface -type FakeDeleteBackupRequests struct { - Fake *FakeVeleroV1 - ns string -} - -var deletebackuprequestsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "deletebackuprequests"} - -var deletebackuprequestsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "DeleteBackupRequest"} - -// Get takes name of the deleteBackupRequest, and returns the corresponding deleteBackupRequest object, and an error if there is any. -func (c *FakeDeleteBackupRequests) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.DeleteBackupRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(deletebackuprequestsResource, c.ns, name), &velerov1.DeleteBackupRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DeleteBackupRequest), err -} - -// List takes label and field selectors, and returns the list of DeleteBackupRequests that match those selectors. -func (c *FakeDeleteBackupRequests) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.DeleteBackupRequestList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(deletebackuprequestsResource, deletebackuprequestsKind, c.ns, opts), &velerov1.DeleteBackupRequestList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.DeleteBackupRequestList{ListMeta: obj.(*velerov1.DeleteBackupRequestList).ListMeta} - for _, item := range obj.(*velerov1.DeleteBackupRequestList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested deleteBackupRequests. -func (c *FakeDeleteBackupRequests) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(deletebackuprequestsResource, c.ns, opts)) - -} - -// Create takes the representation of a deleteBackupRequest and creates it. Returns the server's representation of the deleteBackupRequest, and an error, if there is any. -func (c *FakeDeleteBackupRequests) Create(ctx context.Context, deleteBackupRequest *velerov1.DeleteBackupRequest, opts v1.CreateOptions) (result *velerov1.DeleteBackupRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(deletebackuprequestsResource, c.ns, deleteBackupRequest), &velerov1.DeleteBackupRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DeleteBackupRequest), err -} - -// Update takes the representation of a deleteBackupRequest and updates it. Returns the server's representation of the deleteBackupRequest, and an error, if there is any. -func (c *FakeDeleteBackupRequests) Update(ctx context.Context, deleteBackupRequest *velerov1.DeleteBackupRequest, opts v1.UpdateOptions) (result *velerov1.DeleteBackupRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(deletebackuprequestsResource, c.ns, deleteBackupRequest), &velerov1.DeleteBackupRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DeleteBackupRequest), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeDeleteBackupRequests) UpdateStatus(ctx context.Context, deleteBackupRequest *velerov1.DeleteBackupRequest, opts v1.UpdateOptions) (*velerov1.DeleteBackupRequest, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(deletebackuprequestsResource, "status", c.ns, deleteBackupRequest), &velerov1.DeleteBackupRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DeleteBackupRequest), err -} - -// Delete takes name of the deleteBackupRequest and deletes it. Returns an error if one occurs. -func (c *FakeDeleteBackupRequests) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(deletebackuprequestsResource, c.ns, name), &velerov1.DeleteBackupRequest{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeDeleteBackupRequests) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(deletebackuprequestsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.DeleteBackupRequestList{}) - return err -} - -// Patch applies the patch and returns the patched deleteBackupRequest. -func (c *FakeDeleteBackupRequests) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.DeleteBackupRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(deletebackuprequestsResource, c.ns, name, pt, data, subresources...), &velerov1.DeleteBackupRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DeleteBackupRequest), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_downloadrequest.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_downloadrequest.go deleted file mode 100644 index 04e7f0e6e..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_downloadrequest.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeDownloadRequests implements DownloadRequestInterface -type FakeDownloadRequests struct { - Fake *FakeVeleroV1 - ns string -} - -var downloadrequestsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "downloadrequests"} - -var downloadrequestsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "DownloadRequest"} - -// Get takes name of the downloadRequest, and returns the corresponding downloadRequest object, and an error if there is any. -func (c *FakeDownloadRequests) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.DownloadRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(downloadrequestsResource, c.ns, name), &velerov1.DownloadRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DownloadRequest), err -} - -// List takes label and field selectors, and returns the list of DownloadRequests that match those selectors. -func (c *FakeDownloadRequests) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.DownloadRequestList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(downloadrequestsResource, downloadrequestsKind, c.ns, opts), &velerov1.DownloadRequestList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.DownloadRequestList{ListMeta: obj.(*velerov1.DownloadRequestList).ListMeta} - for _, item := range obj.(*velerov1.DownloadRequestList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested downloadRequests. -func (c *FakeDownloadRequests) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(downloadrequestsResource, c.ns, opts)) - -} - -// Create takes the representation of a downloadRequest and creates it. Returns the server's representation of the downloadRequest, and an error, if there is any. -func (c *FakeDownloadRequests) Create(ctx context.Context, downloadRequest *velerov1.DownloadRequest, opts v1.CreateOptions) (result *velerov1.DownloadRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(downloadrequestsResource, c.ns, downloadRequest), &velerov1.DownloadRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DownloadRequest), err -} - -// Update takes the representation of a downloadRequest and updates it. Returns the server's representation of the downloadRequest, and an error, if there is any. -func (c *FakeDownloadRequests) Update(ctx context.Context, downloadRequest *velerov1.DownloadRequest, opts v1.UpdateOptions) (result *velerov1.DownloadRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(downloadrequestsResource, c.ns, downloadRequest), &velerov1.DownloadRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DownloadRequest), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeDownloadRequests) UpdateStatus(ctx context.Context, downloadRequest *velerov1.DownloadRequest, opts v1.UpdateOptions) (*velerov1.DownloadRequest, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(downloadrequestsResource, "status", c.ns, downloadRequest), &velerov1.DownloadRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DownloadRequest), err -} - -// Delete takes name of the downloadRequest and deletes it. Returns an error if one occurs. -func (c *FakeDownloadRequests) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(downloadrequestsResource, c.ns, name), &velerov1.DownloadRequest{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeDownloadRequests) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(downloadrequestsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.DownloadRequestList{}) - return err -} - -// Patch applies the patch and returns the patched downloadRequest. -func (c *FakeDownloadRequests) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.DownloadRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(downloadrequestsResource, c.ns, name, pt, data, subresources...), &velerov1.DownloadRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.DownloadRequest), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumebackup.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumebackup.go deleted file mode 100644 index 00c76f935..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumebackup.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePodVolumeBackups implements PodVolumeBackupInterface -type FakePodVolumeBackups struct { - Fake *FakeVeleroV1 - ns string -} - -var podvolumebackupsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "podvolumebackups"} - -var podvolumebackupsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "PodVolumeBackup"} - -// Get takes name of the podVolumeBackup, and returns the corresponding podVolumeBackup object, and an error if there is any. -func (c *FakePodVolumeBackups) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.PodVolumeBackup, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(podvolumebackupsResource, c.ns, name), &velerov1.PodVolumeBackup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeBackup), err -} - -// List takes label and field selectors, and returns the list of PodVolumeBackups that match those selectors. -func (c *FakePodVolumeBackups) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.PodVolumeBackupList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(podvolumebackupsResource, podvolumebackupsKind, c.ns, opts), &velerov1.PodVolumeBackupList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.PodVolumeBackupList{ListMeta: obj.(*velerov1.PodVolumeBackupList).ListMeta} - for _, item := range obj.(*velerov1.PodVolumeBackupList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested podVolumeBackups. -func (c *FakePodVolumeBackups) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(podvolumebackupsResource, c.ns, opts)) - -} - -// Create takes the representation of a podVolumeBackup and creates it. Returns the server's representation of the podVolumeBackup, and an error, if there is any. -func (c *FakePodVolumeBackups) Create(ctx context.Context, podVolumeBackup *velerov1.PodVolumeBackup, opts v1.CreateOptions) (result *velerov1.PodVolumeBackup, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(podvolumebackupsResource, c.ns, podVolumeBackup), &velerov1.PodVolumeBackup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeBackup), err -} - -// Update takes the representation of a podVolumeBackup and updates it. Returns the server's representation of the podVolumeBackup, and an error, if there is any. -func (c *FakePodVolumeBackups) Update(ctx context.Context, podVolumeBackup *velerov1.PodVolumeBackup, opts v1.UpdateOptions) (result *velerov1.PodVolumeBackup, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(podvolumebackupsResource, c.ns, podVolumeBackup), &velerov1.PodVolumeBackup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeBackup), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePodVolumeBackups) UpdateStatus(ctx context.Context, podVolumeBackup *velerov1.PodVolumeBackup, opts v1.UpdateOptions) (*velerov1.PodVolumeBackup, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(podvolumebackupsResource, "status", c.ns, podVolumeBackup), &velerov1.PodVolumeBackup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeBackup), err -} - -// Delete takes name of the podVolumeBackup and deletes it. Returns an error if one occurs. -func (c *FakePodVolumeBackups) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(podvolumebackupsResource, c.ns, name), &velerov1.PodVolumeBackup{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePodVolumeBackups) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(podvolumebackupsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.PodVolumeBackupList{}) - return err -} - -// Patch applies the patch and returns the patched podVolumeBackup. -func (c *FakePodVolumeBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.PodVolumeBackup, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(podvolumebackupsResource, c.ns, name, pt, data, subresources...), &velerov1.PodVolumeBackup{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeBackup), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumerestore.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumerestore.go deleted file mode 100644 index da1238797..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_podvolumerestore.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakePodVolumeRestores implements PodVolumeRestoreInterface -type FakePodVolumeRestores struct { - Fake *FakeVeleroV1 - ns string -} - -var podvolumerestoresResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "podvolumerestores"} - -var podvolumerestoresKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "PodVolumeRestore"} - -// Get takes name of the podVolumeRestore, and returns the corresponding podVolumeRestore object, and an error if there is any. -func (c *FakePodVolumeRestores) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.PodVolumeRestore, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(podvolumerestoresResource, c.ns, name), &velerov1.PodVolumeRestore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeRestore), err -} - -// List takes label and field selectors, and returns the list of PodVolumeRestores that match those selectors. -func (c *FakePodVolumeRestores) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.PodVolumeRestoreList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(podvolumerestoresResource, podvolumerestoresKind, c.ns, opts), &velerov1.PodVolumeRestoreList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.PodVolumeRestoreList{ListMeta: obj.(*velerov1.PodVolumeRestoreList).ListMeta} - for _, item := range obj.(*velerov1.PodVolumeRestoreList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested podVolumeRestores. -func (c *FakePodVolumeRestores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(podvolumerestoresResource, c.ns, opts)) - -} - -// Create takes the representation of a podVolumeRestore and creates it. Returns the server's representation of the podVolumeRestore, and an error, if there is any. -func (c *FakePodVolumeRestores) Create(ctx context.Context, podVolumeRestore *velerov1.PodVolumeRestore, opts v1.CreateOptions) (result *velerov1.PodVolumeRestore, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(podvolumerestoresResource, c.ns, podVolumeRestore), &velerov1.PodVolumeRestore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeRestore), err -} - -// Update takes the representation of a podVolumeRestore and updates it. Returns the server's representation of the podVolumeRestore, and an error, if there is any. -func (c *FakePodVolumeRestores) Update(ctx context.Context, podVolumeRestore *velerov1.PodVolumeRestore, opts v1.UpdateOptions) (result *velerov1.PodVolumeRestore, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(podvolumerestoresResource, c.ns, podVolumeRestore), &velerov1.PodVolumeRestore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeRestore), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakePodVolumeRestores) UpdateStatus(ctx context.Context, podVolumeRestore *velerov1.PodVolumeRestore, opts v1.UpdateOptions) (*velerov1.PodVolumeRestore, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(podvolumerestoresResource, "status", c.ns, podVolumeRestore), &velerov1.PodVolumeRestore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeRestore), err -} - -// Delete takes name of the podVolumeRestore and deletes it. Returns an error if one occurs. -func (c *FakePodVolumeRestores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(podvolumerestoresResource, c.ns, name), &velerov1.PodVolumeRestore{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakePodVolumeRestores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(podvolumerestoresResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.PodVolumeRestoreList{}) - return err -} - -// Patch applies the patch and returns the patched podVolumeRestore. -func (c *FakePodVolumeRestores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.PodVolumeRestore, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(podvolumerestoresResource, c.ns, name, pt, data, subresources...), &velerov1.PodVolumeRestore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.PodVolumeRestore), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_restore.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_restore.go deleted file mode 100644 index 73e00128a..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_restore.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeRestores implements RestoreInterface -type FakeRestores struct { - Fake *FakeVeleroV1 - ns string -} - -var restoresResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "restores"} - -var restoresKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "Restore"} - -// Get takes name of the restore, and returns the corresponding restore object, and an error if there is any. -func (c *FakeRestores) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.Restore, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(restoresResource, c.ns, name), &velerov1.Restore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Restore), err -} - -// List takes label and field selectors, and returns the list of Restores that match those selectors. -func (c *FakeRestores) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.RestoreList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(restoresResource, restoresKind, c.ns, opts), &velerov1.RestoreList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.RestoreList{ListMeta: obj.(*velerov1.RestoreList).ListMeta} - for _, item := range obj.(*velerov1.RestoreList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested restores. -func (c *FakeRestores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(restoresResource, c.ns, opts)) - -} - -// Create takes the representation of a restore and creates it. Returns the server's representation of the restore, and an error, if there is any. -func (c *FakeRestores) Create(ctx context.Context, restore *velerov1.Restore, opts v1.CreateOptions) (result *velerov1.Restore, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(restoresResource, c.ns, restore), &velerov1.Restore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Restore), err -} - -// Update takes the representation of a restore and updates it. Returns the server's representation of the restore, and an error, if there is any. -func (c *FakeRestores) Update(ctx context.Context, restore *velerov1.Restore, opts v1.UpdateOptions) (result *velerov1.Restore, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(restoresResource, c.ns, restore), &velerov1.Restore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Restore), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeRestores) UpdateStatus(ctx context.Context, restore *velerov1.Restore, opts v1.UpdateOptions) (*velerov1.Restore, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(restoresResource, "status", c.ns, restore), &velerov1.Restore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Restore), err -} - -// Delete takes name of the restore and deletes it. Returns an error if one occurs. -func (c *FakeRestores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(restoresResource, c.ns, name), &velerov1.Restore{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeRestores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(restoresResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.RestoreList{}) - return err -} - -// Patch applies the patch and returns the patched restore. -func (c *FakeRestores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.Restore, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(restoresResource, c.ns, name, pt, data, subresources...), &velerov1.Restore{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Restore), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_schedule.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_schedule.go deleted file mode 100644 index 5789e7e9e..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_schedule.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeSchedules implements ScheduleInterface -type FakeSchedules struct { - Fake *FakeVeleroV1 - ns string -} - -var schedulesResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "schedules"} - -var schedulesKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "Schedule"} - -// Get takes name of the schedule, and returns the corresponding schedule object, and an error if there is any. -func (c *FakeSchedules) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.Schedule, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(schedulesResource, c.ns, name), &velerov1.Schedule{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Schedule), err -} - -// List takes label and field selectors, and returns the list of Schedules that match those selectors. -func (c *FakeSchedules) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.ScheduleList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(schedulesResource, schedulesKind, c.ns, opts), &velerov1.ScheduleList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.ScheduleList{ListMeta: obj.(*velerov1.ScheduleList).ListMeta} - for _, item := range obj.(*velerov1.ScheduleList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested schedules. -func (c *FakeSchedules) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(schedulesResource, c.ns, opts)) - -} - -// Create takes the representation of a schedule and creates it. Returns the server's representation of the schedule, and an error, if there is any. -func (c *FakeSchedules) Create(ctx context.Context, schedule *velerov1.Schedule, opts v1.CreateOptions) (result *velerov1.Schedule, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(schedulesResource, c.ns, schedule), &velerov1.Schedule{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Schedule), err -} - -// Update takes the representation of a schedule and updates it. Returns the server's representation of the schedule, and an error, if there is any. -func (c *FakeSchedules) Update(ctx context.Context, schedule *velerov1.Schedule, opts v1.UpdateOptions) (result *velerov1.Schedule, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(schedulesResource, c.ns, schedule), &velerov1.Schedule{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Schedule), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeSchedules) UpdateStatus(ctx context.Context, schedule *velerov1.Schedule, opts v1.UpdateOptions) (*velerov1.Schedule, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(schedulesResource, "status", c.ns, schedule), &velerov1.Schedule{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Schedule), err -} - -// Delete takes name of the schedule and deletes it. Returns an error if one occurs. -func (c *FakeSchedules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(schedulesResource, c.ns, name), &velerov1.Schedule{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeSchedules) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(schedulesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.ScheduleList{}) - return err -} - -// Patch applies the patch and returns the patched schedule. -func (c *FakeSchedules) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.Schedule, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(schedulesResource, c.ns, name, pt, data, subresources...), &velerov1.Schedule{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.Schedule), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_serverstatusrequest.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_serverstatusrequest.go deleted file mode 100644 index dc0c9e4ee..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_serverstatusrequest.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeServerStatusRequests implements ServerStatusRequestInterface -type FakeServerStatusRequests struct { - Fake *FakeVeleroV1 - ns string -} - -var serverstatusrequestsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "serverstatusrequests"} - -var serverstatusrequestsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "ServerStatusRequest"} - -// Get takes name of the serverStatusRequest, and returns the corresponding serverStatusRequest object, and an error if there is any. -func (c *FakeServerStatusRequests) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.ServerStatusRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(serverstatusrequestsResource, c.ns, name), &velerov1.ServerStatusRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ServerStatusRequest), err -} - -// List takes label and field selectors, and returns the list of ServerStatusRequests that match those selectors. -func (c *FakeServerStatusRequests) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.ServerStatusRequestList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(serverstatusrequestsResource, serverstatusrequestsKind, c.ns, opts), &velerov1.ServerStatusRequestList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.ServerStatusRequestList{ListMeta: obj.(*velerov1.ServerStatusRequestList).ListMeta} - for _, item := range obj.(*velerov1.ServerStatusRequestList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested serverStatusRequests. -func (c *FakeServerStatusRequests) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(serverstatusrequestsResource, c.ns, opts)) - -} - -// Create takes the representation of a serverStatusRequest and creates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. -func (c *FakeServerStatusRequests) Create(ctx context.Context, serverStatusRequest *velerov1.ServerStatusRequest, opts v1.CreateOptions) (result *velerov1.ServerStatusRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(serverstatusrequestsResource, c.ns, serverStatusRequest), &velerov1.ServerStatusRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ServerStatusRequest), err -} - -// Update takes the representation of a serverStatusRequest and updates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. -func (c *FakeServerStatusRequests) Update(ctx context.Context, serverStatusRequest *velerov1.ServerStatusRequest, opts v1.UpdateOptions) (result *velerov1.ServerStatusRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(serverstatusrequestsResource, c.ns, serverStatusRequest), &velerov1.ServerStatusRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ServerStatusRequest), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeServerStatusRequests) UpdateStatus(ctx context.Context, serverStatusRequest *velerov1.ServerStatusRequest, opts v1.UpdateOptions) (*velerov1.ServerStatusRequest, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(serverstatusrequestsResource, "status", c.ns, serverStatusRequest), &velerov1.ServerStatusRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ServerStatusRequest), err -} - -// Delete takes name of the serverStatusRequest and deletes it. Returns an error if one occurs. -func (c *FakeServerStatusRequests) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(serverstatusrequestsResource, c.ns, name), &velerov1.ServerStatusRequest{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeServerStatusRequests) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(serverstatusrequestsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.ServerStatusRequestList{}) - return err -} - -// Patch applies the patch and returns the patched serverStatusRequest. -func (c *FakeServerStatusRequests) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.ServerStatusRequest, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(serverstatusrequestsResource, c.ns, name, pt, data, subresources...), &velerov1.ServerStatusRequest{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ServerStatusRequest), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go deleted file mode 100644 index 444c1f89f..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_velero_client.go +++ /dev/null @@ -1,80 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeVeleroV1 struct { - *testing.Fake -} - -func (c *FakeVeleroV1) Backups(namespace string) v1.BackupInterface { - return &FakeBackups{c, namespace} -} - -func (c *FakeVeleroV1) BackupRepositories(namespace string) v1.BackupRepositoryInterface { - return &FakeBackupRepositories{c, namespace} -} - -func (c *FakeVeleroV1) BackupStorageLocations(namespace string) v1.BackupStorageLocationInterface { - return &FakeBackupStorageLocations{c, namespace} -} - -func (c *FakeVeleroV1) DeleteBackupRequests(namespace string) v1.DeleteBackupRequestInterface { - return &FakeDeleteBackupRequests{c, namespace} -} - -func (c *FakeVeleroV1) DownloadRequests(namespace string) v1.DownloadRequestInterface { - return &FakeDownloadRequests{c, namespace} -} - -func (c *FakeVeleroV1) PodVolumeBackups(namespace string) v1.PodVolumeBackupInterface { - return &FakePodVolumeBackups{c, namespace} -} - -func (c *FakeVeleroV1) PodVolumeRestores(namespace string) v1.PodVolumeRestoreInterface { - return &FakePodVolumeRestores{c, namespace} -} - -func (c *FakeVeleroV1) Restores(namespace string) v1.RestoreInterface { - return &FakeRestores{c, namespace} -} - -func (c *FakeVeleroV1) Schedules(namespace string) v1.ScheduleInterface { - return &FakeSchedules{c, namespace} -} - -func (c *FakeVeleroV1) ServerStatusRequests(namespace string) v1.ServerStatusRequestInterface { - return &FakeServerStatusRequests{c, namespace} -} - -func (c *FakeVeleroV1) VolumeSnapshotLocations(namespace string) v1.VolumeSnapshotLocationInterface { - return &FakeVolumeSnapshotLocations{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeVeleroV1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_volumesnapshotlocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_volumesnapshotlocation.go deleted file mode 100644 index c5a7b4fb2..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_volumesnapshotlocation.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeVolumeSnapshotLocations implements VolumeSnapshotLocationInterface -type FakeVolumeSnapshotLocations struct { - Fake *FakeVeleroV1 - ns string -} - -var volumesnapshotlocationsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "volumesnapshotlocations"} - -var volumesnapshotlocationsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "VolumeSnapshotLocation"} - -// Get takes name of the volumeSnapshotLocation, and returns the corresponding volumeSnapshotLocation object, and an error if there is any. -func (c *FakeVolumeSnapshotLocations) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.VolumeSnapshotLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(volumesnapshotlocationsResource, c.ns, name), &velerov1.VolumeSnapshotLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.VolumeSnapshotLocation), err -} - -// List takes label and field selectors, and returns the list of VolumeSnapshotLocations that match those selectors. -func (c *FakeVolumeSnapshotLocations) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.VolumeSnapshotLocationList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(volumesnapshotlocationsResource, volumesnapshotlocationsKind, c.ns, opts), &velerov1.VolumeSnapshotLocationList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.VolumeSnapshotLocationList{ListMeta: obj.(*velerov1.VolumeSnapshotLocationList).ListMeta} - for _, item := range obj.(*velerov1.VolumeSnapshotLocationList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested volumeSnapshotLocations. -func (c *FakeVolumeSnapshotLocations) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(volumesnapshotlocationsResource, c.ns, opts)) - -} - -// Create takes the representation of a volumeSnapshotLocation and creates it. Returns the server's representation of the volumeSnapshotLocation, and an error, if there is any. -func (c *FakeVolumeSnapshotLocations) Create(ctx context.Context, volumeSnapshotLocation *velerov1.VolumeSnapshotLocation, opts v1.CreateOptions) (result *velerov1.VolumeSnapshotLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(volumesnapshotlocationsResource, c.ns, volumeSnapshotLocation), &velerov1.VolumeSnapshotLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.VolumeSnapshotLocation), err -} - -// Update takes the representation of a volumeSnapshotLocation and updates it. Returns the server's representation of the volumeSnapshotLocation, and an error, if there is any. -func (c *FakeVolumeSnapshotLocations) Update(ctx context.Context, volumeSnapshotLocation *velerov1.VolumeSnapshotLocation, opts v1.UpdateOptions) (result *velerov1.VolumeSnapshotLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(volumesnapshotlocationsResource, c.ns, volumeSnapshotLocation), &velerov1.VolumeSnapshotLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.VolumeSnapshotLocation), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeVolumeSnapshotLocations) UpdateStatus(ctx context.Context, volumeSnapshotLocation *velerov1.VolumeSnapshotLocation, opts v1.UpdateOptions) (*velerov1.VolumeSnapshotLocation, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(volumesnapshotlocationsResource, "status", c.ns, volumeSnapshotLocation), &velerov1.VolumeSnapshotLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.VolumeSnapshotLocation), err -} - -// Delete takes name of the volumeSnapshotLocation and deletes it. Returns an error if one occurs. -func (c *FakeVolumeSnapshotLocations) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(volumesnapshotlocationsResource, c.ns, name), &velerov1.VolumeSnapshotLocation{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeVolumeSnapshotLocations) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(volumesnapshotlocationsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.VolumeSnapshotLocationList{}) - return err -} - -// Patch applies the patch and returns the patched volumeSnapshotLocation. -func (c *FakeVolumeSnapshotLocations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.VolumeSnapshotLocation, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(volumesnapshotlocationsResource, c.ns, name, pt, data, subresources...), &velerov1.VolumeSnapshotLocation{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.VolumeSnapshotLocation), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go deleted file mode 100644 index 5032fd6a4..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go +++ /dev/null @@ -1,41 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -type BackupExpansion interface{} - -type BackupRepositoryExpansion interface{} - -type BackupStorageLocationExpansion interface{} - -type DeleteBackupRequestExpansion interface{} - -type DownloadRequestExpansion interface{} - -type PodVolumeBackupExpansion interface{} - -type PodVolumeRestoreExpansion interface{} - -type RestoreExpansion interface{} - -type ScheduleExpansion interface{} - -type ServerStatusRequestExpansion interface{} - -type VolumeSnapshotLocationExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupInterface.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupInterface.go deleted file mode 100644 index ba059729d..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupInterface.go +++ /dev/null @@ -1,252 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - types "k8s.io/apimachinery/pkg/types" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - - watch "k8s.io/apimachinery/pkg/watch" -) - -// BackupInterface is an autogenerated mock type for the BackupInterface type -type BackupInterface struct { - mock.Mock -} - -// Create provides a mock function with given fields: ctx, backup, opts -func (_m *BackupInterface) Create(ctx context.Context, backup *v1.Backup, opts metav1.CreateOptions) (*v1.Backup, error) { - ret := _m.Called(ctx, backup, opts) - - var r0 *v1.Backup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup, metav1.CreateOptions) (*v1.Backup, error)); ok { - return rf(ctx, backup, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup, metav1.CreateOptions) *v1.Backup); ok { - r0 = rf(ctx, backup, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Backup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Backup, metav1.CreateOptions) error); ok { - r1 = rf(ctx, backup, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Delete provides a mock function with given fields: ctx, name, opts -func (_m *BackupInterface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - ret := _m.Called(ctx, name, opts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.DeleteOptions) error); ok { - r0 = rf(ctx, name, opts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCollection provides a mock function with given fields: ctx, opts, listOpts -func (_m *BackupInterface) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - ret := _m.Called(ctx, opts, listOpts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.DeleteOptions, metav1.ListOptions) error); ok { - r0 = rf(ctx, opts, listOpts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Get provides a mock function with given fields: ctx, name, opts -func (_m *BackupInterface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Backup, error) { - ret := _m.Called(ctx, name, opts) - - var r0 *v1.Backup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) (*v1.Backup, error)); ok { - return rf(ctx, name, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) *v1.Backup); ok { - r0 = rf(ctx, name, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Backup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.GetOptions) error); ok { - r1 = rf(ctx, name, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// List provides a mock function with given fields: ctx, opts -func (_m *BackupInterface) List(ctx context.Context, opts metav1.ListOptions) (*v1.BackupList, error) { - ret := _m.Called(ctx, opts) - - var r0 *v1.BackupList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.BackupList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.BackupList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.BackupList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources -func (_m *BackupInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.Backup, error) { - _va := make([]interface{}, len(subresources)) - for _i := range subresources { - _va[_i] = subresources[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, name, pt, data, opts) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *v1.Backup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*v1.Backup, error)); ok { - return rf(ctx, name, pt, data, opts, subresources...) - } - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) *v1.Backup); ok { - r0 = rf(ctx, name, pt, data, opts, subresources...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Backup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) error); ok { - r1 = rf(ctx, name, pt, data, opts, subresources...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Update provides a mock function with given fields: ctx, backup, opts -func (_m *BackupInterface) Update(ctx context.Context, backup *v1.Backup, opts metav1.UpdateOptions) (*v1.Backup, error) { - ret := _m.Called(ctx, backup, opts) - - var r0 *v1.Backup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup, metav1.UpdateOptions) (*v1.Backup, error)); ok { - return rf(ctx, backup, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup, metav1.UpdateOptions) *v1.Backup); ok { - r0 = rf(ctx, backup, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Backup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Backup, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, backup, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateStatus provides a mock function with given fields: ctx, backup, opts -func (_m *BackupInterface) UpdateStatus(ctx context.Context, backup *v1.Backup, opts metav1.UpdateOptions) (*v1.Backup, error) { - ret := _m.Called(ctx, backup, opts) - - var r0 *v1.Backup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup, metav1.UpdateOptions) (*v1.Backup, error)); ok { - return rf(ctx, backup, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup, metav1.UpdateOptions) *v1.Backup); ok { - r0 = rf(ctx, backup, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Backup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Backup, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, backup, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Watch provides a mock function with given fields: ctx, opts -func (_m *BackupInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - ret := _m.Called(ctx, opts) - - var r0 watch.Interface - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (watch.Interface, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) watch.Interface); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(watch.Interface) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewBackupInterface creates a new instance of BackupInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewBackupInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *BackupInterface { - mock := &BackupInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupsGetter.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupsGetter.go deleted file mode 100644 index 6c64af7d0..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/BackupsGetter.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" -) - -// BackupsGetter is an autogenerated mock type for the BackupsGetter type -type BackupsGetter struct { - mock.Mock -} - -// Backups provides a mock function with given fields: namespace -func (_m *BackupsGetter) Backups(namespace string) v1.BackupInterface { - ret := _m.Called(namespace) - - var r0 v1.BackupInterface - if rf, ok := ret.Get(0).(func(string) v1.BackupInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.BackupInterface) - } - } - - return r0 -} - -// NewBackupsGetter creates a new instance of BackupsGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewBackupsGetter(t interface { - mock.TestingT - Cleanup(func()) -}) *BackupsGetter { - mock := &BackupsGetter{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestInterface.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestInterface.go deleted file mode 100644 index f26ce4021..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestInterface.go +++ /dev/null @@ -1,252 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - types "k8s.io/apimachinery/pkg/types" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - - watch "k8s.io/apimachinery/pkg/watch" -) - -// DeleteBackupRequestInterface is an autogenerated mock type for the DeleteBackupRequestInterface type -type DeleteBackupRequestInterface struct { - mock.Mock -} - -// Create provides a mock function with given fields: ctx, deleteBackupRequest, opts -func (_m *DeleteBackupRequestInterface) Create(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.CreateOptions) (*v1.DeleteBackupRequest, error) { - ret := _m.Called(ctx, deleteBackupRequest, opts) - - var r0 *v1.DeleteBackupRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.DeleteBackupRequest, metav1.CreateOptions) (*v1.DeleteBackupRequest, error)); ok { - return rf(ctx, deleteBackupRequest, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.DeleteBackupRequest, metav1.CreateOptions) *v1.DeleteBackupRequest); ok { - r0 = rf(ctx, deleteBackupRequest, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.DeleteBackupRequest) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.DeleteBackupRequest, metav1.CreateOptions) error); ok { - r1 = rf(ctx, deleteBackupRequest, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Delete provides a mock function with given fields: ctx, name, opts -func (_m *DeleteBackupRequestInterface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - ret := _m.Called(ctx, name, opts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.DeleteOptions) error); ok { - r0 = rf(ctx, name, opts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCollection provides a mock function with given fields: ctx, opts, listOpts -func (_m *DeleteBackupRequestInterface) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - ret := _m.Called(ctx, opts, listOpts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.DeleteOptions, metav1.ListOptions) error); ok { - r0 = rf(ctx, opts, listOpts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Get provides a mock function with given fields: ctx, name, opts -func (_m *DeleteBackupRequestInterface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.DeleteBackupRequest, error) { - ret := _m.Called(ctx, name, opts) - - var r0 *v1.DeleteBackupRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) (*v1.DeleteBackupRequest, error)); ok { - return rf(ctx, name, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) *v1.DeleteBackupRequest); ok { - r0 = rf(ctx, name, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.DeleteBackupRequest) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.GetOptions) error); ok { - r1 = rf(ctx, name, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// List provides a mock function with given fields: ctx, opts -func (_m *DeleteBackupRequestInterface) List(ctx context.Context, opts metav1.ListOptions) (*v1.DeleteBackupRequestList, error) { - ret := _m.Called(ctx, opts) - - var r0 *v1.DeleteBackupRequestList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.DeleteBackupRequestList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.DeleteBackupRequestList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.DeleteBackupRequestList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources -func (_m *DeleteBackupRequestInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.DeleteBackupRequest, error) { - _va := make([]interface{}, len(subresources)) - for _i := range subresources { - _va[_i] = subresources[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, name, pt, data, opts) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *v1.DeleteBackupRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*v1.DeleteBackupRequest, error)); ok { - return rf(ctx, name, pt, data, opts, subresources...) - } - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) *v1.DeleteBackupRequest); ok { - r0 = rf(ctx, name, pt, data, opts, subresources...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.DeleteBackupRequest) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) error); ok { - r1 = rf(ctx, name, pt, data, opts, subresources...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Update provides a mock function with given fields: ctx, deleteBackupRequest, opts -func (_m *DeleteBackupRequestInterface) Update(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.UpdateOptions) (*v1.DeleteBackupRequest, error) { - ret := _m.Called(ctx, deleteBackupRequest, opts) - - var r0 *v1.DeleteBackupRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.DeleteBackupRequest, metav1.UpdateOptions) (*v1.DeleteBackupRequest, error)); ok { - return rf(ctx, deleteBackupRequest, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.DeleteBackupRequest, metav1.UpdateOptions) *v1.DeleteBackupRequest); ok { - r0 = rf(ctx, deleteBackupRequest, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.DeleteBackupRequest) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.DeleteBackupRequest, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, deleteBackupRequest, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateStatus provides a mock function with given fields: ctx, deleteBackupRequest, opts -func (_m *DeleteBackupRequestInterface) UpdateStatus(ctx context.Context, deleteBackupRequest *v1.DeleteBackupRequest, opts metav1.UpdateOptions) (*v1.DeleteBackupRequest, error) { - ret := _m.Called(ctx, deleteBackupRequest, opts) - - var r0 *v1.DeleteBackupRequest - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.DeleteBackupRequest, metav1.UpdateOptions) (*v1.DeleteBackupRequest, error)); ok { - return rf(ctx, deleteBackupRequest, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.DeleteBackupRequest, metav1.UpdateOptions) *v1.DeleteBackupRequest); ok { - r0 = rf(ctx, deleteBackupRequest, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.DeleteBackupRequest) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.DeleteBackupRequest, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, deleteBackupRequest, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Watch provides a mock function with given fields: ctx, opts -func (_m *DeleteBackupRequestInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - ret := _m.Called(ctx, opts) - - var r0 watch.Interface - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (watch.Interface, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) watch.Interface); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(watch.Interface) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewDeleteBackupRequestInterface creates a new instance of DeleteBackupRequestInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewDeleteBackupRequestInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *DeleteBackupRequestInterface { - mock := &DeleteBackupRequestInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestsGetter.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestsGetter.go deleted file mode 100644 index 5ddf8b575..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/DeleteBackupRequestsGetter.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" -) - -// DeleteBackupRequestsGetter is an autogenerated mock type for the DeleteBackupRequestsGetter type -type DeleteBackupRequestsGetter struct { - mock.Mock -} - -// DeleteBackupRequests provides a mock function with given fields: namespace -func (_m *DeleteBackupRequestsGetter) DeleteBackupRequests(namespace string) v1.DeleteBackupRequestInterface { - ret := _m.Called(namespace) - - var r0 v1.DeleteBackupRequestInterface - if rf, ok := ret.Get(0).(func(string) v1.DeleteBackupRequestInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.DeleteBackupRequestInterface) - } - } - - return r0 -} - -// NewDeleteBackupRequestsGetter creates a new instance of DeleteBackupRequestsGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewDeleteBackupRequestsGetter(t interface { - mock.TestingT - Cleanup(func()) -}) *DeleteBackupRequestsGetter { - mock := &DeleteBackupRequestsGetter{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/PodVolumeBackupInterface.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/PodVolumeBackupInterface.go deleted file mode 100644 index 9778ca6bf..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/PodVolumeBackupInterface.go +++ /dev/null @@ -1,252 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - types "k8s.io/apimachinery/pkg/types" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - - watch "k8s.io/apimachinery/pkg/watch" -) - -// PodVolumeBackupInterface is an autogenerated mock type for the PodVolumeBackupInterface type -type PodVolumeBackupInterface struct { - mock.Mock -} - -// Create provides a mock function with given fields: ctx, podVolumeBackup, opts -func (_m *PodVolumeBackupInterface) Create(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.CreateOptions) (*v1.PodVolumeBackup, error) { - ret := _m.Called(ctx, podVolumeBackup, opts) - - var r0 *v1.PodVolumeBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.PodVolumeBackup, metav1.CreateOptions) (*v1.PodVolumeBackup, error)); ok { - return rf(ctx, podVolumeBackup, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.PodVolumeBackup, metav1.CreateOptions) *v1.PodVolumeBackup); ok { - r0 = rf(ctx, podVolumeBackup, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodVolumeBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.PodVolumeBackup, metav1.CreateOptions) error); ok { - r1 = rf(ctx, podVolumeBackup, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Delete provides a mock function with given fields: ctx, name, opts -func (_m *PodVolumeBackupInterface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - ret := _m.Called(ctx, name, opts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.DeleteOptions) error); ok { - r0 = rf(ctx, name, opts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCollection provides a mock function with given fields: ctx, opts, listOpts -func (_m *PodVolumeBackupInterface) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - ret := _m.Called(ctx, opts, listOpts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.DeleteOptions, metav1.ListOptions) error); ok { - r0 = rf(ctx, opts, listOpts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Get provides a mock function with given fields: ctx, name, opts -func (_m *PodVolumeBackupInterface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.PodVolumeBackup, error) { - ret := _m.Called(ctx, name, opts) - - var r0 *v1.PodVolumeBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) (*v1.PodVolumeBackup, error)); ok { - return rf(ctx, name, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) *v1.PodVolumeBackup); ok { - r0 = rf(ctx, name, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodVolumeBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.GetOptions) error); ok { - r1 = rf(ctx, name, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// List provides a mock function with given fields: ctx, opts -func (_m *PodVolumeBackupInterface) List(ctx context.Context, opts metav1.ListOptions) (*v1.PodVolumeBackupList, error) { - ret := _m.Called(ctx, opts) - - var r0 *v1.PodVolumeBackupList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.PodVolumeBackupList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.PodVolumeBackupList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodVolumeBackupList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources -func (_m *PodVolumeBackupInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.PodVolumeBackup, error) { - _va := make([]interface{}, len(subresources)) - for _i := range subresources { - _va[_i] = subresources[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, name, pt, data, opts) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *v1.PodVolumeBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*v1.PodVolumeBackup, error)); ok { - return rf(ctx, name, pt, data, opts, subresources...) - } - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) *v1.PodVolumeBackup); ok { - r0 = rf(ctx, name, pt, data, opts, subresources...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodVolumeBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) error); ok { - r1 = rf(ctx, name, pt, data, opts, subresources...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Update provides a mock function with given fields: ctx, podVolumeBackup, opts -func (_m *PodVolumeBackupInterface) Update(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.UpdateOptions) (*v1.PodVolumeBackup, error) { - ret := _m.Called(ctx, podVolumeBackup, opts) - - var r0 *v1.PodVolumeBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.PodVolumeBackup, metav1.UpdateOptions) (*v1.PodVolumeBackup, error)); ok { - return rf(ctx, podVolumeBackup, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.PodVolumeBackup, metav1.UpdateOptions) *v1.PodVolumeBackup); ok { - r0 = rf(ctx, podVolumeBackup, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodVolumeBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.PodVolumeBackup, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, podVolumeBackup, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateStatus provides a mock function with given fields: ctx, podVolumeBackup, opts -func (_m *PodVolumeBackupInterface) UpdateStatus(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.UpdateOptions) (*v1.PodVolumeBackup, error) { - ret := _m.Called(ctx, podVolumeBackup, opts) - - var r0 *v1.PodVolumeBackup - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.PodVolumeBackup, metav1.UpdateOptions) (*v1.PodVolumeBackup, error)); ok { - return rf(ctx, podVolumeBackup, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.PodVolumeBackup, metav1.UpdateOptions) *v1.PodVolumeBackup); ok { - r0 = rf(ctx, podVolumeBackup, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.PodVolumeBackup) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.PodVolumeBackup, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, podVolumeBackup, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Watch provides a mock function with given fields: ctx, opts -func (_m *PodVolumeBackupInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - ret := _m.Called(ctx, opts) - - var r0 watch.Interface - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (watch.Interface, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) watch.Interface); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(watch.Interface) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewPodVolumeBackupInterface creates a new instance of PodVolumeBackupInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewPodVolumeBackupInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *PodVolumeBackupInterface { - mock := &PodVolumeBackupInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/ScheduleInterface.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/ScheduleInterface.go deleted file mode 100644 index 228047f9d..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/ScheduleInterface.go +++ /dev/null @@ -1,252 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - types "k8s.io/apimachinery/pkg/types" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - - watch "k8s.io/apimachinery/pkg/watch" -) - -// ScheduleInterface is an autogenerated mock type for the ScheduleInterface type -type ScheduleInterface struct { - mock.Mock -} - -// Create provides a mock function with given fields: ctx, schedule, opts -func (_m *ScheduleInterface) Create(ctx context.Context, schedule *v1.Schedule, opts metav1.CreateOptions) (*v1.Schedule, error) { - ret := _m.Called(ctx, schedule, opts) - - var r0 *v1.Schedule - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Schedule, metav1.CreateOptions) (*v1.Schedule, error)); ok { - return rf(ctx, schedule, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Schedule, metav1.CreateOptions) *v1.Schedule); ok { - r0 = rf(ctx, schedule, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Schedule) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Schedule, metav1.CreateOptions) error); ok { - r1 = rf(ctx, schedule, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Delete provides a mock function with given fields: ctx, name, opts -func (_m *ScheduleInterface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - ret := _m.Called(ctx, name, opts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.DeleteOptions) error); ok { - r0 = rf(ctx, name, opts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCollection provides a mock function with given fields: ctx, opts, listOpts -func (_m *ScheduleInterface) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - ret := _m.Called(ctx, opts, listOpts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.DeleteOptions, metav1.ListOptions) error); ok { - r0 = rf(ctx, opts, listOpts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Get provides a mock function with given fields: ctx, name, opts -func (_m *ScheduleInterface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Schedule, error) { - ret := _m.Called(ctx, name, opts) - - var r0 *v1.Schedule - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) (*v1.Schedule, error)); ok { - return rf(ctx, name, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) *v1.Schedule); ok { - r0 = rf(ctx, name, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Schedule) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.GetOptions) error); ok { - r1 = rf(ctx, name, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// List provides a mock function with given fields: ctx, opts -func (_m *ScheduleInterface) List(ctx context.Context, opts metav1.ListOptions) (*v1.ScheduleList, error) { - ret := _m.Called(ctx, opts) - - var r0 *v1.ScheduleList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.ScheduleList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.ScheduleList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.ScheduleList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources -func (_m *ScheduleInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.Schedule, error) { - _va := make([]interface{}, len(subresources)) - for _i := range subresources { - _va[_i] = subresources[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, name, pt, data, opts) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *v1.Schedule - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*v1.Schedule, error)); ok { - return rf(ctx, name, pt, data, opts, subresources...) - } - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) *v1.Schedule); ok { - r0 = rf(ctx, name, pt, data, opts, subresources...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Schedule) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) error); ok { - r1 = rf(ctx, name, pt, data, opts, subresources...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Update provides a mock function with given fields: ctx, schedule, opts -func (_m *ScheduleInterface) Update(ctx context.Context, schedule *v1.Schedule, opts metav1.UpdateOptions) (*v1.Schedule, error) { - ret := _m.Called(ctx, schedule, opts) - - var r0 *v1.Schedule - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Schedule, metav1.UpdateOptions) (*v1.Schedule, error)); ok { - return rf(ctx, schedule, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Schedule, metav1.UpdateOptions) *v1.Schedule); ok { - r0 = rf(ctx, schedule, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Schedule) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Schedule, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, schedule, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateStatus provides a mock function with given fields: ctx, schedule, opts -func (_m *ScheduleInterface) UpdateStatus(ctx context.Context, schedule *v1.Schedule, opts metav1.UpdateOptions) (*v1.Schedule, error) { - ret := _m.Called(ctx, schedule, opts) - - var r0 *v1.Schedule - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.Schedule, metav1.UpdateOptions) (*v1.Schedule, error)); ok { - return rf(ctx, schedule, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.Schedule, metav1.UpdateOptions) *v1.Schedule); ok { - r0 = rf(ctx, schedule, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.Schedule) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.Schedule, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, schedule, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Watch provides a mock function with given fields: ctx, opts -func (_m *ScheduleInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - ret := _m.Called(ctx, opts) - - var r0 watch.Interface - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (watch.Interface, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) watch.Interface); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(watch.Interface) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewScheduleInterface creates a new instance of ScheduleInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewScheduleInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *ScheduleInterface { - mock := &ScheduleInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/SchedulesGetter.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/SchedulesGetter.go deleted file mode 100644 index dfe9c5844..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/SchedulesGetter.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" -) - -// SchedulesGetter is an autogenerated mock type for the SchedulesGetter type -type SchedulesGetter struct { - mock.Mock -} - -// Schedules provides a mock function with given fields: namespace -func (_m *SchedulesGetter) Schedules(namespace string) v1.ScheduleInterface { - ret := _m.Called(namespace) - - var r0 v1.ScheduleInterface - if rf, ok := ret.Get(0).(func(string) v1.ScheduleInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.ScheduleInterface) - } - } - - return r0 -} - -// NewSchedulesGetter creates a new instance of SchedulesGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewSchedulesGetter(t interface { - mock.TestingT - Cleanup(func()) -}) *SchedulesGetter { - mock := &SchedulesGetter{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VeleroV1Interface.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VeleroV1Interface.go deleted file mode 100644 index cf521e619..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VeleroV1Interface.go +++ /dev/null @@ -1,221 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - rest "k8s.io/client-go/rest" - - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" -) - -// VeleroV1Interface is an autogenerated mock type for the VeleroV1Interface type -type VeleroV1Interface struct { - mock.Mock -} - -// BackupRepositories provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) BackupRepositories(namespace string) v1.BackupRepositoryInterface { - ret := _m.Called(namespace) - - var r0 v1.BackupRepositoryInterface - if rf, ok := ret.Get(0).(func(string) v1.BackupRepositoryInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.BackupRepositoryInterface) - } - } - - return r0 -} - -// BackupStorageLocations provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) BackupStorageLocations(namespace string) v1.BackupStorageLocationInterface { - ret := _m.Called(namespace) - - var r0 v1.BackupStorageLocationInterface - if rf, ok := ret.Get(0).(func(string) v1.BackupStorageLocationInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.BackupStorageLocationInterface) - } - } - - return r0 -} - -// Backups provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) Backups(namespace string) v1.BackupInterface { - ret := _m.Called(namespace) - - var r0 v1.BackupInterface - if rf, ok := ret.Get(0).(func(string) v1.BackupInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.BackupInterface) - } - } - - return r0 -} - -// DeleteBackupRequests provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) DeleteBackupRequests(namespace string) v1.DeleteBackupRequestInterface { - ret := _m.Called(namespace) - - var r0 v1.DeleteBackupRequestInterface - if rf, ok := ret.Get(0).(func(string) v1.DeleteBackupRequestInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.DeleteBackupRequestInterface) - } - } - - return r0 -} - -// DownloadRequests provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) DownloadRequests(namespace string) v1.DownloadRequestInterface { - ret := _m.Called(namespace) - - var r0 v1.DownloadRequestInterface - if rf, ok := ret.Get(0).(func(string) v1.DownloadRequestInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.DownloadRequestInterface) - } - } - - return r0 -} - -// PodVolumeBackups provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) PodVolumeBackups(namespace string) v1.PodVolumeBackupInterface { - ret := _m.Called(namespace) - - var r0 v1.PodVolumeBackupInterface - if rf, ok := ret.Get(0).(func(string) v1.PodVolumeBackupInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.PodVolumeBackupInterface) - } - } - - return r0 -} - -// PodVolumeRestores provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) PodVolumeRestores(namespace string) v1.PodVolumeRestoreInterface { - ret := _m.Called(namespace) - - var r0 v1.PodVolumeRestoreInterface - if rf, ok := ret.Get(0).(func(string) v1.PodVolumeRestoreInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.PodVolumeRestoreInterface) - } - } - - return r0 -} - -// RESTClient provides a mock function with given fields: -func (_m *VeleroV1Interface) RESTClient() rest.Interface { - ret := _m.Called() - - var r0 rest.Interface - if rf, ok := ret.Get(0).(func() rest.Interface); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(rest.Interface) - } - } - - return r0 -} - -// Restores provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) Restores(namespace string) v1.RestoreInterface { - ret := _m.Called(namespace) - - var r0 v1.RestoreInterface - if rf, ok := ret.Get(0).(func(string) v1.RestoreInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.RestoreInterface) - } - } - - return r0 -} - -// Schedules provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) Schedules(namespace string) v1.ScheduleInterface { - ret := _m.Called(namespace) - - var r0 v1.ScheduleInterface - if rf, ok := ret.Get(0).(func(string) v1.ScheduleInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.ScheduleInterface) - } - } - - return r0 -} - -// ServerStatusRequests provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) ServerStatusRequests(namespace string) v1.ServerStatusRequestInterface { - ret := _m.Called(namespace) - - var r0 v1.ServerStatusRequestInterface - if rf, ok := ret.Get(0).(func(string) v1.ServerStatusRequestInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.ServerStatusRequestInterface) - } - } - - return r0 -} - -// VolumeSnapshotLocations provides a mock function with given fields: namespace -func (_m *VeleroV1Interface) VolumeSnapshotLocations(namespace string) v1.VolumeSnapshotLocationInterface { - ret := _m.Called(namespace) - - var r0 v1.VolumeSnapshotLocationInterface - if rf, ok := ret.Get(0).(func(string) v1.VolumeSnapshotLocationInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.VolumeSnapshotLocationInterface) - } - } - - return r0 -} - -// NewVeleroV1Interface creates a new instance of VeleroV1Interface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewVeleroV1Interface(t interface { - mock.TestingT - Cleanup(func()) -}) *VeleroV1Interface { - mock := &VeleroV1Interface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationInterface.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationInterface.go deleted file mode 100644 index 3845fda88..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationInterface.go +++ /dev/null @@ -1,252 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - context "context" - - mock "github.com/stretchr/testify/mock" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - types "k8s.io/apimachinery/pkg/types" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - - watch "k8s.io/apimachinery/pkg/watch" -) - -// VolumeSnapshotLocationInterface is an autogenerated mock type for the VolumeSnapshotLocationInterface type -type VolumeSnapshotLocationInterface struct { - mock.Mock -} - -// Create provides a mock function with given fields: ctx, volumeSnapshotLocation, opts -func (_m *VolumeSnapshotLocationInterface) Create(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.CreateOptions) (*v1.VolumeSnapshotLocation, error) { - ret := _m.Called(ctx, volumeSnapshotLocation, opts) - - var r0 *v1.VolumeSnapshotLocation - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.CreateOptions) (*v1.VolumeSnapshotLocation, error)); ok { - return rf(ctx, volumeSnapshotLocation, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.CreateOptions) *v1.VolumeSnapshotLocation); ok { - r0 = rf(ctx, volumeSnapshotLocation, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.VolumeSnapshotLocation) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.CreateOptions) error); ok { - r1 = rf(ctx, volumeSnapshotLocation, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Delete provides a mock function with given fields: ctx, name, opts -func (_m *VolumeSnapshotLocationInterface) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - ret := _m.Called(ctx, name, opts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.DeleteOptions) error); ok { - r0 = rf(ctx, name, opts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// DeleteCollection provides a mock function with given fields: ctx, opts, listOpts -func (_m *VolumeSnapshotLocationInterface) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - ret := _m.Called(ctx, opts, listOpts) - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.DeleteOptions, metav1.ListOptions) error); ok { - r0 = rf(ctx, opts, listOpts) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// Get provides a mock function with given fields: ctx, name, opts -func (_m *VolumeSnapshotLocationInterface) Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.VolumeSnapshotLocation, error) { - ret := _m.Called(ctx, name, opts) - - var r0 *v1.VolumeSnapshotLocation - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) (*v1.VolumeSnapshotLocation, error)); ok { - return rf(ctx, name, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, string, metav1.GetOptions) *v1.VolumeSnapshotLocation); ok { - r0 = rf(ctx, name, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.VolumeSnapshotLocation) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, metav1.GetOptions) error); ok { - r1 = rf(ctx, name, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// List provides a mock function with given fields: ctx, opts -func (_m *VolumeSnapshotLocationInterface) List(ctx context.Context, opts metav1.ListOptions) (*v1.VolumeSnapshotLocationList, error) { - ret := _m.Called(ctx, opts) - - var r0 *v1.VolumeSnapshotLocationList - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (*v1.VolumeSnapshotLocationList, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) *v1.VolumeSnapshotLocationList); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.VolumeSnapshotLocationList) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Patch provides a mock function with given fields: ctx, name, pt, data, opts, subresources -func (_m *VolumeSnapshotLocationInterface) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*v1.VolumeSnapshotLocation, error) { - _va := make([]interface{}, len(subresources)) - for _i := range subresources { - _va[_i] = subresources[_i] - } - var _ca []interface{} - _ca = append(_ca, ctx, name, pt, data, opts) - _ca = append(_ca, _va...) - ret := _m.Called(_ca...) - - var r0 *v1.VolumeSnapshotLocation - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) (*v1.VolumeSnapshotLocation, error)); ok { - return rf(ctx, name, pt, data, opts, subresources...) - } - if rf, ok := ret.Get(0).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) *v1.VolumeSnapshotLocation); ok { - r0 = rf(ctx, name, pt, data, opts, subresources...) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.VolumeSnapshotLocation) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, string, types.PatchType, []byte, metav1.PatchOptions, ...string) error); ok { - r1 = rf(ctx, name, pt, data, opts, subresources...) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Update provides a mock function with given fields: ctx, volumeSnapshotLocation, opts -func (_m *VolumeSnapshotLocationInterface) Update(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.UpdateOptions) (*v1.VolumeSnapshotLocation, error) { - ret := _m.Called(ctx, volumeSnapshotLocation, opts) - - var r0 *v1.VolumeSnapshotLocation - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.UpdateOptions) (*v1.VolumeSnapshotLocation, error)); ok { - return rf(ctx, volumeSnapshotLocation, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.UpdateOptions) *v1.VolumeSnapshotLocation); ok { - r0 = rf(ctx, volumeSnapshotLocation, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.VolumeSnapshotLocation) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, volumeSnapshotLocation, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// UpdateStatus provides a mock function with given fields: ctx, volumeSnapshotLocation, opts -func (_m *VolumeSnapshotLocationInterface) UpdateStatus(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.UpdateOptions) (*v1.VolumeSnapshotLocation, error) { - ret := _m.Called(ctx, volumeSnapshotLocation, opts) - - var r0 *v1.VolumeSnapshotLocation - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.UpdateOptions) (*v1.VolumeSnapshotLocation, error)); ok { - return rf(ctx, volumeSnapshotLocation, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.UpdateOptions) *v1.VolumeSnapshotLocation); ok { - r0 = rf(ctx, volumeSnapshotLocation, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(*v1.VolumeSnapshotLocation) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, *v1.VolumeSnapshotLocation, metav1.UpdateOptions) error); ok { - r1 = rf(ctx, volumeSnapshotLocation, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// Watch provides a mock function with given fields: ctx, opts -func (_m *VolumeSnapshotLocationInterface) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - ret := _m.Called(ctx, opts) - - var r0 watch.Interface - var r1 error - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) (watch.Interface, error)); ok { - return rf(ctx, opts) - } - if rf, ok := ret.Get(0).(func(context.Context, metav1.ListOptions) watch.Interface); ok { - r0 = rf(ctx, opts) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(watch.Interface) - } - } - - if rf, ok := ret.Get(1).(func(context.Context, metav1.ListOptions) error); ok { - r1 = rf(ctx, opts) - } else { - r1 = ret.Error(1) - } - - return r0, r1 -} - -// NewVolumeSnapshotLocationInterface creates a new instance of VolumeSnapshotLocationInterface. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewVolumeSnapshotLocationInterface(t interface { - mock.TestingT - Cleanup(func()) -}) *VolumeSnapshotLocationInterface { - mock := &VolumeSnapshotLocationInterface{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationsGetter.go b/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationsGetter.go deleted file mode 100644 index 2ec52368e..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/mocks/VolumeSnapshotLocationsGetter.go +++ /dev/null @@ -1,43 +0,0 @@ -// Code generated by mockery v2.30.1. DO NOT EDIT. - -package mocks - -import ( - mock "github.com/stretchr/testify/mock" - v1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" -) - -// VolumeSnapshotLocationsGetter is an autogenerated mock type for the VolumeSnapshotLocationsGetter type -type VolumeSnapshotLocationsGetter struct { - mock.Mock -} - -// VolumeSnapshotLocations provides a mock function with given fields: namespace -func (_m *VolumeSnapshotLocationsGetter) VolumeSnapshotLocations(namespace string) v1.VolumeSnapshotLocationInterface { - ret := _m.Called(namespace) - - var r0 v1.VolumeSnapshotLocationInterface - if rf, ok := ret.Get(0).(func(string) v1.VolumeSnapshotLocationInterface); ok { - r0 = rf(namespace) - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(v1.VolumeSnapshotLocationInterface) - } - } - - return r0 -} - -// NewVolumeSnapshotLocationsGetter creates a new instance of VolumeSnapshotLocationsGetter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewVolumeSnapshotLocationsGetter(t interface { - mock.TestingT - Cleanup(func()) -}) *VolumeSnapshotLocationsGetter { - mock := &VolumeSnapshotLocationsGetter{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/podvolumebackup.go b/pkg/generated/clientset/versioned/typed/velero/v1/podvolumebackup.go deleted file mode 100644 index 836d78b58..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/podvolumebackup.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PodVolumeBackupsGetter has a method to return a PodVolumeBackupInterface. -// A group's client should implement this interface. -type PodVolumeBackupsGetter interface { - PodVolumeBackups(namespace string) PodVolumeBackupInterface -} - -// PodVolumeBackupInterface has methods to work with PodVolumeBackup resources. -type PodVolumeBackupInterface interface { - Create(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.CreateOptions) (*v1.PodVolumeBackup, error) - Update(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.UpdateOptions) (*v1.PodVolumeBackup, error) - UpdateStatus(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.UpdateOptions) (*v1.PodVolumeBackup, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.PodVolumeBackup, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.PodVolumeBackupList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PodVolumeBackup, err error) - PodVolumeBackupExpansion -} - -// podVolumeBackups implements PodVolumeBackupInterface -type podVolumeBackups struct { - client rest.Interface - ns string -} - -// newPodVolumeBackups returns a PodVolumeBackups -func newPodVolumeBackups(c *VeleroV1Client, namespace string) *podVolumeBackups { - return &podVolumeBackups{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the podVolumeBackup, and returns the corresponding podVolumeBackup object, and an error if there is any. -func (c *podVolumeBackups) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.PodVolumeBackup, err error) { - result = &v1.PodVolumeBackup{} - err = c.client.Get(). - Namespace(c.ns). - Resource("podvolumebackups"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of PodVolumeBackups that match those selectors. -func (c *podVolumeBackups) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PodVolumeBackupList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.PodVolumeBackupList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("podvolumebackups"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested podVolumeBackups. -func (c *podVolumeBackups) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("podvolumebackups"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a podVolumeBackup and creates it. Returns the server's representation of the podVolumeBackup, and an error, if there is any. -func (c *podVolumeBackups) Create(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.CreateOptions) (result *v1.PodVolumeBackup, err error) { - result = &v1.PodVolumeBackup{} - err = c.client.Post(). - Namespace(c.ns). - Resource("podvolumebackups"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(podVolumeBackup). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a podVolumeBackup and updates it. Returns the server's representation of the podVolumeBackup, and an error, if there is any. -func (c *podVolumeBackups) Update(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.UpdateOptions) (result *v1.PodVolumeBackup, err error) { - result = &v1.PodVolumeBackup{} - err = c.client.Put(). - Namespace(c.ns). - Resource("podvolumebackups"). - Name(podVolumeBackup.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(podVolumeBackup). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *podVolumeBackups) UpdateStatus(ctx context.Context, podVolumeBackup *v1.PodVolumeBackup, opts metav1.UpdateOptions) (result *v1.PodVolumeBackup, err error) { - result = &v1.PodVolumeBackup{} - err = c.client.Put(). - Namespace(c.ns). - Resource("podvolumebackups"). - Name(podVolumeBackup.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(podVolumeBackup). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the podVolumeBackup and deletes it. Returns an error if one occurs. -func (c *podVolumeBackups) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("podvolumebackups"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *podVolumeBackups) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("podvolumebackups"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched podVolumeBackup. -func (c *podVolumeBackups) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PodVolumeBackup, err error) { - result = &v1.PodVolumeBackup{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("podvolumebackups"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/podvolumerestore.go b/pkg/generated/clientset/versioned/typed/velero/v1/podvolumerestore.go deleted file mode 100644 index dffd51b1b..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/podvolumerestore.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// PodVolumeRestoresGetter has a method to return a PodVolumeRestoreInterface. -// A group's client should implement this interface. -type PodVolumeRestoresGetter interface { - PodVolumeRestores(namespace string) PodVolumeRestoreInterface -} - -// PodVolumeRestoreInterface has methods to work with PodVolumeRestore resources. -type PodVolumeRestoreInterface interface { - Create(ctx context.Context, podVolumeRestore *v1.PodVolumeRestore, opts metav1.CreateOptions) (*v1.PodVolumeRestore, error) - Update(ctx context.Context, podVolumeRestore *v1.PodVolumeRestore, opts metav1.UpdateOptions) (*v1.PodVolumeRestore, error) - UpdateStatus(ctx context.Context, podVolumeRestore *v1.PodVolumeRestore, opts metav1.UpdateOptions) (*v1.PodVolumeRestore, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.PodVolumeRestore, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.PodVolumeRestoreList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PodVolumeRestore, err error) - PodVolumeRestoreExpansion -} - -// podVolumeRestores implements PodVolumeRestoreInterface -type podVolumeRestores struct { - client rest.Interface - ns string -} - -// newPodVolumeRestores returns a PodVolumeRestores -func newPodVolumeRestores(c *VeleroV1Client, namespace string) *podVolumeRestores { - return &podVolumeRestores{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the podVolumeRestore, and returns the corresponding podVolumeRestore object, and an error if there is any. -func (c *podVolumeRestores) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.PodVolumeRestore, err error) { - result = &v1.PodVolumeRestore{} - err = c.client.Get(). - Namespace(c.ns). - Resource("podvolumerestores"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of PodVolumeRestores that match those selectors. -func (c *podVolumeRestores) List(ctx context.Context, opts metav1.ListOptions) (result *v1.PodVolumeRestoreList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.PodVolumeRestoreList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("podvolumerestores"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested podVolumeRestores. -func (c *podVolumeRestores) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("podvolumerestores"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a podVolumeRestore and creates it. Returns the server's representation of the podVolumeRestore, and an error, if there is any. -func (c *podVolumeRestores) Create(ctx context.Context, podVolumeRestore *v1.PodVolumeRestore, opts metav1.CreateOptions) (result *v1.PodVolumeRestore, err error) { - result = &v1.PodVolumeRestore{} - err = c.client.Post(). - Namespace(c.ns). - Resource("podvolumerestores"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(podVolumeRestore). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a podVolumeRestore and updates it. Returns the server's representation of the podVolumeRestore, and an error, if there is any. -func (c *podVolumeRestores) Update(ctx context.Context, podVolumeRestore *v1.PodVolumeRestore, opts metav1.UpdateOptions) (result *v1.PodVolumeRestore, err error) { - result = &v1.PodVolumeRestore{} - err = c.client.Put(). - Namespace(c.ns). - Resource("podvolumerestores"). - Name(podVolumeRestore.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(podVolumeRestore). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *podVolumeRestores) UpdateStatus(ctx context.Context, podVolumeRestore *v1.PodVolumeRestore, opts metav1.UpdateOptions) (result *v1.PodVolumeRestore, err error) { - result = &v1.PodVolumeRestore{} - err = c.client.Put(). - Namespace(c.ns). - Resource("podvolumerestores"). - Name(podVolumeRestore.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(podVolumeRestore). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the podVolumeRestore and deletes it. Returns an error if one occurs. -func (c *podVolumeRestores) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("podvolumerestores"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *podVolumeRestores) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("podvolumerestores"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched podVolumeRestore. -func (c *podVolumeRestores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.PodVolumeRestore, err error) { - result = &v1.PodVolumeRestore{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("podvolumerestores"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/restore.go b/pkg/generated/clientset/versioned/typed/velero/v1/restore.go deleted file mode 100644 index a43b823a6..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/restore.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// RestoresGetter has a method to return a RestoreInterface. -// A group's client should implement this interface. -type RestoresGetter interface { - Restores(namespace string) RestoreInterface -} - -// RestoreInterface has methods to work with Restore resources. -type RestoreInterface interface { - Create(ctx context.Context, restore *v1.Restore, opts metav1.CreateOptions) (*v1.Restore, error) - Update(ctx context.Context, restore *v1.Restore, opts metav1.UpdateOptions) (*v1.Restore, error) - UpdateStatus(ctx context.Context, restore *v1.Restore, opts metav1.UpdateOptions) (*v1.Restore, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Restore, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.RestoreList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Restore, err error) - RestoreExpansion -} - -// restores implements RestoreInterface -type restores struct { - client rest.Interface - ns string -} - -// newRestores returns a Restores -func newRestores(c *VeleroV1Client, namespace string) *restores { - return &restores{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the restore, and returns the corresponding restore object, and an error if there is any. -func (c *restores) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Restore, err error) { - result = &v1.Restore{} - err = c.client.Get(). - Namespace(c.ns). - Resource("restores"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Restores that match those selectors. -func (c *restores) List(ctx context.Context, opts metav1.ListOptions) (result *v1.RestoreList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.RestoreList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("restores"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested restores. -func (c *restores) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("restores"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a restore and creates it. Returns the server's representation of the restore, and an error, if there is any. -func (c *restores) Create(ctx context.Context, restore *v1.Restore, opts metav1.CreateOptions) (result *v1.Restore, err error) { - result = &v1.Restore{} - err = c.client.Post(). - Namespace(c.ns). - Resource("restores"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(restore). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a restore and updates it. Returns the server's representation of the restore, and an error, if there is any. -func (c *restores) Update(ctx context.Context, restore *v1.Restore, opts metav1.UpdateOptions) (result *v1.Restore, err error) { - result = &v1.Restore{} - err = c.client.Put(). - Namespace(c.ns). - Resource("restores"). - Name(restore.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(restore). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *restores) UpdateStatus(ctx context.Context, restore *v1.Restore, opts metav1.UpdateOptions) (result *v1.Restore, err error) { - result = &v1.Restore{} - err = c.client.Put(). - Namespace(c.ns). - Resource("restores"). - Name(restore.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(restore). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the restore and deletes it. Returns an error if one occurs. -func (c *restores) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("restores"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *restores) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("restores"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched restore. -func (c *restores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Restore, err error) { - result = &v1.Restore{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("restores"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/schedule.go b/pkg/generated/clientset/versioned/typed/velero/v1/schedule.go deleted file mode 100644 index 8a003b008..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/schedule.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// SchedulesGetter has a method to return a ScheduleInterface. -// A group's client should implement this interface. -type SchedulesGetter interface { - Schedules(namespace string) ScheduleInterface -} - -// ScheduleInterface has methods to work with Schedule resources. -type ScheduleInterface interface { - Create(ctx context.Context, schedule *v1.Schedule, opts metav1.CreateOptions) (*v1.Schedule, error) - Update(ctx context.Context, schedule *v1.Schedule, opts metav1.UpdateOptions) (*v1.Schedule, error) - UpdateStatus(ctx context.Context, schedule *v1.Schedule, opts metav1.UpdateOptions) (*v1.Schedule, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.Schedule, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.ScheduleList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Schedule, err error) - ScheduleExpansion -} - -// schedules implements ScheduleInterface -type schedules struct { - client rest.Interface - ns string -} - -// newSchedules returns a Schedules -func newSchedules(c *VeleroV1Client, namespace string) *schedules { - return &schedules{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the schedule, and returns the corresponding schedule object, and an error if there is any. -func (c *schedules) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.Schedule, err error) { - result = &v1.Schedule{} - err = c.client.Get(). - Namespace(c.ns). - Resource("schedules"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Schedules that match those selectors. -func (c *schedules) List(ctx context.Context, opts metav1.ListOptions) (result *v1.ScheduleList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.ScheduleList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("schedules"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested schedules. -func (c *schedules) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("schedules"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a schedule and creates it. Returns the server's representation of the schedule, and an error, if there is any. -func (c *schedules) Create(ctx context.Context, schedule *v1.Schedule, opts metav1.CreateOptions) (result *v1.Schedule, err error) { - result = &v1.Schedule{} - err = c.client.Post(). - Namespace(c.ns). - Resource("schedules"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(schedule). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a schedule and updates it. Returns the server's representation of the schedule, and an error, if there is any. -func (c *schedules) Update(ctx context.Context, schedule *v1.Schedule, opts metav1.UpdateOptions) (result *v1.Schedule, err error) { - result = &v1.Schedule{} - err = c.client.Put(). - Namespace(c.ns). - Resource("schedules"). - Name(schedule.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(schedule). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *schedules) UpdateStatus(ctx context.Context, schedule *v1.Schedule, opts metav1.UpdateOptions) (result *v1.Schedule, err error) { - result = &v1.Schedule{} - err = c.client.Put(). - Namespace(c.ns). - Resource("schedules"). - Name(schedule.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(schedule). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the schedule and deletes it. Returns an error if one occurs. -func (c *schedules) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("schedules"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *schedules) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("schedules"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched schedule. -func (c *schedules) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.Schedule, err error) { - result = &v1.Schedule{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("schedules"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/serverstatusrequest.go b/pkg/generated/clientset/versioned/typed/velero/v1/serverstatusrequest.go deleted file mode 100644 index c8a16d80f..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/serverstatusrequest.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ServerStatusRequestsGetter has a method to return a ServerStatusRequestInterface. -// A group's client should implement this interface. -type ServerStatusRequestsGetter interface { - ServerStatusRequests(namespace string) ServerStatusRequestInterface -} - -// ServerStatusRequestInterface has methods to work with ServerStatusRequest resources. -type ServerStatusRequestInterface interface { - Create(ctx context.Context, serverStatusRequest *v1.ServerStatusRequest, opts metav1.CreateOptions) (*v1.ServerStatusRequest, error) - Update(ctx context.Context, serverStatusRequest *v1.ServerStatusRequest, opts metav1.UpdateOptions) (*v1.ServerStatusRequest, error) - UpdateStatus(ctx context.Context, serverStatusRequest *v1.ServerStatusRequest, opts metav1.UpdateOptions) (*v1.ServerStatusRequest, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.ServerStatusRequest, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.ServerStatusRequestList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ServerStatusRequest, err error) - ServerStatusRequestExpansion -} - -// serverStatusRequests implements ServerStatusRequestInterface -type serverStatusRequests struct { - client rest.Interface - ns string -} - -// newServerStatusRequests returns a ServerStatusRequests -func newServerStatusRequests(c *VeleroV1Client, namespace string) *serverStatusRequests { - return &serverStatusRequests{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the serverStatusRequest, and returns the corresponding serverStatusRequest object, and an error if there is any. -func (c *serverStatusRequests) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.ServerStatusRequest, err error) { - result = &v1.ServerStatusRequest{} - err = c.client.Get(). - Namespace(c.ns). - Resource("serverstatusrequests"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ServerStatusRequests that match those selectors. -func (c *serverStatusRequests) List(ctx context.Context, opts metav1.ListOptions) (result *v1.ServerStatusRequestList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.ServerStatusRequestList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("serverstatusrequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested serverStatusRequests. -func (c *serverStatusRequests) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("serverstatusrequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a serverStatusRequest and creates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. -func (c *serverStatusRequests) Create(ctx context.Context, serverStatusRequest *v1.ServerStatusRequest, opts metav1.CreateOptions) (result *v1.ServerStatusRequest, err error) { - result = &v1.ServerStatusRequest{} - err = c.client.Post(). - Namespace(c.ns). - Resource("serverstatusrequests"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serverStatusRequest). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a serverStatusRequest and updates it. Returns the server's representation of the serverStatusRequest, and an error, if there is any. -func (c *serverStatusRequests) Update(ctx context.Context, serverStatusRequest *v1.ServerStatusRequest, opts metav1.UpdateOptions) (result *v1.ServerStatusRequest, err error) { - result = &v1.ServerStatusRequest{} - err = c.client.Put(). - Namespace(c.ns). - Resource("serverstatusrequests"). - Name(serverStatusRequest.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serverStatusRequest). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *serverStatusRequests) UpdateStatus(ctx context.Context, serverStatusRequest *v1.ServerStatusRequest, opts metav1.UpdateOptions) (result *v1.ServerStatusRequest, err error) { - result = &v1.ServerStatusRequest{} - err = c.client.Put(). - Namespace(c.ns). - Resource("serverstatusrequests"). - Name(serverStatusRequest.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serverStatusRequest). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the serverStatusRequest and deletes it. Returns an error if one occurs. -func (c *serverStatusRequests) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("serverstatusrequests"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *serverStatusRequests) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("serverstatusrequests"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched serverStatusRequest. -func (c *serverStatusRequests) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ServerStatusRequest, err error) { - result = &v1.ServerStatusRequest{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("serverstatusrequests"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go deleted file mode 100644 index 39f85628c..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type VeleroV1Interface interface { - RESTClient() rest.Interface - BackupsGetter - BackupRepositoriesGetter - BackupStorageLocationsGetter - DeleteBackupRequestsGetter - DownloadRequestsGetter - PodVolumeBackupsGetter - PodVolumeRestoresGetter - RestoresGetter - SchedulesGetter - ServerStatusRequestsGetter - VolumeSnapshotLocationsGetter -} - -// VeleroV1Client is used to interact with features provided by the velero.io group. -type VeleroV1Client struct { - restClient rest.Interface -} - -func (c *VeleroV1Client) Backups(namespace string) BackupInterface { - return newBackups(c, namespace) -} - -func (c *VeleroV1Client) BackupRepositories(namespace string) BackupRepositoryInterface { - return newBackupRepositories(c, namespace) -} - -func (c *VeleroV1Client) BackupStorageLocations(namespace string) BackupStorageLocationInterface { - return newBackupStorageLocations(c, namespace) -} - -func (c *VeleroV1Client) DeleteBackupRequests(namespace string) DeleteBackupRequestInterface { - return newDeleteBackupRequests(c, namespace) -} - -func (c *VeleroV1Client) DownloadRequests(namespace string) DownloadRequestInterface { - return newDownloadRequests(c, namespace) -} - -func (c *VeleroV1Client) PodVolumeBackups(namespace string) PodVolumeBackupInterface { - return newPodVolumeBackups(c, namespace) -} - -func (c *VeleroV1Client) PodVolumeRestores(namespace string) PodVolumeRestoreInterface { - return newPodVolumeRestores(c, namespace) -} - -func (c *VeleroV1Client) Restores(namespace string) RestoreInterface { - return newRestores(c, namespace) -} - -func (c *VeleroV1Client) Schedules(namespace string) ScheduleInterface { - return newSchedules(c, namespace) -} - -func (c *VeleroV1Client) ServerStatusRequests(namespace string) ServerStatusRequestInterface { - return newServerStatusRequests(c, namespace) -} - -func (c *VeleroV1Client) VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationInterface { - return newVolumeSnapshotLocations(c, namespace) -} - -// NewForConfig creates a new VeleroV1Client for the given config. -func NewForConfig(c *rest.Config) (*VeleroV1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &VeleroV1Client{client}, nil -} - -// NewForConfigOrDie creates a new VeleroV1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *VeleroV1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new VeleroV1Client for the given RESTClient. -func New(c rest.Interface) *VeleroV1Client { - return &VeleroV1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *VeleroV1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/volumesnapshotlocation.go b/pkg/generated/clientset/versioned/typed/velero/v1/volumesnapshotlocation.go deleted file mode 100644 index a4c11e93a..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/volumesnapshotlocation.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - "time" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// VolumeSnapshotLocationsGetter has a method to return a VolumeSnapshotLocationInterface. -// A group's client should implement this interface. -type VolumeSnapshotLocationsGetter interface { - VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationInterface -} - -// VolumeSnapshotLocationInterface has methods to work with VolumeSnapshotLocation resources. -type VolumeSnapshotLocationInterface interface { - Create(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.CreateOptions) (*v1.VolumeSnapshotLocation, error) - Update(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.UpdateOptions) (*v1.VolumeSnapshotLocation, error) - UpdateStatus(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.UpdateOptions) (*v1.VolumeSnapshotLocation, error) - Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error - Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.VolumeSnapshotLocation, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.VolumeSnapshotLocationList, error) - Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.VolumeSnapshotLocation, err error) - VolumeSnapshotLocationExpansion -} - -// volumeSnapshotLocations implements VolumeSnapshotLocationInterface -type volumeSnapshotLocations struct { - client rest.Interface - ns string -} - -// newVolumeSnapshotLocations returns a VolumeSnapshotLocations -func newVolumeSnapshotLocations(c *VeleroV1Client, namespace string) *volumeSnapshotLocations { - return &volumeSnapshotLocations{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the volumeSnapshotLocation, and returns the corresponding volumeSnapshotLocation object, and an error if there is any. -func (c *volumeSnapshotLocations) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.VolumeSnapshotLocation, err error) { - result = &v1.VolumeSnapshotLocation{} - err = c.client.Get(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of VolumeSnapshotLocations that match those selectors. -func (c *volumeSnapshotLocations) List(ctx context.Context, opts metav1.ListOptions) (result *v1.VolumeSnapshotLocationList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1.VolumeSnapshotLocationList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested volumeSnapshotLocations. -func (c *volumeSnapshotLocations) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a volumeSnapshotLocation and creates it. Returns the server's representation of the volumeSnapshotLocation, and an error, if there is any. -func (c *volumeSnapshotLocations) Create(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.CreateOptions) (result *v1.VolumeSnapshotLocation, err error) { - result = &v1.VolumeSnapshotLocation{} - err = c.client.Post(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(volumeSnapshotLocation). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a volumeSnapshotLocation and updates it. Returns the server's representation of the volumeSnapshotLocation, and an error, if there is any. -func (c *volumeSnapshotLocations) Update(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.UpdateOptions) (result *v1.VolumeSnapshotLocation, err error) { - result = &v1.VolumeSnapshotLocation{} - err = c.client.Put(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - Name(volumeSnapshotLocation.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(volumeSnapshotLocation). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *volumeSnapshotLocations) UpdateStatus(ctx context.Context, volumeSnapshotLocation *v1.VolumeSnapshotLocation, opts metav1.UpdateOptions) (result *v1.VolumeSnapshotLocation, err error) { - result = &v1.VolumeSnapshotLocation{} - err = c.client.Put(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - Name(volumeSnapshotLocation.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(volumeSnapshotLocation). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the volumeSnapshotLocation and deletes it. Returns an error if one occurs. -func (c *volumeSnapshotLocations) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *volumeSnapshotLocations) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched volumeSnapshotLocation. -func (c *volumeSnapshotLocations) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.VolumeSnapshotLocation, err error) { - result = &v1.VolumeSnapshotLocation{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("volumesnapshotlocations"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go deleted file mode 100644 index 511677675..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/datadownload.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - "context" - "time" - - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// DataDownloadsGetter has a method to return a DataDownloadInterface. -// A group's client should implement this interface. -type DataDownloadsGetter interface { - DataDownloads(namespace string) DataDownloadInterface -} - -// DataDownloadInterface has methods to work with DataDownload resources. -type DataDownloadInterface interface { - Create(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.CreateOptions) (*v2alpha1.DataDownload, error) - Update(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (*v2alpha1.DataDownload, error) - UpdateStatus(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (*v2alpha1.DataDownload, error) - Delete(ctx context.Context, name string, opts v1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v2alpha1.DataDownload, error) - List(ctx context.Context, opts v1.ListOptions) (*v2alpha1.DataDownloadList, error) - Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataDownload, err error) - DataDownloadExpansion -} - -// dataDownloads implements DataDownloadInterface -type dataDownloads struct { - client rest.Interface - ns string -} - -// newDataDownloads returns a DataDownloads -func newDataDownloads(c *VeleroV2alpha1Client, namespace string) *dataDownloads { - return &dataDownloads{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the dataDownload, and returns the corresponding dataDownload object, and an error if there is any. -func (c *dataDownloads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataDownload, err error) { - result = &v2alpha1.DataDownload{} - err = c.client.Get(). - Namespace(c.ns). - Resource("datadownloads"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of DataDownloads that match those selectors. -func (c *dataDownloads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataDownloadList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v2alpha1.DataDownloadList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("datadownloads"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested dataDownloads. -func (c *dataDownloads) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("datadownloads"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a dataDownload and creates it. Returns the server's representation of the dataDownload, and an error, if there is any. -func (c *dataDownloads) Create(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.CreateOptions) (result *v2alpha1.DataDownload, err error) { - result = &v2alpha1.DataDownload{} - err = c.client.Post(). - Namespace(c.ns). - Resource("datadownloads"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(dataDownload). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a dataDownload and updates it. Returns the server's representation of the dataDownload, and an error, if there is any. -func (c *dataDownloads) Update(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (result *v2alpha1.DataDownload, err error) { - result = &v2alpha1.DataDownload{} - err = c.client.Put(). - Namespace(c.ns). - Resource("datadownloads"). - Name(dataDownload.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(dataDownload). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *dataDownloads) UpdateStatus(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (result *v2alpha1.DataDownload, err error) { - result = &v2alpha1.DataDownload{} - err = c.client.Put(). - Namespace(c.ns). - Resource("datadownloads"). - Name(dataDownload.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(dataDownload). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the dataDownload and deletes it. Returns an error if one occurs. -func (c *dataDownloads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("datadownloads"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *dataDownloads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("datadownloads"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched dataDownload. -func (c *dataDownloads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataDownload, err error) { - result = &v2alpha1.DataDownload{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("datadownloads"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go deleted file mode 100644 index 4da27d527..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/dataupload.go +++ /dev/null @@ -1,195 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - "context" - "time" - - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - scheme "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// DataUploadsGetter has a method to return a DataUploadInterface. -// A group's client should implement this interface. -type DataUploadsGetter interface { - DataUploads(namespace string) DataUploadInterface -} - -// DataUploadInterface has methods to work with DataUpload resources. -type DataUploadInterface interface { - Create(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.CreateOptions) (*v2alpha1.DataUpload, error) - Update(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (*v2alpha1.DataUpload, error) - UpdateStatus(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (*v2alpha1.DataUpload, error) - Delete(ctx context.Context, name string, opts v1.DeleteOptions) error - DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v2alpha1.DataUpload, error) - List(ctx context.Context, opts v1.ListOptions) (*v2alpha1.DataUploadList, error) - Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataUpload, err error) - DataUploadExpansion -} - -// dataUploads implements DataUploadInterface -type dataUploads struct { - client rest.Interface - ns string -} - -// newDataUploads returns a DataUploads -func newDataUploads(c *VeleroV2alpha1Client, namespace string) *dataUploads { - return &dataUploads{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the dataUpload, and returns the corresponding dataUpload object, and an error if there is any. -func (c *dataUploads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataUpload, err error) { - result = &v2alpha1.DataUpload{} - err = c.client.Get(). - Namespace(c.ns). - Resource("datauploads"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of DataUploads that match those selectors. -func (c *dataUploads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataUploadList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v2alpha1.DataUploadList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("datauploads"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested dataUploads. -func (c *dataUploads) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("datauploads"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a dataUpload and creates it. Returns the server's representation of the dataUpload, and an error, if there is any. -func (c *dataUploads) Create(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.CreateOptions) (result *v2alpha1.DataUpload, err error) { - result = &v2alpha1.DataUpload{} - err = c.client.Post(). - Namespace(c.ns). - Resource("datauploads"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(dataUpload). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a dataUpload and updates it. Returns the server's representation of the dataUpload, and an error, if there is any. -func (c *dataUploads) Update(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (result *v2alpha1.DataUpload, err error) { - result = &v2alpha1.DataUpload{} - err = c.client.Put(). - Namespace(c.ns). - Resource("datauploads"). - Name(dataUpload.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(dataUpload). - Do(ctx). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *dataUploads) UpdateStatus(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (result *v2alpha1.DataUpload, err error) { - result = &v2alpha1.DataUpload{} - err = c.client.Put(). - Namespace(c.ns). - Resource("datauploads"). - Name(dataUpload.Name). - SubResource("status"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(dataUpload). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the dataUpload and deletes it. Returns an error if one occurs. -func (c *dataUploads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("datauploads"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *dataUploads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("datauploads"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched dataUpload. -func (c *dataUploads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataUpload, err error) { - result = &v2alpha1.DataUpload{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("datauploads"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go deleted file mode 100644 index 18b5cb4d4..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v2alpha1 diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go deleted file mode 100644 index de930591e..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go deleted file mode 100644 index 40db6018b..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_datadownload.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeDataDownloads implements DataDownloadInterface -type FakeDataDownloads struct { - Fake *FakeVeleroV2alpha1 - ns string -} - -var datadownloadsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v2alpha1", Resource: "datadownloads"} - -var datadownloadsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v2alpha1", Kind: "DataDownload"} - -// Get takes name of the dataDownload, and returns the corresponding dataDownload object, and an error if there is any. -func (c *FakeDataDownloads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataDownload, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(datadownloadsResource, c.ns, name), &v2alpha1.DataDownload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataDownload), err -} - -// List takes label and field selectors, and returns the list of DataDownloads that match those selectors. -func (c *FakeDataDownloads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataDownloadList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(datadownloadsResource, datadownloadsKind, c.ns, opts), &v2alpha1.DataDownloadList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v2alpha1.DataDownloadList{ListMeta: obj.(*v2alpha1.DataDownloadList).ListMeta} - for _, item := range obj.(*v2alpha1.DataDownloadList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested dataDownloads. -func (c *FakeDataDownloads) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(datadownloadsResource, c.ns, opts)) - -} - -// Create takes the representation of a dataDownload and creates it. Returns the server's representation of the dataDownload, and an error, if there is any. -func (c *FakeDataDownloads) Create(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.CreateOptions) (result *v2alpha1.DataDownload, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(datadownloadsResource, c.ns, dataDownload), &v2alpha1.DataDownload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataDownload), err -} - -// Update takes the representation of a dataDownload and updates it. Returns the server's representation of the dataDownload, and an error, if there is any. -func (c *FakeDataDownloads) Update(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (result *v2alpha1.DataDownload, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(datadownloadsResource, c.ns, dataDownload), &v2alpha1.DataDownload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataDownload), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeDataDownloads) UpdateStatus(ctx context.Context, dataDownload *v2alpha1.DataDownload, opts v1.UpdateOptions) (*v2alpha1.DataDownload, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(datadownloadsResource, "status", c.ns, dataDownload), &v2alpha1.DataDownload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataDownload), err -} - -// Delete takes name of the dataDownload and deletes it. Returns an error if one occurs. -func (c *FakeDataDownloads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(datadownloadsResource, c.ns, name), &v2alpha1.DataDownload{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeDataDownloads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(datadownloadsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v2alpha1.DataDownloadList{}) - return err -} - -// Patch applies the patch and returns the patched dataDownload. -func (c *FakeDataDownloads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataDownload, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(datadownloadsResource, c.ns, name, pt, data, subresources...), &v2alpha1.DataDownload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataDownload), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go deleted file mode 100644 index d40b50874..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_dataupload.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - "context" - - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeDataUploads implements DataUploadInterface -type FakeDataUploads struct { - Fake *FakeVeleroV2alpha1 - ns string -} - -var datauploadsResource = schema.GroupVersionResource{Group: "velero.io", Version: "v2alpha1", Resource: "datauploads"} - -var datauploadsKind = schema.GroupVersionKind{Group: "velero.io", Version: "v2alpha1", Kind: "DataUpload"} - -// Get takes name of the dataUpload, and returns the corresponding dataUpload object, and an error if there is any. -func (c *FakeDataUploads) Get(ctx context.Context, name string, options v1.GetOptions) (result *v2alpha1.DataUpload, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(datauploadsResource, c.ns, name), &v2alpha1.DataUpload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataUpload), err -} - -// List takes label and field selectors, and returns the list of DataUploads that match those selectors. -func (c *FakeDataUploads) List(ctx context.Context, opts v1.ListOptions) (result *v2alpha1.DataUploadList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(datauploadsResource, datauploadsKind, c.ns, opts), &v2alpha1.DataUploadList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v2alpha1.DataUploadList{ListMeta: obj.(*v2alpha1.DataUploadList).ListMeta} - for _, item := range obj.(*v2alpha1.DataUploadList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested dataUploads. -func (c *FakeDataUploads) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(datauploadsResource, c.ns, opts)) - -} - -// Create takes the representation of a dataUpload and creates it. Returns the server's representation of the dataUpload, and an error, if there is any. -func (c *FakeDataUploads) Create(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.CreateOptions) (result *v2alpha1.DataUpload, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(datauploadsResource, c.ns, dataUpload), &v2alpha1.DataUpload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataUpload), err -} - -// Update takes the representation of a dataUpload and updates it. Returns the server's representation of the dataUpload, and an error, if there is any. -func (c *FakeDataUploads) Update(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (result *v2alpha1.DataUpload, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(datauploadsResource, c.ns, dataUpload), &v2alpha1.DataUpload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataUpload), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeDataUploads) UpdateStatus(ctx context.Context, dataUpload *v2alpha1.DataUpload, opts v1.UpdateOptions) (*v2alpha1.DataUpload, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(datauploadsResource, "status", c.ns, dataUpload), &v2alpha1.DataUpload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataUpload), err -} - -// Delete takes name of the dataUpload and deletes it. Returns an error if one occurs. -func (c *FakeDataUploads) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(datauploadsResource, c.ns, name), &v2alpha1.DataUpload{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeDataUploads) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(datauploadsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v2alpha1.DataUploadList{}) - return err -} - -// Patch applies the patch and returns the patched dataUpload. -func (c *FakeDataUploads) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v2alpha1.DataUpload, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(datauploadsResource, c.ns, name, pt, data, subresources...), &v2alpha1.DataUpload{}) - - if obj == nil { - return nil, err - } - return obj.(*v2alpha1.DataUpload), err -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go deleted file mode 100644 index 25fee2e7a..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/fake/fake_velero_client.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v2alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeVeleroV2alpha1 struct { - *testing.Fake -} - -func (c *FakeVeleroV2alpha1) DataDownloads(namespace string) v2alpha1.DataDownloadInterface { - return &FakeDataDownloads{c, namespace} -} - -func (c *FakeVeleroV2alpha1) DataUploads(namespace string) v2alpha1.DataUploadInterface { - return &FakeDataUploads{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeVeleroV2alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go deleted file mode 100644 index 1ea0b5ae2..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/generated_expansion.go +++ /dev/null @@ -1,23 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v2alpha1 - -type DataDownloadExpansion interface{} - -type DataUploadExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.go deleted file mode 100644 index 6b2ea0980..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v2alpha1/velero_client.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/scheme" - rest "k8s.io/client-go/rest" -) - -type VeleroV2alpha1Interface interface { - RESTClient() rest.Interface - DataDownloadsGetter - DataUploadsGetter -} - -// VeleroV2alpha1Client is used to interact with features provided by the velero.io group. -type VeleroV2alpha1Client struct { - restClient rest.Interface -} - -func (c *VeleroV2alpha1Client) DataDownloads(namespace string) DataDownloadInterface { - return newDataDownloads(c, namespace) -} - -func (c *VeleroV2alpha1Client) DataUploads(namespace string) DataUploadInterface { - return newDataUploads(c, namespace) -} - -// NewForConfig creates a new VeleroV2alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*VeleroV2alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &VeleroV2alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new VeleroV2alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *VeleroV2alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new VeleroV2alpha1Client for the given RESTClient. -func New(c rest.Interface) *VeleroV2alpha1Client { - return &VeleroV2alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v2alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *VeleroV2alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go deleted file mode 100644 index d90132add..000000000 --- a/pkg/generated/informers/externalversions/factory.go +++ /dev/null @@ -1,180 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - reflect "reflect" - sync "sync" - time "time" - - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - velero "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// SharedInformerOption defines the functional option type for SharedInformerFactory. -type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory - -type sharedInformerFactory struct { - client versioned.Interface - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc - lock sync.Mutex - defaultResync time.Duration - customResync map[reflect.Type]time.Duration - - informers map[reflect.Type]cache.SharedIndexInformer - // startedInformers is used for tracking which informers have been started. - // This allows Start() to be called multiple times safely. - startedInformers map[reflect.Type]bool -} - -// WithCustomResyncConfig sets a custom resync period for the specified informer types. -func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - for k, v := range resyncConfig { - factory.customResync[reflect.TypeOf(k)] = v - } - return factory - } -} - -// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. -func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.tweakListOptions = tweakListOptions - return factory - } -} - -// WithNamespace limits the SharedInformerFactory to the specified namespace. -func WithNamespace(namespace string) SharedInformerOption { - return func(factory *sharedInformerFactory) *sharedInformerFactory { - factory.namespace = namespace - return factory - } -} - -// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. -func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync) -} - -// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. -// Listers obtained via this SharedInformerFactory will be subject to the same filters -// as specified here. -// Deprecated: Please use NewSharedInformerFactoryWithOptions instead -func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { - return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) -} - -// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. -func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { - factory := &sharedInformerFactory{ - client: client, - namespace: v1.NamespaceAll, - defaultResync: defaultResync, - informers: make(map[reflect.Type]cache.SharedIndexInformer), - startedInformers: make(map[reflect.Type]bool), - customResync: make(map[reflect.Type]time.Duration), - } - - // Apply all options - for _, opt := range options { - factory = opt(factory) - } - - return factory -} - -// Start initializes all requested informers. -func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { - f.lock.Lock() - defer f.lock.Unlock() - - for informerType, informer := range f.informers { - if !f.startedInformers[informerType] { - go informer.Run(stopCh) - f.startedInformers[informerType] = true - } - } -} - -// WaitForCacheSync waits for all started informers' cache were synced. -func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { - informers := func() map[reflect.Type]cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informers := map[reflect.Type]cache.SharedIndexInformer{} - for informerType, informer := range f.informers { - if f.startedInformers[informerType] { - informers[informerType] = informer - } - } - return informers - }() - - res := map[reflect.Type]bool{} - for informType, informer := range informers { - res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) - } - return res -} - -// InternalInformerFor returns the SharedIndexInformer for obj using an internal -// client. -func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { - f.lock.Lock() - defer f.lock.Unlock() - - informerType := reflect.TypeOf(obj) - informer, exists := f.informers[informerType] - if exists { - return informer - } - - resyncPeriod, exists := f.customResync[informerType] - if !exists { - resyncPeriod = f.defaultResync - } - - informer = newFunc(f.client, resyncPeriod) - f.informers[informerType] = informer - - return informer -} - -// SharedInformerFactory provides shared informers for resources in all known -// API group versions. -type SharedInformerFactory interface { - internalinterfaces.SharedInformerFactory - ForResource(resource schema.GroupVersionResource) (GenericInformer, error) - WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - - Velero() velero.Interface -} - -func (f *sharedInformerFactory) Velero() velero.Interface { - return velero.New(f, f.namespace, f.tweakListOptions) -} diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go deleted file mode 100644 index 7e0533afc..000000000 --- a/pkg/generated/informers/externalversions/generic.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package externalversions - -import ( - "fmt" - - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - schema "k8s.io/apimachinery/pkg/runtime/schema" - cache "k8s.io/client-go/tools/cache" -) - -// GenericInformer is type of SharedIndexInformer which will locate and delegate to other -// sharedInformers based on type -type GenericInformer interface { - Informer() cache.SharedIndexInformer - Lister() cache.GenericLister -} - -type genericInformer struct { - informer cache.SharedIndexInformer - resource schema.GroupResource -} - -// Informer returns the SharedIndexInformer. -func (f *genericInformer) Informer() cache.SharedIndexInformer { - return f.informer -} - -// Lister returns the GenericLister. -func (f *genericInformer) Lister() cache.GenericLister { - return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) -} - -// ForResource gives generic access to a shared informer of the matching type -// TODO extend this to unknown resources with a client pool -func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { - switch resource { - // Group=velero.io, Version=v1 - case v1.SchemeGroupVersion.WithResource("backups"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().Backups().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("backuprepositories"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().BackupRepositories().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("backupstoragelocations"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().BackupStorageLocations().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("deletebackuprequests"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().DeleteBackupRequests().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("downloadrequests"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().DownloadRequests().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("podvolumebackups"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().PodVolumeBackups().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("podvolumerestores"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().PodVolumeRestores().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("restores"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().Restores().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("schedules"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().Schedules().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("serverstatusrequests"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().ServerStatusRequests().Informer()}, nil - case v1.SchemeGroupVersion.WithResource("volumesnapshotlocations"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().VolumeSnapshotLocations().Informer()}, nil - - // Group=velero.io, Version=v2alpha1 - case v2alpha1.SchemeGroupVersion.WithResource("datadownloads"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V2alpha1().DataDownloads().Informer()}, nil - case v2alpha1.SchemeGroupVersion.WithResource("datauploads"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V2alpha1().DataUploads().Informer()}, nil - - } - - return nil, fmt.Errorf("no informer found for %v", resource) -} diff --git a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go deleted file mode 100644 index 4e78062c9..000000000 --- a/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package internalinterfaces - -import ( - time "time" - - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - cache "k8s.io/client-go/tools/cache" -) - -// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. -type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer - -// SharedInformerFactory a small interface to allow for adding an informer without an import cycle -type SharedInformerFactory interface { - Start(stopCh <-chan struct{}) - InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer -} - -// TweakListOptionsFunc is a function that transforms a v1.ListOptions. -type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/generated/informers/externalversions/velero/interface.go b/pkg/generated/informers/externalversions/velero/interface.go deleted file mode 100644 index 87fc652e6..000000000 --- a/pkg/generated/informers/externalversions/velero/interface.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package velero - -import ( - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" - v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v2alpha1" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1 provides access to shared informers for resources in V1. - V1() v1.Interface - // V2alpha1 provides access to shared informers for resources in V2alpha1. - V2alpha1() v2alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1 returns a new v1.Interface. -func (g *group) V1() v1.Interface { - return v1.New(g.factory, g.namespace, g.tweakListOptions) -} - -// V2alpha1 returns a new v2alpha1.Interface. -func (g *group) V2alpha1() v2alpha1.Interface { - return v2alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/backup.go b/pkg/generated/informers/externalversions/velero/v1/backup.go deleted file mode 100644 index f874a2090..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/backup.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// BackupInformer provides access to a shared informer and lister for -// Backups. -type BackupInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.BackupLister -} - -type backupInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewBackupInformer constructs a new informer for Backup type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredBackupInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredBackupInformer constructs a new informer for Backup type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().Backups(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().Backups(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.Backup{}, - resyncPeriod, - indexers, - ) -} - -func (f *backupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredBackupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *backupInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.Backup{}, f.defaultInformer) -} - -func (f *backupInformer) Lister() v1.BackupLister { - return v1.NewBackupLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/backuprepository.go b/pkg/generated/informers/externalversions/velero/v1/backuprepository.go deleted file mode 100644 index 59865c894..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/backuprepository.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// BackupRepositoryInformer provides access to a shared informer and lister for -// BackupRepositories. -type BackupRepositoryInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.BackupRepositoryLister -} - -type backupRepositoryInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewBackupRepositoryInformer constructs a new informer for BackupRepository type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewBackupRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredBackupRepositoryInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredBackupRepositoryInformer constructs a new informer for BackupRepository type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredBackupRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().BackupRepositories(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().BackupRepositories(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.BackupRepository{}, - resyncPeriod, - indexers, - ) -} - -func (f *backupRepositoryInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredBackupRepositoryInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *backupRepositoryInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.BackupRepository{}, f.defaultInformer) -} - -func (f *backupRepositoryInformer) Lister() v1.BackupRepositoryLister { - return v1.NewBackupRepositoryLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go b/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go deleted file mode 100644 index 4c732c8e6..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/backupstoragelocation.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// BackupStorageLocationInformer provides access to a shared informer and lister for -// BackupStorageLocations. -type BackupStorageLocationInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.BackupStorageLocationLister -} - -type backupStorageLocationInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewBackupStorageLocationInformer constructs a new informer for BackupStorageLocation type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewBackupStorageLocationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredBackupStorageLocationInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredBackupStorageLocationInformer constructs a new informer for BackupStorageLocation type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredBackupStorageLocationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().BackupStorageLocations(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().BackupStorageLocations(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.BackupStorageLocation{}, - resyncPeriod, - indexers, - ) -} - -func (f *backupStorageLocationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredBackupStorageLocationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *backupStorageLocationInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.BackupStorageLocation{}, f.defaultInformer) -} - -func (f *backupStorageLocationInformer) Lister() v1.BackupStorageLocationLister { - return v1.NewBackupStorageLocationLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/deletebackuprequest.go b/pkg/generated/informers/externalversions/velero/v1/deletebackuprequest.go deleted file mode 100644 index 7019d3bff..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/deletebackuprequest.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// DeleteBackupRequestInformer provides access to a shared informer and lister for -// DeleteBackupRequests. -type DeleteBackupRequestInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.DeleteBackupRequestLister -} - -type deleteBackupRequestInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewDeleteBackupRequestInformer constructs a new informer for DeleteBackupRequest type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewDeleteBackupRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredDeleteBackupRequestInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredDeleteBackupRequestInformer constructs a new informer for DeleteBackupRequest type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredDeleteBackupRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().DeleteBackupRequests(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().DeleteBackupRequests(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.DeleteBackupRequest{}, - resyncPeriod, - indexers, - ) -} - -func (f *deleteBackupRequestInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredDeleteBackupRequestInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *deleteBackupRequestInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.DeleteBackupRequest{}, f.defaultInformer) -} - -func (f *deleteBackupRequestInformer) Lister() v1.DeleteBackupRequestLister { - return v1.NewDeleteBackupRequestLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/downloadrequest.go b/pkg/generated/informers/externalversions/velero/v1/downloadrequest.go deleted file mode 100644 index 23d91e399..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/downloadrequest.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// DownloadRequestInformer provides access to a shared informer and lister for -// DownloadRequests. -type DownloadRequestInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.DownloadRequestLister -} - -type downloadRequestInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewDownloadRequestInformer constructs a new informer for DownloadRequest type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewDownloadRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredDownloadRequestInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredDownloadRequestInformer constructs a new informer for DownloadRequest type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredDownloadRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().DownloadRequests(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().DownloadRequests(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.DownloadRequest{}, - resyncPeriod, - indexers, - ) -} - -func (f *downloadRequestInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredDownloadRequestInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *downloadRequestInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.DownloadRequest{}, f.defaultInformer) -} - -func (f *downloadRequestInformer) Lister() v1.DownloadRequestLister { - return v1.NewDownloadRequestLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/interface.go b/pkg/generated/informers/externalversions/velero/v1/interface.go deleted file mode 100644 index 087dd3356..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/interface.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Backups returns a BackupInformer. - Backups() BackupInformer - // BackupRepositories returns a BackupRepositoryInformer. - BackupRepositories() BackupRepositoryInformer - // BackupStorageLocations returns a BackupStorageLocationInformer. - BackupStorageLocations() BackupStorageLocationInformer - // DeleteBackupRequests returns a DeleteBackupRequestInformer. - DeleteBackupRequests() DeleteBackupRequestInformer - // DownloadRequests returns a DownloadRequestInformer. - DownloadRequests() DownloadRequestInformer - // PodVolumeBackups returns a PodVolumeBackupInformer. - PodVolumeBackups() PodVolumeBackupInformer - // PodVolumeRestores returns a PodVolumeRestoreInformer. - PodVolumeRestores() PodVolumeRestoreInformer - // Restores returns a RestoreInformer. - Restores() RestoreInformer - // Schedules returns a ScheduleInformer. - Schedules() ScheduleInformer - // ServerStatusRequests returns a ServerStatusRequestInformer. - ServerStatusRequests() ServerStatusRequestInformer - // VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer. - VolumeSnapshotLocations() VolumeSnapshotLocationInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Backups returns a BackupInformer. -func (v *version) Backups() BackupInformer { - return &backupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// BackupRepositories returns a BackupRepositoryInformer. -func (v *version) BackupRepositories() BackupRepositoryInformer { - return &backupRepositoryInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// BackupStorageLocations returns a BackupStorageLocationInformer. -func (v *version) BackupStorageLocations() BackupStorageLocationInformer { - return &backupStorageLocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// DeleteBackupRequests returns a DeleteBackupRequestInformer. -func (v *version) DeleteBackupRequests() DeleteBackupRequestInformer { - return &deleteBackupRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// DownloadRequests returns a DownloadRequestInformer. -func (v *version) DownloadRequests() DownloadRequestInformer { - return &downloadRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// PodVolumeBackups returns a PodVolumeBackupInformer. -func (v *version) PodVolumeBackups() PodVolumeBackupInformer { - return &podVolumeBackupInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// PodVolumeRestores returns a PodVolumeRestoreInformer. -func (v *version) PodVolumeRestores() PodVolumeRestoreInformer { - return &podVolumeRestoreInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// Restores returns a RestoreInformer. -func (v *version) Restores() RestoreInformer { - return &restoreInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// Schedules returns a ScheduleInformer. -func (v *version) Schedules() ScheduleInformer { - return &scheduleInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// ServerStatusRequests returns a ServerStatusRequestInformer. -func (v *version) ServerStatusRequests() ServerStatusRequestInformer { - return &serverStatusRequestInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// VolumeSnapshotLocations returns a VolumeSnapshotLocationInformer. -func (v *version) VolumeSnapshotLocations() VolumeSnapshotLocationInformer { - return &volumeSnapshotLocationInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/generated/informers/externalversions/velero/v1/podvolumebackup.go b/pkg/generated/informers/externalversions/velero/v1/podvolumebackup.go deleted file mode 100644 index d2835b2ea..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/podvolumebackup.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PodVolumeBackupInformer provides access to a shared informer and lister for -// PodVolumeBackups. -type PodVolumeBackupInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.PodVolumeBackupLister -} - -type podVolumeBackupInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewPodVolumeBackupInformer constructs a new informer for PodVolumeBackup type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPodVolumeBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPodVolumeBackupInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredPodVolumeBackupInformer constructs a new informer for PodVolumeBackup type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPodVolumeBackupInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().PodVolumeBackups(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().PodVolumeBackups(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.PodVolumeBackup{}, - resyncPeriod, - indexers, - ) -} - -func (f *podVolumeBackupInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPodVolumeBackupInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *podVolumeBackupInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.PodVolumeBackup{}, f.defaultInformer) -} - -func (f *podVolumeBackupInformer) Lister() v1.PodVolumeBackupLister { - return v1.NewPodVolumeBackupLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/podvolumerestore.go b/pkg/generated/informers/externalversions/velero/v1/podvolumerestore.go deleted file mode 100644 index eccad43b2..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/podvolumerestore.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// PodVolumeRestoreInformer provides access to a shared informer and lister for -// PodVolumeRestores. -type PodVolumeRestoreInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.PodVolumeRestoreLister -} - -type podVolumeRestoreInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewPodVolumeRestoreInformer constructs a new informer for PodVolumeRestore type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewPodVolumeRestoreInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredPodVolumeRestoreInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredPodVolumeRestoreInformer constructs a new informer for PodVolumeRestore type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredPodVolumeRestoreInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().PodVolumeRestores(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().PodVolumeRestores(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.PodVolumeRestore{}, - resyncPeriod, - indexers, - ) -} - -func (f *podVolumeRestoreInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredPodVolumeRestoreInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *podVolumeRestoreInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.PodVolumeRestore{}, f.defaultInformer) -} - -func (f *podVolumeRestoreInformer) Lister() v1.PodVolumeRestoreLister { - return v1.NewPodVolumeRestoreLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/restore.go b/pkg/generated/informers/externalversions/velero/v1/restore.go deleted file mode 100644 index 691d1b7e8..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/restore.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// RestoreInformer provides access to a shared informer and lister for -// Restores. -type RestoreInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.RestoreLister -} - -type restoreInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewRestoreInformer constructs a new informer for Restore type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewRestoreInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredRestoreInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredRestoreInformer constructs a new informer for Restore type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredRestoreInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().Restores(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().Restores(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.Restore{}, - resyncPeriod, - indexers, - ) -} - -func (f *restoreInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredRestoreInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *restoreInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.Restore{}, f.defaultInformer) -} - -func (f *restoreInformer) Lister() v1.RestoreLister { - return v1.NewRestoreLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/schedule.go b/pkg/generated/informers/externalversions/velero/v1/schedule.go deleted file mode 100644 index 31114d809..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/schedule.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ScheduleInformer provides access to a shared informer and lister for -// Schedules. -type ScheduleInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.ScheduleLister -} - -type scheduleInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewScheduleInformer constructs a new informer for Schedule type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewScheduleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredScheduleInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredScheduleInformer constructs a new informer for Schedule type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredScheduleInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().Schedules(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().Schedules(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.Schedule{}, - resyncPeriod, - indexers, - ) -} - -func (f *scheduleInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredScheduleInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *scheduleInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.Schedule{}, f.defaultInformer) -} - -func (f *scheduleInformer) Lister() v1.ScheduleLister { - return v1.NewScheduleLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/serverstatusrequest.go b/pkg/generated/informers/externalversions/velero/v1/serverstatusrequest.go deleted file mode 100644 index 53290d408..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/serverstatusrequest.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ServerStatusRequestInformer provides access to a shared informer and lister for -// ServerStatusRequests. -type ServerStatusRequestInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.ServerStatusRequestLister -} - -type serverStatusRequestInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewServerStatusRequestInformer constructs a new informer for ServerStatusRequest type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewServerStatusRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredServerStatusRequestInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredServerStatusRequestInformer constructs a new informer for ServerStatusRequest type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredServerStatusRequestInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().ServerStatusRequests(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().ServerStatusRequests(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.ServerStatusRequest{}, - resyncPeriod, - indexers, - ) -} - -func (f *serverStatusRequestInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredServerStatusRequestInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *serverStatusRequestInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.ServerStatusRequest{}, f.defaultInformer) -} - -func (f *serverStatusRequestInformer) Lister() v1.ServerStatusRequestLister { - return v1.NewServerStatusRequestLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v1/volumesnapshotlocation.go b/pkg/generated/informers/externalversions/velero/v1/volumesnapshotlocation.go deleted file mode 100644 index 3b6c1eca1..000000000 --- a/pkg/generated/informers/externalversions/velero/v1/volumesnapshotlocation.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1 - -import ( - "context" - time "time" - - velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// VolumeSnapshotLocationInformer provides access to a shared informer and lister for -// VolumeSnapshotLocations. -type VolumeSnapshotLocationInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1.VolumeSnapshotLocationLister -} - -type volumeSnapshotLocationInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewVolumeSnapshotLocationInformer constructs a new informer for VolumeSnapshotLocation type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewVolumeSnapshotLocationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredVolumeSnapshotLocationInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredVolumeSnapshotLocationInformer constructs a new informer for VolumeSnapshotLocation type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredVolumeSnapshotLocationInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().VolumeSnapshotLocations(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV1().VolumeSnapshotLocations(namespace).Watch(context.TODO(), options) - }, - }, - &velerov1.VolumeSnapshotLocation{}, - resyncPeriod, - indexers, - ) -} - -func (f *volumeSnapshotLocationInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredVolumeSnapshotLocationInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *volumeSnapshotLocationInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.VolumeSnapshotLocation{}, f.defaultInformer) -} - -func (f *volumeSnapshotLocationInformer) Lister() v1.VolumeSnapshotLocationLister { - return v1.NewVolumeSnapshotLocationLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go b/pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go deleted file mode 100644 index 3b539b332..000000000 --- a/pkg/generated/informers/externalversions/velero/v2alpha1/datadownload.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - "context" - time "time" - - velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v2alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// DataDownloadInformer provides access to a shared informer and lister for -// DataDownloads. -type DataDownloadInformer interface { - Informer() cache.SharedIndexInformer - Lister() v2alpha1.DataDownloadLister -} - -type dataDownloadInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewDataDownloadInformer constructs a new informer for DataDownload type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewDataDownloadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredDataDownloadInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredDataDownloadInformer constructs a new informer for DataDownload type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredDataDownloadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV2alpha1().DataDownloads(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV2alpha1().DataDownloads(namespace).Watch(context.TODO(), options) - }, - }, - &velerov2alpha1.DataDownload{}, - resyncPeriod, - indexers, - ) -} - -func (f *dataDownloadInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredDataDownloadInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *dataDownloadInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov2alpha1.DataDownload{}, f.defaultInformer) -} - -func (f *dataDownloadInformer) Lister() v2alpha1.DataDownloadLister { - return v2alpha1.NewDataDownloadLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go b/pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go deleted file mode 100644 index f7e8f8d07..000000000 --- a/pkg/generated/informers/externalversions/velero/v2alpha1/dataupload.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - "context" - time "time" - - velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - versioned "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" - v2alpha1 "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v2alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// DataUploadInformer provides access to a shared informer and lister for -// DataUploads. -type DataUploadInformer interface { - Informer() cache.SharedIndexInformer - Lister() v2alpha1.DataUploadLister -} - -type dataUploadInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewDataUploadInformer constructs a new informer for DataUpload type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewDataUploadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredDataUploadInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredDataUploadInformer constructs a new informer for DataUpload type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredDataUploadInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV2alpha1().DataUploads(namespace).List(context.TODO(), options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.VeleroV2alpha1().DataUploads(namespace).Watch(context.TODO(), options) - }, - }, - &velerov2alpha1.DataUpload{}, - resyncPeriod, - indexers, - ) -} - -func (f *dataUploadInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredDataUploadInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *dataUploadInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov2alpha1.DataUpload{}, f.defaultInformer) -} - -func (f *dataUploadInformer) Lister() v2alpha1.DataUploadLister { - return v2alpha1.NewDataUploadLister(f.Informer().GetIndexer()) -} diff --git a/pkg/generated/informers/externalversions/velero/v2alpha1/interface.go b/pkg/generated/informers/externalversions/velero/v2alpha1/interface.go deleted file mode 100644 index 41f8edabf..000000000 --- a/pkg/generated/informers/externalversions/velero/v2alpha1/interface.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - internalinterfaces "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // DataDownloads returns a DataDownloadInformer. - DataDownloads() DataDownloadInformer - // DataUploads returns a DataUploadInformer. - DataUploads() DataUploadInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// DataDownloads returns a DataDownloadInformer. -func (v *version) DataDownloads() DataDownloadInformer { - return &dataDownloadInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// DataUploads returns a DataUploadInformer. -func (v *version) DataUploads() DataUploadInformer { - return &dataUploadInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/generated/listers/velero/v1/backup.go b/pkg/generated/listers/velero/v1/backup.go deleted file mode 100644 index fa3f5cb6f..000000000 --- a/pkg/generated/listers/velero/v1/backup.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// BackupLister helps list Backups. -// All objects returned here must be treated as read-only. -type BackupLister interface { - // List lists all Backups in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Backup, err error) - // Backups returns an object that can list and get Backups. - Backups(namespace string) BackupNamespaceLister - BackupListerExpansion -} - -// backupLister implements the BackupLister interface. -type backupLister struct { - indexer cache.Indexer -} - -// NewBackupLister returns a new BackupLister. -func NewBackupLister(indexer cache.Indexer) BackupLister { - return &backupLister{indexer: indexer} -} - -// List lists all Backups in the indexer. -func (s *backupLister) List(selector labels.Selector) (ret []*v1.Backup, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Backup)) - }) - return ret, err -} - -// Backups returns an object that can list and get Backups. -func (s *backupLister) Backups(namespace string) BackupNamespaceLister { - return backupNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// BackupNamespaceLister helps list and get Backups. -// All objects returned here must be treated as read-only. -type BackupNamespaceLister interface { - // List lists all Backups in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Backup, err error) - // Get retrieves the Backup from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.Backup, error) - BackupNamespaceListerExpansion -} - -// backupNamespaceLister implements the BackupNamespaceLister -// interface. -type backupNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Backups in the indexer for a given namespace. -func (s backupNamespaceLister) List(selector labels.Selector) (ret []*v1.Backup, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Backup)) - }) - return ret, err -} - -// Get retrieves the Backup from the indexer for a given namespace and name. -func (s backupNamespaceLister) Get(name string) (*v1.Backup, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("backup"), name) - } - return obj.(*v1.Backup), nil -} diff --git a/pkg/generated/listers/velero/v1/backuprepository.go b/pkg/generated/listers/velero/v1/backuprepository.go deleted file mode 100644 index ef619baf1..000000000 --- a/pkg/generated/listers/velero/v1/backuprepository.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// BackupRepositoryLister helps list BackupRepositories. -// All objects returned here must be treated as read-only. -type BackupRepositoryLister interface { - // List lists all BackupRepositories in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.BackupRepository, err error) - // BackupRepositories returns an object that can list and get BackupRepositories. - BackupRepositories(namespace string) BackupRepositoryNamespaceLister - BackupRepositoryListerExpansion -} - -// backupRepositoryLister implements the BackupRepositoryLister interface. -type backupRepositoryLister struct { - indexer cache.Indexer -} - -// NewBackupRepositoryLister returns a new BackupRepositoryLister. -func NewBackupRepositoryLister(indexer cache.Indexer) BackupRepositoryLister { - return &backupRepositoryLister{indexer: indexer} -} - -// List lists all BackupRepositories in the indexer. -func (s *backupRepositoryLister) List(selector labels.Selector) (ret []*v1.BackupRepository, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.BackupRepository)) - }) - return ret, err -} - -// BackupRepositories returns an object that can list and get BackupRepositories. -func (s *backupRepositoryLister) BackupRepositories(namespace string) BackupRepositoryNamespaceLister { - return backupRepositoryNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// BackupRepositoryNamespaceLister helps list and get BackupRepositories. -// All objects returned here must be treated as read-only. -type BackupRepositoryNamespaceLister interface { - // List lists all BackupRepositories in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.BackupRepository, err error) - // Get retrieves the BackupRepository from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.BackupRepository, error) - BackupRepositoryNamespaceListerExpansion -} - -// backupRepositoryNamespaceLister implements the BackupRepositoryNamespaceLister -// interface. -type backupRepositoryNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all BackupRepositories in the indexer for a given namespace. -func (s backupRepositoryNamespaceLister) List(selector labels.Selector) (ret []*v1.BackupRepository, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.BackupRepository)) - }) - return ret, err -} - -// Get retrieves the BackupRepository from the indexer for a given namespace and name. -func (s backupRepositoryNamespaceLister) Get(name string) (*v1.BackupRepository, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("backuprepository"), name) - } - return obj.(*v1.BackupRepository), nil -} diff --git a/pkg/generated/listers/velero/v1/backupstoragelocation.go b/pkg/generated/listers/velero/v1/backupstoragelocation.go deleted file mode 100644 index 74daf16dc..000000000 --- a/pkg/generated/listers/velero/v1/backupstoragelocation.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// BackupStorageLocationLister helps list BackupStorageLocations. -// All objects returned here must be treated as read-only. -type BackupStorageLocationLister interface { - // List lists all BackupStorageLocations in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) - // BackupStorageLocations returns an object that can list and get BackupStorageLocations. - BackupStorageLocations(namespace string) BackupStorageLocationNamespaceLister - BackupStorageLocationListerExpansion -} - -// backupStorageLocationLister implements the BackupStorageLocationLister interface. -type backupStorageLocationLister struct { - indexer cache.Indexer -} - -// NewBackupStorageLocationLister returns a new BackupStorageLocationLister. -func NewBackupStorageLocationLister(indexer cache.Indexer) BackupStorageLocationLister { - return &backupStorageLocationLister{indexer: indexer} -} - -// List lists all BackupStorageLocations in the indexer. -func (s *backupStorageLocationLister) List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.BackupStorageLocation)) - }) - return ret, err -} - -// BackupStorageLocations returns an object that can list and get BackupStorageLocations. -func (s *backupStorageLocationLister) BackupStorageLocations(namespace string) BackupStorageLocationNamespaceLister { - return backupStorageLocationNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// BackupStorageLocationNamespaceLister helps list and get BackupStorageLocations. -// All objects returned here must be treated as read-only. -type BackupStorageLocationNamespaceLister interface { - // List lists all BackupStorageLocations in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) - // Get retrieves the BackupStorageLocation from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.BackupStorageLocation, error) - BackupStorageLocationNamespaceListerExpansion -} - -// backupStorageLocationNamespaceLister implements the BackupStorageLocationNamespaceLister -// interface. -type backupStorageLocationNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all BackupStorageLocations in the indexer for a given namespace. -func (s backupStorageLocationNamespaceLister) List(selector labels.Selector) (ret []*v1.BackupStorageLocation, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.BackupStorageLocation)) - }) - return ret, err -} - -// Get retrieves the BackupStorageLocation from the indexer for a given namespace and name. -func (s backupStorageLocationNamespaceLister) Get(name string) (*v1.BackupStorageLocation, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("backupstoragelocation"), name) - } - return obj.(*v1.BackupStorageLocation), nil -} diff --git a/pkg/generated/listers/velero/v1/deletebackuprequest.go b/pkg/generated/listers/velero/v1/deletebackuprequest.go deleted file mode 100644 index 954e9aaf8..000000000 --- a/pkg/generated/listers/velero/v1/deletebackuprequest.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// DeleteBackupRequestLister helps list DeleteBackupRequests. -// All objects returned here must be treated as read-only. -type DeleteBackupRequestLister interface { - // List lists all DeleteBackupRequests in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.DeleteBackupRequest, err error) - // DeleteBackupRequests returns an object that can list and get DeleteBackupRequests. - DeleteBackupRequests(namespace string) DeleteBackupRequestNamespaceLister - DeleteBackupRequestListerExpansion -} - -// deleteBackupRequestLister implements the DeleteBackupRequestLister interface. -type deleteBackupRequestLister struct { - indexer cache.Indexer -} - -// NewDeleteBackupRequestLister returns a new DeleteBackupRequestLister. -func NewDeleteBackupRequestLister(indexer cache.Indexer) DeleteBackupRequestLister { - return &deleteBackupRequestLister{indexer: indexer} -} - -// List lists all DeleteBackupRequests in the indexer. -func (s *deleteBackupRequestLister) List(selector labels.Selector) (ret []*v1.DeleteBackupRequest, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.DeleteBackupRequest)) - }) - return ret, err -} - -// DeleteBackupRequests returns an object that can list and get DeleteBackupRequests. -func (s *deleteBackupRequestLister) DeleteBackupRequests(namespace string) DeleteBackupRequestNamespaceLister { - return deleteBackupRequestNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// DeleteBackupRequestNamespaceLister helps list and get DeleteBackupRequests. -// All objects returned here must be treated as read-only. -type DeleteBackupRequestNamespaceLister interface { - // List lists all DeleteBackupRequests in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.DeleteBackupRequest, err error) - // Get retrieves the DeleteBackupRequest from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.DeleteBackupRequest, error) - DeleteBackupRequestNamespaceListerExpansion -} - -// deleteBackupRequestNamespaceLister implements the DeleteBackupRequestNamespaceLister -// interface. -type deleteBackupRequestNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all DeleteBackupRequests in the indexer for a given namespace. -func (s deleteBackupRequestNamespaceLister) List(selector labels.Selector) (ret []*v1.DeleteBackupRequest, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.DeleteBackupRequest)) - }) - return ret, err -} - -// Get retrieves the DeleteBackupRequest from the indexer for a given namespace and name. -func (s deleteBackupRequestNamespaceLister) Get(name string) (*v1.DeleteBackupRequest, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("deletebackuprequest"), name) - } - return obj.(*v1.DeleteBackupRequest), nil -} diff --git a/pkg/generated/listers/velero/v1/downloadrequest.go b/pkg/generated/listers/velero/v1/downloadrequest.go deleted file mode 100644 index 6552cf02d..000000000 --- a/pkg/generated/listers/velero/v1/downloadrequest.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// DownloadRequestLister helps list DownloadRequests. -// All objects returned here must be treated as read-only. -type DownloadRequestLister interface { - // List lists all DownloadRequests in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.DownloadRequest, err error) - // DownloadRequests returns an object that can list and get DownloadRequests. - DownloadRequests(namespace string) DownloadRequestNamespaceLister - DownloadRequestListerExpansion -} - -// downloadRequestLister implements the DownloadRequestLister interface. -type downloadRequestLister struct { - indexer cache.Indexer -} - -// NewDownloadRequestLister returns a new DownloadRequestLister. -func NewDownloadRequestLister(indexer cache.Indexer) DownloadRequestLister { - return &downloadRequestLister{indexer: indexer} -} - -// List lists all DownloadRequests in the indexer. -func (s *downloadRequestLister) List(selector labels.Selector) (ret []*v1.DownloadRequest, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.DownloadRequest)) - }) - return ret, err -} - -// DownloadRequests returns an object that can list and get DownloadRequests. -func (s *downloadRequestLister) DownloadRequests(namespace string) DownloadRequestNamespaceLister { - return downloadRequestNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// DownloadRequestNamespaceLister helps list and get DownloadRequests. -// All objects returned here must be treated as read-only. -type DownloadRequestNamespaceLister interface { - // List lists all DownloadRequests in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.DownloadRequest, err error) - // Get retrieves the DownloadRequest from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.DownloadRequest, error) - DownloadRequestNamespaceListerExpansion -} - -// downloadRequestNamespaceLister implements the DownloadRequestNamespaceLister -// interface. -type downloadRequestNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all DownloadRequests in the indexer for a given namespace. -func (s downloadRequestNamespaceLister) List(selector labels.Selector) (ret []*v1.DownloadRequest, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.DownloadRequest)) - }) - return ret, err -} - -// Get retrieves the DownloadRequest from the indexer for a given namespace and name. -func (s downloadRequestNamespaceLister) Get(name string) (*v1.DownloadRequest, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("downloadrequest"), name) - } - return obj.(*v1.DownloadRequest), nil -} diff --git a/pkg/generated/listers/velero/v1/expansion_generated.go b/pkg/generated/listers/velero/v1/expansion_generated.go deleted file mode 100644 index c0cd57654..000000000 --- a/pkg/generated/listers/velero/v1/expansion_generated.go +++ /dev/null @@ -1,107 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -// BackupListerExpansion allows custom methods to be added to -// BackupLister. -type BackupListerExpansion interface{} - -// BackupNamespaceListerExpansion allows custom methods to be added to -// BackupNamespaceLister. -type BackupNamespaceListerExpansion interface{} - -// BackupRepositoryListerExpansion allows custom methods to be added to -// BackupRepositoryLister. -type BackupRepositoryListerExpansion interface{} - -// BackupRepositoryNamespaceListerExpansion allows custom methods to be added to -// BackupRepositoryNamespaceLister. -type BackupRepositoryNamespaceListerExpansion interface{} - -// BackupStorageLocationListerExpansion allows custom methods to be added to -// BackupStorageLocationLister. -type BackupStorageLocationListerExpansion interface{} - -// BackupStorageLocationNamespaceListerExpansion allows custom methods to be added to -// BackupStorageLocationNamespaceLister. -type BackupStorageLocationNamespaceListerExpansion interface{} - -// DeleteBackupRequestListerExpansion allows custom methods to be added to -// DeleteBackupRequestLister. -type DeleteBackupRequestListerExpansion interface{} - -// DeleteBackupRequestNamespaceListerExpansion allows custom methods to be added to -// DeleteBackupRequestNamespaceLister. -type DeleteBackupRequestNamespaceListerExpansion interface{} - -// DownloadRequestListerExpansion allows custom methods to be added to -// DownloadRequestLister. -type DownloadRequestListerExpansion interface{} - -// DownloadRequestNamespaceListerExpansion allows custom methods to be added to -// DownloadRequestNamespaceLister. -type DownloadRequestNamespaceListerExpansion interface{} - -// PodVolumeBackupListerExpansion allows custom methods to be added to -// PodVolumeBackupLister. -type PodVolumeBackupListerExpansion interface{} - -// PodVolumeBackupNamespaceListerExpansion allows custom methods to be added to -// PodVolumeBackupNamespaceLister. -type PodVolumeBackupNamespaceListerExpansion interface{} - -// PodVolumeRestoreListerExpansion allows custom methods to be added to -// PodVolumeRestoreLister. -type PodVolumeRestoreListerExpansion interface{} - -// PodVolumeRestoreNamespaceListerExpansion allows custom methods to be added to -// PodVolumeRestoreNamespaceLister. -type PodVolumeRestoreNamespaceListerExpansion interface{} - -// RestoreListerExpansion allows custom methods to be added to -// RestoreLister. -type RestoreListerExpansion interface{} - -// RestoreNamespaceListerExpansion allows custom methods to be added to -// RestoreNamespaceLister. -type RestoreNamespaceListerExpansion interface{} - -// ScheduleListerExpansion allows custom methods to be added to -// ScheduleLister. -type ScheduleListerExpansion interface{} - -// ScheduleNamespaceListerExpansion allows custom methods to be added to -// ScheduleNamespaceLister. -type ScheduleNamespaceListerExpansion interface{} - -// ServerStatusRequestListerExpansion allows custom methods to be added to -// ServerStatusRequestLister. -type ServerStatusRequestListerExpansion interface{} - -// ServerStatusRequestNamespaceListerExpansion allows custom methods to be added to -// ServerStatusRequestNamespaceLister. -type ServerStatusRequestNamespaceListerExpansion interface{} - -// VolumeSnapshotLocationListerExpansion allows custom methods to be added to -// VolumeSnapshotLocationLister. -type VolumeSnapshotLocationListerExpansion interface{} - -// VolumeSnapshotLocationNamespaceListerExpansion allows custom methods to be added to -// VolumeSnapshotLocationNamespaceLister. -type VolumeSnapshotLocationNamespaceListerExpansion interface{} diff --git a/pkg/generated/listers/velero/v1/podvolumebackup.go b/pkg/generated/listers/velero/v1/podvolumebackup.go deleted file mode 100644 index 08ed20d6f..000000000 --- a/pkg/generated/listers/velero/v1/podvolumebackup.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PodVolumeBackupLister helps list PodVolumeBackups. -// All objects returned here must be treated as read-only. -type PodVolumeBackupLister interface { - // List lists all PodVolumeBackups in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.PodVolumeBackup, err error) - // PodVolumeBackups returns an object that can list and get PodVolumeBackups. - PodVolumeBackups(namespace string) PodVolumeBackupNamespaceLister - PodVolumeBackupListerExpansion -} - -// podVolumeBackupLister implements the PodVolumeBackupLister interface. -type podVolumeBackupLister struct { - indexer cache.Indexer -} - -// NewPodVolumeBackupLister returns a new PodVolumeBackupLister. -func NewPodVolumeBackupLister(indexer cache.Indexer) PodVolumeBackupLister { - return &podVolumeBackupLister{indexer: indexer} -} - -// List lists all PodVolumeBackups in the indexer. -func (s *podVolumeBackupLister) List(selector labels.Selector) (ret []*v1.PodVolumeBackup, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.PodVolumeBackup)) - }) - return ret, err -} - -// PodVolumeBackups returns an object that can list and get PodVolumeBackups. -func (s *podVolumeBackupLister) PodVolumeBackups(namespace string) PodVolumeBackupNamespaceLister { - return podVolumeBackupNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// PodVolumeBackupNamespaceLister helps list and get PodVolumeBackups. -// All objects returned here must be treated as read-only. -type PodVolumeBackupNamespaceLister interface { - // List lists all PodVolumeBackups in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.PodVolumeBackup, err error) - // Get retrieves the PodVolumeBackup from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.PodVolumeBackup, error) - PodVolumeBackupNamespaceListerExpansion -} - -// podVolumeBackupNamespaceLister implements the PodVolumeBackupNamespaceLister -// interface. -type podVolumeBackupNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all PodVolumeBackups in the indexer for a given namespace. -func (s podVolumeBackupNamespaceLister) List(selector labels.Selector) (ret []*v1.PodVolumeBackup, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.PodVolumeBackup)) - }) - return ret, err -} - -// Get retrieves the PodVolumeBackup from the indexer for a given namespace and name. -func (s podVolumeBackupNamespaceLister) Get(name string) (*v1.PodVolumeBackup, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("podvolumebackup"), name) - } - return obj.(*v1.PodVolumeBackup), nil -} diff --git a/pkg/generated/listers/velero/v1/podvolumerestore.go b/pkg/generated/listers/velero/v1/podvolumerestore.go deleted file mode 100644 index 93f96b24b..000000000 --- a/pkg/generated/listers/velero/v1/podvolumerestore.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// PodVolumeRestoreLister helps list PodVolumeRestores. -// All objects returned here must be treated as read-only. -type PodVolumeRestoreLister interface { - // List lists all PodVolumeRestores in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.PodVolumeRestore, err error) - // PodVolumeRestores returns an object that can list and get PodVolumeRestores. - PodVolumeRestores(namespace string) PodVolumeRestoreNamespaceLister - PodVolumeRestoreListerExpansion -} - -// podVolumeRestoreLister implements the PodVolumeRestoreLister interface. -type podVolumeRestoreLister struct { - indexer cache.Indexer -} - -// NewPodVolumeRestoreLister returns a new PodVolumeRestoreLister. -func NewPodVolumeRestoreLister(indexer cache.Indexer) PodVolumeRestoreLister { - return &podVolumeRestoreLister{indexer: indexer} -} - -// List lists all PodVolumeRestores in the indexer. -func (s *podVolumeRestoreLister) List(selector labels.Selector) (ret []*v1.PodVolumeRestore, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.PodVolumeRestore)) - }) - return ret, err -} - -// PodVolumeRestores returns an object that can list and get PodVolumeRestores. -func (s *podVolumeRestoreLister) PodVolumeRestores(namespace string) PodVolumeRestoreNamespaceLister { - return podVolumeRestoreNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// PodVolumeRestoreNamespaceLister helps list and get PodVolumeRestores. -// All objects returned here must be treated as read-only. -type PodVolumeRestoreNamespaceLister interface { - // List lists all PodVolumeRestores in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.PodVolumeRestore, err error) - // Get retrieves the PodVolumeRestore from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.PodVolumeRestore, error) - PodVolumeRestoreNamespaceListerExpansion -} - -// podVolumeRestoreNamespaceLister implements the PodVolumeRestoreNamespaceLister -// interface. -type podVolumeRestoreNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all PodVolumeRestores in the indexer for a given namespace. -func (s podVolumeRestoreNamespaceLister) List(selector labels.Selector) (ret []*v1.PodVolumeRestore, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.PodVolumeRestore)) - }) - return ret, err -} - -// Get retrieves the PodVolumeRestore from the indexer for a given namespace and name. -func (s podVolumeRestoreNamespaceLister) Get(name string) (*v1.PodVolumeRestore, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("podvolumerestore"), name) - } - return obj.(*v1.PodVolumeRestore), nil -} diff --git a/pkg/generated/listers/velero/v1/restore.go b/pkg/generated/listers/velero/v1/restore.go deleted file mode 100644 index de0b89ce8..000000000 --- a/pkg/generated/listers/velero/v1/restore.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// RestoreLister helps list Restores. -// All objects returned here must be treated as read-only. -type RestoreLister interface { - // List lists all Restores in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Restore, err error) - // Restores returns an object that can list and get Restores. - Restores(namespace string) RestoreNamespaceLister - RestoreListerExpansion -} - -// restoreLister implements the RestoreLister interface. -type restoreLister struct { - indexer cache.Indexer -} - -// NewRestoreLister returns a new RestoreLister. -func NewRestoreLister(indexer cache.Indexer) RestoreLister { - return &restoreLister{indexer: indexer} -} - -// List lists all Restores in the indexer. -func (s *restoreLister) List(selector labels.Selector) (ret []*v1.Restore, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Restore)) - }) - return ret, err -} - -// Restores returns an object that can list and get Restores. -func (s *restoreLister) Restores(namespace string) RestoreNamespaceLister { - return restoreNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// RestoreNamespaceLister helps list and get Restores. -// All objects returned here must be treated as read-only. -type RestoreNamespaceLister interface { - // List lists all Restores in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Restore, err error) - // Get retrieves the Restore from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.Restore, error) - RestoreNamespaceListerExpansion -} - -// restoreNamespaceLister implements the RestoreNamespaceLister -// interface. -type restoreNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Restores in the indexer for a given namespace. -func (s restoreNamespaceLister) List(selector labels.Selector) (ret []*v1.Restore, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Restore)) - }) - return ret, err -} - -// Get retrieves the Restore from the indexer for a given namespace and name. -func (s restoreNamespaceLister) Get(name string) (*v1.Restore, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("restore"), name) - } - return obj.(*v1.Restore), nil -} diff --git a/pkg/generated/listers/velero/v1/schedule.go b/pkg/generated/listers/velero/v1/schedule.go deleted file mode 100644 index 90a262a46..000000000 --- a/pkg/generated/listers/velero/v1/schedule.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ScheduleLister helps list Schedules. -// All objects returned here must be treated as read-only. -type ScheduleLister interface { - // List lists all Schedules in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Schedule, err error) - // Schedules returns an object that can list and get Schedules. - Schedules(namespace string) ScheduleNamespaceLister - ScheduleListerExpansion -} - -// scheduleLister implements the ScheduleLister interface. -type scheduleLister struct { - indexer cache.Indexer -} - -// NewScheduleLister returns a new ScheduleLister. -func NewScheduleLister(indexer cache.Indexer) ScheduleLister { - return &scheduleLister{indexer: indexer} -} - -// List lists all Schedules in the indexer. -func (s *scheduleLister) List(selector labels.Selector) (ret []*v1.Schedule, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Schedule)) - }) - return ret, err -} - -// Schedules returns an object that can list and get Schedules. -func (s *scheduleLister) Schedules(namespace string) ScheduleNamespaceLister { - return scheduleNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// ScheduleNamespaceLister helps list and get Schedules. -// All objects returned here must be treated as read-only. -type ScheduleNamespaceLister interface { - // List lists all Schedules in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.Schedule, err error) - // Get retrieves the Schedule from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.Schedule, error) - ScheduleNamespaceListerExpansion -} - -// scheduleNamespaceLister implements the ScheduleNamespaceLister -// interface. -type scheduleNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Schedules in the indexer for a given namespace. -func (s scheduleNamespaceLister) List(selector labels.Selector) (ret []*v1.Schedule, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.Schedule)) - }) - return ret, err -} - -// Get retrieves the Schedule from the indexer for a given namespace and name. -func (s scheduleNamespaceLister) Get(name string) (*v1.Schedule, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("schedule"), name) - } - return obj.(*v1.Schedule), nil -} diff --git a/pkg/generated/listers/velero/v1/serverstatusrequest.go b/pkg/generated/listers/velero/v1/serverstatusrequest.go deleted file mode 100644 index c03b60c48..000000000 --- a/pkg/generated/listers/velero/v1/serverstatusrequest.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ServerStatusRequestLister helps list ServerStatusRequests. -// All objects returned here must be treated as read-only. -type ServerStatusRequestLister interface { - // List lists all ServerStatusRequests in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) - // ServerStatusRequests returns an object that can list and get ServerStatusRequests. - ServerStatusRequests(namespace string) ServerStatusRequestNamespaceLister - ServerStatusRequestListerExpansion -} - -// serverStatusRequestLister implements the ServerStatusRequestLister interface. -type serverStatusRequestLister struct { - indexer cache.Indexer -} - -// NewServerStatusRequestLister returns a new ServerStatusRequestLister. -func NewServerStatusRequestLister(indexer cache.Indexer) ServerStatusRequestLister { - return &serverStatusRequestLister{indexer: indexer} -} - -// List lists all ServerStatusRequests in the indexer. -func (s *serverStatusRequestLister) List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ServerStatusRequest)) - }) - return ret, err -} - -// ServerStatusRequests returns an object that can list and get ServerStatusRequests. -func (s *serverStatusRequestLister) ServerStatusRequests(namespace string) ServerStatusRequestNamespaceLister { - return serverStatusRequestNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// ServerStatusRequestNamespaceLister helps list and get ServerStatusRequests. -// All objects returned here must be treated as read-only. -type ServerStatusRequestNamespaceLister interface { - // List lists all ServerStatusRequests in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) - // Get retrieves the ServerStatusRequest from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.ServerStatusRequest, error) - ServerStatusRequestNamespaceListerExpansion -} - -// serverStatusRequestNamespaceLister implements the ServerStatusRequestNamespaceLister -// interface. -type serverStatusRequestNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ServerStatusRequests in the indexer for a given namespace. -func (s serverStatusRequestNamespaceLister) List(selector labels.Selector) (ret []*v1.ServerStatusRequest, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ServerStatusRequest)) - }) - return ret, err -} - -// Get retrieves the ServerStatusRequest from the indexer for a given namespace and name. -func (s serverStatusRequestNamespaceLister) Get(name string) (*v1.ServerStatusRequest, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("serverstatusrequest"), name) - } - return obj.(*v1.ServerStatusRequest), nil -} diff --git a/pkg/generated/listers/velero/v1/volumesnapshotlocation.go b/pkg/generated/listers/velero/v1/volumesnapshotlocation.go deleted file mode 100644 index 8c8aa432f..000000000 --- a/pkg/generated/listers/velero/v1/volumesnapshotlocation.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1 - -import ( - v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// VolumeSnapshotLocationLister helps list VolumeSnapshotLocations. -// All objects returned here must be treated as read-only. -type VolumeSnapshotLocationLister interface { - // List lists all VolumeSnapshotLocations in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.VolumeSnapshotLocation, err error) - // VolumeSnapshotLocations returns an object that can list and get VolumeSnapshotLocations. - VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationNamespaceLister - VolumeSnapshotLocationListerExpansion -} - -// volumeSnapshotLocationLister implements the VolumeSnapshotLocationLister interface. -type volumeSnapshotLocationLister struct { - indexer cache.Indexer -} - -// NewVolumeSnapshotLocationLister returns a new VolumeSnapshotLocationLister. -func NewVolumeSnapshotLocationLister(indexer cache.Indexer) VolumeSnapshotLocationLister { - return &volumeSnapshotLocationLister{indexer: indexer} -} - -// List lists all VolumeSnapshotLocations in the indexer. -func (s *volumeSnapshotLocationLister) List(selector labels.Selector) (ret []*v1.VolumeSnapshotLocation, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.VolumeSnapshotLocation)) - }) - return ret, err -} - -// VolumeSnapshotLocations returns an object that can list and get VolumeSnapshotLocations. -func (s *volumeSnapshotLocationLister) VolumeSnapshotLocations(namespace string) VolumeSnapshotLocationNamespaceLister { - return volumeSnapshotLocationNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// VolumeSnapshotLocationNamespaceLister helps list and get VolumeSnapshotLocations. -// All objects returned here must be treated as read-only. -type VolumeSnapshotLocationNamespaceLister interface { - // List lists all VolumeSnapshotLocations in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.VolumeSnapshotLocation, err error) - // Get retrieves the VolumeSnapshotLocation from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.VolumeSnapshotLocation, error) - VolumeSnapshotLocationNamespaceListerExpansion -} - -// volumeSnapshotLocationNamespaceLister implements the VolumeSnapshotLocationNamespaceLister -// interface. -type volumeSnapshotLocationNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all VolumeSnapshotLocations in the indexer for a given namespace. -func (s volumeSnapshotLocationNamespaceLister) List(selector labels.Selector) (ret []*v1.VolumeSnapshotLocation, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.VolumeSnapshotLocation)) - }) - return ret, err -} - -// Get retrieves the VolumeSnapshotLocation from the indexer for a given namespace and name. -func (s volumeSnapshotLocationNamespaceLister) Get(name string) (*v1.VolumeSnapshotLocation, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("volumesnapshotlocation"), name) - } - return obj.(*v1.VolumeSnapshotLocation), nil -} diff --git a/pkg/generated/listers/velero/v2alpha1/datadownload.go b/pkg/generated/listers/velero/v2alpha1/datadownload.go deleted file mode 100644 index dadf14b60..000000000 --- a/pkg/generated/listers/velero/v2alpha1/datadownload.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// DataDownloadLister helps list DataDownloads. -// All objects returned here must be treated as read-only. -type DataDownloadLister interface { - // List lists all DataDownloads in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) - // DataDownloads returns an object that can list and get DataDownloads. - DataDownloads(namespace string) DataDownloadNamespaceLister - DataDownloadListerExpansion -} - -// dataDownloadLister implements the DataDownloadLister interface. -type dataDownloadLister struct { - indexer cache.Indexer -} - -// NewDataDownloadLister returns a new DataDownloadLister. -func NewDataDownloadLister(indexer cache.Indexer) DataDownloadLister { - return &dataDownloadLister{indexer: indexer} -} - -// List lists all DataDownloads in the indexer. -func (s *dataDownloadLister) List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v2alpha1.DataDownload)) - }) - return ret, err -} - -// DataDownloads returns an object that can list and get DataDownloads. -func (s *dataDownloadLister) DataDownloads(namespace string) DataDownloadNamespaceLister { - return dataDownloadNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// DataDownloadNamespaceLister helps list and get DataDownloads. -// All objects returned here must be treated as read-only. -type DataDownloadNamespaceLister interface { - // List lists all DataDownloads in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) - // Get retrieves the DataDownload from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v2alpha1.DataDownload, error) - DataDownloadNamespaceListerExpansion -} - -// dataDownloadNamespaceLister implements the DataDownloadNamespaceLister -// interface. -type dataDownloadNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all DataDownloads in the indexer for a given namespace. -func (s dataDownloadNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.DataDownload, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v2alpha1.DataDownload)) - }) - return ret, err -} - -// Get retrieves the DataDownload from the indexer for a given namespace and name. -func (s dataDownloadNamespaceLister) Get(name string) (*v2alpha1.DataDownload, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v2alpha1.Resource("datadownload"), name) - } - return obj.(*v2alpha1.DataDownload), nil -} diff --git a/pkg/generated/listers/velero/v2alpha1/dataupload.go b/pkg/generated/listers/velero/v2alpha1/dataupload.go deleted file mode 100644 index 0dbe6bed1..000000000 --- a/pkg/generated/listers/velero/v2alpha1/dataupload.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v2alpha1 - -import ( - v2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// DataUploadLister helps list DataUploads. -// All objects returned here must be treated as read-only. -type DataUploadLister interface { - // List lists all DataUploads in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) - // DataUploads returns an object that can list and get DataUploads. - DataUploads(namespace string) DataUploadNamespaceLister - DataUploadListerExpansion -} - -// dataUploadLister implements the DataUploadLister interface. -type dataUploadLister struct { - indexer cache.Indexer -} - -// NewDataUploadLister returns a new DataUploadLister. -func NewDataUploadLister(indexer cache.Indexer) DataUploadLister { - return &dataUploadLister{indexer: indexer} -} - -// List lists all DataUploads in the indexer. -func (s *dataUploadLister) List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v2alpha1.DataUpload)) - }) - return ret, err -} - -// DataUploads returns an object that can list and get DataUploads. -func (s *dataUploadLister) DataUploads(namespace string) DataUploadNamespaceLister { - return dataUploadNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// DataUploadNamespaceLister helps list and get DataUploads. -// All objects returned here must be treated as read-only. -type DataUploadNamespaceLister interface { - // List lists all DataUploads in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) - // Get retrieves the DataUpload from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v2alpha1.DataUpload, error) - DataUploadNamespaceListerExpansion -} - -// dataUploadNamespaceLister implements the DataUploadNamespaceLister -// interface. -type dataUploadNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all DataUploads in the indexer for a given namespace. -func (s dataUploadNamespaceLister) List(selector labels.Selector) (ret []*v2alpha1.DataUpload, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v2alpha1.DataUpload)) - }) - return ret, err -} - -// Get retrieves the DataUpload from the indexer for a given namespace and name. -func (s dataUploadNamespaceLister) Get(name string) (*v2alpha1.DataUpload, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v2alpha1.Resource("dataupload"), name) - } - return obj.(*v2alpha1.DataUpload), nil -} diff --git a/pkg/generated/listers/velero/v2alpha1/expansion_generated.go b/pkg/generated/listers/velero/v2alpha1/expansion_generated.go deleted file mode 100644 index 1bdb85ec0..000000000 --- a/pkg/generated/listers/velero/v2alpha1/expansion_generated.go +++ /dev/null @@ -1,35 +0,0 @@ -/* -Copyright the Velero contributors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v2alpha1 - -// DataDownloadListerExpansion allows custom methods to be added to -// DataDownloadLister. -type DataDownloadListerExpansion interface{} - -// DataDownloadNamespaceListerExpansion allows custom methods to be added to -// DataDownloadNamespaceLister. -type DataDownloadNamespaceListerExpansion interface{} - -// DataUploadListerExpansion allows custom methods to be added to -// DataUploadLister. -type DataUploadListerExpansion interface{} - -// DataUploadNamespaceListerExpansion allows custom methods to be added to -// DataUploadNamespaceLister. -type DataUploadNamespaceListerExpansion interface{} From ed0ef67c16dbd119365527e2581a6af505b555f1 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 14 Aug 2024 13:16:35 +0800 Subject: [PATCH 54/69] data mover ms smoke testing Signed-off-by: Lyndon-Li --- pkg/cmd/cli/datamover/backup.go | 1 + pkg/cmd/cli/datamover/restore.go | 3 +- pkg/cmd/cli/nodeagent/server.go | 6 +- pkg/controller/data_download_controller.go | 58 ++++++++----------- .../data_download_controller_test.go | 13 +---- pkg/controller/data_upload_controller.go | 24 +++----- pkg/controller/data_upload_controller_test.go | 21 +------ .../pod_volume_backup_controller.go | 6 +- .../pod_volume_restore_controller.go | 6 +- pkg/datapath/micro_service_watcher.go | 29 +--------- 10 files changed, 50 insertions(+), 117 deletions(-) diff --git a/pkg/cmd/cli/datamover/backup.go b/pkg/cmd/cli/datamover/backup.go index ca600faab..4d704b04c 100644 --- a/pkg/cmd/cli/datamover/backup.go +++ b/pkg/cmd/cli/datamover/backup.go @@ -224,6 +224,7 @@ func (s *dataMoverBackup) runDataPath() { err = dpService.Init() if err != nil { + dpService.Shutdown() s.cancelFunc() funcExitWithMessage(s.logger, false, "Failed to init data path service for DataUpload %s: %v", s.config.duName, err) return diff --git a/pkg/cmd/cli/datamover/restore.go b/pkg/cmd/cli/datamover/restore.go index fc74a64f1..244060cc9 100644 --- a/pkg/cmd/cli/datamover/restore.go +++ b/pkg/cmd/cli/datamover/restore.go @@ -215,6 +215,7 @@ func (s *dataMoverRestore) runDataPath() { err = dpService.Init() if err != nil { + dpService.Shutdown() s.cancelFunc() funcExitWithMessage(s.logger, false, "Failed to init data path service for DataDownload %s: %v", s.config.ddName, err) return @@ -222,8 +223,8 @@ func (s *dataMoverRestore) runDataPath() { result, err := dpService.RunCancelableDataPath(s.ctx) if err != nil { - s.cancelFunc() dpService.Shutdown() + s.cancelFunc() funcExitWithMessage(s.logger, false, "Failed to run data path service for DataDownload %s: %v", s.config.ddName, err) return } diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 181afbf69..f30c8df4a 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -292,12 +292,12 @@ func (s *nodeAgentServer) run() { if s.dataPathConfigs != nil && len(s.dataPathConfigs.LoadAffinity) > 0 { loadAffinity = s.dataPathConfigs.LoadAffinity[0] } - dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, repoEnsurer, clock.RealClock{}, credentialGetter, s.nodeName, s.fileSystem, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, clock.RealClock{}, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataUploadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data upload controller") } - dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, repoEnsurer, credentialGetter, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataDownloadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data download controller") } @@ -312,8 +312,6 @@ func (s *nodeAgentServer) run() { if err := dataDownloadReconciler.AttemptDataDownloadResume(s.ctx, s.mgr.GetClient(), s.logger.WithField("node", s.nodeName), s.namespace); err != nil { s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data download resume") } - - s.logger.Info("Attempt complete to resume dataUploads and dataDownloads") }() s.logger.Info("Controllers starting...") diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index a161f60bd..0b9805a0a 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -39,7 +39,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" @@ -47,45 +46,37 @@ import ( "github.com/vmware-tanzu/velero/pkg/datapath" "github.com/vmware-tanzu/velero/pkg/exposer" "github.com/vmware-tanzu/velero/pkg/metrics" - repository "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/uploader" - "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" ) // DataDownloadReconciler reconciles a DataDownload object type DataDownloadReconciler struct { - client client.Client - kubeClient kubernetes.Interface - mgr manager.Manager - logger logrus.FieldLogger - credentialGetter *credentials.CredentialGetter - fileSystem filesystem.Interface - Clock clock.WithTickerAndDelayedExecution - restoreExposer exposer.GenericRestoreExposer - nodeName string - repositoryEnsurer *repository.Ensurer - dataPathMgr *datapath.Manager - preparingTimeout time.Duration - metrics *metrics.ServerMetrics + client client.Client + kubeClient kubernetes.Interface + mgr manager.Manager + logger logrus.FieldLogger + Clock clock.WithTickerAndDelayedExecution + restoreExposer exposer.GenericRestoreExposer + nodeName string + dataPathMgr *datapath.Manager + preparingTimeout time.Duration + metrics *metrics.ServerMetrics } func NewDataDownloadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, - repoEnsurer *repository.Ensurer, credentialGetter *credentials.CredentialGetter, nodeName string, preparingTimeout time.Duration, logger logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataDownloadReconciler { + nodeName string, preparingTimeout time.Duration, logger logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataDownloadReconciler { return &DataDownloadReconciler{ - client: client, - kubeClient: kubeClient, - mgr: mgr, - logger: logger.WithField("controller", "DataDownload"), - credentialGetter: credentialGetter, - fileSystem: filesystem.NewFileSystem(), - Clock: &clock.RealClock{}, - nodeName: nodeName, - repositoryEnsurer: repoEnsurer, - restoreExposer: exposer.NewGenericRestoreExposer(kubeClient, logger), - dataPathMgr: dataPathMgr, - preparingTimeout: preparingTimeout, - metrics: metrics, + client: client, + kubeClient: kubeClient, + mgr: mgr, + logger: logger.WithField("controller", "DataDownload"), + Clock: &clock.RealClock{}, + nodeName: nodeName, + restoreExposer: exposer.NewGenericRestoreExposer(kubeClient, logger), + dataPathMgr: dataPathMgr, + preparingTimeout: preparingTimeout, + metrics: metrics, } } @@ -282,7 +273,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request } if err := r.initCancelableDataPath(ctx, asyncBR, result, log); err != nil { - log.WithError(err).Warnf("Failed to init cancelable data path for %s, will close and retry", dd.Name) + log.WithError(err).Errorf("Failed to init cancelable data path for %s", dd.Name) r.closeDataPath(ctx, dd.Name) return r.errorOut(ctx, dd, err, "error initializing data path", log) @@ -372,7 +363,7 @@ func (r *DataDownloadReconciler) startCancelableDataPath(asyncBR datapath.AsyncB return errors.Wrapf(err, "error starting async restore for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) } - log.Info("Async restore started for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) + log.Infof("Async restore started for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) return nil } @@ -420,7 +411,7 @@ func (r *DataDownloadReconciler) OnDataDownloadFailed(ctx context.Context, names if getErr := r.client.Get(ctx, types.NamespacedName{Name: ddName, Namespace: namespace}, &dd); getErr != nil { log.WithError(getErr).Warn("Failed to get data download on failure") } else { - r.errorOut(ctx, &dd, err, "data path restore failed", log) + _, _ = r.errorOut(ctx, &dd, err, "data path restore failed", log) } } @@ -797,6 +788,7 @@ func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, err := funcResumeCancellableDataRestore(r, ctx, dd, logger) if err == nil { + logger.WithField("dd", dd.Name).WithField("current node", r.nodeName).Info("Completed to resume in progress DD") continue } diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index d20db30f0..bb9fe6f7c 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -43,7 +43,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" - "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/builder" @@ -139,19 +138,9 @@ func initDataDownloadReconcilerWithError(objects []runtime.Object, needError ... return nil, err } - credentialFileStore, err := credentials.NewNamespacedFileStore( - fakeClient, - velerov1api.DefaultNamespace, - "/tmp/credentials", - fakeFS, - ) - if err != nil { - return nil, err - } - dataPathMgr := datapath.NewManager(1) - return NewDataDownloadReconciler(fakeClient, nil, fakeKubeClient, dataPathMgr, nil, &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil + return NewDataDownloadReconciler(fakeClient, nil, fakeKubeClient, dataPathMgr, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } func TestDataDownloadReconcile(t *testing.T) { diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index c6a15ceca..fb9e75709 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -41,7 +41,6 @@ import ( snapshotter "github.com/kubernetes-csi/external-snapshotter/client/v7/clientset/versioned/typed/volumesnapshot/v1" - "github.com/vmware-tanzu/velero/internal/credentials" "github.com/vmware-tanzu/velero/pkg/apis/velero/shared" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" @@ -50,9 +49,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/exposer" "github.com/vmware-tanzu/velero/pkg/metrics" "github.com/vmware-tanzu/velero/pkg/nodeagent" - "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/uploader" - "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -69,11 +66,8 @@ type DataUploadReconciler struct { kubeClient kubernetes.Interface csiSnapshotClient snapshotter.SnapshotV1Interface mgr manager.Manager - repoEnsurer *repository.Ensurer Clock clocks.WithTickerAndDelayedExecution - credentialGetter *credentials.CredentialGetter nodeName string - fileSystem filesystem.Interface logger logrus.FieldLogger snapshotExposerList map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer dataPathMgr *datapath.Manager @@ -83,19 +77,16 @@ type DataUploadReconciler struct { } func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, - dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, repoEnsurer *repository.Ensurer, clock clocks.WithTickerAndDelayedExecution, - cred *credentials.CredentialGetter, nodeName string, fs filesystem.Interface, preparingTimeout time.Duration, log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { + dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, clock clocks.WithTickerAndDelayedExecution, nodeName string, preparingTimeout time.Duration, + log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { return &DataUploadReconciler{ client: client, mgr: mgr, kubeClient: kubeClient, csiSnapshotClient: csiSnapshotClient, Clock: clock, - credentialGetter: cred, nodeName: nodeName, - fileSystem: fs, logger: log, - repoEnsurer: repoEnsurer, snapshotExposerList: map[velerov2alpha1api.SnapshotType]exposer.SnapshotExposer{velerov2alpha1api.SnapshotTypeCSI: exposer.NewCSISnapshotExposer(kubeClient, csiSnapshotClient, log)}, dataPathMgr: dataPathMgr, loadAffinity: loadAffinity, @@ -293,7 +284,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request) } if err := r.initCancelableDataPath(ctx, asyncBR, res, log); err != nil { - log.WithError(err).Warnf("Failed to init cancelable data path for %s, will close and retry", du.Name) + log.WithError(err).Errorf("Failed to init cancelable data path for %s", du.Name) r.closeDataPath(ctx, du.Name) return r.errorOut(ctx, du, err, "error initializing data path", log) @@ -383,7 +374,7 @@ func (r *DataUploadReconciler) startCancelableDataPath(asyncBR datapath.AsyncBR, return errors.Wrapf(err, "error starting async backup for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) } - log.Info("Async backup started for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) + log.Infof("Async backup started for pod %s, volume %s", res.ByPod.HostingPod.Name, res.ByPod.VolumeName) return nil } @@ -442,7 +433,7 @@ func (r *DataUploadReconciler) OnDataUploadFailed(ctx context.Context, namespace if getErr := r.client.Get(ctx, types.NamespacedName{Name: duName, Namespace: namespace}, &du); getErr != nil { log.WithError(getErr).Warn("Failed to get dataupload on failure") } else { - r.errorOut(ctx, &du, err, "data path backup failed", log) + _, _ = r.errorOut(ctx, &du, err, "data path backup failed", log) } } @@ -502,7 +493,7 @@ func (r *DataUploadReconciler) tryCancelAcceptedDataUpload(ctx context.Context, r.cleanUp(ctx, du, log) } -func (r *DataUploadReconciler) cleanUp(ctx context.Context, du *velerov2alpha1api.DataUpload, log *logrus.Entry) { +func (r *DataUploadReconciler) cleanUp(ctx context.Context, du *velerov2alpha1api.DataUpload, log logrus.FieldLogger) { ep, ok := r.snapshotExposerList[du.Spec.SnapshotType] if !ok { log.WithError(fmt.Errorf("%v type of snapshot exposer is not exist", du.Spec.SnapshotType)). @@ -650,7 +641,7 @@ func (r *DataUploadReconciler) errorOut(ctx context.Context, du *velerov2alpha1a } se.CleanUp(ctx, getOwnerObject(du), volumeSnapshotName, du.Spec.SourceNamespace) } else { - log.Warnf("failed to clean up exposed snapshot could not find %s snapshot exposer", du.Spec.SnapshotType) + log.Errorf("failed to clean up exposed snapshot could not find %s snapshot exposer", du.Spec.SnapshotType) } return ctrl.Result{}, r.updateStatusToFailed(ctx, du, err, msg, log) @@ -894,6 +885,7 @@ func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, cli err := funcResumeCancellableDataBackup(r, ctx, du, logger) if err == nil { + logger.WithField("du", du.Name).WithField("current node", r.nodeName).Info("Completed to resume in progress DU") continue } diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index 8df98b60d..e4c98f196 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -47,7 +47,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov2alpha1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1" "github.com/vmware-tanzu/velero/pkg/builder" @@ -229,24 +228,9 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci fakeSnapshotClient := snapshotFake.NewSimpleClientset(vsObject, vscObj) fakeKubeClient := clientgofake.NewSimpleClientset(daemonSet) - fakeFS := velerotest.NewFakeFileSystem() - pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", "", dataUploadName) - _, err = fakeFS.Create(pathGlob) - if err != nil { - return nil, err - } - credentialFileStore, err := credentials.NewNamespacedFileStore( - fakeClient, - velerov1api.DefaultNamespace, - "/tmp/credentials", - fakeFS, - ) - if err != nil { - return nil, err - } - return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, nil, - testclocks.NewFakeClock(now), &credentials.CredentialGetter{FromFile: credentialFileStore}, "test-node", fakeFS, time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil + return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, + testclocks.NewFakeClock(now), "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } func dataUploadBuilder() *builder.DataUploadBuilder { @@ -626,7 +610,6 @@ func TestReconcile(t *testing.T) { if !testCreateFsBR && du.Status.Phase != velerov2alpha1api.DataUploadPhaseInProgress { assert.Nil(t, r.dataPathMgr.GetAsyncBR(test.du.Name)) } - }) } } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index e626df2b3..548ab0d0c 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -202,7 +202,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ } func (r *PodVolumeBackupReconciler) OnDataPathCompleted(ctx context.Context, namespace string, pvbName string, result datapath.Result) { - defer r.closeDataPath(ctx, pvbName) + defer r.dataPathMgr.RemoveAsyncBR(pvbName) log := r.logger.WithField("pvb", pvbName) @@ -240,7 +240,7 @@ func (r *PodVolumeBackupReconciler) OnDataPathCompleted(ctx context.Context, nam } func (r *PodVolumeBackupReconciler) OnDataPathFailed(ctx context.Context, namespace, pvbName string, err error) { - defer r.closeDataPath(ctx, pvbName) + defer r.dataPathMgr.RemoveAsyncBR(pvbName) log := r.logger.WithField("pvb", pvbName) @@ -255,7 +255,7 @@ func (r *PodVolumeBackupReconciler) OnDataPathFailed(ctx context.Context, namesp } func (r *PodVolumeBackupReconciler) OnDataPathCancelled(ctx context.Context, namespace string, pvbName string) { - defer r.closeDataPath(ctx, pvbName) + defer r.dataPathMgr.RemoveAsyncBR(pvbName) log := r.logger.WithField("pvb", pvbName) diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3f285789d..c4a3e7451 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -265,7 +265,7 @@ func getInitContainerIndex(pod *corev1api.Pod) int { } func (c *PodVolumeRestoreReconciler) OnDataPathCompleted(ctx context.Context, namespace string, pvrName string, result datapath.Result) { - defer c.closeDataPath(ctx, pvrName) + defer c.dataPathMgr.RemoveAsyncBR(pvrName) log := c.logger.WithField("pvr", pvrName) @@ -325,7 +325,7 @@ func (c *PodVolumeRestoreReconciler) OnDataPathCompleted(ctx context.Context, na } func (c *PodVolumeRestoreReconciler) OnDataPathFailed(ctx context.Context, namespace string, pvrName string, err error) { - defer c.closeDataPath(ctx, pvrName) + defer c.dataPathMgr.RemoveAsyncBR(pvrName) log := c.logger.WithField("pvr", pvrName) @@ -340,7 +340,7 @@ func (c *PodVolumeRestoreReconciler) OnDataPathFailed(ctx context.Context, names } func (c *PodVolumeRestoreReconciler) OnDataPathCancelled(ctx context.Context, namespace string, pvrName string) { - defer c.closeDataPath(ctx, pvrName) + defer c.dataPathMgr.RemoveAsyncBR(pvrName) log := c.logger.WithField("pvr", pvrName) diff --git a/pkg/datapath/micro_service_watcher.go b/pkg/datapath/micro_service_watcher.go index 8a129bde8..d74ca2fc2 100644 --- a/pkg/datapath/micro_service_watcher.go +++ b/pkg/datapath/micro_service_watcher.go @@ -103,8 +103,6 @@ func newMicroServiceBRWatcher(client client.Client, kubeClient kubernetes.Interf } func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) error { - succeeded := false - eventInformer, err := ms.mgr.GetCache().GetInformer(ctx, &v1.Event{}) if err != nil { return errors.Wrap(err, "error getting event informer") @@ -135,19 +133,10 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er }, }, ) - if err != nil { return errors.Wrap(err, "error registering event handler") } - defer func() { - if !succeeded { - if err := eventInformer.RemoveEventHandler(eventHandler); err != nil { - ms.log.WithError(err).Warn("Failed to remove event handler") - } - } - }() - podHandler, err := podInformer.AddEventHandler( cache.ResourceEventHandlerFuncs{ UpdateFunc: func(_, obj interface{}) { @@ -162,19 +151,10 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er }, }, ) - if err != nil { return errors.Wrap(err, "error registering pod handler") } - defer func() { - if !succeeded { - if err := podInformer.RemoveEventHandler(podHandler); err != nil { - ms.log.WithError(err).Warn("Failed to remove pod handler") - } - } - }() - if err := ms.reEnsureThisPod(ctx); err != nil { return err } @@ -193,10 +173,7 @@ func (ms *microServiceBRWatcher) Init(ctx context.Context, param interface{}) er "thisPod": ms.thisPod, }).Info("MicroServiceBR is initialized") - succeeded = true - return nil - } func (ms *microServiceBRWatcher) Close(ctx context.Context) { @@ -301,7 +278,7 @@ func (ms *microServiceBRWatcher) startWatch() { } if lastPod == nil { - ms.log.Warn("Watch loop is cancelled on waiting data path pod") + ms.log.Warn("Watch loop is canceled on waiting data path pod") return } @@ -309,7 +286,7 @@ func (ms *microServiceBRWatcher) startWatch() { for !ms.startedFromEvent || !ms.terminatedFromEvent { select { case <-ms.ctx.Done(): - ms.log.Warn("Watch loop is cancelled on waiting final event") + ms.log.Warn("Watch loop is canceled on waiting final event") return case <-time.After(eventWaitTimeout): break epilogLoop @@ -373,7 +350,7 @@ func (ms *microServiceBRWatcher) onEvent(evt *v1.Event) { case EventReasonCancelling: ms.log.Infof("Received data path canceling message: %s", evt.Message) default: - ms.log.Infof("Received event for data path %s,reason: %s, message: %s", ms.taskName, evt.Reason, evt.Message) + ms.log.Infof("Received event for data path %s, reason: %s, message: %s", ms.taskName, evt.Reason, evt.Message) } } From 0ed1a7fc8699a4205e538d8cf65c9b24ac9dd2ad Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 15 Aug 2024 15:06:31 +0800 Subject: [PATCH 55/69] data mover ms smoke testing Signed-off-by: Lyndon-Li --- pkg/cmd/cli/nodeagent/server.go | 40 ++++++++++++++++--- pkg/controller/data_download_controller.go | 8 ++-- .../data_download_controller_test.go | 2 +- pkg/controller/data_upload_controller.go | 8 ++-- pkg/controller/data_upload_controller_test.go | 2 +- 5 files changed, 44 insertions(+), 16 deletions(-) diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 513eb89de..8aa52b1c5 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -61,6 +61,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/logging" + + cacheutil "k8s.io/client-go/tools/cache" ) var ( @@ -309,14 +311,17 @@ func (s *nodeAgentServer) run() { } go func() { - s.mgr.GetCache().WaitForCacheSync(s.ctx) - - if err := dataUploadReconciler.AttemptDataUploadResume(s.ctx, s.mgr.GetClient(), s.logger.WithField("node", s.nodeName), s.namespace); err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data upload resume") + if err := s.waitCacheForResume(); err != nil { + s.logger.WithError(err).Error("Failed to wait cache for resume, will not resume DU/DD") + return } - if err := dataDownloadReconciler.AttemptDataDownloadResume(s.ctx, s.mgr.GetClient(), s.logger.WithField("node", s.nodeName), s.namespace); err != nil { - s.logger.WithError(errors.WithStack(err)).Error("failed to attempt data download resume") + if err := dataUploadReconciler.AttemptDataUploadResume(s.ctx, s.logger.WithField("node", s.nodeName), s.namespace); err != nil { + s.logger.WithError(errors.WithStack(err)).Error("Failed to attempt data upload resume") + } + + if err := dataDownloadReconciler.AttemptDataDownloadResume(s.ctx, s.logger.WithField("node", s.nodeName), s.namespace); err != nil { + s.logger.WithError(errors.WithStack(err)).Error("Failed to attempt data download resume") } }() @@ -327,6 +332,29 @@ func (s *nodeAgentServer) run() { } } +func (s *nodeAgentServer) waitCacheForResume() error { + podInformer, err := s.mgr.GetCache().GetInformer(s.ctx, &v1.Pod{}) + if err != nil { + return errors.Wrap(err, "error getting pod informer") + } + + duInformer, err := s.mgr.GetCache().GetInformer(s.ctx, &velerov2alpha1api.DataUpload{}) + if err != nil { + return errors.Wrap(err, "error getting du informer") + } + + ddInformer, err := s.mgr.GetCache().GetInformer(s.ctx, &velerov2alpha1api.DataDownload{}) + if err != nil { + return errors.Wrap(err, "error getting dd informer") + } + + if !cacheutil.WaitForCacheSync(s.ctx.Done(), podInformer.HasSynced, duInformer.HasSynced, ddInformer.HasSynced) { + return errors.New("error waiting informer synced") + } + + return nil +} + // validatePodVolumesHostPath validates that the pod volumes path contains a // directory for each Pod running on this node func (s *nodeAgentServer) validatePodVolumesHostPath(client kubernetes.Interface) error { diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index 0b9805a0a..990d7455b 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -767,9 +767,9 @@ func UpdateDataDownloadWithRetry(ctx context.Context, client client.Client, name var funcResumeCancellableDataRestore = (*DataDownloadReconciler).resumeCancellableDataPath -func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, cli client.Client, logger *logrus.Entry, ns string) error { +func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, logger *logrus.Entry, ns string) error { dataDownloads := &velerov2alpha1api.DataDownloadList{} - if err := cli.List(ctx, dataDownloads, &client.ListOptions{Namespace: ns}); err != nil { + if err := r.client.List(ctx, dataDownloads, &client.ListOptions{Namespace: ns}); err != nil { r.logger.WithError(errors.WithStack(err)).Error("failed to list datadownloads") return errors.Wrapf(err, "error to list datadownloads") } @@ -795,7 +795,7 @@ func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, logger.WithField("datadownload", dd.GetName()).WithError(err).Warn("Failed to resume data path for dd, have to cancel it") resumeErr := err - err = UpdateDataDownloadWithRetry(ctx, cli, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, logger.WithField("datadownload", dd.Name), + err = UpdateDataDownloadWithRetry(ctx, r.client, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, logger.WithField("datadownload", dd.Name), func(dataDownload *velerov2alpha1api.DataDownload) bool { if dataDownload.Spec.Cancel { return false @@ -812,7 +812,7 @@ func (r *DataDownloadReconciler) AttemptDataDownloadResume(ctx context.Context, } else if dd.Status.Phase == velerov2alpha1api.DataDownloadPhaseAccepted { r.logger.WithField("datadownload", dd.GetName()).Warn("Cancel dd under Accepted phase") - err := UpdateDataDownloadWithRetry(ctx, cli, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, + err := UpdateDataDownloadWithRetry(ctx, r.client, types.NamespacedName{Namespace: dd.Namespace, Name: dd.Name}, r.logger.WithField("datadownload", dd.Name), func(dataDownload *velerov2alpha1api.DataDownload) bool { if dataDownload.Spec.Cancel { return false diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index bb9fe6f7c..a54135d03 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -1079,7 +1079,7 @@ func TestAttemptDataDownloadResume(t *testing.T) { funcResumeCancellableDataBackup = dt.resumeCancellableDataPath // Run the test - err = r.AttemptDataDownloadResume(ctx, r.client, r.logger.WithField("name", test.name), test.dd.Namespace) + err = r.AttemptDataDownloadResume(ctx, r.logger.WithField("name", test.name), test.dd.Namespace) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 4c26baf38..22dc59bd5 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -867,9 +867,9 @@ func UpdateDataUploadWithRetry(ctx context.Context, client client.Client, namesp var funcResumeCancellableDataBackup = (*DataUploadReconciler).resumeCancellableDataPath -func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, cli client.Client, logger *logrus.Entry, ns string) error { +func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, logger *logrus.Entry, ns string) error { dataUploads := &velerov2alpha1api.DataUploadList{} - if err := cli.List(ctx, dataUploads, &client.ListOptions{Namespace: ns}); err != nil { + if err := r.client.List(ctx, dataUploads, &client.ListOptions{Namespace: ns}); err != nil { r.logger.WithError(errors.WithStack(err)).Error("failed to list datauploads") return errors.Wrapf(err, "error to list datauploads") } @@ -895,7 +895,7 @@ func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, cli logger.WithField("dataupload", du.GetName()).WithError(err).Warn("Failed to resume data path for du, have to cancel it") resumeErr := err - err = UpdateDataUploadWithRetry(ctx, cli, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, logger.WithField("dataupload", du.Name), + err = UpdateDataUploadWithRetry(ctx, r.client, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, logger.WithField("dataupload", du.Name), func(dataUpload *velerov2alpha1api.DataUpload) bool { if dataUpload.Spec.Cancel { return false @@ -912,7 +912,7 @@ func (r *DataUploadReconciler) AttemptDataUploadResume(ctx context.Context, cli } else if du.Status.Phase == velerov2alpha1api.DataUploadPhaseAccepted { r.logger.WithField("dataupload", du.GetName()).Warn("Cancel du under Accepted phase") - err := UpdateDataUploadWithRetry(ctx, cli, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, r.logger.WithField("dataupload", du.Name), + err := UpdateDataUploadWithRetry(ctx, r.client, types.NamespacedName{Namespace: du.Namespace, Name: du.Name}, r.logger.WithField("dataupload", du.Name), func(dataUpload *velerov2alpha1api.DataUpload) bool { if dataUpload.Spec.Cancel { return false diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index b373a7a6a..a6ee25574 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -1127,7 +1127,7 @@ func TestAttemptDataUploadResume(t *testing.T) { funcResumeCancellableDataBackup = dt.resumeCancellableDataPath // Run the test - err = r.AttemptDataUploadResume(ctx, r.client, r.logger.WithField("name", test.name), test.du.Namespace) + err = r.AttemptDataUploadResume(ctx, r.logger.WithField("name", test.name), test.du.Namespace) if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) From c0402075fbb75950917a2c24542b3e4ed4f4e66a Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 15 Aug 2024 17:06:22 +0800 Subject: [PATCH 56/69] Remove code-generator from hack/update-3generated-crd.code.sh Signed-off-by: Xun Jiang --- hack/update-3generated-crd-code.sh | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/hack/update-3generated-crd-code.sh b/hack/update-3generated-crd-code.sh index 6bd185cc3..720639a40 100755 --- a/hack/update-3generated-crd-code.sh +++ b/hack/update-3generated-crd-code.sh @@ -30,23 +30,6 @@ if ! command -v controller-gen > /dev/null; then exit 1 fi -# get code-generation tools (for now keep in GOPATH since they're not fully modules-compatible yet) -mkdir -p ${GOPATH}/src/k8s.io -pushd ${GOPATH}/src/k8s.io -git clone -b v0.22.2 https://github.com/kubernetes/code-generator -popd - -${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ - all \ - github.com/vmware-tanzu/velero/pkg/generated \ - github.com/vmware-tanzu/velero/pkg/apis \ - "velero:v1,v2alpha1" \ - --go-header-file ./hack/boilerplate.go.txt \ - --output-base ../../.. \ - $@ - -# Generate apiextensions.k8s.io/v1 - # Generate CRD for v1. controller-gen \ crd:crdVersions=v1 \ From 8cf1749ae08c761632c57f752f351ef0b397801c Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 8 Aug 2024 17:18:53 +0800 Subject: [PATCH 57/69] issue 8032: make node agent configMap name configurable Signed-off-by: Lyndon-Li --- changelogs/unreleased/8097-Lyndon-Li | 1 + pkg/cmd/cli/nodeagent/server.go | 12 +++--- pkg/cmd/cli/nodeagent/server_test.go | 37 ++++++++++++------- pkg/nodeagent/node_agent.go | 11 ++---- pkg/nodeagent/node_agent_test.go | 6 +-- .../data-movement-backup-node-selection.md | 21 +++++++++-- .../docs/main/node-agent-concurrency.md | 18 ++++++++- 7 files changed, 70 insertions(+), 36 deletions(-) create mode 100644 changelogs/unreleased/8097-Lyndon-Li diff --git a/changelogs/unreleased/8097-Lyndon-Li b/changelogs/unreleased/8097-Lyndon-Li new file mode 100644 index 000000000..760c29a15 --- /dev/null +++ b/changelogs/unreleased/8097-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #8032, make node-agent configMap name configurable \ No newline at end of file diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index e19e1ab7f..32715ad42 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -84,6 +84,7 @@ type nodeAgentServerConfig struct { metricsAddress string resourceTimeout time.Duration dataMoverPrepareTimeout time.Duration + nodeAgentConfig string } func NewServerCommand(f client.Factory) *cobra.Command { @@ -120,6 +121,7 @@ func NewServerCommand(f client.Factory) *cobra.Command { command.Flags().DurationVar(&config.resourceTimeout, "resource-timeout", config.resourceTimeout, "How long to wait for resource processes which are not covered by other specific timeout parameters. Default is 10 minutes.") command.Flags().DurationVar(&config.dataMoverPrepareTimeout, "data-mover-prepare-timeout", config.dataMoverPrepareTimeout, "How long to wait for preparing a DataUpload/DataDownload. Default is 30 minutes.") command.Flags().StringVar(&config.metricsAddress, "metrics-address", config.metricsAddress, "The address to expose prometheus metrics") + command.Flags().StringVar(&config.nodeAgentConfig, "node-agent-config", config.nodeAgentConfig, "The name of configMap containing node-agent configurations.") return command } @@ -463,14 +465,14 @@ func (s *nodeAgentServer) markInProgressPVRsFailed(client ctrlclient.Client) { var getConfigsFunc = nodeagent.GetConfigs func (s *nodeAgentServer) getDataPathConfigs() { - configs, err := getConfigsFunc(s.ctx, s.namespace, s.kubeClient) - if err != nil { - s.logger.WithError(err).Warn("Failed to get node agent configs") + if s.config.nodeAgentConfig == "" { + s.logger.Info("No node-agent configMap is specified") return } - if configs == nil { - s.logger.Infof("Node agent configs are not found") + configs, err := getConfigsFunc(s.ctx, s.namespace, s.kubeClient, s.config.nodeAgentConfig) + if err != nil { + s.logger.WithError(err).Warnf("Failed to get node agent configs from configMap %s, ignore it", s.config.nodeAgentConfig) return } diff --git a/pkg/cmd/cli/nodeagent/server_test.go b/pkg/cmd/cli/nodeagent/server_test.go index fd2c6de94..bf3e203ca 100644 --- a/pkg/cmd/cli/nodeagent/server_test.go +++ b/pkg/cmd/cli/nodeagent/server_test.go @@ -17,12 +17,12 @@ package nodeagent import ( "context" + "errors" "fmt" "os" "path/filepath" "testing" - "github.com/pkg/errors" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -122,28 +122,36 @@ func Test_getDataPathConfigs(t *testing.T) { tests := []struct { name string - getFunc func(context.Context, string, kubernetes.Interface) (*nodeagent.Configs, error) + getFunc func(context.Context, string, kubernetes.Interface, string) (*nodeagent.Configs, error) + configMapName string expectConfigs *nodeagent.Configs expectLog string }{ { - name: "failed to get configs", - getFunc: func(context.Context, string, kubernetes.Interface) (*nodeagent.Configs, error) { - return nil, errors.New("fake-get-error") - }, - expectLog: "Failed to get node agent configs", + name: "no config specified", + expectLog: "No node-agent configMap is specified", }, { - name: "configs cm not found", - getFunc: func(context.Context, string, kubernetes.Interface) (*nodeagent.Configs, error) { - return nil, nil + name: "failed to get configs", + configMapName: "node-agent-config", + getFunc: func(context.Context, string, kubernetes.Interface, string) (*nodeagent.Configs, error) { + return nil, errors.New("fake-get-error") }, - expectLog: "Node agent configs are not found", + expectLog: "Failed to get node agent configs from configMap node-agent-config, ignore it", + }, + { + name: "configs cm not found", + configMapName: "node-agent-config", + getFunc: func(context.Context, string, kubernetes.Interface, string) (*nodeagent.Configs, error) { + return nil, errors.New("fake-not-found-error") + }, + expectLog: "Failed to get node agent configs from configMap node-agent-config, ignore it", }, { - name: "succeed", - getFunc: func(context.Context, string, kubernetes.Interface) (*nodeagent.Configs, error) { + name: "succeed", + configMapName: "node-agent-config", + getFunc: func(context.Context, string, kubernetes.Interface, string) (*nodeagent.Configs, error) { return configs, nil }, expectConfigs: configs, @@ -155,6 +163,9 @@ func Test_getDataPathConfigs(t *testing.T) { logBuffer := "" s := &nodeAgentServer{ + config: nodeAgentServerConfig{ + nodeAgentConfig: test.configMapName, + }, logger: testutil.NewSingleLogger(&logBuffer), } diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index e3597e27a..9120f7c69 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -34,8 +34,7 @@ import ( const ( // daemonSet is the name of the Velero node agent daemonset. - daemonSet = "node-agent" - configName = "node-agent-config" + daemonSet = "node-agent" ) var ( @@ -121,14 +120,10 @@ func GetPodSpec(ctx context.Context, kubeClient kubernetes.Interface, namespace return &ds.Spec.Template.Spec, nil } -func GetConfigs(ctx context.Context, namespace string, kubeClient kubernetes.Interface) (*Configs, error) { +func GetConfigs(ctx context.Context, namespace string, kubeClient kubernetes.Interface, configName string) (*Configs, error) { cm, err := kubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, configName, metav1.GetOptions{}) if err != nil { - if apierrors.IsNotFound(err) { - return nil, nil - } else { - return nil, errors.Wrapf(err, "error to get node agent configs %s", configName) - } + return nil, errors.Wrapf(err, "error to get node agent configs %s", configName) } if cm.Data == nil { diff --git a/pkg/nodeagent/node_agent_test.go b/pkg/nodeagent/node_agent_test.go index 2638b2213..9bbf0f46d 100644 --- a/pkg/nodeagent/node_agent_test.go +++ b/pkg/nodeagent/node_agent_test.go @@ -254,10 +254,6 @@ func TestGetConfigs(t *testing.T) { expectResult *Configs expectErr string }{ - { - name: "cm is not found", - namespace: "fake-ns", - }, { name: "cm get error", namespace: "fake-ns", @@ -318,7 +314,7 @@ func TestGetConfigs(t *testing.T) { fakeKubeClient.Fake.PrependReactor(reactor.verb, reactor.resource, reactor.reactorFunc) } - result, err := GetConfigs(context.TODO(), test.namespace, fakeKubeClient) + result, err := GetConfigs(context.TODO(), test.namespace, fakeKubeClient, "node-agent-config") if test.expectErr == "" { assert.NoError(t, err) diff --git a/site/content/docs/main/data-movement-backup-node-selection.md b/site/content/docs/main/data-movement-backup-node-selection.md index f5b20fa8a..d8bd8bbd7 100644 --- a/site/content/docs/main/data-movement-backup-node-selection.md +++ b/site/content/docs/main/data-movement-backup-node-selection.md @@ -11,12 +11,12 @@ Velero data movement backup supports to constrain the nodes where it runs. This - Constrain the data movement backup to run in specific nodes because these nodes have more resources than others - Constrain the data movement backup to run in specific nodes because the storage allows volume/snapshot provisions in these nodes only -Velero introduces a new section in ```node-agent-config``` configMap, called ```loadAffinity```, through which you can specify the nodes to/not to run data movement backups, in the affinity and anti-affinity flavors. -If it is not there, ```node-agent-config``` should be created manually. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only. +Velero introduces a new section in the node-agent configMap, called ```loadAffinity```, through which you can specify the nodes to/not to run data movement backups, in the affinity and anti-affinity flavors. +If it is not there, a configMap should be created manually. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only. The name of the configMap should be specified in the node-agent server parameter ```--node-agent-config```. Node-agent server checks these configurations at startup time. Therefore, you could edit this configMap any time, but in order to make the changes effective, node-agent server needs to be restarted. ### Sample -Here is a sample of the ```node-agent-config``` configMap with ```loadAffinity```: +Here is a sample of the configMap with ```loadAffinity```: ```json { "loadAffinity": [ @@ -50,6 +50,21 @@ To create the configMap, save something like the above sample to a json file and kubectl create cm node-agent-config -n velero --from-file= ``` +To provide the configMap to node-agent, edit the node-agent daemonset and add the ```- --node-agent-config``` argument to the spec: +1. Open the node-agent daemonset spec +``` +kubectl edit ds node-agent -n velero +``` +2. Add ```- --node-agent-config``` to ```spec.template.spec.containers``` +``` +spec: + template: + spec: + containers: + - args: + - --node-agent-config= +``` + ### Affinity Affinity configuration means allowing the data movement backup to run in the nodes specified. There are two ways to define it: - It could be defined by `MatchLabels`. The labels defined in `MatchLabels` means a `LabelSelectorOpIn` operation by default, so in the current context, they will be treated as affinity rules. In the above sample, it defines to run data movement backups in nodes with label `beta.kubernetes.io/instance-type` of value `Standard_B4ms` (Run data movement backups in `Standard_B4ms` nodes only). diff --git a/site/content/docs/main/node-agent-concurrency.md b/site/content/docs/main/node-agent-concurrency.md index 9a7e1b255..b4c6554b5 100644 --- a/site/content/docs/main/node-agent-concurrency.md +++ b/site/content/docs/main/node-agent-concurrency.md @@ -8,7 +8,7 @@ Varying from the data size, data complexity, resource availability, the tasks ma Node-agent concurrency configurations allow you to configure the concurrent number of node-agent loads per node. When the resources are sufficient in nodes, you can set a large concurrent number, so as to reduce the backup/restore time; otherwise, the concurrency should be reduced, otherwise, the backup/restore may encounter problems, i.e., time lagging, hang or OOM kill. -To set Node-agent concurrency configurations, a configMap named ```node-agent-config``` should be created manually. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only. +To set Node-agent concurrency configurations, a configMap should be created manually. The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to node-agent in that namespace only. The name of the configMap should be specified in the node-agent server parameter ```--node-agent-config```. Node-agent server checks these configurations at startup time. Therefore, you could edit this configMap any time, but in order to make the changes effective, node-agent server needs to be restarted. ### Global concurrent number @@ -32,7 +32,7 @@ At least one node is expected to have a label with the specified ```RuledConfigs If one node falls into more than one rules, e.g., if node1 also has the label ```beta.kubernetes.io/instance-type=Standard_B4ms```, the smallest number (3) will be used. ### Sample -A sample of the complete ```node-agent-config``` configMap is as below: +A sample of the complete configMap is as below: ```json { "loadConcurrency": { @@ -62,5 +62,19 @@ To create the configMap, save something like the above sample to a json file and ``` kubectl create cm node-agent-config -n velero --from-file= ``` +To provide the configMap to node-agent, edit the node-agent daemonset and add the ```- --node-agent-config``` argument to the spec: +1. Open the node-agent daemonset spec +``` +kubectl edit ds node-agent -n velero +``` +2. Add ```- --node-agent-config``` to ```spec.template.spec.containers``` +``` +spec: + template: + spec: + containers: + - args: + - --node-agent-config= +``` From a6c543384b87e971f595e2b7b701451f136aee41 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Mon, 19 Aug 2024 20:45:59 +0200 Subject: [PATCH 58/69] Use native cache from actions/setup-go (#7768) Signed-off-by: Matthieu MOREL --- .github/workflows/crds-verify-kind.yaml | 20 +-- .github/workflows/e2e-test-kind.yaml | 33 +---- .github/workflows/pr-ci-check.yml | 14 +- .github/workflows/pr-linter-check.yml | 1 - .github/workflows/push.yml | 163 +++++++++++------------- 5 files changed, 88 insertions(+), 143 deletions(-) diff --git a/.github/workflows/crds-verify-kind.yaml b/.github/workflows/crds-verify-kind.yaml index 8ababc60e..3d51599e8 100644 --- a/.github/workflows/crds-verify-kind.yaml +++ b/.github/workflows/crds-verify-kind.yaml @@ -11,11 +11,12 @@ jobs: build-cli: runs-on: ubuntu-latest steps: + - name: Check out the code + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.22' - id: go + go-version-file: 'go.mod' # Look for a CLI that's made for this PR - name: Fetch built CLI id: cache @@ -29,26 +30,11 @@ jobs: # This key controls the prefixes that we'll look at in the cache to restore from restore-keys: | velero-${{ github.event.pull_request.number }}- - - - name: Fetch cached go modules - uses: actions/cache@v4 - if: steps.cache.outputs.cache-hit != 'true' - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - - name: Check out the code - uses: actions/checkout@v4 - if: steps.cache.outputs.cache-hit != 'true' - # If no binaries were built for this PR, build it now. - name: Build Velero CLI if: steps.cache.outputs.cache-hit != 'true' run: | make local - # Check the common CLI against all Kubernetes versions crd-check: needs: build-cli diff --git a/.github/workflows/e2e-test-kind.yaml b/.github/workflows/e2e-test-kind.yaml index 9a379738a..538ddbddc 100644 --- a/.github/workflows/e2e-test-kind.yaml +++ b/.github/workflows/e2e-test-kind.yaml @@ -11,11 +11,12 @@ jobs: build: runs-on: ubuntu-latest steps: + - name: Check out the code + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.22' - id: go + go-version-file: 'go.mod' # Look for a CLI that's made for this PR - name: Fetch built CLI id: cli-cache @@ -31,17 +32,6 @@ jobs: path: ./velero.tar # The cache key a combination of the current PR number and the commit SHA key: velero-image-${{ github.event.pull_request.number }}-${{ github.sha }} - - name: Fetch cached go modules - uses: actions/cache@v4 - if: steps.cli-cache.outputs.cache-hit != 'true' - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Check out the code - uses: actions/checkout@v4 - if: steps.cli-cache.outputs.cache-hit != 'true' || steps.image-cache.outputs.cache-hit != 'true' # If no binaries were built for this PR, build it now. - name: Build Velero CLI if: steps.cli-cache.outputs.cache-hit != 'true' @@ -75,13 +65,12 @@ jobs: - (NamespaceMapping && Single && Restic) || (NamespaceMapping && Multiple && Restic) fail-fast: false steps: + - name: Check out the code + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.22' - id: go - - name: Check out the code - uses: actions/checkout@v4 + go-version-file: 'go.mod' - name: Install MinIO run: docker run -d --rm -p 9000:9000 -e "MINIO_ACCESS_KEY=minio" -e "MINIO_SECRET_KEY=minio123" -e "MINIO_DEFAULT_BUCKETS=bucket,additional-bucket" bitnami/minio:2021.6.17-debian-10-r7 @@ -104,14 +93,6 @@ jobs: - name: Load Velero Image run: kind load image-archive velero.tar - # always try to fetch the cached go modules as the e2e test needs it either - - name: Fetch cached go modules - uses: actions/cache@v4 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - name: Run E2E test run: | cat << EOF > /tmp/credential @@ -143,4 +124,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: DebugBundle - path: /home/runner/work/velero/velero/test/e2e/debug-bundle* \ No newline at end of file + path: /home/runner/work/velero/velero/test/e2e/debug-bundle* diff --git a/.github/workflows/pr-ci-check.yml b/.github/workflows/pr-ci-check.yml index 1da24a85d..4bcc28cee 100644 --- a/.github/workflows/pr-ci-check.yml +++ b/.github/workflows/pr-ci-check.yml @@ -7,20 +7,12 @@ jobs: strategy: fail-fast: false steps: + - name: Check out the code + uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: '1.22' - id: go - - name: Check out the code - uses: actions/checkout@v4 - - name: Fetch cached go modules - uses: actions/cache@v4 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version-file: 'go.mod' - name: Make ci run: make ci - name: Upload test coverage diff --git a/.github/workflows/pr-linter-check.yml b/.github/workflows/pr-linter-check.yml index d6d056cdd..429b7b169 100644 --- a/.github/workflows/pr-linter-check.yml +++ b/.github/workflows/pr-linter-check.yml @@ -12,7 +12,6 @@ jobs: uses: actions/setup-go@v5 with: go-version-file: 'go.mod' - id: go - name: Linter check uses: golangci/golangci-lint-action@v6 with: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 51ea81bac..bbc1f16ea 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -14,95 +14,82 @@ jobs: name: Build runs-on: ubuntu-latest steps: - - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: '1.22' - id: go - - - uses: actions/checkout@v4 - - # Fix issue of setup-gcloud - - run: | - sudo apt-get install python2.7 - export CLOUDSDK_PYTHON="/usr/bin/python2" - - - id: 'auth' - uses: google-github-actions/auth@v2 - with: - credentials_json: '${{ secrets.GCS_SA_KEY }}' - - - name: 'set up GCloud SDK' - uses: google-github-actions/setup-gcloud@v2 - - - name: 'use gcloud CLI' - run: | - gcloud info - - - name: Set up QEMU - id: qemu - uses: docker/setup-qemu-action@v3 - with: - platforms: all - - - name: Set up Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - with: - version: latest - - - name: Build - run: | - make local - # Clean go cache to ease the build environment storage pressure. - go clean -modcache -cache - - - name: Test - run: make test - - - name: Upload test coverage - uses: codecov/codecov-action@v4 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: coverage.out - verbose: true - - # Use the JSON key in secret to login gcr.io - - uses: 'docker/login-action@v3' - with: - registry: 'gcr.io' # or REGION.docker.pkg.dev - username: '_json_key' - password: '${{ secrets.GCR_SA_KEY }}' - - # Only try to publish the container image from the root repo; forks don't have permission to do so and will always get failures. - - name: Publish container image - if: github.repository == 'vmware-tanzu/velero' - run: | - sudo swapoff -a - sudo rm -f /mnt/swapfile - docker system prune -a --force + - name: Check out the code + uses: actions/checkout@v4 + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + # Fix issue of setup-gcloud + - run: | + sudo apt-get install python2.7 + export CLOUDSDK_PYTHON="/usr/bin/python2" + - id: 'auth' + uses: google-github-actions/auth@v2 + with: + credentials_json: '${{ secrets.GCS_SA_KEY }}' + - name: 'set up GCloud SDK' + uses: google-github-actions/setup-gcloud@v2 + - name: 'use gcloud CLI' + run: | + gcloud info + - name: Set up QEMU + id: qemu + uses: docker/setup-qemu-action@v3 + with: + platforms: all + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v3 + with: + version: latest + - name: Build + run: | + make local + # Clean go cache to ease the build environment storage pressure. + go clean -modcache -cache + - name: Test + run: make test + - name: Upload test coverage + uses: codecov/codecov-action@v4 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.out + verbose: true + # Use the JSON key in secret to login gcr.io + - uses: 'docker/login-action@v3' + with: + registry: 'gcr.io' # or REGION.docker.pkg.dev + username: '_json_key' + password: '${{ secrets.GCR_SA_KEY }}' + # Only try to publish the container image from the root repo; forks don't have permission to do so and will always get failures. + - name: Publish container image + if: github.repository == 'vmware-tanzu/velero' + run: | + sudo swapoff -a + sudo rm -f /mnt/swapfile + docker system prune -a --force - # Build and push Velero image to docker registry - docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} - VERSION=$(./hack/docker-push.sh | grep 'VERSION:' | awk -F: '{print $2}' | xargs) + # Build and push Velero image to docker registry + docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PASSWORD }} + VERSION=$(./hack/docker-push.sh | grep 'VERSION:' | awk -F: '{print $2}' | xargs) - # Upload Velero image package to GCS - source hack/ci/build_util.sh - BIN=velero - RESTORE_HELPER_BIN=velero-restore-helper - GCS_BUCKET=velero-builds - VELERO_IMAGE=${BIN}-${VERSION} - VELERO_RESTORE_HELPER_IMAGE=${RESTORE_HELPER_BIN}-${VERSION} - VELERO_IMAGE_FILE=${VELERO_IMAGE}.tar.gz - VELERO_RESTORE_HELPER_IMAGE_FILE=${VELERO_RESTORE_HELPER_IMAGE}.tar.gz - VELERO_IMAGE_BACKUP_FILE=${VELERO_IMAGE}-'build.'${GITHUB_RUN_NUMBER}.tar.gz - VELERO_RESTORE_HELPER_IMAGE_BACKUP_FILE=${VELERO_RESTORE_HELPER_IMAGE}-'build.'${GITHUB_RUN_NUMBER}.tar.gz + # Upload Velero image package to GCS + source hack/ci/build_util.sh + BIN=velero + RESTORE_HELPER_BIN=velero-restore-helper + GCS_BUCKET=velero-builds + VELERO_IMAGE=${BIN}-${VERSION} + VELERO_RESTORE_HELPER_IMAGE=${RESTORE_HELPER_BIN}-${VERSION} + VELERO_IMAGE_FILE=${VELERO_IMAGE}.tar.gz + VELERO_RESTORE_HELPER_IMAGE_FILE=${VELERO_RESTORE_HELPER_IMAGE}.tar.gz + VELERO_IMAGE_BACKUP_FILE=${VELERO_IMAGE}-'build.'${GITHUB_RUN_NUMBER}.tar.gz + VELERO_RESTORE_HELPER_IMAGE_BACKUP_FILE=${VELERO_RESTORE_HELPER_IMAGE}-'build.'${GITHUB_RUN_NUMBER}.tar.gz - cp ${VELERO_IMAGE_FILE} ${VELERO_IMAGE_BACKUP_FILE} - cp ${VELERO_RESTORE_HELPER_IMAGE_FILE} ${VELERO_RESTORE_HELPER_IMAGE_BACKUP_FILE} + cp ${VELERO_IMAGE_FILE} ${VELERO_IMAGE_BACKUP_FILE} + cp ${VELERO_RESTORE_HELPER_IMAGE_FILE} ${VELERO_RESTORE_HELPER_IMAGE_BACKUP_FILE} - uploader ${VELERO_IMAGE_FILE} ${GCS_BUCKET} - uploader ${VELERO_RESTORE_HELPER_IMAGE_FILE} ${GCS_BUCKET} - uploader ${VELERO_IMAGE_BACKUP_FILE} ${GCS_BUCKET} - uploader ${VELERO_RESTORE_HELPER_IMAGE_BACKUP_FILE} ${GCS_BUCKET} + uploader ${VELERO_IMAGE_FILE} ${GCS_BUCKET} + uploader ${VELERO_RESTORE_HELPER_IMAGE_FILE} ${GCS_BUCKET} + uploader ${VELERO_IMAGE_BACKUP_FILE} ${GCS_BUCKET} + uploader ${VELERO_RESTORE_HELPER_IMAGE_BACKUP_FILE} ${GCS_BUCKET} From d4e7d1472e82f283150627a74a145d6f6aea568d Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Thu, 15 Aug 2024 12:18:54 -0700 Subject: [PATCH 59/69] add docs for backup pvc config support Signed-off-by: Shubham Pampattiwar add changelog Signed-off-by: Shubham Pampattiwar add section to csi dm doc and minor fixes Signed-off-by: Shubham Pampattiwar configMap name is configurable Signed-off-by: Shubham Pampattiwar --- .../unreleased/8119-shubham-pampattiwar | 1 + .../docs/main/csi-snapshot-data-movement.md | 7 ++- .../data-movement-backup-pvc-configuration.md | 54 +++++++++++++++++++ site/data/docs/main-toc.yml | 4 +- 4 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/8119-shubham-pampattiwar create mode 100644 site/content/docs/main/data-movement-backup-pvc-configuration.md diff --git a/changelogs/unreleased/8119-shubham-pampattiwar b/changelogs/unreleased/8119-shubham-pampattiwar new file mode 100644 index 000000000..48b4c0b09 --- /dev/null +++ b/changelogs/unreleased/8119-shubham-pampattiwar @@ -0,0 +1 @@ +Add docs for backup pvc config support diff --git a/site/content/docs/main/csi-snapshot-data-movement.md b/site/content/docs/main/csi-snapshot-data-movement.md index 60d72aecf..31419e3ee 100644 --- a/site/content/docs/main/csi-snapshot-data-movement.md +++ b/site/content/docs/main/csi-snapshot-data-movement.md @@ -491,8 +491,11 @@ For Velero built-in data mover, it uses Kubernetes' scheduler to mount a snapsho For the backup, you can intervene this scheduling process through [Data Movement Backup Node Selection][15], so that you can decide which node(s) should/should not run the data movement backup for various purposes. For the restore, this is not supported because sometimes the data movement restore must run in the same node where the restored workload pod is scheduled. +### BackupPVC Configuration - +The `BackupPVC` serves as an intermediate Persistent Volume Claim (PVC) utilized during data movement backup operations, providing efficient access to data. +In complex storage environments, optimizing `BackupPVC` configurations can significantly enhance the performance of backup operations. [This document][16] outlines +advanced configuration options for `BackupPVC`, allowing users to fine-tune access modes and storage class settings based on their storage provider's capabilities. [1]: https://github.com/vmware-tanzu/velero/pull/5968 [2]: csi.md @@ -509,3 +512,5 @@ For the restore, this is not supported because sometimes the data movement resto [13]: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ [14]: node-agent-concurrency.md [15]: data-movement-backup-node-selection.md +[16]: data-movement-backup-pvc-configuration.md + diff --git a/site/content/docs/main/data-movement-backup-pvc-configuration.md b/site/content/docs/main/data-movement-backup-pvc-configuration.md new file mode 100644 index 000000000..61bbf53e1 --- /dev/null +++ b/site/content/docs/main/data-movement-backup-pvc-configuration.md @@ -0,0 +1,54 @@ +--- +title: "BackupPVC Configuration for Data Movement Backup" +layout: docs +--- + +`BackupPVC` is an intermediate PVC to access data from during the data movement backup operation. + +In some scenarios users may need to configure some advanced options of the backupPVC so that the data movement backup +operation could perform better. Specifically: +- For some storage providers, when creating a read-only volume from a snapshot, it is very fast; whereas, if a writable volume + is created from the snapshot, they need to clone the entire disk data, which is time consuming. If the `backupPVC`'s `accessModes` is + set as `ReadOnlyMany`, the volume driver is able to tell the storage to create a read-only volume, which may dramatically shorten the + snapshot expose time. On the other hand, `ReadOnlyMany` is not supported by all volumes. Therefore, users should be allowed to configure + the `accessModes` for the `backupPVC`. +- Some storage providers create one or more replicas when creating a volume, the number of replicas is defined in the storage class. + However, it doesn't make any sense to keep replicas when an intermediate volume used by the backup. Therefore, users should be allowed + to configure another storage class specifically used by the `backupPVC`. + +Velero introduces a new section in the node agent configuration configMap (the name of this configMap is passed using `--node-agent-config` velero server argument) +called `backupPVC`, through which you can specify the following +configurations: + +- `storageClass`: This specifies the storage class to be used for the backupPVC. If this value does not exist or is empty then by +default the source PVC's storage class will be used. + +- `readOnly`: This is a boolean value. If set to `true` then `ReadOnlyMany` will be the only value set to the backupPVC's access modes. Otherwise +`ReadWriteOnce` value will be used. + +A sample of `backupPVC` config as part of the configMap would look like: +```json +{ + "backupPVC": { + "storage-class-1": { + "storageClass": "backupPVC-storage-class", + "readOnly": true + }, + "storage-class-2": { + "storageClass": "backupPVC-storage-class" + }, + "storage-class-3": { + "readOnly": true + } + } +} +``` + +**Note:** +- Users should make sure that the storage class specified in `backupPVC` config should exist in the cluster and can be used by the +`backupPVC`, otherwise the corresponding DataUpload CR will stay in `Accepted` phase until timeout (data movement prepare timeout value is 30m by default). +- If the users are setting `readOnly` value as `true` in the `backupPVC` config then they must also make sure that the storage class that is being used for +`backupPVC` should support creation of `ReadOnlyMany` PVC from a snapshot, otherwise the corresponding DataUpload CR will stay in `Accepted` phase until +timeout (data movement prepare timeout value is 30m by default). +- If any of the above problems occur, then the DataUpload CR is `canceled` after timeout, and the backupPod and backupPVC will be deleted, and the backup +will be marked as `PartiallyFailed`. diff --git a/site/data/docs/main-toc.yml b/site/data/docs/main-toc.yml index cab546823..ec5f04604 100644 --- a/site/data/docs/main-toc.yml +++ b/site/data/docs/main-toc.yml @@ -52,7 +52,9 @@ toc: - page: CSI Snapshot Data Movement url: /csi-snapshot-data-movement - page: Node-agent Concurrency - url: /node-agent-concurrency + url: /node-agent-concurrency + - page: Backup PVC Configuration + url: /data-movement-backup-pvc-configuration - page: Verifying Self-signed Certificates url: /self-signed-certificates - page: Changing RBAC permissions From af62dd4b3e868f8fd9d2d0b6093fefa50e50d396 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Tue, 20 Aug 2024 10:49:56 +0800 Subject: [PATCH 60/69] Modify E2E and perf test result output directory. Add LongTime label to more E2E cases. Signed-off-by: Xun Jiang --- changelogs/unreleased/8129-blackpiglet | 1 + test/Makefile | 4 ++-- test/e2e/e2e_suite_test.go | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 changelogs/unreleased/8129-blackpiglet diff --git a/changelogs/unreleased/8129-blackpiglet b/changelogs/unreleased/8129-blackpiglet new file mode 100644 index 000000000..c776b66eb --- /dev/null +++ b/changelogs/unreleased/8129-blackpiglet @@ -0,0 +1 @@ +Modify E2E and perf test report generated directory \ No newline at end of file diff --git a/test/Makefile b/test/Makefile index ff62230b3..fce0b9d8e 100644 --- a/test/Makefile +++ b/test/Makefile @@ -167,7 +167,7 @@ run-e2e: ginkgo (echo "Cloud provider for target cloud/plugin provider is required, please rerun with CLOUD_PROVIDER="; exit 1) @$(GINKGO) run \ -v \ - --junit-report report.xml \ + --junit-report e2e/report.xml \ --label-filter="$(GINKGO_LABELS)" \ --timeout=5h \ ./e2e \ @@ -207,7 +207,7 @@ run-perf: ginkgo (echo "Cloud provider for target cloud/plugin provider is required, please rerun with CLOUD_PROVIDER="; exit 1) @$(GINKGO) run \ -v \ - --junit-report report.xml \ + --junit-report perf/report.xml \ --label-filter="$(GINKGO_LABELS)" \ --timeout=5h \ ./perf \ diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 0d22510bc..103195808 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -113,7 +113,7 @@ func init() { // cases can be executed as expected successful result. var _ = Describe("Velero tests with various CRD API group versions", - Label("APIGroup", "APIVersion", "SKIP_KIND"), APIGroupVersionsTest) + Label("APIGroup", "APIVersion", "SKIP_KIND", "LongTime"), APIGroupVersionsTest) var _ = Describe("CRD of apiextentions v1beta1 should be B/R successfully from cluster(k8s version < 1.22) to cluster(k8s version >= 1.22)", Label("APIGroup", "APIExtensions", "SKIP_KIND"), APIExtensionsVersionsTest) @@ -197,9 +197,9 @@ var _ = Describe("Backups in object storage are synced to a new Velero and delet var _ = Describe("Backup will be created periodically by schedule defined by a Cron expression", Label("Schedule", "BR", "Pause", "LongTime"), ScheduleBackupTest) var _ = Describe("Backup resources should follow the specific order in schedule", - Label("Schedule", "OrderedResources"), ScheduleOrderedResources) + Label("Schedule", "OrderedResources", "LongTime"), ScheduleOrderedResources) var _ = Describe("Schedule controller wouldn't create a new backup when it still has pending or InProgress backup", - Label("Schedule", "BackupCreation", "SKIP_KIND"), ScheduleBackupCreationTest) + Label("Schedule", "BackupCreation", "SKIP_KIND", "LongTime"), ScheduleBackupCreationTest) var _ = Describe("Velero test on ssr object when controller namespace mix-ups", Label("PrivilegesMgmt", "SSR"), SSRTest) From bdff60178ad288fa4e8e1089c87192e2edf2fd9e Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 20 Aug 2024 14:24:32 +0800 Subject: [PATCH 61/69] add doc for backup repo config Signed-off-by: Lyndon-Li --- changelogs/unreleased/8131-Lyndon-Li | 1 + .../main/backup-repository-configuration.md | 50 +++++++++++++++++++ .../docs/main/csi-snapshot-data-movement.md | 3 +- site/content/docs/main/file-system-backup.md | 3 +- 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/8131-Lyndon-Li create mode 100644 site/content/docs/main/backup-repository-configuration.md diff --git a/changelogs/unreleased/8131-Lyndon-Li b/changelogs/unreleased/8131-Lyndon-Li new file mode 100644 index 000000000..4616c3ba0 --- /dev/null +++ b/changelogs/unreleased/8131-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #7620, add doc for backup repo config \ No newline at end of file diff --git a/site/content/docs/main/backup-repository-configuration.md b/site/content/docs/main/backup-repository-configuration.md new file mode 100644 index 000000000..0e1d6343c --- /dev/null +++ b/site/content/docs/main/backup-repository-configuration.md @@ -0,0 +1,50 @@ +--- +title: "Backup Repository Configuration" +layout: docs +--- + +Velero uses selectable backup repositories for various backup/restore methods, i.e., [file-system backup][1], [CSI snapshot data movement][2], etc. To achieve the best performance, backup repositories may need to be configured according to the running environments. + +Velero uses a BackupRepository CR to represent the instance of the backup repository. Now, a new field `repositoryConfig` is added to support various configurations to the underlying backup repository. + +Velero also allows you to specify configurations before the BackupRepository CR is created through a configMap. The configurations in the configMap will be copied to the BackupRepository CR when it is created at the due time. +The configMap should be in the same namespace where Velero is installed. If multiple Velero instances are installed in different namespaces, there should be one configMap in each namespace which applies to Velero instance in that namespace only. The name of the configMap should be specified in the Velero server parameter `--backup-repository-config`. + +Conclusively, you have two ways to add/change/delete configurations of a backup repository: +- If the BackupRepository CR for the backup repository is already there, you should modify the `repositoryConfig` field. The new changes will be applied to the backup repository at the due time, it doesn't require Velero server to restart. +- Otherwise, you can create the backup repository configMap as a template for the BackupRepository CRs that are going to be created. + +The backup repository configMap is repository type specified, so for one repository type, you only need to create one set of configurations, they will be applied to all BackupRepository CRs of the same type. Whereas, the changes of `repositoryConfig` field apply to the specific BackupRepository CR only, you may need to change every BackupRepository CR of the same type. + +Below is an example of the BackupRepository configMap with the configurations: +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: + namespace: velero +data: + : | + { + "cacheLimitMB": 2048 + } + : | + { + "cacheLimitMB": 1024 + } +``` + +To create the configMap, you need to save something like the above sample to a file and then run below commands: +```shell +kubectl apply -f +``` + +When and how the configurations are used is decided by the backup repository itself. Though you can specify any configuration to the configMap or `repositoryConfig`, the configuration may/may not be used by the backup repository, or the configuration may be used at an arbitrary time. + +Below is the supported configurations by Velero and the specific backup repository. +***Kopia repository:*** +`cacheLimitMB`: specifies the size limit(in MB) for the local data cache. The more data is cached locally, the less data may be downloaded from the backup storage, so the better performance may be achieved. Practically, you can specify any size that is smaller than the free space so that the disk space won't run out. This parameter is for repository connection, that is, you could change it before connecting to the repository. E.g., before a backup/restore/maintenance. + + +[1]: file-system-backup.md +[2]: csi-snapshot-data-movement.md \ No newline at end of file diff --git a/site/content/docs/main/csi-snapshot-data-movement.md b/site/content/docs/main/csi-snapshot-data-movement.md index 60d72aecf..621997c53 100644 --- a/site/content/docs/main/csi-snapshot-data-movement.md +++ b/site/content/docs/main/csi-snapshot-data-movement.md @@ -481,7 +481,7 @@ For Velero built-in data mover, Velero uses [BestEffort as the QoS][13] for node If you want to constraint the CPU/memory usage, you need to [customize the resource limits][11]. The CPU/memory consumption is always related to the scale of data to be backed up/restored, refer to [Performance Guidance][12] for more details, so it is highly recommended that you perform your own testing to find the best resource limits for your data. During the restore, the repository may also cache data/metadata so as to reduce the network footprint and speed up the restore. The repository uses its own policy to store and clean up the cache. -For Kopia repository, the cache is stored in the node-agent pod's root file system and the cleanup is triggered for the data/metadata that are older than 10 minutes (not configurable at present). So you should prepare enough disk space, otherwise, the node-agent pod may be evicted due to running out of the ephemeral storage. +For Kopia repository, the cache is stored in the node-agent pod's root file system. Velero allows you to configure a limit of the cache size so that the data mover pod won't be evicted due to running out of the ephemeral storage. For more details, check [Backup Repository Configuration][16]. ### Node Selection @@ -509,3 +509,4 @@ For the restore, this is not supported because sometimes the data movement resto [13]: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ [14]: node-agent-concurrency.md [15]: data-movement-backup-node-selection.md +[16]: backup-repository-configuration.md diff --git a/site/content/docs/main/file-system-backup.md b/site/content/docs/main/file-system-backup.md index 5dac5e3c5..a2d81461c 100644 --- a/site/content/docs/main/file-system-backup.md +++ b/site/content/docs/main/file-system-backup.md @@ -642,7 +642,7 @@ Velero node-agent uses [BestEffort as the QoS][14] for node-agent pods (so no CP If you want to constraint the CPU/memory usage, you need to [customize the resource limits][15]. The CPU/memory consumption is always related to the scale of data to be backed up/restored, refer to [Performance Guidance][16] for more details, so it is highly recommended that you perform your own testing to find the best resource limits for your data. During the restore, the repository may also cache data/metadata so as to reduce the network footprint and speed up the restore. The repository uses its own policy to store and clean up the cache. -For Kopia repository, the cache is stored in the node-agent pod's root file system and the cleanup is triggered for the data/metadata that are older than 10 minutes (not configurable at present). So you should prepare enough disk space, otherwise, the node-agent pod may be evicted due to running out of the ephemeral storage. +For Kopia repository, the cache is stored in the node-agent pod's root file system. Velero allows you to configure a limit of the cache size so that the node-agent pod won't be evicted due to running out of the ephemeral storage. For more details, check [Backup Repository Configuration][18]. ## Restic Deprecation @@ -695,3 +695,4 @@ In the output of `velero backup describe` command for a backup with fs-backup: [15]: customize-installation.md#customize-resource-requests-and-limits [16]: performance-guidance.md [17]: https://github.com/vmware-tanzu/velero/blob/main/GOVERNANCE.md#deprecation-policy +[18]: backup-repository-configuration.md From 37e0ab12cc22dda9bd8689c1c554f99249c944b4 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 21 Aug 2024 16:15:40 +0800 Subject: [PATCH 62/69] backup repo config doc Signed-off-by: Lyndon-Li --- site/data/docs/main-toc.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/site/data/docs/main-toc.yml b/site/data/docs/main-toc.yml index ec5f04604..26e35ca72 100644 --- a/site/data/docs/main-toc.yml +++ b/site/data/docs/main-toc.yml @@ -55,6 +55,8 @@ toc: url: /node-agent-concurrency - page: Backup PVC Configuration url: /data-movement-backup-pvc-configuration + - page: Backup Repository Configuration + url: /backup-repository-configuration - page: Verifying Self-signed Certificates url: /self-signed-certificates - page: Changing RBAC permissions From c2cd6b7176baf659ab645271bc4e02dde99de0c2 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Wed, 21 Aug 2024 23:59:13 +0800 Subject: [PATCH 63/69] Add resource modifier for velero restore describe CLI Signed-off-by: Xun Jiang --- changelogs/unreleased/8139-blackpiglet | 1 + pkg/cmd/util/output/restore_describer.go | 23 +++++++++++++- pkg/cmd/util/output/restore_describer_test.go | 30 +++++++++++++++++-- 3 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/8139-blackpiglet diff --git a/changelogs/unreleased/8139-blackpiglet b/changelogs/unreleased/8139-blackpiglet new file mode 100644 index 000000000..1d23f2c3b --- /dev/null +++ b/changelogs/unreleased/8139-blackpiglet @@ -0,0 +1 @@ +Add resource modifier for velero restore describe CLI diff --git a/pkg/cmd/util/output/restore_describer.go b/pkg/cmd/util/output/restore_describer.go index 103ad12d8..55363b4b6 100644 --- a/pkg/cmd/util/output/restore_describer.go +++ b/pkg/cmd/util/output/restore_describer.go @@ -27,6 +27,7 @@ import ( "github.com/vmware-tanzu/velero/internal/volume" + v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" kbclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -39,7 +40,15 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/results" ) -func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *velerov1api.Restore, podVolumeRestores []velerov1api.PodVolumeRestore, details bool, insecureSkipTLSVerify bool, caCertFile string) string { +func DescribeRestore( + ctx context.Context, + kbClient kbclient.Client, + restore *velerov1api.Restore, + podVolumeRestores []velerov1api.PodVolumeRestore, + details bool, + insecureSkipTLSVerify bool, + caCertFile string, +) string { return Describe(func(d *Describer) { d.DescribeMetadata(restore.ObjectMeta) @@ -196,6 +205,11 @@ func DescribeRestore(ctx context.Context, kbClient kbclient.Client, restore *vel d.Println() d.Printf("Preserve Service NodePorts:\t%s\n", BoolPointerString(restore.Spec.PreserveNodePorts, "false", "true", "auto")) + if restore.Spec.ResourceModifier != nil { + d.Println() + DescribeResourceModifier(d, restore.Spec.ResourceModifier) + } + describeUploaderConfigForRestore(d, restore.Spec) d.Println() @@ -472,3 +486,10 @@ func describeRestoreResourceList(ctx context.Context, kbClient kbclient.Client, d.Printf("\t%s:\n\t\t- %s\n", gvk, strings.Join(resourceList[gvk], "\n\t\t- ")) } } + +// DescribeResourceModifier describes resource policies in human-readable format +func DescribeResourceModifier(d *Describer, resModifier *v1.TypedLocalObjectReference) { + d.Printf("Resource modifier:\n") + d.Printf("\tType:\t%s\n", resModifier.Kind) + d.Printf("\tName:\t%s\n", resModifier.Name) +} diff --git a/pkg/cmd/util/output/restore_describer_test.go b/pkg/cmd/util/output/restore_describer_test.go index 1a1e0100a..94e15c61c 100644 --- a/pkg/cmd/util/output/restore_describer_test.go +++ b/pkg/cmd/util/output/restore_describer_test.go @@ -2,15 +2,16 @@ package output import ( "bytes" + "fmt" "testing" "text/tabwriter" "time" - "github.com/vmware-tanzu/velero/internal/volume" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + v1 "k8s.io/api/core/v1" + "github.com/vmware-tanzu/velero/internal/volume" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/itemoperation" @@ -389,3 +390,28 @@ CSI Snapshot Restores: }) } } + +func TestDescribeResourceModifier(t *testing.T) { + d := &Describer{ + Prefix: "", + out: &tabwriter.Writer{}, + buf: &bytes.Buffer{}, + } + + d.out.Init(d.buf, 0, 8, 2, ' ', 0) + + DescribeResourceModifier(d, &v1.TypedLocalObjectReference{ + APIGroup: &v1.SchemeGroupVersion.Group, + Kind: "ConfigMap", + Name: "resourceModifier", + }) + d.out.Flush() + + expectOutput := `Resource modifier: + Type: ConfigMap + Name: resourceModifier +` + + fmt.Println(d.buf.String()) + require.Equal(t, expectOutput, d.buf.String()) +} From 627e2fede6ff364dc5736e1351174b825cd7b581 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 21 Aug 2024 19:21:07 +0800 Subject: [PATCH 64/69] nod-agent config for pod resources Signed-off-by: Lyndon-Li --- changelogs/unreleased/8143-Lyndon-Li | 1 + .../vgdp-micro-service/vgdp-micro-service.md | 22 +++++++++++++++++++ pkg/cmd/cli/nodeagent/server.go | 17 ++++++++++++-- pkg/controller/data_download_controller.go | 6 +++-- .../data_download_controller_test.go | 6 ++--- pkg/controller/data_upload_controller.go | 7 ++++-- pkg/controller/data_upload_controller_test.go | 2 +- pkg/exposer/csi_snapshot.go | 8 +++++-- pkg/exposer/generic_restore.go | 9 ++++---- pkg/exposer/generic_restore_test.go | 2 +- pkg/exposer/mocks/generic_restore.go | 10 ++++----- pkg/nodeagent/node_agent.go | 10 +++++++++ 12 files changed, 78 insertions(+), 22 deletions(-) create mode 100644 changelogs/unreleased/8143-Lyndon-Li diff --git a/changelogs/unreleased/8143-Lyndon-Li b/changelogs/unreleased/8143-Lyndon-Li new file mode 100644 index 000000000..17bdec7eb --- /dev/null +++ b/changelogs/unreleased/8143-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #8134, allow to config resource request/limit for data mover micro service pods \ No newline at end of file diff --git a/design/vgdp-micro-service/vgdp-micro-service.md b/design/vgdp-micro-service/vgdp-micro-service.md index 8d777e17e..c6d1e117e 100644 --- a/design/vgdp-micro-service/vgdp-micro-service.md +++ b/design/vgdp-micro-service/vgdp-micro-service.md @@ -176,6 +176,27 @@ Below diagram shows how VGDP logs are redirected: This log redirecting mechanism is thread safe since the hook acquires the write lock before writing the log buffer, so it guarantees that in the node-agent log there is no corruptions after redirecting the log, and the redirected logs and the original node-agent logs are not projected into each other. +### Resource Control +The CPU/memory resource of backupPod/restorePod is configurable, which means users are allowed to configure resources per volume backup/restore. +By default, the [Best Effort policy][5] is used, and users are allowed to change it through the ```node-agent-config``` configMap. Specifically, we add below structures to the configMap: +``` +type Configs struct { + // PodResources is the resource config for various types of pods launched by node-agent, i.e., data mover pods. + PodResources *PodResources `json:"podResources,omitempty"` +} + +type PodResources struct { + CPURequest string `json:"cpuRequest,omitempty"` + MemoryRequest string `json:"memoryRequest,omitempty"` + CPULimit string `json:"cpuLimit,omitempty"` + MemoryLimit string `json:"memoryLimit,omitempty"` +} +``` +The string values must mactch Kubernetes Quantity expressions; for each resource, the "request" value must not be larger than the "limit" value. Otherwise, if any one of the values fail, all the resource configurations will be ignored. + +The configurations are loaded node-agent at start time, so users can change the values in the configMap any time, but the changes won't effect until node-agent restarts. + + ## node-agent node-agent is still required. Even though VGDP is now not running inside node-agent, node-agent still hosts the data mover controller which reconciles DUCR/DDCR and operates DUCR/DDCR in other steps before the VGDP instance is started, i.e., Accept, Expose, etc. Privileged mode and root user are not required for node-agent anymore by Volume Snapshot Data Movement, however, they are still required by PVB(PodVolumeBackup) and PVR(PodVolumeRestore). Therefore, we will keep the node-agent deamonset as is, for any users who don't use PVB/PVR and have concern about the privileged mode/root user, they need to manually modify the deamonset spec to remove the dependencies. @@ -198,4 +219,5 @@ CLI is not changed. [2]: ../volume-snapshot-data-movement/volume-snapshot-data-movement.md [3]: https://kubernetes.io/blog/2022/09/02/cosi-kubernetes-object-storage-management/ [4]: ../Implemented/node-agent-concurrency.md +[5]: https://kubernetes.io/docs/concepts/workloads/pods/pod-qos/ diff --git a/pkg/cmd/cli/nodeagent/server.go b/pkg/cmd/cli/nodeagent/server.go index 20a4654fb..7365c4e8c 100644 --- a/pkg/cmd/cli/nodeagent/server.go +++ b/pkg/cmd/cli/nodeagent/server.go @@ -60,6 +60,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/nodeagent" "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/filesystem" + "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" cacheutil "k8s.io/client-go/tools/cache" @@ -295,19 +296,31 @@ func (s *nodeAgentServer) run() { var loadAffinity *nodeagent.LoadAffinity if s.dataPathConfigs != nil && len(s.dataPathConfigs.LoadAffinity) > 0 { loadAffinity = s.dataPathConfigs.LoadAffinity[0] + s.logger.Infof("Using customized loadAffinity %v", loadAffinity) } var backupPVCConfig map[string]nodeagent.BackupPVC if s.dataPathConfigs != nil && s.dataPathConfigs.BackupPVCConfig != nil { backupPVCConfig = s.dataPathConfigs.BackupPVCConfig + s.logger.Infof("Using customized backupPVC config %v", backupPVCConfig) } - dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, backupPVCConfig, clock.RealClock{}, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + podResources := v1.ResourceRequirements{} + if s.dataPathConfigs != nil && s.dataPathConfigs.PodResources != nil { + if res, err := kube.ParseResourceRequirements(s.dataPathConfigs.PodResources.CPURequest, s.dataPathConfigs.PodResources.MemoryRequest, s.dataPathConfigs.PodResources.CPULimit, s.dataPathConfigs.PodResources.MemoryLimit); err != nil { + s.logger.WithError(err).Warn("Pod resource requirements are invalid, ignore") + } else { + podResources = res + s.logger.Infof("Using customized pod resource requirements %v", s.dataPathConfigs.PodResources) + } + } + + dataUploadReconciler := controller.NewDataUploadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.csiSnapshotClient.SnapshotV1(), s.dataPathMgr, loadAffinity, backupPVCConfig, podResources, clock.RealClock{}, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataUploadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data upload controller") } - dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) + dataDownloadReconciler := controller.NewDataDownloadReconciler(s.mgr.GetClient(), s.mgr, s.kubeClient, s.dataPathMgr, podResources, s.nodeName, s.config.dataMoverPrepareTimeout, s.logger, s.metrics) if err = dataDownloadReconciler.SetupWithManager(s.mgr); err != nil { s.logger.WithError(err).Fatal("Unable to create the data download controller") } diff --git a/pkg/controller/data_download_controller.go b/pkg/controller/data_download_controller.go index 990d7455b..7d6f6e0c6 100644 --- a/pkg/controller/data_download_controller.go +++ b/pkg/controller/data_download_controller.go @@ -60,12 +60,13 @@ type DataDownloadReconciler struct { restoreExposer exposer.GenericRestoreExposer nodeName string dataPathMgr *datapath.Manager + podResources v1.ResourceRequirements preparingTimeout time.Duration metrics *metrics.ServerMetrics } func NewDataDownloadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, dataPathMgr *datapath.Manager, - nodeName string, preparingTimeout time.Duration, logger logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataDownloadReconciler { + podResources v1.ResourceRequirements, nodeName string, preparingTimeout time.Duration, logger logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataDownloadReconciler { return &DataDownloadReconciler{ client: client, kubeClient: kubeClient, @@ -75,6 +76,7 @@ func NewDataDownloadReconciler(client client.Client, mgr manager.Manager, kubeCl nodeName: nodeName, restoreExposer: exposer.NewGenericRestoreExposer(kubeClient, logger), dataPathMgr: dataPathMgr, + podResources: podResources, preparingTimeout: preparingTimeout, metrics: metrics, } @@ -179,7 +181,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request // Expose() will trigger to create one pod whose volume is restored by a given volume snapshot, // but the pod maybe is not in the same node of the current controller, so we need to return it here. // And then only the controller who is in the same node could do the rest work. - err = r.restoreExposer.Expose(ctx, getDataDownloadOwnerObject(dd), dd.Spec.TargetVolume.PVC, dd.Spec.TargetVolume.Namespace, hostingPodLabels, dd.Spec.OperationTimeout.Duration) + err = r.restoreExposer.Expose(ctx, getDataDownloadOwnerObject(dd), dd.Spec.TargetVolume.PVC, dd.Spec.TargetVolume.Namespace, hostingPodLabels, r.podResources, dd.Spec.OperationTimeout.Duration) if err != nil { if err := r.client.Get(ctx, req.NamespacedName, dd); err != nil { if !apierrors.IsNotFound(err) { diff --git a/pkg/controller/data_download_controller_test.go b/pkg/controller/data_download_controller_test.go index a54135d03..a675b73cd 100644 --- a/pkg/controller/data_download_controller_test.go +++ b/pkg/controller/data_download_controller_test.go @@ -140,7 +140,7 @@ func initDataDownloadReconcilerWithError(objects []runtime.Object, needError ... dataPathMgr := datapath.NewManager(1) - return NewDataDownloadReconciler(fakeClient, nil, fakeKubeClient, dataPathMgr, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil + return NewDataDownloadReconciler(fakeClient, nil, fakeKubeClient, dataPathMgr, corev1.ResourceRequirements{}, "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } func TestDataDownloadReconcile(t *testing.T) { @@ -441,7 +441,7 @@ func TestDataDownloadReconcile(t *testing.T) { r.restoreExposer = func() exposer.GenericRestoreExposer { ep := exposermockes.NewGenericRestoreExposer(t) if test.isExposeErr { - ep.On("Expose", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("Error to expose restore exposer")) + ep.On("Expose", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(errors.New("Error to expose restore exposer")) } else if test.notNilExpose { hostingPod := builder.ForPod("test-ns", "test-name").Volumes(&corev1.Volume{Name: "test-pvc"}).Result() hostingPod.ObjectMeta.SetUID("test-uid") @@ -959,7 +959,7 @@ func (dt *ddResumeTestHelper) resumeCancellableDataPath(_ *DataUploadReconciler, return dt.resumeErr } -func (dt *ddResumeTestHelper) Expose(context.Context, corev1.ObjectReference, string, string, map[string]string, time.Duration) error { +func (dt *ddResumeTestHelper) Expose(context.Context, corev1.ObjectReference, string, string, map[string]string, corev1.ResourceRequirements, time.Duration) error { return nil } diff --git a/pkg/controller/data_upload_controller.go b/pkg/controller/data_upload_controller.go index 22dc59bd5..5b3169621 100644 --- a/pkg/controller/data_upload_controller.go +++ b/pkg/controller/data_upload_controller.go @@ -73,13 +73,14 @@ type DataUploadReconciler struct { dataPathMgr *datapath.Manager loadAffinity *nodeagent.LoadAffinity backupPVCConfig map[string]nodeagent.BackupPVC + podResources corev1.ResourceRequirements preparingTimeout time.Duration metrics *metrics.ServerMetrics } func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClient kubernetes.Interface, csiSnapshotClient snapshotter.SnapshotV1Interface, - dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, backupPVCConfig map[string]nodeagent.BackupPVC, clock clocks.WithTickerAndDelayedExecution, - nodeName string, preparingTimeout time.Duration, log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { + dataPathMgr *datapath.Manager, loadAffinity *nodeagent.LoadAffinity, backupPVCConfig map[string]nodeagent.BackupPVC, podResources corev1.ResourceRequirements, + clock clocks.WithTickerAndDelayedExecution, nodeName string, preparingTimeout time.Duration, log logrus.FieldLogger, metrics *metrics.ServerMetrics) *DataUploadReconciler { return &DataUploadReconciler{ client: client, mgr: mgr, @@ -92,6 +93,7 @@ func NewDataUploadReconciler(client client.Client, mgr manager.Manager, kubeClie dataPathMgr: dataPathMgr, loadAffinity: loadAffinity, backupPVCConfig: backupPVCConfig, + podResources: podResources, preparingTimeout: preparingTimeout, metrics: metrics, } @@ -795,6 +797,7 @@ func (r *DataUploadReconciler) setupExposeParam(du *velerov2alpha1api.DataUpload VolumeSize: pvc.Spec.Resources.Requests[corev1.ResourceStorage], Affinity: r.loadAffinity, BackupPVCConfig: r.backupPVCConfig, + Resources: r.podResources, }, nil } return nil, nil diff --git a/pkg/controller/data_upload_controller_test.go b/pkg/controller/data_upload_controller_test.go index a6ee25574..5de53c100 100644 --- a/pkg/controller/data_upload_controller_test.go +++ b/pkg/controller/data_upload_controller_test.go @@ -232,7 +232,7 @@ func initDataUploaderReconcilerWithError(needError ...error) (*DataUploadReconci fakeKubeClient := clientgofake.NewSimpleClientset(daemonSet) return NewDataUploadReconciler(fakeClient, nil, fakeKubeClient, fakeSnapshotClient.SnapshotV1(), dataPathMgr, nil, map[string]nodeagent.BackupPVC{}, - testclocks.NewFakeClock(now), "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil + corev1.ResourceRequirements{}, testclocks.NewFakeClock(now), "test-node", time.Minute*5, velerotest.NewLogger(), metrics.NewServerMetrics()), nil } func dataUploadBuilder() *builder.DataUploadBuilder { diff --git a/pkg/exposer/csi_snapshot.go b/pkg/exposer/csi_snapshot.go index ccdb70dd2..630a8aad8 100644 --- a/pkg/exposer/csi_snapshot.go +++ b/pkg/exposer/csi_snapshot.go @@ -70,6 +70,9 @@ type CSISnapshotExposeParam struct { // BackupPVCConfig is the config for backupPVC (intermediate PVC) of snapshot data movement BackupPVCConfig map[string]nodeagent.BackupPVC + + // Resources defines the resource requirements of the hosting pod + Resources corev1.ResourceRequirements } // CSISnapshotExposeWaitParam define the input param for WaitExposed of CSI snapshots @@ -191,7 +194,7 @@ func (e *csiSnapshotExposer) Expose(ctx context.Context, ownerObject corev1.Obje } }() - backupPod, err := e.createBackupPod(ctx, ownerObject, backupPVC, csiExposeParam.OperationTimeout, csiExposeParam.HostingPodLabels, csiExposeParam.Affinity) + backupPod, err := e.createBackupPod(ctx, ownerObject, backupPVC, csiExposeParam.OperationTimeout, csiExposeParam.HostingPodLabels, csiExposeParam.Affinity, csiExposeParam.Resources) if err != nil { return errors.Wrap(err, "error to create backup pod") } @@ -423,7 +426,7 @@ func (e *csiSnapshotExposer) createBackupPVC(ctx context.Context, ownerObject co } func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject corev1.ObjectReference, backupPVC *corev1.PersistentVolumeClaim, operationTimeout time.Duration, - label map[string]string, affinity *nodeagent.LoadAffinity) (*corev1.Pod, error) { + label map[string]string, affinity *nodeagent.LoadAffinity, resources corev1.ResourceRequirements) (*corev1.Pod, error) { podName := ownerObject.Name containerName := string(ownerObject.UID) @@ -513,6 +516,7 @@ func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject co VolumeMounts: volumeMounts, VolumeDevices: volumeDevices, Env: podInfo.env, + Resources: resources, }, }, ServiceAccountName: podInfo.serviceAccount, diff --git a/pkg/exposer/generic_restore.go b/pkg/exposer/generic_restore.go index da8ad9b8f..b5c927602 100644 --- a/pkg/exposer/generic_restore.go +++ b/pkg/exposer/generic_restore.go @@ -37,7 +37,7 @@ import ( // GenericRestoreExposer is the interfaces for a generic restore exposer type GenericRestoreExposer interface { // Expose starts the process to a restore expose, the expose process may take long time - Expose(context.Context, corev1.ObjectReference, string, string, map[string]string, time.Duration) error + Expose(context.Context, corev1.ObjectReference, string, string, map[string]string, corev1.ResourceRequirements, time.Duration) error // GetExposed polls the status of the expose. // If the expose is accessible by the current caller, it waits the expose ready and returns the expose result. @@ -69,7 +69,7 @@ type genericRestoreExposer struct { log logrus.FieldLogger } -func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1.ObjectReference, targetPVCName string, sourceNamespace string, hostingPodLabels map[string]string, timeout time.Duration) error { +func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1.ObjectReference, targetPVCName string, sourceNamespace string, hostingPodLabels map[string]string, resources corev1.ResourceRequirements, timeout time.Duration) error { curLog := e.log.WithFields(logrus.Fields{ "owner": ownerObject.Name, "target PVC": targetPVCName, @@ -87,7 +87,7 @@ func (e *genericRestoreExposer) Expose(ctx context.Context, ownerObject corev1.O return errors.Errorf("Target PVC %s/%s has already been bound, abort", sourceNamespace, targetPVCName) } - restorePod, err := e.createRestorePod(ctx, ownerObject, targetPVC, timeout, hostingPodLabels, selectedNode) + restorePod, err := e.createRestorePod(ctx, ownerObject, targetPVC, timeout, hostingPodLabels, selectedNode, resources) if err != nil { return errors.Wrapf(err, "error to create restore pod") } @@ -297,7 +297,7 @@ func (e *genericRestoreExposer) RebindVolume(ctx context.Context, ownerObject co } func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObject corev1.ObjectReference, targetPVC *corev1.PersistentVolumeClaim, - operationTimeout time.Duration, label map[string]string, selectedNode string) (*corev1.Pod, error) { + operationTimeout time.Duration, label map[string]string, selectedNode string, resources corev1.ResourceRequirements) (*corev1.Pod, error) { restorePodName := ownerObject.Name restorePVCName := ownerObject.Name @@ -370,6 +370,7 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec VolumeMounts: volumeMounts, VolumeDevices: volumeDevices, Env: podInfo.env, + Resources: resources, }, }, ServiceAccountName: podInfo.serviceAccount, diff --git a/pkg/exposer/generic_restore_test.go b/pkg/exposer/generic_restore_test.go index 02fa85728..4c3221b5c 100644 --- a/pkg/exposer/generic_restore_test.go +++ b/pkg/exposer/generic_restore_test.go @@ -180,7 +180,7 @@ func TestRestoreExpose(t *testing.T) { } } - err := exposer.Expose(context.Background(), ownerObject, test.targetPVCName, test.sourceNamespace, map[string]string{}, time.Millisecond) + err := exposer.Expose(context.Background(), ownerObject, test.targetPVCName, test.sourceNamespace, map[string]string{}, corev1.ResourceRequirements{}, time.Millisecond) assert.EqualError(t, err, test.err) }) } diff --git a/pkg/exposer/mocks/generic_restore.go b/pkg/exposer/mocks/generic_restore.go index 6981f5cef..e0b76d6e7 100644 --- a/pkg/exposer/mocks/generic_restore.go +++ b/pkg/exposer/mocks/generic_restore.go @@ -26,17 +26,17 @@ func (_m *GenericRestoreExposer) CleanUp(_a0 context.Context, _a1 v1.ObjectRefer _m.Called(_a0, _a1) } -// Expose provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4, _a5 -func (_m *GenericRestoreExposer) Expose(_a0 context.Context, _a1 v1.ObjectReference, _a2 string, _a3 string, _a4 map[string]string, _a5 time.Duration) error { - ret := _m.Called(_a0, _a1, _a2, _a3, _a4, _a5) +// Expose provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4, _a5, _a6 +func (_m *GenericRestoreExposer) Expose(_a0 context.Context, _a1 v1.ObjectReference, _a2 string, _a3 string, _a4 map[string]string, _a5 v1.ResourceRequirements, _a6 time.Duration) error { + ret := _m.Called(_a0, _a1, _a2, _a3, _a4, _a5, _a6) if len(ret) == 0 { panic("no return value specified for Expose") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, v1.ObjectReference, string, string, map[string]string, time.Duration) error); ok { - r0 = rf(_a0, _a1, _a2, _a3, _a4, _a5) + if rf, ok := ret.Get(0).(func(context.Context, v1.ObjectReference, string, string, map[string]string, v1.ResourceRequirements, time.Duration) error); ok { + r0 = rf(_a0, _a1, _a2, _a3, _a4, _a5, _a6) } else { r0 = ret.Error(0) } diff --git a/pkg/nodeagent/node_agent.go b/pkg/nodeagent/node_agent.go index 78cdc2771..f7a9629dc 100644 --- a/pkg/nodeagent/node_agent.go +++ b/pkg/nodeagent/node_agent.go @@ -70,6 +70,13 @@ type BackupPVC struct { ReadOnly bool `json:"readOnly,omitempty"` } +type PodResources struct { + CPURequest string `json:"cpuRequest,omitempty"` + MemoryRequest string `json:"memoryRequest,omitempty"` + CPULimit string `json:"cpuLimit,omitempty"` + MemoryLimit string `json:"memoryLimit,omitempty"` +} + type Configs struct { // LoadConcurrency is the config for data path load concurrency per node. LoadConcurrency *LoadConcurrency `json:"loadConcurrency,omitempty"` @@ -79,6 +86,9 @@ type Configs struct { // BackupPVCConfig is the config for backupPVC (intermediate PVC) of snapshot data movement BackupPVCConfig map[string]BackupPVC `json:"backupPVC,omitempty"` + + // PodResources is the resource config for various types of pods launched by node-agent, i.e., data mover pods. + PodResources *PodResources `json:"podResources,omitempty"` } // IsRunning checks if the node agent daemonset is running properly. If not, return the error found From f5671c728c1c7f4d61a790715ef6e0d251c004c1 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Fri, 23 Aug 2024 07:15:10 -0400 Subject: [PATCH 65/69] Scrub namespace terminating status and deletion timestamp on restore. Descriptive restore error on terminating namespace. (#7424) revert utils_test.go address https://github.com/vmware-tanzu/velero/pull/7424/files/c7b189dd6035839c9eb8ce3dab4ead574de77adb#r1494194484 Update pkg/util/kube/utils.go Signed-off-by: Tiger Kaovilai Signed-off-by: Tiger Kaovilai --- changelogs/unreleased/7424-kaovilai | 1 + pkg/util/kube/utils.go | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/7424-kaovilai diff --git a/changelogs/unreleased/7424-kaovilai b/changelogs/unreleased/7424-kaovilai new file mode 100644 index 000000000..45116a1b9 --- /dev/null +++ b/changelogs/unreleased/7424-kaovilai @@ -0,0 +1 @@ +Descriptive restore error when restoring into a terminating namespace. \ No newline at end of file diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index 3eb853829..d50e68728 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -65,18 +65,23 @@ func NamespaceAndName(objMeta metav1.Object) string { } // EnsureNamespaceExistsAndIsReady attempts to create the provided Kubernetes namespace. -// It returns three values: a bool indicating whether or not the namespace is ready, -// a bool indicating whether or not the namespace was created and an error if the creation failed -// for a reason other than that the namespace already exists. Note that in the case where the -// namespace already exists and is not ready, this function will return (false, false, nil). -// If the namespace exists and is marked for deletion, this function will wait up to the timeout for it to fully delete. -func EnsureNamespaceExistsAndIsReady(namespace *corev1api.Namespace, client corev1client.NamespaceInterface, timeout time.Duration) (bool, bool, error) { +// It returns three values: +// - a bool indicating whether or not the namespace is ready, +// - a bool indicating whether or not the namespace was created +// - an error if one occurred. +// +// examples: +// +// namespace already exists and is not ready, this function will return (false, false, nil). +// If the namespace exists and is marked for deletion, this function will wait up to the timeout for it to fully delete. +func EnsureNamespaceExistsAndIsReady(namespace *corev1api.Namespace, client corev1client.NamespaceInterface, timeout time.Duration) (ready bool, nsCreated bool, err error) { // nsCreated tells whether the namespace was created by this method // required for keeping track of number of restored items - var nsCreated bool - var ready bool - err := wait.PollUntilContextTimeout(context.Background(), time.Second, timeout, true, func(ctx context.Context) (bool, error) { + // if namespace is marked for deletion, and we timed out, report an error + var terminatingNamespace bool + err = wait.PollUntilContextTimeout(context.Background(), time.Second, timeout, true, func(ctx context.Context) (bool, error) { clusterNS, err := client.Get(ctx, namespace.Name, metav1.GetOptions{}) + // if namespace is marked for deletion, and we timed out, report an error if apierrors.IsNotFound(err) { // Namespace isn't in cluster, we're good to create. @@ -90,6 +95,7 @@ func EnsureNamespaceExistsAndIsReady(namespace *corev1api.Namespace, client core if clusterNS != nil && (clusterNS.GetDeletionTimestamp() != nil || clusterNS.Status.Phase == corev1api.NamespaceTerminating) { // Marked for deletion, keep waiting + terminatingNamespace = true return false, nil } @@ -100,6 +106,9 @@ func EnsureNamespaceExistsAndIsReady(namespace *corev1api.Namespace, client core // err will be set if we timed out or encountered issues retrieving the namespace, if err != nil { + if terminatingNamespace { + return false, nsCreated, errors.Wrapf(err, "timed out waiting for terminating namespace %s to disappear before restoring", namespace.Name) + } return false, nsCreated, errors.Wrapf(err, "error getting namespace %s", namespace.Name) } From 3f1853c96183bb9fa5c5ccc406fd69a59bfe3634 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 26 Aug 2024 15:07:40 +0800 Subject: [PATCH 66/69] Issues with "backlog" label should never stale Signed-off-by: Daniel Jiang --- .github/workflows/stale-issues.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml index 7b29970fc..5273efeaa 100644 --- a/.github/workflows/stale-issues.yml +++ b/.github/workflows/stale-issues.yml @@ -20,4 +20,4 @@ jobs: days-before-pr-close: -1 # Only issues made after Feb 09 2021. start-date: "2021-09-02T00:00:00" - exempt-issue-labels: "Epic,Area/CLI,Area/Cloud/AWS,Area/Cloud/Azure,Area/Cloud/GCP,Area/Cloud/vSphere,Area/CSI,Area/Design,Area/Documentation,Area/Plugins,Bug,Enhancement/User,kind/requirement,kind/refactor,kind/tech-debt,limitation,Needs investigation,Needs triage,Needs Product,P0 - Hair on fire,P1 - Important,P2 - Long-term important,P3 - Wouldn't it be nice if...,Product Requirements,Restic - GA,Restic,release-blocker,Security" + exempt-issue-labels: "Epic,Area/CLI,Area/Cloud/AWS,Area/Cloud/Azure,Area/Cloud/GCP,Area/Cloud/vSphere,Area/CSI,Area/Design,Area/Documentation,Area/Plugins,Bug,Enhancement/User,kind/requirement,kind/refactor,kind/tech-debt,limitation,Needs investigation,Needs triage,Needs Product,P0 - Hair on fire,P1 - Important,P2 - Long-term important,P3 - Wouldn't it be nice if...,Product Requirements,Restic - GA,Restic,release-blocker,Security,backlog" From 252e8a866fe9626cf3f71166e59489ffaa5b7291 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 29 Aug 2024 10:13:32 +0800 Subject: [PATCH 67/69] node-agent config for pod resources Signed-off-by: Lyndon-Li --- design/vgdp-micro-service/vgdp-micro-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/vgdp-micro-service/vgdp-micro-service.md b/design/vgdp-micro-service/vgdp-micro-service.md index c6d1e117e..d40eda1e9 100644 --- a/design/vgdp-micro-service/vgdp-micro-service.md +++ b/design/vgdp-micro-service/vgdp-micro-service.md @@ -194,7 +194,7 @@ type PodResources struct { ``` The string values must mactch Kubernetes Quantity expressions; for each resource, the "request" value must not be larger than the "limit" value. Otherwise, if any one of the values fail, all the resource configurations will be ignored. -The configurations are loaded node-agent at start time, so users can change the values in the configMap any time, but the changes won't effect until node-agent restarts. +The configurations are loaded by node-agent at start time, so users can change the values in the configMap any time, but the changes won't effect until node-agent restarts. ## node-agent From a80c9359bfb7cb03e1f3188579a7e11508360d27 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 28 Aug 2024 17:42:52 +0800 Subject: [PATCH 68/69] bump up kopia Signed-off-by: Lyndon-Li --- changelogs/unreleased/8158-Lyndon-Li | 1 + go.mod | 2 +- go.sum | 4 ++-- pkg/uploader/kopia/block_restore.go | 3 ++- 4 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/8158-Lyndon-Li diff --git a/changelogs/unreleased/8158-Lyndon-Li b/changelogs/unreleased/8158-Lyndon-Li new file mode 100644 index 000000000..ed60df19c --- /dev/null +++ b/changelogs/unreleased/8158-Lyndon-Li @@ -0,0 +1 @@ +Fix issue #8155, Merge Kopia upstream commits for critical issue fixes and performance improvements \ No newline at end of file diff --git a/go.mod b/go.mod index bef073147..7b77a5175 100644 --- a/go.mod +++ b/go.mod @@ -177,4 +177,4 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) -replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20240417031915-e07d5b7de567 +replace github.com/kopia/kopia => github.com/project-velero/kopia v0.0.0-20240829032136-7fca59662a06 diff --git a/go.sum b/go.sum index 184f015c3..51cac8f80 100644 --- a/go.sum +++ b/go.sum @@ -613,8 +613,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/project-velero/kopia v0.0.0-20240417031915-e07d5b7de567 h1:Gb5eZktsqgnhOfQmKWIlVA9yKvschdr8n8d6y1RLFA0= -github.com/project-velero/kopia v0.0.0-20240417031915-e07d5b7de567/go.mod h1:2HlqZb/N6SNsWUCZzyeh9Lw29PeDRHDkMUiuQCEWt4Y= +github.com/project-velero/kopia v0.0.0-20240829032136-7fca59662a06 h1:QLtEHOokfqpsW99nDFyU2IB47LsGhDqMICGPA+ZgjpM= +github.com/project-velero/kopia v0.0.0-20240829032136-7fca59662a06/go.mod h1:2HlqZb/N6SNsWUCZzyeh9Lw29PeDRHDkMUiuQCEWt4Y= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= diff --git a/pkg/uploader/kopia/block_restore.go b/pkg/uploader/kopia/block_restore.go index 22c8ec1fc..b9fba99fc 100644 --- a/pkg/uploader/kopia/block_restore.go +++ b/pkg/uploader/kopia/block_restore.go @@ -41,7 +41,7 @@ var _ restore.Output = &BlockOutput{} const bufferSize = 128 * 1024 -func (o *BlockOutput) WriteFile(ctx context.Context, relativePath string, remoteFile fs.File) error { +func (o *BlockOutput) WriteFile(ctx context.Context, relativePath string, remoteFile fs.File, progressCb restore.FileWriteProgress) error { remoteReader, err := remoteFile.Open(ctx) if err != nil { return errors.Wrapf(err, "failed to open remote file %s", remoteFile.Name()) @@ -70,6 +70,7 @@ func (o *BlockOutput) WriteFile(ctx context.Context, relativePath string, remote offset := 0 for bytesToWrite > 0 { if bytesWritten, err := targetFile.Write(buffer[offset:bytesToWrite]); err == nil { + progressCb(int64(bytesWritten)) bytesToWrite -= bytesWritten offset += bytesWritten } else { From f6e2b0107f1a20737a357dfde9bf87ec91855eb3 Mon Sep 17 00:00:00 2001 From: Shubham Pampattiwar Date: Wed, 21 Aug 2024 16:05:25 -0700 Subject: [PATCH 69/69] Apply backupPVCConfig to backupPod volume spec Signed-off-by: Shubham Pampattiwar add changelog file Signed-off-by: Shubham Pampattiwar make backupPod volume mount always readOnly Signed-off-by: Shubham Pampattiwar use assert.True() Signed-off-by: Shubham Pampattiwar Add readOnly param for MakePodPVCAttachment func lint fix Signed-off-by: Shubham Pampattiwar --- .../unreleased/8141-shubham-pampattiwar | 1 + pkg/exposer/csi_snapshot.go | 3 +- pkg/exposer/csi_snapshot_test.go | 112 ++++++++++++++++-- pkg/exposer/generic_restore.go | 2 +- pkg/util/kube/pvc_pv.go | 3 +- pkg/util/kube/pvc_pv_test.go | 24 +++- 6 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/8141-shubham-pampattiwar diff --git a/changelogs/unreleased/8141-shubham-pampattiwar b/changelogs/unreleased/8141-shubham-pampattiwar new file mode 100644 index 000000000..4550628f7 --- /dev/null +++ b/changelogs/unreleased/8141-shubham-pampattiwar @@ -0,0 +1 @@ +Apply backupPVCConfig to backupPod volume spec diff --git a/pkg/exposer/csi_snapshot.go b/pkg/exposer/csi_snapshot.go index ccdb70dd2..f6a61c48f 100644 --- a/pkg/exposer/csi_snapshot.go +++ b/pkg/exposer/csi_snapshot.go @@ -435,7 +435,7 @@ func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject co } var gracePeriod int64 = 0 - volumeMounts, volumeDevices, volumePath := kube.MakePodPVCAttachment(volumeName, backupPVC.Spec.VolumeMode) + volumeMounts, volumeDevices, volumePath := kube.MakePodPVCAttachment(volumeName, backupPVC.Spec.VolumeMode, true) volumeMounts = append(volumeMounts, podInfo.volumeMounts...) volumes := []corev1.Volume{{ @@ -443,6 +443,7 @@ func (e *csiSnapshotExposer) createBackupPod(ctx context.Context, ownerObject co VolumeSource: corev1.VolumeSource{ PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ ClaimName: backupPVC.Name, + ReadOnly: true, }, }, }} diff --git a/pkg/exposer/csi_snapshot_test.go b/pkg/exposer/csi_snapshot_test.go index 82044dbb8..afde0b7f0 100644 --- a/pkg/exposer/csi_snapshot_test.go +++ b/pkg/exposer/csi_snapshot_test.go @@ -155,15 +155,17 @@ func TestExpose(t *testing.T) { } tests := []struct { - name string - snapshotClientObj []runtime.Object - kubeClientObj []runtime.Object - ownerBackup *velerov1.Backup - exposeParam CSISnapshotExposeParam - snapReactors []reactor - kubeReactors []reactor - err string - expectedVolumeSize *resource.Quantity + name string + snapshotClientObj []runtime.Object + kubeClientObj []runtime.Object + ownerBackup *velerov1.Backup + exposeParam CSISnapshotExposeParam + snapReactors []reactor + kubeReactors []reactor + err string + expectedVolumeSize *resource.Quantity + expectedReadOnlyPVC bool + expectedBackupPVCStorageClass string }{ { name: "wait vs ready fail", @@ -390,6 +392,84 @@ func TestExpose(t *testing.T) { }, expectedVolumeSize: resource.NewQuantity(567890, ""), }, + { + name: "backupPod mounts read only backupPVC", + ownerBackup: backup, + exposeParam: CSISnapshotExposeParam{ + SnapshotName: "fake-vs", + SourceNamespace: "fake-ns", + StorageClass: "fake-sc", + AccessMode: AccessModeFileSystem, + OperationTimeout: time.Millisecond, + ExposeTimeout: time.Millisecond, + BackupPVCConfig: map[string]nodeagent.BackupPVC{ + "fake-sc": { + StorageClass: "fake-sc-read-only", + ReadOnly: true, + }, + }, + }, + snapshotClientObj: []runtime.Object{ + vsObject, + vscObj, + }, + kubeClientObj: []runtime.Object{ + daemonSet, + }, + expectedReadOnlyPVC: true, + }, + { + name: "backupPod mounts read only backupPVC and storageClass specified in backupPVC config", + ownerBackup: backup, + exposeParam: CSISnapshotExposeParam{ + SnapshotName: "fake-vs", + SourceNamespace: "fake-ns", + StorageClass: "fake-sc", + AccessMode: AccessModeFileSystem, + OperationTimeout: time.Millisecond, + ExposeTimeout: time.Millisecond, + BackupPVCConfig: map[string]nodeagent.BackupPVC{ + "fake-sc": { + StorageClass: "fake-sc-read-only", + ReadOnly: true, + }, + }, + }, + snapshotClientObj: []runtime.Object{ + vsObject, + vscObj, + }, + kubeClientObj: []runtime.Object{ + daemonSet, + }, + expectedReadOnlyPVC: true, + expectedBackupPVCStorageClass: "fake-sc-read-only", + }, + { + name: "backupPod mounts backupPVC with storageClass specified in backupPVC config", + ownerBackup: backup, + exposeParam: CSISnapshotExposeParam{ + SnapshotName: "fake-vs", + SourceNamespace: "fake-ns", + StorageClass: "fake-sc", + AccessMode: AccessModeFileSystem, + OperationTimeout: time.Millisecond, + ExposeTimeout: time.Millisecond, + BackupPVCConfig: map[string]nodeagent.BackupPVC{ + "fake-sc": { + StorageClass: "fake-sc-read-only", + }, + }, + }, + snapshotClientObj: []runtime.Object{ + vsObject, + vscObj, + }, + kubeClientObj: []runtime.Object{ + daemonSet, + }, + expectedBackupPVCStorageClass: "fake-sc-read-only", + }, } for _, test := range tests { @@ -452,6 +532,20 @@ func TestExpose(t *testing.T) { } else { assert.Equal(t, *resource.NewQuantity(restoreSize, ""), backupPVC.Spec.Resources.Requests[corev1.ResourceStorage]) } + + if test.expectedReadOnlyPVC { + gotReadOnlyAccessMode := false + for _, accessMode := range backupPVC.Spec.AccessModes { + if accessMode == corev1.ReadOnlyMany { + gotReadOnlyAccessMode = true + } + } + assert.Equal(t, test.expectedReadOnlyPVC, gotReadOnlyAccessMode) + } + + if test.expectedBackupPVCStorageClass != "" { + assert.Equal(t, test.expectedBackupPVCStorageClass, *backupPVC.Spec.StorageClassName) + } } else { assert.EqualError(t, err, test.err) } diff --git a/pkg/exposer/generic_restore.go b/pkg/exposer/generic_restore.go index da8ad9b8f..89f932cf4 100644 --- a/pkg/exposer/generic_restore.go +++ b/pkg/exposer/generic_restore.go @@ -310,7 +310,7 @@ func (e *genericRestoreExposer) createRestorePod(ctx context.Context, ownerObjec } var gracePeriod int64 = 0 - volumeMounts, volumeDevices, volumePath := kube.MakePodPVCAttachment(volumeName, targetPVC.Spec.VolumeMode) + volumeMounts, volumeDevices, volumePath := kube.MakePodPVCAttachment(volumeName, targetPVC.Spec.VolumeMode, false) volumeMounts = append(volumeMounts, podInfo.volumeMounts...) volumes := []corev1.Volume{{ diff --git a/pkg/util/kube/pvc_pv.go b/pkg/util/kube/pvc_pv.go index 070e5ed7b..1811a2c1d 100644 --- a/pkg/util/kube/pvc_pv.go +++ b/pkg/util/kube/pvc_pv.go @@ -346,7 +346,7 @@ func IsPVCBound(pvc *corev1api.PersistentVolumeClaim) bool { } // MakePodPVCAttachment returns the volume mounts and devices for a pod needed to attach a PVC -func MakePodPVCAttachment(volumeName string, volumeMode *corev1api.PersistentVolumeMode) ([]corev1api.VolumeMount, []corev1api.VolumeDevice, string) { +func MakePodPVCAttachment(volumeName string, volumeMode *corev1api.PersistentVolumeMode, readOnly bool) ([]corev1api.VolumeMount, []corev1api.VolumeDevice, string) { var volumeMounts []corev1api.VolumeMount = nil var volumeDevices []corev1api.VolumeDevice = nil volumePath := "/" + volumeName @@ -360,6 +360,7 @@ func MakePodPVCAttachment(volumeName string, volumeMode *corev1api.PersistentVol volumeMounts = []corev1api.VolumeMount{{ Name: volumeName, MountPath: volumePath, + ReadOnly: readOnly, }} } diff --git a/pkg/util/kube/pvc_pv_test.go b/pkg/util/kube/pvc_pv_test.go index 5f6c3be74..55c31774d 100644 --- a/pkg/util/kube/pvc_pv_test.go +++ b/pkg/util/kube/pvc_pv_test.go @@ -1386,6 +1386,7 @@ func TestMakePodPVCAttachment(t *testing.T) { name string volumeName string volumeMode corev1api.PersistentVolumeMode + readOnly bool expectedVolumeMount []corev1api.VolumeMount expectedVolumeDevice []corev1api.VolumeDevice expectedVolumePath string @@ -1393,10 +1394,12 @@ func TestMakePodPVCAttachment(t *testing.T) { { name: "no volume mode specified", volumeName: "volume-1", + readOnly: true, expectedVolumeMount: []corev1api.VolumeMount{ { Name: "volume-1", MountPath: "/volume-1", + ReadOnly: true, }, }, expectedVolumePath: "/volume-1", @@ -1405,10 +1408,12 @@ func TestMakePodPVCAttachment(t *testing.T) { name: "fs mode specified", volumeName: "volume-2", volumeMode: corev1api.PersistentVolumeFilesystem, + readOnly: true, expectedVolumeMount: []corev1api.VolumeMount{ { Name: "volume-2", MountPath: "/volume-2", + ReadOnly: true, }, }, expectedVolumePath: "/volume-2", @@ -1425,6 +1430,20 @@ func TestMakePodPVCAttachment(t *testing.T) { }, expectedVolumePath: "/volume-3", }, + { + name: "fs mode specified with readOnly as false", + volumeName: "volume-4", + readOnly: false, + volumeMode: corev1api.PersistentVolumeFilesystem, + expectedVolumeMount: []corev1api.VolumeMount{ + { + Name: "volume-4", + MountPath: "/volume-4", + ReadOnly: false, + }, + }, + expectedVolumePath: "/volume-4", + }, } for _, tc := range testCases { @@ -1434,11 +1453,14 @@ func TestMakePodPVCAttachment(t *testing.T) { volMode = &tc.volumeMode } - mount, device, path := MakePodPVCAttachment(tc.volumeName, volMode) + mount, device, path := MakePodPVCAttachment(tc.volumeName, volMode, tc.readOnly) assert.Equal(t, tc.expectedVolumeMount, mount) assert.Equal(t, tc.expectedVolumeDevice, device) assert.Equal(t, tc.expectedVolumePath, path) + if tc.expectedVolumeMount != nil { + assert.Equal(t, tc.expectedVolumeMount[0].ReadOnly, tc.readOnly) + } }) } }