From bf467e3ac338d790e9f2ed1cd165968c1117ba9c Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 19 May 2022 16:48:34 +0800 Subject: [PATCH 01/38] Unified Repository Design Signed-off-by: Lyndon-Li --- changelogs/unreleased/4926-lyndon | 1 + .../br-workflow.png | Bin 0 -> 143971 bytes .../debug-log-repository.png | Bin 0 -> 58712 bytes .../debug-log-uploader.png | Bin 0 -> 62774 bytes .../maintenance-workflow.png | Bin 0 -> 80375 bytes .../progress-update.png | Bin 0 -> 57729 bytes .../scope.png | Bin 0 -> 32284 bytes .../snapshot-deletion-workflow.png | Bin 0 -> 122200 bytes .../unified-repo-and-kopia-integration.md | 320 ++++++++++++++++++ .../unified-repo.png | Bin 0 -> 38955 bytes 10 files changed, 321 insertions(+) create mode 100644 changelogs/unreleased/4926-lyndon create mode 100644 design/unified-repo-and-kopia-integration/br-workflow.png create mode 100644 design/unified-repo-and-kopia-integration/debug-log-repository.png create mode 100644 design/unified-repo-and-kopia-integration/debug-log-uploader.png create mode 100644 design/unified-repo-and-kopia-integration/maintenance-workflow.png create mode 100644 design/unified-repo-and-kopia-integration/progress-update.png create mode 100644 design/unified-repo-and-kopia-integration/scope.png create mode 100644 design/unified-repo-and-kopia-integration/snapshot-deletion-workflow.png create mode 100644 design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md create mode 100644 design/unified-repo-and-kopia-integration/unified-repo.png diff --git a/changelogs/unreleased/4926-lyndon b/changelogs/unreleased/4926-lyndon new file mode 100644 index 000000000..d5c23db47 --- /dev/null +++ b/changelogs/unreleased/4926-lyndon @@ -0,0 +1 @@ +Unified Repository Design \ No newline at end of file diff --git a/design/unified-repo-and-kopia-integration/br-workflow.png b/design/unified-repo-and-kopia-integration/br-workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..6d935c7d043d293cfd27aefbe91f3a48aeec9936 GIT binary patch literal 143971 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%0Af6skY@TEUZdQs^ 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%0Aa^9zEVP)2v1A1`tD;79AT-Jv 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 MLSDQ~)F9~p0i_KJH2?qr literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/debug-log-repository.png b/design/unified-repo-and-kopia-integration/debug-log-repository.png new file mode 100644 index 0000000000000000000000000000000000000000..640d12944659f2690918d933cd79483651feaa62 GIT binary patch literal 58712 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 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/maintenance-workflow.png b/design/unified-repo-and-kopia-integration/maintenance-workflow.png new file mode 100644 index 0000000000000000000000000000000000000000..938eb45d24211ae1b07fef53fb3b41bb7ed864eb GIT binary patch literal 80375 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 EKkGi}SpWb4 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/progress-update.png b/design/unified-repo-and-kopia-integration/progress-update.png new file mode 100644 index 0000000000000000000000000000000000000000..33305332b6ea9d1d53f2b6895eec1a3bcb3952ca GIT binary patch literal 57729 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 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/scope.png b/design/unified-repo-and-kopia-integration/scope.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff97da03fde12bc203bdc160759976078e27d9a GIT binary patch literal 32284 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 literal 0 HcmV?d00001 diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md new file mode 100644 index 000000000..4df9d4265 --- /dev/null +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -0,0 +1,320 @@ +# Unified Repository & Kopia Integration Design + +## Glossary & Abbreviation + +**BR**: Backup & Restore +**Backup Storage**: The storage that meets BR requirements, for example, scalable, durable, cost-effective, etc., therefore, Backup Storage is usually implemented as Object storage or File System storage, it may be on-premise or in cloud. Backup Storage is not BR specific necessarily, so it usually doesn’t provide most of the BR related features. On the other hand, storage vendors may provide BR specific storages that include some BR features like deduplication, compression, encryption, etc. For a standalone BR solution (i.e. Velero), the Backup Storage is not part of the solution, it is provided by users, so the BR solution should not assume the BR related features are always available from the Backup Storage. +**Backup Repository**: Backup repository is layered between BR data movers and Backup Storage to provide BR related features. Backup Repository is a part of BR solution, so generally, BR solution by default leverages the Backup Repository to provide the features because Backup Repository is always available; when Backup Storage provides duplicated features, and the latter is more beneficial (i.e., performance is better), BR solution should have the ability to opt to use the Backup Storage’s implementation. +**Data Mover**: The BR module to read/write data from/to workloads, the aim is to eliminate the differences of workloads. +**TCO**: Total Cost of Ownership. This is a general criteria for products/solutions, but also means a lot for BR solutions. For example, this means what kind of backup storage (and its cost) it requires, the retention policy of backup copies, the ways to remove backup data redundancy, etc. +**RTO**: Recovery Time Objective. This is the duration of time that users’ business can recover after a disaster. + +## Background + +As a Kubernetes BR solution, Velero is pursuing the capability to back up data from the volatile and limited production environment into the durable, heterogeneous and scalable backup storage. This relies on two parts: + +- Move data from various production workloads. The data mover has this role. Depending on the type of workload, Velero needs different data movers. For example, file system data mover, block data mover, and data movers for specific applications. At present, Velero supports moving file system data from PVs through Restic, which plays the role of the File System Data Mover. +- Persist data in backup storage. For a BR solution, this is the responsibility of the backup repository. Specifically, the backup repository is required to: + - Efficiently save data so as to reduce TCO. For example, deduplicate and compress the data before saving it + - Securely save data so as to meet security criteria. For example, encrypt the data on rest, make the data immutable after backup, and detect/protect from ransomware + - Efficiently retrieve data during restore so as to meet RTO. For example, restore a small unit of data or data associated with a small span of time + - Effectively manage data from all kinds of data movers in all kinds of backup storage. This means 2 things: first, apparently, backup storages are different from each other; second, some data movers may save quite different data from others, for example, some data movers save a portion of the logical object for each backup and need to visit and manage the portions as an entire logic object, aka. incremental backup. The backup repository needs to provide unified functionalities to eliminate the differences from the both ends + - Provide scalabilities so that users could assign resources (CPU, memory, network, etc.) in a flexible way to the backup repository since backup repository contains resource consuming modules + +At present, Velero provides some of these capabilities by leveraging Restic (e.g., deduplication and encryption on rest). This means that in addition to being a data mover for file system level data, Restic also plays the role of a backup repository, albeit one that is incomplete and limited: + +- Restic is an inseparable unit made up of a file system data mover and a repository. This means that the repository capabilities are only available for Restic file system backup. We cannot provide the same capabilities to other data movers using Restic. +- The backup storage Velero supports through our Restic backup path depends on the storage Restic supports. As a result, if there is a requirement to introduce backup storage that Restic doesn’t support, we have no way to make it. +- There is no way to enhance or extend the repository capabilities, because of the same reason – Restic is an inseparable unit, we cannot insert one or more customized layers to make the enhancements and extensions. + +Moreover, as reflected by user-reported issues, Restic seems to have many performance issues on both the file system data mover side and the repository side. + +On the other hand, based on a previous analysis and testing, we found that Kopia has better performance, with more features and more suitable to fulfill Velero’s repository targets (Kopia’s architecture divides modules more clearly according to their responsibilities, every module plays a complete role with clear interfaces. This makes it easier to take individual modules to Velero without losing critical functionalities). + +## Goals + +- Define a Unified Repository Interface that various data movers could interact with. This is for below purposes: + - All kinds of data movers acquire the same set of backup repository capabilities very easily + - Provide the possibility to plugin in different backup repositories/backup storages without affecting the upper layers + - Provide the possibility to plugin in modules between data mover and backup repository, so as to extend the repository capabilities + - Provide the possibility to scale the backup repository without affecting the upper layers +- Use Kopia repository to implement the Unified Repository +- Use Kopia uploader as the file system data mover for Pod Volume Backup +- Have Kopia uploader calling the Unified Repository Interface and save/retrieve data to/from the Unified Repository +- Use the existing logic or add new logic to manage the unified repository and Kopia uploader +- Preserve the legacy Restic path, this is for the consideration of backward compatibility + +## Non-Goals + +- The Unified Repository supports all kinds of data movers to save logic objects into it. How these logic objects are organized for a specific data mover (for example, how a volume’s block data is organized and represented by a unified repository object) should be included in the related data mover design. +- At present, Velero saves Kubernetes resources, backup metedata, debug logs separately. Eventually, we want to save them in the Unified Repository. How to organize these data into the Unified Repository should be included in a separate design. +- Kopia uploader could be used as a generic file system data mover. How it is integrated in other cases, for example, CSI file system mode BR, should be included in the related data mover design. +- The adanced modes of the Unified Repository, for example, backup repository/storage plugin, backup repository extension, etc. are not included in this design. We will have separate designs to cover them whenever necessary. + +## Architecture of Unified Repository + +Below shows the primary modules and their responsibilities: + +- Kopia uploader is used as a generic file system data mover, so it could move all file system data either from the production PV (as Velero’s Pod Volume Backup does), or from any kind of snapshot (i.e., CSI snapshot). +- Kopia uploader, the same as other data movers, calls the Unified Repository Interface to write/read data to/from the Unified Repository. +- Kopia repository layers, CAOS and CABS, work as the backup repository and expose the Kopia Repository interface. +- A Kopia Repository Library works as an adapter between Unified Repository Interface and Kopia Repository interface. Specifically, it implements Unified Repository Interface and calls Kopia Repository interface. +- At present, there is only one kind of backup repository -- Kopia Repository. If a new backup repository/storage is required, we need to create a new Library as an adapter to the Unified Repository Interface +- At present, the Kopia Repository works as a single piece in the same process of the caller, in future, we may run its CABS into a dedicated process or node. +- At present, we don’t have a requirement to extend the backup repository, if needed, an extra module could be added as an upper layer into the Unified Repository without changing the data movers. + +The Unified Repository takes two kinds of data: +- Unified Repository Object: This is the user's logical data, for example, files/directories, blocks of a volume, data of a database, etc. +- Unified Repository Manifest: This could include all other data to maintain the object data, for example, snapshot information, etc. + +For Unified Repository Object/Manifest, a brief guidance to data movers are as below: +- Data movers treat the simple unit of data they recognize as an Object. For example, a file system data mover treats a file or a directory as an Object; a block data mover treats a volume as an Object +- Data movers don't need to care about the differences between full and incremental backups regarding the data organization. Data movers always have full views of their objects, if an object is partially written, they use the object writer's Seek function to skip the unchanged parts +- Unified Repository may divide the data movers' logical Object into sub-objects or slices, or append internal metadata, but they are transparent to data movers +- Every Object has an unified identifier, in order to retrieve the Object later, data movers need to save the identifiers into the snapshot information. The snapshot information is saved as a Manifest. +- Manifests could hold any kind of small piece data in a K-V manner. Inside the backup repository, these kinds of data may be processed differently from Object data, but it is transparent to data movers. +- A Manifest also has an unified identifier, the Unified Repository provides the capabilities to list all the Manifests or a specified Manifest by its identifier, or a specified Manifest by its name, or a set of Manifests by their labels. + +![A Unified Repository Architecture](unified-repo.png) + +Velero by default uses the Unified Repository for all kinds of data movement, it is also able to integrate with other data movement paths from any party, for any purpose. Details are concluded as below: + +- Built-in Data Path: this is the default data movement path, which uses Velero built-in data movers to backup/restore workloads, the data is written to/read from the Unified Repository. +- Data Mover Replacement: Any party could write its own data movers and plug them into Velero. Meanwhile, these plugin data movers could also write/read data to/from Velero’s Unified Repository so that these data movers could expose the same capabilities that provided by the Unified Repository. In order to do this, the data mover providers need to call the Unified Repository Interface from inside their plugin data movers. +- Data Path Replacement: Some vendors may already have their own data movers and backup repository and they want to replace Velero’s entire data path (including data movers and backup repository). In this case, the providers only need to implement their plugin data movers, all the things downwards are a black box to Velero and managed by providers themselves (including API call, data transport, installation, life cycle management, etc.). Therefore, this case is out of the scope of Unified Repository. +![A Scope](scope.png) + +# Detailed Design + +## The Unified Repository Interface +Below are the definitions of the Unified Repository Interface +``` +///BackupRepoService is used to initialize, open or maintain a backup repository +type BackupRepoService interface { + ///Create a backup repository or connect to an existing backup repository + ///repoOption: option to the backup repository and the underlying backup storage + ///createNew: indicates whether to create a new or connect to an existing backup repository + ///result: the backup repository specific output that could be used to open the backup repository later + Init(repoOption RepoOptions, createNew bool) (result map[string]string, err error) + + ///Open an backup repository that has been created/connected + ///config: options to open the backup repository and the underlying storage + Open(config map[string]string) (BackupRepo, error) + + ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance + ///config: options to open the backup repository and the underlying storage + Maintain(config map[string]string) error +} + +///BackupRepo provides the access to the backup repository +type BackupRepo interface { + ///Open an existing object for read + ///id: the object's unified identifier + OpenObject(id ID) (ObjectReader, error) + + ///Get a manifest data + GetManifest(id ID, mani *RepoManifest) error + + ///Get one or more manifest data that match the given labels + FindManifests(filter ManifestFilter) ([]*ManifestEntryMetadata, error) + + ///Create a new object and return the object's writer interface + ///return: A unified identifier of the object on success + NewObjectWriter(opt ObjectWriteOptions) ObjectWriter + + ///Save a manifest object + PutManifest(mani RepoManifest) (ID, error) + + ///Delete a manifest object + DeleteManifest(id ID) error + + ///Flush all the backup repository data + Flush() error + + ///Get the local time of the backup repository. It may be different from the time of the caller + Time() time.Time + + ///Close the backup repository + Close() error +} + +type ObjectReader interface { + io.ReadCloser + io.Seeker + + ///Length returns the logical size of the object + Length() int64 +} + +type ObjectWriter interface { + io.WriteCloser + + ///For some cases, i.e. block incremental, the object is not written sequentially + io.Seeker + + + ///Wait for the completion of the object write + ///Result returns the object's unified identifier after the write completes + Result() (ID, error) +} +``` + +Some data structure & constants used by the interfaces: +``` +type RepoOptions struct { + ///A repository specific string to identify a backup storage, i.e., "s3", "filesystem" + StorageType string + ///Backup repository password, if any + RepoPassword string + ///A custom path to save the repository's configuration, if any + ConfigFilePath string + ///Other repository specific options + GeneralOptions map[string]string + ///Storage specific options + StorageOptions map[string]string +} + +///ObjectWriteOptions defines the options when creating an object for write +type ObjectWriteOptions struct { + FullPath string ///Full logical path of the object + Description string ///A description of the object, could be empty + Prefix ID ///A prefix of the name used to save the object + AccessMode int ///OBJECT_DATA_ACCESS_* + BackupMode int ///OBJECT_DATA_BACKUP_* +} + +const ( + ///Below consts defines the access mode when creating an object for write + OBJECT_DATA_ACCESS_MODE_UNKNOWN int = 0 + OBJECT_DATA_ACCESS_MODE_FILE int = 1 + OBJECT_DATA_ACCESS_MODE_BLOCK int = 2 + + OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 + OBJECT_DATA_BACKUP_MODE_FULL int = 1 + OBJECT_DATA_BACKUP_MODE_INC int = 2 +) + +///ManifestEntryMetadata is the metadata describing one manifest data +type ManifestEntryMetadata struct { + ID ID ///The ID of the manifest data + Length int ///The data size of the manifest data + Labels map[string]string ///Labels saved together with the manifest data + ModTime time.Time ///Modified time of the manifest data +} + +type RepoManifest struct { + Payload interface{} ///The user data of manifest + Metadata *ManifestEntryMetadata ///The metadata data of manifest +} + +type ManifestFilter struct { + Labels map[string]string +} +``` + +## Workflow + +### Backup & Restore Workflow + +We preserve the bone of the existing BR workflow, that is: + +- Still use the Velero Server pod and PodVolumeBackup daemonSet (originally called Restic daemonset) pods to hold the corresponding controllers and modules +- Still use the Backup/Restore CR and BackupRepository CR (originally called ResticRepository CR) to drive the BR workflow + +The modules in gray color in below diagram are the existing modules and with no significant changes. +In the new design, we will have separate and independent modules/logics for backup repository and uploader (data mover), specifically: + +- Repository Provider provides functionalities to manage the backup repository. For example, initialize a repository, connect to a repository, manage the snapshots in the repository, maintain a repository, etc. +- Uploader Provider provides functionalities to run a backup or restore. + +The Repository Provider and Uploader Provider use an option called “Legacy” to choose the path --- Restic Repository vs. Unified Repository or Restic Uploader vs. Kopia Uploader. Specifically, if Legacy = true, Repository Provider will manage Restic Repository only, otherwise, it manages Unified Repository only; if Legacy = true, Uploader Provider calls Restic to do the BR, otherwise, it calls Kopia to do the BR. +In order to manage Restic Repository, the Repository Provider calls Restic Repository Provider, the latter invokes the existing Restic CLIs. +In order to manage Unified Repository, the Repository Provider calls Unified Repository Provider, the latter calls the Unified Repository module through the udmrepo.BackupRepoService interface. It doesn’t know how the Unified Repository is implemented necessarily. +In order to use Restic to do BR, the Uploader Provider calls Restic Uploader Provider, the latter invokes the existing Restic CLIs. +In order to use Kopia to do BR, the Uploader Provider calls Kopia Uploader Provider, the latter do the following things: + +- Call Unified Repository through the udmrepo.BackupRepoService interface to open the unified repository for read/write. Again, it doesn’t know how the Unified Repository is implemented necessarily. It gets a BackupRepo’s read/write handle after the call succeeds +- Wrap the BackupRepo handle into a Kopia Shim which implements Kopia Repository interface +- Call the Kopia Uploader. Kopia Uploader is a Kopia module without any change, so it only understands Kopia Repository interface +- Kopia Uploader starts to backup/restore the corresponding PV’s file system data and write/read data to/from the provided Kopia Repository implementation, that is, Kopia Shim here +- When read/write calls go into Kopia Shim, it in turn calls the BackupRepo handle for read/write +- Finally, the read/write calls flow to Unified Repository module + +The Unified Repository provides all-in-one functionalities of a Backup Repository and exposes the Unified Repository Interface. Inside, Kopia Library is an adapter for Kopia Repository to translate the Unified Repository Interface calls to Kopia Repository interface calls. +![A BR Workflow](br-workflow.png) +The modules in blue color in below diagram represent the newly added modules/logics or reorganized logics. +The modules in yellow color in below diagram represent the called Kopia modules without changes. + +### Delete Snapshot Workflow +The Delete Snapshot workflow follows the similar manner with BR workflow, that is, we preserve the upper-level workflows until the calls reach to BackupDeletionController, then: +- Leverage Repository Provider to switch between Restic implementation and Unified Repository implementation in the same mechanism as BR +- For Restic implementation, the Restic Repository Provider invokes the existing “Forget” Restic CLI +- For Unified Repository implementation, the Unified Repository Provider calls udmrepo.BackupRepo’s DeleteManifest to delete a snapshot +![A Snapshot Deletion Workflow](snapshot-deletion-workflow.png) + +### Maintenance Workflow +Backup Repository/Backup Storage may need to periodically reorganize its data so that it could guarantee its QOS during the long-time service. Some Backup Repository/Backup Storage does this in background automatically, so the user doesn’t need to interfere; some others need the caller to explicitly call their maintenance interface periodically. Restic and Kopia both go with the second way, that is, Velero needs to periodically call their maintenance interface. +Velero already has an existing workflow to call Restic maintenance (it is called “Prune” in Restic, so Velero uses the same word). The existing workflow is as follows: +- The Prune is triggered at the time of the backup +- When a BackupRepsoitory CR (originally called ResticRepository CR) is created by PodVolumeBackup/Restore Controller, the BackupRepository controller checks if it reaches to the Prune Due Time, if so, it calls PruneRepo +- In the new design, the Repository Provider implements PruneRepo call, it uses the same way to switch between Restic Repository Provider and Unified Repository Provider, then: + - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic + - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function + +Kopia supports two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), in this way, the maintenance will finish very fast and make less impact. We will also take this quick maintenance into Velero. +We will add a new Due Time to Velero, finally, we have two Prune Due Time: +- Normal Due Time: For Restic, this will invoke Restic Prune; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(full) call and finally call Kopia’s full maintenance +- Quick Due Time: For Restic, this does nothing; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(quick) call and finally call Kopia’s quick maintenance + +We assign different values to Normal Due Time and Quick Due Time, as a result of which, the quick maintenance happens more frequently than full maintenance. +![A Maintenance Workflow](maintenance-workflow.png) + +### Progress Update +Because Kopia Uploader is an unchanged Kopia module, we need to find a way to get its progress during the BR. +Kopia Uploader accepts a Progress interface to update rich information during the BR, so the Kopia Uploader Provider will implement a Kopia’s Progress interface and then pass it to Kopia Uploader during its initialization. +In this way, Velero will be able to get the progress as shown in the diagram below. +![A Progress Update](progress-update.png) + +### Logs +In the current design, Velero is using two unchanged Kopia modules --- the Kopia Uploader and the Kopia Repository. Both will generate debug logs during their run. Velero will collect these logs in order to aid the debug. +Kopia’s Uploader and Repository both get the Logger information from the current GO Context, therefore, the Kopia Uploader Provider/Kopia Library could set the Logger interface into the current context and pass the context to Kopia Uploader/Kopia Repository. +Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other uploaders/data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. +Kopia’s debug logs will be written to the same log file as Velero server or PodVolumeBackup daemonset, so Velero doesn’t need to upload/download these debug logs separately. +![A Debug Log for Uploader](debug-log-uploader.png) +![A Debug Log for Repository](debug-log-repository.png) + +## Path Switch & Coexist +As mentioned above, we will use an option “Legacy” to choose different paths. We don’t pursue a dynamic switch as there is no user requirement. +Instead, we assume the value of this option is set at the time of installation of Velero and never changed unless Velero is uninstalled. This means, if users want to switch the path, they need to uninstall Velero first and reinstall it. +Specifically, we will have the “Legacy” option/mode in two places: +- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. For details of installation, see below Installation section. +- Add a mode value in the BackupRepository CRs and PodVolumeBackup CRs. + +The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. In this way, the corresponding controllers could handle the switch correctly for both fresh installation and upgrade. + +### Upgrade +Changing path during upgrade is not prohibited, however some mismatches cases will happen, below shows how the mismatches are handled: +- If a BackupRepository CR already exists, but users install Velero again with the “Legacy” value changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized +- If PodVolumeBackup CRs already exist, but users install Velero again with the “Legacy” value changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved +- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. + +## Velero CR Changes +We will change below CRs' name to make them more generic: +- "ResticRepository" CR to "BackupRepository" CR + +This means, we add a new CR type and desperate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. +As a side effect, when upgrading from an old release, even though the path is not changed, the repository is initialized all the time, because Velero will not refer to the old CR's status. This means, the same repository will be initialized again. This is a minor side effect, we don't see critical problems. +Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. + +## Installation +The “legacy” flag will be set into Velero server deployment and PodVolumeBackup daemonset deployment so that both the RepositoryProvider and UploaderProvider see the flag. +The same “legacy” option will be added for Velero’s installation, including CLI installation and Helm Chart Installation. Then we need to transfer users’ selection into the two deployments mentioned above: +- Helm Chart Installation: add a “Legacy” value into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. +- CLI Installation: add the “Legacy” option into the installation command line, and then set the flag when creating the two deployments accordingly. Users could change the option at the time of installation. + +## User Experience Changes +Below user experiences are changed for this design: +- Installation CLI change: a new option is added to the installation CLI, see the Installation section for details +- CR change: One or more existing CRs have been renamed, see the Velero CR Changes section for details +- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "Restic" daemonset, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes + diff --git a/design/unified-repo-and-kopia-integration/unified-repo.png b/design/unified-repo-and-kopia-integration/unified-repo.png new file mode 100644 index 0000000000000000000000000000000000000000..609f6db58fe332aa9d77c1e0df5d9fec690bc00e GIT binary patch literal 38955 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

HH{gK`C<@dYGUAEcYq*$&n(jDZVQAKay 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 Date: Thu, 26 May 2022 20:29:04 +0800 Subject: [PATCH 02/38] Add Installation Examples Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 119 ++++++++++++++++-- 1 file changed, 112 insertions(+), 7 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 4df9d4265..59d64b0b8 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -287,9 +287,8 @@ Kopia’s debug logs will be written to the same log file as Velero server or Po As mentioned above, we will use an option “Legacy” to choose different paths. We don’t pursue a dynamic switch as there is no user requirement. Instead, we assume the value of this option is set at the time of installation of Velero and never changed unless Velero is uninstalled. This means, if users want to switch the path, they need to uninstall Velero first and reinstall it. Specifically, we will have the “Legacy” option/mode in two places: -- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. For details of installation, see below Installation section. -- Add a mode value in the BackupRepository CRs and PodVolumeBackup CRs. - +- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. +- Add a "legacy-mode" value in the BackupRepository CRs and PodVolumeBackup CRs. The value could be added as a tag in the CRs' spec. If the tag is missing, it by default means "legacy-mode=true", so the CRs were created to manage the Restic repository/backup using Restic. The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. In this way, the corresponding controllers could handle the switch correctly for both fresh installation and upgrade. ### Upgrade @@ -307,10 +306,116 @@ As a side effect, when upgrading from an old release, even though the path is no Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. ## Installation -The “legacy” flag will be set into Velero server deployment and PodVolumeBackup daemonset deployment so that both the RepositoryProvider and UploaderProvider see the flag. -The same “legacy” option will be added for Velero’s installation, including CLI installation and Helm Chart Installation. Then we need to transfer users’ selection into the two deployments mentioned above: -- Helm Chart Installation: add a “Legacy” value into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. -- CLI Installation: add the “Legacy” option into the installation command line, and then set the flag when creating the two deployments accordingly. Users could change the option at the time of installation. + We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has below two values: + **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + ``` + spec: + containers: + - args: + - server + - --features= + - --legacy + command: + - /velero +``` +``` + spec: + containers: + - args: + - restic + - server + - --features= + - --legacy + command: + - /velero +``` +The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +``` +spec: + tags: + legacy-mode: true +``` +``` +spec: + tags: + backup: bakup-testns-36 + backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c + ns: testns-36 + pod: deployment-2636-68b9697c56-6hpz5 + pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 + pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + volume: volume1 + legacy-mode: true +``` + **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + ``` + spec: + containers: + - args: + - server + - --features= + - debug + - --legacy=false + command: + - /velero +``` +``` + spec: + containers: + - args: + - restic + - server + - --features= + - debug + - --legacy=false + command: + - /velero +``` +The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +``` +spec: + tags: + legacy-mode: false +``` +``` +spec: + tags: + backup: bakup-testns-36 + backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c + ns: testns-36 + pod: deployment-2636-68b9697c56-6hpz5 + pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 + pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + volume: volume1 + legacy-mode: false +``` +We will add the flag for both CLI installation and Helm Chart Installation. Specifically: +- Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: +``` + command: + - /velero + args: + - server + {{- with .Values.configuration }} + {{- if .pod-volume-backup-uploader "restic" }} + - --legacy + {{- end }} +``` + +``` + command: + - /velero + args: + - restic + - server + {{- with .Values.configuration }} + {{- if .pod-volume-backup-uploader "restic" }} + - --legacy + {{- end }} +``` +- CLI Installation: add the "--pod-volume-backup-uploader" flag into the installation command line, and then create the two deployments accordingly. Users could change the option at the time of installation. The CLI is as below: +```velero install --pod-volume-backup-uploader=restic``` +```velero install --pod-volume-backup-uploader=kopia``` ## User Experience Changes Below user experiences are changed for this design: From aea127652c629c69df3d18959215b71a905f775a Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Fri, 17 Jun 2022 09:22:25 +0800 Subject: [PATCH 03/38] Post Review Session Update Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 59d64b0b8..549a2cd2b 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -41,6 +41,7 @@ On the other hand, based on a previous analysis and testing, we found that Kopia - Use Kopia repository to implement the Unified Repository - Use Kopia uploader as the file system data mover for Pod Volume Backup - Have Kopia uploader calling the Unified Repository Interface and save/retrieve data to/from the Unified Repository +- Make Kopia uploader generic enough to move any file system data so that other data movement cases could use it - Use the existing logic or add new logic to manage the unified repository and Kopia uploader - Preserve the legacy Restic path, this is for the consideration of backward compatibility @@ -48,14 +49,15 @@ On the other hand, based on a previous analysis and testing, we found that Kopia - The Unified Repository supports all kinds of data movers to save logic objects into it. How these logic objects are organized for a specific data mover (for example, how a volume’s block data is organized and represented by a unified repository object) should be included in the related data mover design. - At present, Velero saves Kubernetes resources, backup metedata, debug logs separately. Eventually, we want to save them in the Unified Repository. How to organize these data into the Unified Repository should be included in a separate design. -- Kopia uploader could be used as a generic file system data mover. How it is integrated in other cases, for example, CSI file system mode BR, should be included in the related data mover design. +- For PodVolume BR, this design focuses on the data path only, other parts beyond the data read/write and data persistency are irrelevant and kept unchanged. +- Kopia uploader is made generic enough to move any file system data. How it is integrated in other cases, is irrelevant to this design. Take CSI snapshot backup for example, how the snapshot is taken and exposed to Kopia uploader should be included in the related data mover design. - The adanced modes of the Unified Repository, for example, backup repository/storage plugin, backup repository extension, etc. are not included in this design. We will have separate designs to cover them whenever necessary. ## Architecture of Unified Repository Below shows the primary modules and their responsibilities: -- Kopia uploader is used as a generic file system data mover, so it could move all file system data either from the production PV (as Velero’s Pod Volume Backup does), or from any kind of snapshot (i.e., CSI snapshot). +- Kopia uploader, as been well isolated, could move all file system data either from the production PV (as Velero’s PodVolume BR does), or from any kind of snapshot (i.e., CSI snapshot). - Kopia uploader, the same as other data movers, calls the Unified Repository Interface to write/read data to/from the Unified Repository. - Kopia repository layers, CAOS and CABS, work as the backup repository and expose the Kopia Repository interface. - A Kopia Repository Library works as an adapter between Unified Repository Interface and Kopia Repository interface. Specifically, it implements Unified Repository Interface and calls Kopia Repository interface. @@ -68,7 +70,7 @@ The Unified Repository takes two kinds of data: - Unified Repository Manifest: This could include all other data to maintain the object data, for example, snapshot information, etc. For Unified Repository Object/Manifest, a brief guidance to data movers are as below: -- Data movers treat the simple unit of data they recognize as an Object. For example, a file system data mover treats a file or a directory as an Object; a block data mover treats a volume as an Object +- Data movers treat the simple unit of data they recognize as an Object. For example, file system data movers treat a file or a directory as an Object; block data movers treat a volume as an Object. However, it is unnecessary that every data mover has a unique data format in the Unified Repository, to the opposite, it is recommended that data movers could share the data formats unless there is any reason not to, in this way, the data generated by one data mover could be used by other data movers. - Data movers don't need to care about the differences between full and incremental backups regarding the data organization. Data movers always have full views of their objects, if an object is partially written, they use the object writer's Seek function to skip the unchanged parts - Unified Repository may divide the data movers' logical Object into sub-objects or slices, or append internal metadata, but they are transparent to data movers - Every Object has an unified identifier, in order to retrieve the Object later, data movers need to save the identifiers into the snapshot information. The snapshot information is saved as a Manifest. @@ -256,7 +258,7 @@ The Delete Snapshot workflow follows the similar manner with BR workflow, that i Backup Repository/Backup Storage may need to periodically reorganize its data so that it could guarantee its QOS during the long-time service. Some Backup Repository/Backup Storage does this in background automatically, so the user doesn’t need to interfere; some others need the caller to explicitly call their maintenance interface periodically. Restic and Kopia both go with the second way, that is, Velero needs to periodically call their maintenance interface. Velero already has an existing workflow to call Restic maintenance (it is called “Prune” in Restic, so Velero uses the same word). The existing workflow is as follows: - The Prune is triggered at the time of the backup -- When a BackupRepsoitory CR (originally called ResticRepository CR) is created by PodVolumeBackup/Restore Controller, the BackupRepository controller checks if it reaches to the Prune Due Time, if so, it calls PruneRepo +- When a BackupRepository CR (originally called ResticRepository CR) is created by PodVolumeBackup/Restore Controller, the BackupRepository controller checks if it reaches to the Prune Due Time, if so, it calls PruneRepo - In the new design, the Repository Provider implements PruneRepo call, it uses the same way to switch between Restic Repository Provider and Unified Repository Provider, then: - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function @@ -278,7 +280,7 @@ In this way, Velero will be able to get the progress as shown in the diagram bel ### Logs In the current design, Velero is using two unchanged Kopia modules --- the Kopia Uploader and the Kopia Repository. Both will generate debug logs during their run. Velero will collect these logs in order to aid the debug. Kopia’s Uploader and Repository both get the Logger information from the current GO Context, therefore, the Kopia Uploader Provider/Kopia Library could set the Logger interface into the current context and pass the context to Kopia Uploader/Kopia Repository. -Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other uploaders/data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. +Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. Kopia’s debug logs will be written to the same log file as Velero server or PodVolumeBackup daemonset, so Velero doesn’t need to upload/download these debug logs separately. ![A Debug Log for Uploader](debug-log-uploader.png) ![A Debug Log for Repository](debug-log-repository.png) @@ -301,7 +303,7 @@ Changing path during upgrade is not prohibited, however some mismatches cases wi We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR -This means, we add a new CR type and desperate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. +This means, we add a new CR type and deprecate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. As a side effect, when upgrading from an old release, even though the path is not changed, the repository is initialized all the time, because Velero will not refer to the old CR's status. This means, the same repository will be initialized again. This is a minor side effect, we don't see critical problems. Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. From 0470c961bf24f7b20c18c6fc36ec66aab8d79266 Mon Sep 17 00:00:00 2001 From: Niu Lechuan Date: Mon, 27 Jun 2022 10:11:47 +0800 Subject: [PATCH 04/38] Fix typo in doc. 'Mamespace' to 'Namespace' Signed-off-by: Niu Lechuan --- site/content/docs/main/restore-reference.md | 2 +- site/content/docs/v1.9/restore-reference.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/site/content/docs/main/restore-reference.md b/site/content/docs/main/restore-reference.md index 0511f6512..a91ef960d 100644 --- a/site/content/docs/main/restore-reference.md +++ b/site/content/docs/main/restore-reference.md @@ -73,7 +73,7 @@ The following is an overview of Velero's restore process that starts after you r By default, Velero will restore resources in the following order: * Custom Resource Definitions -* Mamespaces +* Namespaces * StorageClasses * VolumeSnapshotClass * VolumeSnapshotContents diff --git a/site/content/docs/v1.9/restore-reference.md b/site/content/docs/v1.9/restore-reference.md index 0511f6512..a91ef960d 100644 --- a/site/content/docs/v1.9/restore-reference.md +++ b/site/content/docs/v1.9/restore-reference.md @@ -73,7 +73,7 @@ The following is an overview of Velero's restore process that starts after you r By default, Velero will restore resources in the following order: * Custom Resource Definitions -* Mamespaces +* Namespaces * StorageClasses * VolumeSnapshotClass * VolumeSnapshotContents From b62a12263252220feee55c74711c44a5b697a085 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 27 Jun 2022 12:39:49 +0800 Subject: [PATCH 05/38] Post Review Update 02 Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 101 ++++++++++-------- 1 file changed, 57 insertions(+), 44 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 549a2cd2b..3fcd12461 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -58,12 +58,14 @@ On the other hand, based on a previous analysis and testing, we found that Kopia Below shows the primary modules and their responsibilities: - Kopia uploader, as been well isolated, could move all file system data either from the production PV (as Velero’s PodVolume BR does), or from any kind of snapshot (i.e., CSI snapshot). -- Kopia uploader, the same as other data movers, calls the Unified Repository Interface to write/read data to/from the Unified Repository. +- Unified Repository Interface, data movers call the Unified Repository Interface to write/read data to/from the Unified Repository. - Kopia repository layers, CAOS and CABS, work as the backup repository and expose the Kopia Repository interface. - A Kopia Repository Library works as an adapter between Unified Repository Interface and Kopia Repository interface. Specifically, it implements Unified Repository Interface and calls Kopia Repository interface. - At present, there is only one kind of backup repository -- Kopia Repository. If a new backup repository/storage is required, we need to create a new Library as an adapter to the Unified Repository Interface - At present, the Kopia Repository works as a single piece in the same process of the caller, in future, we may run its CABS into a dedicated process or node. -- At present, we don’t have a requirement to extend the backup repository, if needed, an extra module could be added as an upper layer into the Unified Repository without changing the data movers. +- At present, we don’t have a requirement to extend the backup repository, if needed, an extra module could be added as an upper layer into the Unified Repository without changing the data movers. + +Neither Kopia uploader nor Kopia Repository is invoked through CLI, instead, they are invoked through code interfaces, because we need to do lots of customizations. The Unified Repository takes two kinds of data: - Unified Repository Object: This is the user's logical data, for example, files/directories, blocks of a volume, data of a database, etc. @@ -89,7 +91,7 @@ Velero by default uses the Unified Repository for all kinds of data movement, it # Detailed Design ## The Unified Repository Interface -Below are the definitions of the Unified Repository Interface +Below are the definitions of the Unified Repository Interface. All the functions are synchronization functions. ``` ///BackupRepoService is used to initialize, open or maintain a backup repository type BackupRepoService interface { @@ -97,47 +99,47 @@ type BackupRepoService interface { ///repoOption: option to the backup repository and the underlying backup storage ///createNew: indicates whether to create a new or connect to an existing backup repository ///result: the backup repository specific output that could be used to open the backup repository later - Init(repoOption RepoOptions, createNew bool) (result map[string]string, err error) + Init(ctx context.Context, repoOption RepoOptions, createNew bool) (result map[string]string, err error) ///Open an backup repository that has been created/connected ///config: options to open the backup repository and the underlying storage - Open(config map[string]string) (BackupRepo, error) + Open(ctx context.Context, config map[string]string) (BackupRepo, error) ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance ///config: options to open the backup repository and the underlying storage - Maintain(config map[string]string) error + Maintain(ctx context.Context, config map[string]string) error } ///BackupRepo provides the access to the backup repository type BackupRepo interface { ///Open an existing object for read ///id: the object's unified identifier - OpenObject(id ID) (ObjectReader, error) + OpenObject(ctx context.Context, id ID) (ObjectReader, error) ///Get a manifest data - GetManifest(id ID, mani *RepoManifest) error + GetManifest(ctx context.Context, id ID, mani *RepoManifest) error ///Get one or more manifest data that match the given labels - FindManifests(filter ManifestFilter) ([]*ManifestEntryMetadata, error) + FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) ///Create a new object and return the object's writer interface ///return: A unified identifier of the object on success - NewObjectWriter(opt ObjectWriteOptions) ObjectWriter + NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter ///Save a manifest object - PutManifest(mani RepoManifest) (ID, error) + PutManifest(ctx context.Context, mani RepoManifest) (ID, error) ///Delete a manifest object - DeleteManifest(id ID) error + DeleteManifest(ctx context.Context, id ID) error ///Flush all the backup repository data - Flush() error + Flush(ctx context.Context) error ///Get the local time of the backup repository. It may be different from the time of the caller Time() time.Time ///Close the backup repository - Close() error + Close(ctx context.Context) error } type ObjectReader interface { @@ -199,7 +201,7 @@ const ( ///ManifestEntryMetadata is the metadata describing one manifest data type ManifestEntryMetadata struct { ID ID ///The ID of the manifest data - Length int ///The data size of the manifest data + Length int32 ///The data size of the manifest data Labels map[string]string ///Labels saved together with the manifest data ModTime time.Time ///Modified time of the manifest data } @@ -220,7 +222,7 @@ type ManifestFilter struct { We preserve the bone of the existing BR workflow, that is: -- Still use the Velero Server pod and PodVolumeBackup daemonSet (originally called Restic daemonset) pods to hold the corresponding controllers and modules +- Still use the Velero Server pod and VeleroNodeAgent daemonSet (originally called Restic daemonset) pods to hold the corresponding controllers and modules - Still use the Backup/Restore CR and BackupRepository CR (originally called ResticRepository CR) to drive the BR workflow The modules in gray color in below diagram are the existing modules and with no significant changes. @@ -229,7 +231,8 @@ In the new design, we will have separate and independent modules/logics for back - Repository Provider provides functionalities to manage the backup repository. For example, initialize a repository, connect to a repository, manage the snapshots in the repository, maintain a repository, etc. - Uploader Provider provides functionalities to run a backup or restore. -The Repository Provider and Uploader Provider use an option called “Legacy” to choose the path --- Restic Repository vs. Unified Repository or Restic Uploader vs. Kopia Uploader. Specifically, if Legacy = true, Repository Provider will manage Restic Repository only, otherwise, it manages Unified Repository only; if Legacy = true, Uploader Provider calls Restic to do the BR, otherwise, it calls Kopia to do the BR. +The Repository Provider and Uploader Provider use options to choose the path --- legacy path vs. new path (Kopia uploader + Unified Repository). Specifically, for legacy path, Repository Provider will manage Restic Repository only, otherwise, it manages Unified Repository only; for legacy path, Uploader Provider calls Restic to do the BR, otherwise, it calls Kopia uploader to do the BR. + In order to manage Restic Repository, the Repository Provider calls Restic Repository Provider, the latter invokes the existing Restic CLIs. In order to manage Unified Repository, the Repository Provider calls Unified Repository Provider, the latter calls the Unified Repository module through the udmrepo.BackupRepoService interface. It doesn’t know how the Unified Repository is implemented necessarily. In order to use Restic to do BR, the Uploader Provider calls Restic Uploader Provider, the latter invokes the existing Restic CLIs. @@ -243,6 +246,7 @@ In order to use Kopia to do BR, the Uploader Provider calls Kopia Uploader Provi - Finally, the read/write calls flow to Unified Repository module The Unified Repository provides all-in-one functionalities of a Backup Repository and exposes the Unified Repository Interface. Inside, Kopia Library is an adapter for Kopia Repository to translate the Unified Repository Interface calls to Kopia Repository interface calls. +Both Kopia Shim and Kopia Library rely on Kopia Repository interface, so we need to have some Kopia version control. We may need to change Kopia Shim and Kopia Library when upgrading Kopia to a new version and the Kopia Repository interface has some changes in the new version. ![A BR Workflow](br-workflow.png) The modules in blue color in below diagram represent the newly added modules/logics or reorganized logics. The modules in yellow color in below diagram represent the called Kopia modules without changes. @@ -281,25 +285,31 @@ In this way, Velero will be able to get the progress as shown in the diagram bel In the current design, Velero is using two unchanged Kopia modules --- the Kopia Uploader and the Kopia Repository. Both will generate debug logs during their run. Velero will collect these logs in order to aid the debug. Kopia’s Uploader and Repository both get the Logger information from the current GO Context, therefore, the Kopia Uploader Provider/Kopia Library could set the Logger interface into the current context and pass the context to Kopia Uploader/Kopia Repository. Velero will set Logger interfaces separately for Kopia Uploader and Kopia Repository. In this way, the Unified Repository could serve other data movers without losing the debug log capability; and the Kopia Uploader could write to any repository without losing the debug log capability. -Kopia’s debug logs will be written to the same log file as Velero server or PodVolumeBackup daemonset, so Velero doesn’t need to upload/download these debug logs separately. +Kopia’s debug logs will be written to the same log file as Velero server or VeleroNodeAgent daemonset, so Velero doesn’t need to upload/download these debug logs separately. ![A Debug Log for Uploader](debug-log-uploader.png) ![A Debug Log for Repository](debug-log-repository.png) ## Path Switch & Coexist -As mentioned above, we will use an option “Legacy” to choose different paths. We don’t pursue a dynamic switch as there is no user requirement. -Instead, we assume the value of this option is set at the time of installation of Velero and never changed unless Velero is uninstalled. This means, if users want to switch the path, they need to uninstall Velero first and reinstall it. -Specifically, we will have the “Legacy” option/mode in two places: -- Add the “Legacy” option as a parameter of the Velero server and PodVolumeBackup daemonset. The parameters will be set by the installation. -- Add a "legacy-mode" value in the BackupRepository CRs and PodVolumeBackup CRs. The value could be added as a tag in the CRs' spec. If the tag is missing, it by default means "legacy-mode=true", so the CRs were created to manage the Restic repository/backup using Restic. -The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. In this way, the corresponding controllers could handle the switch correctly for both fresh installation and upgrade. +As mentioned above, we will have two paths. We don’t pursue a dynamic switch as there is no user requirement. +Instead, we assume that the path is decided at the time of installation of Velero and never changed unless Velero is reinstalled. This means, if users want to switch the path, the best practice is to fresh install Velero and don't refer to any old backup data. +On the other hand, changing the path during upgrade or during referring to old backup data is not prohibited, though we need to take some mesure to handle the mismatch problems. +Specifically, we will have the option/mode values for path selection in two places: +- Add the “uploader-type” option as a parameter of the Velero server and VeleroNodeAgent daemonset. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). +- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. The values could be added as tags in the CRs' spec. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the tags are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. -### Upgrade -Changing path during upgrade is not prohibited, however some mismatches cases will happen, below shows how the mismatches are handled: -- If a BackupRepository CR already exists, but users install Velero again with the “Legacy” value changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized -- If PodVolumeBackup CRs already exist, but users install Velero again with the “Legacy” value changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved -- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. +The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. +In spite of the above pricipal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. -## Velero CR Changes +### CR Handling Under Mismatched Path +The path is recorded in BackupRepository CR, PodVolumeBackup CR and PodVolumeRestore CR, when the path doesn't match to the current path of the controllers, below shows how the mismatches are handled: +- If a BackupRepository CR already exists, but users install Velero again with the path changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized +- If PodVolumeBackup CRs already exist, but users install Velero again with the path changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved +- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. +### Backup List, Sync and Describe +### Backup Deletion Under Mismatched Path +### Restore Under Mismatched Path + +## Velero CR Name Changes We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR @@ -308,15 +318,20 @@ As a side effect, when upgrading from an old release, even though the path is no Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. ## Installation - We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has below two values: - **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: + - It indicates PodVolume BR as the default method to protect PV data over other methods, i.e., durable snapshot. Therefore, the existing --use-restic option will be replaced + - It indicates the file system uploader to be used by PodVolume BR + - It implies the backup repository type manner, Restic if pod-volume-backup-uploader=restic, Unified Repository in all other cases + + The flag has below two values: + **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: ``` spec: containers: - args: - server - --features= - - --legacy + - --uploader-type=restic command: - /velero ``` @@ -327,7 +342,7 @@ Therefore, users are recommended to uninstall Velero and delete all the resource - restic - server - --features= - - --legacy + - --uploader-type=restic command: - /velero ``` @@ -335,7 +350,7 @@ The BackupRepository CRs and PodVolumeBackup CRs created in this case are as bel ``` spec: tags: - legacy-mode: true + repository-type: restic ``` ``` spec: @@ -347,17 +362,16 @@ spec: pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada volume: volume1 - legacy-mode: true + uploader-type: restic ``` - **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and PodVolumeBackup daemonset will be created as below: + **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: ``` spec: containers: - args: - server - --features= - - debug - - --legacy=false + - --uploader-type=kopia command: - /velero ``` @@ -368,16 +382,15 @@ spec: - restic - server - --features= - - debug - - --legacy=false + - --uploader-type=kopia command: - /velero ``` -The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: ``` spec: tags: - legacy-mode: false + repository-type: kopia ``` ``` spec: @@ -389,7 +402,7 @@ spec: pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada volume: volume1 - legacy-mode: false + uploader-type: kopia ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: From 34e6234ae0ca92a8680fb4c08d2a0ca7a579e40b Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Mon, 27 Jun 2022 20:14:41 +0800 Subject: [PATCH 06/38] add more details for repository CR rename Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 3fcd12461..df4137dfb 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -193,9 +193,9 @@ const ( OBJECT_DATA_ACCESS_MODE_FILE int = 1 OBJECT_DATA_ACCESS_MODE_BLOCK int = 2 - OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 - OBJECT_DATA_BACKUP_MODE_FULL int = 1 - OBJECT_DATA_BACKUP_MODE_INC int = 2 + OBJECT_DATA_BACKUP_MODE_UNKNOWN int = 0 + OBJECT_DATA_BACKUP_MODE_FULL int = 1 + OBJECT_DATA_BACKUP_MODE_INC int = 2 ) ///ManifestEntryMetadata is the metadata describing one manifest data @@ -295,10 +295,10 @@ Instead, we assume that the path is decided at the time of installation of Veler On the other hand, changing the path during upgrade or during referring to old backup data is not prohibited, though we need to take some mesure to handle the mismatch problems. Specifically, we will have the option/mode values for path selection in two places: - Add the “uploader-type” option as a parameter of the Velero server and VeleroNodeAgent daemonset. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). -- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. The values could be added as tags in the CRs' spec. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the tags are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. +- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. -In spite of the above pricipal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. +In spite of the above principal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. ### CR Handling Under Mismatched Path The path is recorded in BackupRepository CR, PodVolumeBackup CR and PodVolumeRestore CR, when the path doesn't match to the current path of the controllers, below shows how the mismatches are handled: @@ -314,8 +314,15 @@ We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR This means, we add a new CR type and deprecate the old one. As a result, if users upgrade from the old release, the old CRs will be orphaned, Velero will neither refer to it nor manage it, users need to delete these CRs manually. -As a side effect, when upgrading from an old release, even though the path is not changed, the repository is initialized all the time, because Velero will not refer to the old CR's status. This means, the same repository will be initialized again. This is a minor side effect, we don't see critical problems. -Therefore, users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. +As a side effect, when upgrading from an old release, even though the path is not changed, the BackupRepository gets created all the time, because Velero will not refer to the old CR's status. This seems to cause the repository to initialize more than once, however, it won't happen. In the BackupRepository controller, before initializing a repository, it always tries to connect to the repository first, if it is connectable, it won't do the initialization. +When backing up with the new release, Velero always creates BackupRepository CRs instead of ResticRepository CRs. +When restoring from an old backup, Velero always creates BackupRepository CRs instead of ResticRepository CRs. +For a upgrade case, if there are already backups or restores running during the upgrade, the backups/restores could fall into below results: +- The backups/restores have started but the ResticRepository CRs have not been created. In this case, the BackupRepository CRs will be created instead, the backups/restores will finish successfully. +- The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. +- The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. + +As shown above, there are complexities for the upgrade case, so users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: @@ -349,20 +356,18 @@ Therefore, users are recommended to uninstall Velero and delete all the resource The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: ``` spec: - tags: - repository-type: restic + backupStorageLocation: default + ... + repository-type: restic + volumeNamespace: nginx-example ``` ``` spec: tags: backup: bakup-testns-36 - backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c - ns: testns-36 - pod: deployment-2636-68b9697c56-6hpz5 - pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 - pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + ... volume: volume1 - uploader-type: restic + uploader-type: restic ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: ``` @@ -389,20 +394,18 @@ spec: The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: ``` spec: - tags: - repository-type: kopia + backupStorageLocation: default + ... + repository-type: kopia + volumeNamespace: nginx-example ``` ``` spec: tags: backup: bakup-testns-36 - backup-uid: 1d5c06ee-bb8a-4e32-9606-145308b9747c - ns: testns-36 - pod: deployment-2636-68b9697c56-6hpz5 - pod-uid: 2858c332-b3d6-4985-b0e6-6ecbbf1d0284 - pvc-uid: b17f03a0-b6f9-4ddf-95e6-59a85e67aada + ... volume: volume1 - uploader-type: kopia + uploader-type: kopia ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: From a08463adbaf19fbea57d8195bc31cb07b1f22dc3 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 28 Jun 2022 13:36:09 +0800 Subject: [PATCH 07/38] dynamic switch Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 84 +++++++------------ 1 file changed, 30 insertions(+), 54 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index df4137dfb..fc3400076 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -267,7 +267,7 @@ Velero already has an existing workflow to call Restic maintenance (it is called - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function -Kopia supports two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), in this way, the maintenance will finish very fast and make less impact. We will also take this quick maintenance into Velero. +Kopia has two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), so quick maintenance is much faster than full maintenance. On ther other hand, quick maintenance also scatters the burden of full maintenance so that the full maintenance could finish fastly and make less impact. We will also take this quick maintenance into Velero. We will add a new Due Time to Velero, finally, we have two Prune Due Time: - Normal Due Time: For Restic, this will invoke Restic Prune; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(full) call and finally call Kopia’s full maintenance - Quick Due Time: For Restic, this does nothing; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(quick) call and finally call Kopia’s quick maintenance @@ -290,21 +290,19 @@ Kopia’s debug logs will be written to the same log file as Velero server or Ve ![A Debug Log for Repository](debug-log-repository.png) ## Path Switch & Coexist -As mentioned above, we will have two paths. We don’t pursue a dynamic switch as there is no user requirement. -Instead, we assume that the path is decided at the time of installation of Velero and never changed unless Velero is reinstalled. This means, if users want to switch the path, the best practice is to fresh install Velero and don't refer to any old backup data. -On the other hand, changing the path during upgrade or during referring to old backup data is not prohibited, though we need to take some mesure to handle the mismatch problems. -Specifically, we will have the option/mode values for path selection in two places: -- Add the “uploader-type” option as a parameter of the Velero server and VeleroNodeAgent daemonset. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). -- Add a "uploader-type" value in the PodVolumeBackup CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. +As mentioned above, There will be two paths. The related controllers need to identify the path during runtime and adjust its working mode. +According to the requirements, path changing is fulfilled at the backup/restore level. In order to let the controllers know the path, we need to add some option values. Specifically, there will be option/mode values for path selection in two places: +- Add the “uploader-type” option as a parameter of the Velero server. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). +- Add a "uploader-type" value in the PodVolume Backup/Restore CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. -The corresponding controllers handle the CRs with the matched mode only, the mismatched ones will be ignored. -In spite of the above principal solutions, some complex cases related to upgrade and old data reference are still valuable to dicuss, as described in below sections. +The corresponding controllers handle the CRs by checking the CRs' path value. Some examples are as below: +- The PodVolume BR controller checks the "uploader-type" value from PodVolume CRs and decide its working path +- The BackupRepository controller checks the "repository-type" value from BackupRepository CRs and decide its working path +- The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup CRs it is going to create +- The Restore controller checks the Backup CR, from which it is going to restore, for the path and then create the Restore CR + +As described above, the “uploader-type” parameter of the Velero server is only used to decide the path when creating a new Backup, for other cases, the path selection is driven by the related CRs. Therefore, we only need to add this parameter to the Velero server. -### CR Handling Under Mismatched Path -The path is recorded in BackupRepository CR, PodVolumeBackup CR and PodVolumeRestore CR, when the path doesn't match to the current path of the controllers, below shows how the mismatches are handled: -- If a BackupRepository CR already exists, but users install Velero again with the path changed, the BackupRepository controller tries to find the existing BackupRepository CR to decide whether it needs to initialize the repository or to connect to the repository. Since the value has changed, the existing CR will be ignored and a new CR is created, during which, the new repository is initialized -- If PodVolumeBackup CRs already exist, but users install Velero again with the path changed, the PodVolumeBackup controller tries to search the parent backup from the existing PodVolumeBackup CRs, since the value has changed, the CRs with the mismatched mode will be skipped, as a result, the correct parent backup could be retrieved -- As you can see above, there may be orphan CRs left after the mode is switched. Velero will add warning logs for the orphan CRs and leverage on users to delete them from kubernetes. ### Backup List, Sync and Describe ### Backup Deletion Under Mismatched Path ### Restore Under Mismatched Path @@ -322,8 +320,6 @@ For a upgrade case, if there are already backups or restores running during the - The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. - The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. -As shown above, there are complexities for the upgrade case, so users are recommended to uninstall Velero and delete all the resources in the Velero namespace before installing the new release. - ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: - It indicates PodVolume BR as the default method to protect PV data over other methods, i.e., durable snapshot. Therefore, the existing --use-restic option will be replaced @@ -331,7 +327,7 @@ As shown above, there are complexities for the upgrade case, so users are recomm - It implies the backup repository type manner, Restic if pod-volume-backup-uploader=restic, Unified Repository in all other cases The flag has below two values: - **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: + **"Restic"**: it means Velero will use Restic to do the pod volume backup. Therefore, the Velero server deployment will be created as below: ``` spec: containers: @@ -342,17 +338,6 @@ As shown above, there are complexities for the upgrade case, so users are recomm command: - /velero ``` -``` - spec: - containers: - - args: - - restic - - server - - --features= - - --uploader-type=restic - command: - - /velero -``` The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: ``` spec: @@ -369,7 +354,7 @@ spec: volume: volume1 uploader-type: restic ``` - **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment and VeleroNodeAgent daemonset will be created as below: + **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: ``` spec: containers: @@ -380,17 +365,6 @@ spec: command: - /velero ``` -``` - spec: - containers: - - args: - - restic - - server - - --features= - - --uploader-type=kopia - command: - - /velero -``` The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: ``` spec: @@ -419,25 +393,27 @@ We will add the flag for both CLI installation and Helm Chart Installation. Spec - --legacy {{- end }} ``` - -``` - command: - - /velero - args: - - restic - - server - {{- with .Values.configuration }} - {{- if .pod-volume-backup-uploader "restic" }} - - --legacy - {{- end }} -``` - CLI Installation: add the "--pod-volume-backup-uploader" flag into the installation command line, and then create the two deployments accordingly. Users could change the option at the time of installation. The CLI is as below: ```velero install --pod-volume-backup-uploader=restic``` -```velero install --pod-volume-backup-uploader=kopia``` +```velero install --pod-volume-backup-uploader=kopia``` + +## Upgrade +For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to privide an easy for querying it. +Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade for both CLI and Helm Chart. + +## CLI +Below Velero CLI or its output needs some changes: +- ```Velero backup describe```: the output should indicate the path +- ```Velero restore describe```: the output should indicate the path +- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print different information according to the path + +At present, we don't have a requirement for slecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. ## User Experience Changes Below user experiences are changed for this design: - Installation CLI change: a new option is added to the installation CLI, see the Installation section for details - CR change: One or more existing CRs have been renamed, see the Velero CR Changes section for details -- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "Restic" daemonset, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes +- Velero CLI name and output change, see the CLI section for details +- Velero daemonset name change +- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes From 12cdb1908e2252c2dba6f7cf67957a629ac2b2a7 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 28 Jun 2022 19:07:34 +0800 Subject: [PATCH 08/38] storage configuration Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 79 ++++++++++++++++--- 1 file changed, 70 insertions(+), 9 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index fc3400076..b11085ccc 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -267,7 +267,7 @@ Velero already has an existing workflow to call Restic maintenance (it is called - For Restic Repository, Restic Repository Provider invokes the existing “Prune” CLI of Restic - For Unified Repository, Unified Repository Provider calls udmrepo.BackupRepoService’s Maintain function -Kopia has two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), so quick maintenance is much faster than full maintenance. On ther other hand, quick maintenance also scatters the burden of full maintenance so that the full maintenance could finish fastly and make less impact. We will also take this quick maintenance into Velero. +Kopia has two maintenance modes – the full maintenance and quick maintenance. There are many differences between full and quick mode, but briefly speaking, quick mode only processes the hottest data (primarily, it is the metadata and index data), so quick maintenance is much faster than full maintenance. On the other hand, quick maintenance also scatters the burden of full maintenance so that the full maintenance could finish fastly and make less impact. We will also take this quick maintenance into Velero. We will add a new Due Time to Velero, finally, we have two Prune Due Time: - Normal Due Time: For Restic, this will invoke Restic Prune; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(full) call and finally call Kopia’s full maintenance - Quick Due Time: For Restic, this does nothing; for Unified Repository, this will invoke udmrepo.BackupRepoService’s Maintain(quick) call and finally call Kopia’s quick maintenance @@ -320,6 +320,55 @@ For a upgrade case, if there are already backups or restores running during the - The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. - The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. +## Storage Configuration +The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will use a structured storage configuration to replace the repoIdentififer string. +The configuration in BackupRepository CRs, PodVolumeBackup CRs and PodVolumeRestore CRs are as below: +``` +spec: + backupStorageLocation: default + maintenanceFrequency: 168h0m0s + storageConfig: + provider: azure + param: + container: container01 + prefix: /restic/nginx-example + volumeNamespace: nginx-example +``` +``` +spec: + backupStorageLocation: default + node: aks-agentpool-27359964-vmss000000 + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: 86aaec56-2b21-4736-9964-621047717133 + storageConfig: + provider: azure + param: + container: container01 + prefix: /restic/nginx-example + tags: + ... + volume: nginx-log +``` +``` +spec: + backupStorageLocation: default + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + storageConfig: + provider: azure + param: + container: container01 + prefix: /restic/nginx-example + snapshotID: 1741e5f1 + volume: nginx-log +``` + ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: - It indicates PodVolume BR as the default method to protect PV data over other methods, i.e., durable snapshot. Therefore, the existing --use-restic option will be replaced @@ -342,17 +391,23 @@ The BackupRepository CRs and PodVolumeBackup CRs created in this case are as bel ``` spec: backupStorageLocation: default - ... + maintenanceFrequency: 168h0m0s repository-type: restic volumeNamespace: nginx-example ``` ``` spec: + backupStorageLocation: default + node: aks-agentpool-27359964-vmss000000 + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: 86aaec56-2b21-4736-9964-621047717133 tags: - backup: bakup-testns-36 ... - volume: volume1 uploader-type: restic + volume: nginx-log ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: ``` @@ -369,17 +424,23 @@ The BackupRepository CRs created in this case are hard set with "kopia" at prese ``` spec: backupStorageLocation: default - ... + maintenanceFrequency: 168h0m0s repository-type: kopia volumeNamespace: nginx-example ``` ``` spec: + backupStorageLocation: default + node: aks-agentpool-27359964-vmss000000 + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: 86aaec56-2b21-4736-9964-621047717133 tags: - backup: bakup-testns-36 ... - volume: volume1 uploader-type: kopia + volume: nginx-log ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: @@ -398,7 +459,7 @@ We will add the flag for both CLI installation and Helm Chart Installation. Spec ```velero install --pod-volume-backup-uploader=kopia``` ## Upgrade -For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to privide an easy for querying it. +For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it. Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade for both CLI and Helm Chart. ## CLI @@ -407,7 +468,7 @@ Below Velero CLI or its output needs some changes: - ```Velero restore describe```: the output should indicate the path - ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print different information according to the path -At present, we don't have a requirement for slecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. +At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. ## User Experience Changes Below user experiences are changed for this design: From d63394ff604a5668d736097e6bbb7c16292aaecf Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Tue, 28 Jun 2022 22:30:26 +0800 Subject: [PATCH 09/38] minor changes Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index b11085ccc..ead91d085 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -298,15 +298,11 @@ According to the requirements, path changing is fulfilled at the backup/restore The corresponding controllers handle the CRs by checking the CRs' path value. Some examples are as below: - The PodVolume BR controller checks the "uploader-type" value from PodVolume CRs and decide its working path - The BackupRepository controller checks the "repository-type" value from BackupRepository CRs and decide its working path -- The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup CRs it is going to create -- The Restore controller checks the Backup CR, from which it is going to restore, for the path and then create the Restore CR +- The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup it is going to create and then create the PodVolume Backup CR and BackupRepository CR +- The Restore controller checks the Backup, from which it is going to restore, for the path and then create the PodVolume Restore CR and BackupRepository CR As described above, the “uploader-type” parameter of the Velero server is only used to decide the path when creating a new Backup, for other cases, the path selection is driven by the related CRs. Therefore, we only need to add this parameter to the Velero server. -### Backup List, Sync and Describe -### Backup Deletion Under Mismatched Path -### Restore Under Mismatched Path - ## Velero CR Name Changes We will change below CRs' name to make them more generic: - "ResticRepository" CR to "BackupRepository" CR @@ -466,7 +462,7 @@ Moreover, if users upgrade from the old release, we need to change the existing Below Velero CLI or its output needs some changes: - ```Velero backup describe```: the output should indicate the path - ```Velero restore describe```: the output should indicate the path -- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print different information according to the path +- ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print all the backup repository if Restic repository and Unified Repository exist at the same time At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. From b605bf4f07f45d1d762382cdf44659a7fb8b0a93 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 29 Jun 2022 11:27:14 +0800 Subject: [PATCH 10/38] add CR samples Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index ead91d085..c6fb9b452 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -343,7 +343,7 @@ spec: provider: azure param: container: container01 - prefix: /restic/nginx-example + prefix: /restic/nginx-example tags: ... volume: nginx-log @@ -356,12 +356,12 @@ spec: name: nginx-stateful-0 namespace: nginx-example uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + snapshotID: 1741e5f1 storageConfig: provider: azure param: container: container01 prefix: /restic/nginx-example - snapshotID: 1741e5f1 volume: nginx-log ``` @@ -383,7 +383,7 @@ spec: command: - /velero ``` -The BackupRepository CRs and PodVolumeBackup CRs created in this case are as below: +The BackupRepository CRs and PodVolume Backup/Restore CRs created in this case are as below: ``` spec: backupStorageLocation: default @@ -404,6 +404,18 @@ spec: ... uploader-type: restic volume: nginx-log +``` +``` +spec: + backupStorageLocation: default + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + snapshotID: 1741e5f1 + uploader-type: restic + volume: nginx-log ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: ``` @@ -416,7 +428,7 @@ spec: command: - /velero ``` -The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolumeBackup CRs are created with "kopia" as well: +The BackupRepository CRs created in this case are hard set with "kopia" at present, sice Kopia is the only option as a backup repository. The PodVolume Backup/Restore CRs are created with "kopia" as well: ``` spec: backupStorageLocation: default @@ -438,6 +450,18 @@ spec: uploader-type: kopia volume: nginx-log ``` +``` +spec: + backupStorageLocation: default + pod: + kind: Pod + name: nginx-stateful-0 + namespace: nginx-example + uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 + snapshotID: 1741e5f1 + uploader-type: kopia + volume: nginx-log +``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: - Helm Chart Installation: add the "--pod-volume-backup-uploader" flag into its value.yaml and then generate the deployments according to the value. Value.yaml is the user-provided configuration file, therefore, users could set this value at the time of installation. The changes in Value.yaml are as below: ``` @@ -455,8 +479,9 @@ We will add the flag for both CLI installation and Helm Chart Installation. Spec ```velero install --pod-volume-backup-uploader=kopia``` ## Upgrade -For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade for both CLI and Helm Chart. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it. -Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade for both CLI and Helm Chart. +For upgrade, we allow users to change the path by specifying "--pod-volume-backup-uploader" flag in the same way as the fresh installation. Therefore, the flag change should be applied to the Velero server after upgrade. Additionally, We need to add a label to Velero server to indicate the current path, so as to provide an easy for querying it. +Moreover, if users upgrade from the old release, we need to change the existing Restic Daemonset name to VeleroNodeAgent daemonSet. The name change should be applied after upgrade. +The recommended way for upgrade is to modify the related Velero resource directly through kubectl, the above changes will be applied in the same way. We need to modify the Velero doc for all these changes. ## CLI Below Velero CLI or its output needs some changes: @@ -464,7 +489,13 @@ Below Velero CLI or its output needs some changes: - ```Velero restore describe```: the output should indicate the path - ```Velero restic repo get```: the name of this CLI should be changed to a generic one, for example, "Velero repo get"; the output of this CLI should print all the backup repository if Restic repository and Unified Repository exist at the same time -At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. +At present, we don't have a requirement for selecting the path during backup, so we don't change the ```Velero backup create``` CLI for now. If there is a requirement in future, we could simply add a flag similar to "--pod-volume-backup-uploader" to select the path. + +## CR Example +Below sample files demonstrate complete CRs with all the changes mentioned above: +- BackupRepository CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-backup-repository-sample-yaml +- PodVolumeBackup CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvb-sample-yaml +- PodVolumeRestore CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvr-sample-yaml ## User Experience Changes Below user experiences are changed for this design: From c3f8e91f73b1a16985276d6fae493ff788f7bd66 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 29 Jun 2022 19:07:25 +0800 Subject: [PATCH 11/38] remove storage configuration redundancy Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 45 +++---------------- 1 file changed, 5 insertions(+), 40 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index c6fb9b452..792acd98b 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -311,18 +311,17 @@ This means, we add a new CR type and deprecate the old one. As a result, if user As a side effect, when upgrading from an old release, even though the path is not changed, the BackupRepository gets created all the time, because Velero will not refer to the old CR's status. This seems to cause the repository to initialize more than once, however, it won't happen. In the BackupRepository controller, before initializing a repository, it always tries to connect to the repository first, if it is connectable, it won't do the initialization. When backing up with the new release, Velero always creates BackupRepository CRs instead of ResticRepository CRs. When restoring from an old backup, Velero always creates BackupRepository CRs instead of ResticRepository CRs. -For a upgrade case, if there are already backups or restores running during the upgrade, the backups/restores could fall into below results: -- The backups/restores have started but the ResticRepository CRs have not been created. In this case, the BackupRepository CRs will be created instead, the backups/restores will finish successfully. -- The backups/restores have started and the old ResticRepository CRs have been created, but the CRs have not been processed. In this case, since the new controller doesn't process the old CRs, the backups/restores will wait there until a timeout. At present, the timeout is 1 minute. -- The backups/restores have started and the old ResticRepository CRs have been created and processed. In this case, the backup repository has been successfully connected, the backups/restores could finish successfully. +When there are already backups or restores running during the upgrade, since after upgrade, the Velero server pods and VeleroNodeAgent daemonset pods are restarted, the existing backups/restores will fail immediately. ## Storage Configuration -The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will use a structured storage configuration to replace the repoIdentififer string. -The configuration in BackupRepository CRs, PodVolumeBackup CRs and PodVolumeRestore CRs are as below: +The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will add a structured storage configuration and preserved the existing repoIdentififer string for Restic, considering backward compatibility. +Moreover, the storage configuration structure is added into the BackupRepository CRs only. When a PodVolume controller processes a PodVolume CR, it could get the corresponding BackupRepository CR first and then get the storage configuration from the CR. By this there will not be redundant information. +The configuration in BackupRepository CRs are as below: ``` spec: backupStorageLocation: default maintenanceFrequency: 168h0m0s + resticIdentifier: azure:container01:/restic/nginx-example storageConfig: provider: azure param: @@ -330,40 +329,6 @@ spec: prefix: /restic/nginx-example volumeNamespace: nginx-example ``` -``` -spec: - backupStorageLocation: default - node: aks-agentpool-27359964-vmss000000 - pod: - kind: Pod - name: nginx-stateful-0 - namespace: nginx-example - uid: 86aaec56-2b21-4736-9964-621047717133 - storageConfig: - provider: azure - param: - container: container01 - prefix: /restic/nginx-example - tags: - ... - volume: nginx-log -``` -``` -spec: - backupStorageLocation: default - pod: - kind: Pod - name: nginx-stateful-0 - namespace: nginx-example - uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 - snapshotID: 1741e5f1 - storageConfig: - provider: azure - param: - container: container01 - prefix: /restic/nginx-example - volume: nginx-log -``` ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: From c6625d1424524e358998c53c38c05aa2824147a2 Mon Sep 17 00:00:00 2001 From: Niu Lechuan Date: Thu, 30 Jun 2022 10:09:29 +0800 Subject: [PATCH 12/38] add changelog file for this PR Signed-off-by: Niu Lechuan --- changelogs/unreleased/5051-niulechuan | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelogs/unreleased/5051-niulechuan diff --git a/changelogs/unreleased/5051-niulechuan b/changelogs/unreleased/5051-niulechuan new file mode 100644 index 000000000..1cba2e248 --- /dev/null +++ b/changelogs/unreleased/5051-niulechuan @@ -0,0 +1 @@ +Fix typo in doc, in https://velero.io/docs/main/restore-reference/ "Restore order" section, "Mamespace" should be "Namespace". From fd31336c4afbeeb003ac8390da31518a412ee1e2 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Thu, 30 Jun 2022 14:47:49 +0800 Subject: [PATCH 13/38] add user perspective Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 792acd98b..ee9d798a4 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -462,11 +462,16 @@ Below sample files demonstrate complete CRs with all the changes mentioned above - PodVolumeBackup CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvb-sample-yaml - PodVolumeRestore CR: https://gist.github.com/Lyndon-Li/f38ad69dd8c4785c046cd7ed0ef2b6ed#file-pvr-sample-yaml -## User Experience Changes +## User Perspective +This design aims to provide a flexible backup repository layer and a generic file system uploader, which are fundermental for PodVolume and other data movements. Although this will make Velero more capable, at present, we don't pursue to expose differentiated features end to end. Specifically: +- By default, Velero still uses Restic for PodVolume BR +- Even when changing to the new path, Velero still allows users to restore from the data backed up by Restic +- The capability of PodVolume BR under the new path is kept the same as it under Restic path and the same as the existing PodVolume BR +- The operational experiences are kept the same as much as possible, the known changes are listed below + Below user experiences are changed for this design: - Installation CLI change: a new option is added to the installation CLI, see the Installation section for details - CR change: One or more existing CRs have been renamed, see the Velero CR Changes section for details - Velero CLI name and output change, see the CLI section for details - Velero daemonset name change -- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes - +- Wording Alignment: as the existing situation, many places are using the word of "Restic", for example, "default-volume-to-restic" option, most of them are not accurate anymore, we will change these words and give a detailed list of the changes \ No newline at end of file From c633f68ac036fb4d8ca3a138ce636de0b32c0d84 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Thu, 30 Jun 2022 14:00:19 +0800 Subject: [PATCH 14/38] Propose Yonghui as a maintainer Yonghui joined the velero team earlier this year. He has been leading the effort for kopia integration, and delivered the comprehensive comparison report for kopia .vs. restic https://docs.google.com/document/d/1BMLuRzEpYWYE-Ci_eLg8gWbjDv4DSyqj/edit and the detailed design for using kopia as the unified repository: https://github.com/vmware-tanzu/velero/pull/4926 Signed-off-by: Daniel Jiang --- .github/auto-assignees.yml | 1 + MAINTAINERS.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/auto-assignees.yml b/.github/auto-assignees.yml index 282591d72..8f5a75a57 100644 --- a/.github/auto-assignees.yml +++ b/.github/auto-assignees.yml @@ -16,6 +16,7 @@ reviewers: - blackpiglet - qiuming-best - shubham-pampattiwar + - Lyndon-Li tech-writer: - a-mccarthy diff --git a/MAINTAINERS.md b/MAINTAINERS.md index 2819a77e8..f62eb0f94 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -13,6 +13,7 @@ | Xun Jiang | [blackpiglet](https://github.com/blackpiglet) | [VMware](https://www.github.com/vmware/) | | Ming Qiu | [qiuming-best](https://github.com/qiuming-best) | [VMware](https://www.github.com/vmware/) | | Shubham Pampattiwar | [shubham-pampattiwar](https://github.com/shubham-pampattiwar) | [OpenShift](https://github.com/openshift) +| Yonghui Li | [Lyndon-Li](https://github.com/Lyndon-Li) | [VMware](https://www.github.com/vmware/) | ## Emeritus Maintainers * Adnan Abdulhussein ([prydonius](https://github.com/prydonius)) From fb897471c01fc6623e3ed46e752eb6a04b4d98b4 Mon Sep 17 00:00:00 2001 From: niulechuan <81207605+niulechuan@users.noreply.github.com> Date: Mon, 4 Jul 2022 14:31:20 +0800 Subject: [PATCH 15/38] Move 'velero.io/exclude-from-backup' label string to const (#5053) * move 'velero.io/exclude-from-backup' label name to const Signed-off-by: Niu Lechuan * add changelog file (in changelogs/unreleased) of this PR Signed-off-by: Niu Lechuan --- changelogs/unreleased/5053-niulechuan | 1 + pkg/backup/item_backupper.go | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5053-niulechuan diff --git a/changelogs/unreleased/5053-niulechuan b/changelogs/unreleased/5053-niulechuan new file mode 100644 index 000000000..f44c46a69 --- /dev/null +++ b/changelogs/unreleased/5053-niulechuan @@ -0,0 +1 @@ +Move 'velero.io/exclude-from-backup' label string to const diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index fd4b16028..bd40c3bdb 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -61,6 +61,11 @@ type itemBackupper struct { snapshotLocationVolumeSnapshotters map[string]velero.VolumeSnapshotter } +const ( + // veleroExcludeFromBackupLabel labeled item should be exclude by velero in backup job. + veleroExcludeFromBackupLabel = "velero.io/exclude-from-backup" +) + // backupItem backs up an individual item to tarWriter. The item may be excluded based on the // namespaces IncludesExcludes list. // In addition to the error return, backupItem also returns a bool indicating whether the item @@ -78,8 +83,8 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr log = log.WithField("resource", groupResource.String()) log = log.WithField("namespace", namespace) - if metadata.GetLabels()["velero.io/exclude-from-backup"] == "true" { - log.Info("Excluding item because it has label velero.io/exclude-from-backup=true") + if metadata.GetLabels()[veleroExcludeFromBackupLabel] == "true" { + log.Infof("Excluding item because it has label %s=true", veleroExcludeFromBackupLabel) return false, nil } From f550f8e3cda9b6cfaad065525ccc679703622000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Tue, 28 Jun 2022 18:14:40 +0800 Subject: [PATCH 16/38] Fix bsl validation bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix bsl validation bug: the BSL is validated continually and doesn't respect the validation period configured Fixes #5056 Signed-off-by: Wenkai Yin(尹文开) --- changelogs/unreleased/5101-ywk253100 | 1 + .../backup_storage_location_controller.go | 29 +-- pkg/util/kube/predicate.go | 47 +++++ pkg/util/kube/predicate_test.go | 180 ++++++++++++++++++ 4 files changed, 236 insertions(+), 21 deletions(-) create mode 100644 changelogs/unreleased/5101-ywk253100 create mode 100644 pkg/util/kube/predicate.go create mode 100644 pkg/util/kube/predicate_test.go diff --git a/changelogs/unreleased/5101-ywk253100 b/changelogs/unreleased/5101-ywk253100 new file mode 100644 index 000000000..ade00f2a9 --- /dev/null +++ b/changelogs/unreleased/5101-ywk253100 @@ -0,0 +1 @@ + Fix bsl validation bug: the BSL is validated continually and doesn't respect the validation period configured \ No newline at end of file diff --git a/pkg/controller/backup_storage_location_controller.go b/pkg/controller/backup_storage_location_controller.go index ec35a8916..1b08da897 100644 --- a/pkg/controller/backup_storage_location_controller.go +++ b/pkg/controller/backup_storage_location_controller.go @@ -24,12 +24,10 @@ import ( "github.com/pkg/errors" "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" "github.com/vmware-tanzu/velero/internal/storage" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -39,7 +37,10 @@ import ( ) const ( - backupStorageLocationSyncPeriod = 1 * time.Minute + // keep the enqueue period a smaller value to make sure the BSL can be validated as expected. + // The BSL validation frequency is 1 minute by default, if we set the enqueue period as 1 minute, + // this will cause the actual validation interval for each BSL to be 2 minutes + bslValidationEnqueuePeriod = 10 * time.Second ) // BackupStorageLocationReconciler reconciles a BackupStorageLocation object @@ -185,7 +186,7 @@ func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err r.Log, mgr.GetClient(), &velerov1api.BackupStorageLocationList{}, - backupStorageLocationSyncPeriod, + bslValidationEnqueuePeriod, // Add filter function to enqueue BSL per ValidationFrequency setting. func(object client.Object) bool { location := object.(*velerov1api.BackupStorageLocation) @@ -193,22 +194,8 @@ func (r *BackupStorageLocationReconciler) SetupWithManager(mgr ctrl.Manager) err }, ) return ctrl.NewControllerManagedBy(mgr). - For(&velerov1api.BackupStorageLocation{}). - // Handle BSL's creation event and spec update event to let changed BSL got validation immediately. - WithEventFilter(predicate.Funcs{ - CreateFunc: func(ce event.CreateEvent) bool { - return true - }, - UpdateFunc: func(ue event.UpdateEvent) bool { - return ue.ObjectNew.GetGeneration() != ue.ObjectOld.GetGeneration() - }, - DeleteFunc: func(de event.DeleteEvent) bool { - return false - }, - GenericFunc: func(ge event.GenericEvent) bool { - return false - }, - }). + // As the "status.LastValidationTime" field is always updated, this triggers new reconciling process, skip the update event that include no spec change to avoid the reconcile loop + For(&velerov1api.BackupStorageLocation{}, builder.WithPredicates(kube.SpecChangePredicate{})). Watches(g, nil). Complete(r) } diff --git a/pkg/util/kube/predicate.go b/pkg/util/kube/predicate.go new file mode 100644 index 000000000..3073ef881 --- /dev/null +++ b/pkg/util/kube/predicate.go @@ -0,0 +1,47 @@ +/* +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 ( + "reflect" + + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// SpecChangePredicate implements a default update predicate function on Spec change +// As Velero doesn't enable subresource in CRDs, we cannot use the object's metadata.generation field to check the spec change +// More details about the generation field refer to https://github.com/kubernetes-sigs/controller-runtime/blob/v0.12.2/pkg/predicate/predicate.go#L156 +type SpecChangePredicate struct { + predicate.Funcs +} + +func (SpecChangePredicate) Update(e event.UpdateEvent) bool { + if e.ObjectOld == nil { + return false + } + if e.ObjectNew == nil { + return false + } + oldSpec := reflect.ValueOf(e.ObjectOld).Elem().FieldByName("Spec") + // contains no field named "Spec", return false directly + if oldSpec.IsZero() { + return false + } + newSpec := reflect.ValueOf(e.ObjectNew).Elem().FieldByName("Spec") + return !reflect.DeepEqual(oldSpec.Interface(), newSpec.Interface()) +} diff --git a/pkg/util/kube/predicate_test.go b/pkg/util/kube/predicate_test.go new file mode 100644 index 000000000..d1c3be8df --- /dev/null +++ b/pkg/util/kube/predicate_test.go @@ -0,0 +1,180 @@ +/* +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 ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +func TestSpecChangePredicate(t *testing.T) { + cases := []struct { + name string + oldObj client.Object + newObj client.Object + changed bool + }{ + { + name: "Contains no spec field", + oldObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + }, + }, + newObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + }, + }, + changed: false, + }, + { + name: "ObjectMetas are different, Specs are same", + oldObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + Annotations: map[string]string{"key1": "value1"}, + }, + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + newObj: &velerov1.BackupStorageLocation{ + ObjectMeta: metav1.ObjectMeta{ + Name: "bsl01", + Annotations: map[string]string{"key2": "value2"}, + }, + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + changed: false, + }, + { + name: "Statuses are different, Specs are same", + oldObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + Status: velerov1.BackupStorageLocationStatus{ + Phase: velerov1.BackupStorageLocationPhaseAvailable, + }, + }, + newObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + Status: velerov1.BackupStorageLocationStatus{ + Phase: velerov1.BackupStorageLocationPhaseUnavailable, + }, + }, + changed: false, + }, + { + name: "Specs are different", + oldObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + }, + }, + newObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "aws", + }, + }, + changed: true, + }, + { + name: "Specs are same", + oldObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + Config: map[string]string{"key": "value"}, + Credential: &corev1api.SecretKeySelector{ + LocalObjectReference: corev1api.LocalObjectReference{ + Name: "secret", + }, + Key: "credential", + }, + StorageType: velerov1.StorageType{ + ObjectStorage: &velerov1.ObjectStorageLocation{ + Bucket: "bucket1", + Prefix: "prefix", + CACert: []byte{'a'}, + }, + }, + Default: true, + AccessMode: velerov1.BackupStorageLocationAccessModeReadWrite, + BackupSyncPeriod: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + ValidationFrequency: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + }, + }, + newObj: &velerov1.BackupStorageLocation{ + Spec: velerov1.BackupStorageLocationSpec{ + Provider: "azure", + Config: map[string]string{"key": "value"}, + Credential: &corev1api.SecretKeySelector{ + LocalObjectReference: corev1api.LocalObjectReference{ + Name: "secret", + }, + Key: "credential", + }, + StorageType: velerov1.StorageType{ + ObjectStorage: &velerov1.ObjectStorageLocation{ + Bucket: "bucket1", + Prefix: "prefix", + CACert: []byte{'a'}, + }, + }, + Default: true, + AccessMode: velerov1.BackupStorageLocationAccessModeReadWrite, + BackupSyncPeriod: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + ValidationFrequency: &metav1.Duration{ + Duration: 1 * time.Minute, + }, + }, + }, + changed: false, + }, + } + + predicate := SpecChangePredicate{} + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + changed := predicate.Update(event.UpdateEvent{ + ObjectOld: c.oldObj, + ObjectNew: c.newObj, + }) + assert.Equal(t, c.changed, changed) + }) + } +} From bf8d135876e9bba6c3676f613da0f4e7664678ed Mon Sep 17 00:00:00 2001 From: danfengl Date: Fri, 8 Jul 2022 09:35:07 +0000 Subject: [PATCH 17/38] Fix resource filtering command issue in Doc Signed-off-by: danfengl --- site/content/docs/v1.4/resource-filtering.md | 8 ++++---- site/content/docs/v1.5/resource-filtering.md | 8 ++++---- site/content/docs/v1.6/resource-filtering.md | 8 ++++---- site/content/docs/v1.7/resource-filtering.md | 8 ++++---- site/content/docs/v1.8/resource-filtering.md | 8 ++++---- site/content/docs/v1.9/resource-filtering.md | 8 ++++---- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/site/content/docs/v1.4/resource-filtering.md b/site/content/docs/v1.4/resource-filtering.md index 6c1470e04..0b893c49f 100644 --- a/site/content/docs/v1.4/resource-filtering.md +++ b/site/content/docs/v1.4/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.5/resource-filtering.md b/site/content/docs/v1.5/resource-filtering.md index 6c1470e04..0b893c49f 100644 --- a/site/content/docs/v1.5/resource-filtering.md +++ b/site/content/docs/v1.5/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.6/resource-filtering.md b/site/content/docs/v1.6/resource-filtering.md index 6c1470e04..0b893c49f 100644 --- a/site/content/docs/v1.6/resource-filtering.md +++ b/site/content/docs/v1.6/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.7/resource-filtering.md b/site/content/docs/v1.7/resource-filtering.md index 6c1470e04..0b893c49f 100644 --- a/site/content/docs/v1.7/resource-filtering.md +++ b/site/content/docs/v1.7/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -107,7 +107,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.8/resource-filtering.md b/site/content/docs/v1.8/resource-filtering.md index ddcd97e4a..1f2d2133d 100644 --- a/site/content/docs/v1.8/resource-filtering.md +++ b/site/content/docs/v1.8/resource-filtering.md @@ -24,7 +24,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -38,7 +38,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -72,7 +72,7 @@ Wildcard takes precedence when both a wildcard and specific resource are include * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -113,7 +113,7 @@ Wildcard excludes are ignored. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources diff --git a/site/content/docs/v1.9/resource-filtering.md b/site/content/docs/v1.9/resource-filtering.md index 02ae3d68d..5c0b34c73 100644 --- a/site/content/docs/v1.9/resource-filtering.md +++ b/site/content/docs/v1.9/resource-filtering.md @@ -26,7 +26,7 @@ Namespaces to include. Default is `*`, all namespaces. * Restore two namespaces and their objects. ```bash - velero restore create --include-namespaces , + velero restore create --include-namespaces , --from-backup ``` ### --include-resources @@ -42,7 +42,7 @@ Kubernetes resources to include in the backup, formatted as resource.group, such * Restore all deployments and configmaps in the cluster. ```bash - velero restore create --include-resources deployments,configmaps + velero restore create --include-resources deployments,configmaps --from-backup ``` * Backup the deployments in a namespace. @@ -76,7 +76,7 @@ Includes cluster-scoped resources. This option can have three possible values: * Restore only namespaced resources in the cluster. ```bash - velero restore create --include-cluster-resources=false + velero restore create --include-cluster-resources=false --from-backup ``` * Backup a namespace and include cluster-scoped resources. @@ -119,7 +119,7 @@ Namespaces to exclude. * Exclude two namespaces during a restore. ```bash - velero restore create --exclude-namespaces , + velero restore create --exclude-namespaces , --from-backup ``` ### --exclude-resources From 9102f53131670ba002b41ab9f2170f4212d2a759 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 11 Jul 2022 01:41:00 +0800 Subject: [PATCH 18/38] Dump stack trace when the plugin server handles panic Mitigate the issue mentioned in #4782 When there's a bug or misconfiguration that causes nil pointer there will be more stack trace information to help us debug. Signed-off-by: Daniel Jiang --- changelogs/unreleased/5110-reasonerjt | 1 + pkg/plugin/framework/handle_panic.go | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5110-reasonerjt diff --git a/changelogs/unreleased/5110-reasonerjt b/changelogs/unreleased/5110-reasonerjt new file mode 100644 index 000000000..350f91fa1 --- /dev/null +++ b/changelogs/unreleased/5110-reasonerjt @@ -0,0 +1 @@ +Dump stack trace when the plugin server handles panic \ No newline at end of file diff --git a/pkg/plugin/framework/handle_panic.go b/pkg/plugin/framework/handle_panic.go index 10eb1d2b9..4ea0ec2b5 100644 --- a/pkg/plugin/framework/handle_panic.go +++ b/pkg/plugin/framework/handle_panic.go @@ -17,6 +17,8 @@ limitations under the License. package framework import ( + "runtime/debug" + "github.com/pkg/errors" "google.golang.org/grpc/codes" ) @@ -38,7 +40,8 @@ func handlePanic(p interface{}) error { if _, ok := panicErr.(stackTracer); ok { err = panicErr } else { - err = errors.Wrap(panicErr, "plugin panicked") + errWithStacktrace := errors.Errorf("%v, stack trace: %s", panicErr, debug.Stack()) + err = errors.Wrap(errWithStacktrace, "plugin panicked") } } From 9173ac117e271bbdd56200c9504b8eb581a45053 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Mon, 18 Jul 2022 19:11:53 +0800 Subject: [PATCH 19/38] Let "make shell xxx" respect GOPROXY This commit mitigates the issue for running "make update" locally when the network is not friendly for accessing the default "proxy.golang.org" Signed-off-by: Daniel Jiang --- Makefile | 1 + changelogs/unreleased/5128-reasonerjt | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelogs/unreleased/5128-reasonerjt diff --git a/Makefile b/Makefile index 8dcf6ee50..b3a6a32f2 100644 --- a/Makefile +++ b/Makefile @@ -163,6 +163,7 @@ shell: build-dirs build-env @# under $GOPATH). @docker run \ -e GOFLAGS \ + -e GOPROXY \ -i $(TTY) \ --rm \ -u $$(id -u):$$(id -g) \ diff --git a/changelogs/unreleased/5128-reasonerjt b/changelogs/unreleased/5128-reasonerjt new file mode 100644 index 000000000..3ba53b059 --- /dev/null +++ b/changelogs/unreleased/5128-reasonerjt @@ -0,0 +1 @@ +Let "make shell xxx" respect GOPROXY \ No newline at end of file From 64a8c44104b28d2ed94a9ebc6497d4e539904b98 Mon Sep 17 00:00:00 2001 From: Scott Seago Date: Thu, 14 Jul 2022 16:25:25 -0400 Subject: [PATCH 20/38] Modify BackupStoreGetter to avoid BSL spec changes Pass in a new copy of the map of config values rather than modifying the BSL Spec.Config and then pass in that field. Signed-off-by: Scott Seago --- changelogs/unreleased/5122-sseago | 1 + pkg/persistence/object_store.go | 24 +++++++++++++++--------- 2 files changed, 16 insertions(+), 9 deletions(-) create mode 100644 changelogs/unreleased/5122-sseago diff --git a/changelogs/unreleased/5122-sseago b/changelogs/unreleased/5122-sseago new file mode 100644 index 000000000..ec8dc473e --- /dev/null +++ b/changelogs/unreleased/5122-sseago @@ -0,0 +1 @@ +Modify BackupStoreGetter to avoid BSL spec changes diff --git a/pkg/persistence/object_store.go b/pkg/persistence/object_store.go index 1c0b619e1..acda15323 100644 --- a/pkg/persistence/object_store.go +++ b/pkg/persistence/object_store.go @@ -131,19 +131,25 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio return nil, errors.Errorf("backup storage location's bucket name %q must not contain a '/' (if using a prefix, put it in the 'Prefix' field instead)", location.Spec.ObjectStorage.Bucket) } + // Pass a new map into the object store rather than modifying the passed-in + // location. This prevents Velero controllers from accidentally modifying + // the in-cluster BSL with data which doesn't belong in Spec.Config + objectStoreConfig := make(map[string]string) + if location.Spec.Config != nil { + for key, val := range location.Spec.Config { + objectStoreConfig[key] = val + } + } + // add the bucket name and prefix to the config map so that object stores // can use them when initializing. The AWS object store uses the bucket // name to determine the bucket's region when setting up its client. - if location.Spec.Config == nil { - location.Spec.Config = make(map[string]string) - } - - location.Spec.Config["bucket"] = bucket - location.Spec.Config["prefix"] = prefix + objectStoreConfig["bucket"] = bucket + objectStoreConfig["prefix"] = prefix // Only include a CACert if it's specified in order to maintain compatibility with plugins that don't expect it. if location.Spec.ObjectStorage.CACert != nil { - location.Spec.Config["caCert"] = string(location.Spec.ObjectStorage.CACert) + objectStoreConfig["caCert"] = string(location.Spec.ObjectStorage.CACert) } // If the BSL specifies a credential, fetch its path on disk and pass to @@ -154,7 +160,7 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio return nil, errors.Wrap(err, "unable to get credentials") } - location.Spec.Config["credentialsFile"] = credsFile + objectStoreConfig["credentialsFile"] = credsFile } objectStore, err := objectStoreGetter.GetObjectStore(location.Spec.Provider) @@ -162,7 +168,7 @@ func (b *objectBackupStoreGetter) Get(location *velerov1api.BackupStorageLocatio return nil, err } - if err := objectStore.Init(location.Spec.Config); err != nil { + if err := objectStore.Init(objectStoreConfig); err != nil { return nil, err } From 9a5c3aceffdabbbd321ad0dd2a2501d061c366f1 Mon Sep 17 00:00:00 2001 From: Tiger Kaovilai Date: Mon, 18 Jul 2022 12:01:24 -0400 Subject: [PATCH 21/38] typo initialise -> initialize Signed-off-by: Tiger Kaovilai --- pkg/controller/backup_deletion_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/backup_deletion_controller.go b/pkg/controller/backup_deletion_controller.go index 9cbfe1e8d..52d358042 100644 --- a/pkg/controller/backup_deletion_controller.go +++ b/pkg/controller/backup_deletion_controller.go @@ -198,7 +198,7 @@ func (r *backupDeletionReconciler) Reconcile(ctx context.Context, req ctrl.Reque return ctrl.Result{}, err } - // if the request object has no labels defined, initialise an empty map since + // if the request object has no labels defined, initialize an empty map since // we will be updating labels if dbr.Labels == nil { dbr.Labels = map[string]string{} From 3a802e160bee3d50ac804c284e86995bc7434b09 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 20 Jul 2022 17:42:06 +0800 Subject: [PATCH 22/38] change storage config Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index ee9d798a4..04d006553 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -99,15 +99,15 @@ type BackupRepoService interface { ///repoOption: option to the backup repository and the underlying backup storage ///createNew: indicates whether to create a new or connect to an existing backup repository ///result: the backup repository specific output that could be used to open the backup repository later - Init(ctx context.Context, repoOption RepoOptions, createNew bool) (result map[string]string, err error) + Init(ctx context.Context, repoOption RepoOptions, createNew bool) error ///Open an backup repository that has been created/connected - ///config: options to open the backup repository and the underlying storage - Open(ctx context.Context, config map[string]string) (BackupRepo, error) + ///repoOption: options to open the backup repository and the underlying storage + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance - ///config: options to open the backup repository and the underlying storage - Maintain(ctx context.Context, config map[string]string) error + ///repoOption: options to maintain the backup repository + Maintain(ctx context.Context, repoOption RepoOptions) error } ///BackupRepo provides the access to the backup repository @@ -156,7 +156,11 @@ type ObjectWriter interface { ///For some cases, i.e. block incremental, the object is not written sequentially io.Seeker - + // Periodically called to preserve the state of data written to the repo so far + // Return a unified identifier that represent the current state + // An empty ID could be returned on success if the backup repository doesn't support this + Checkpoint() (ID, error) + ///Wait for the completion of the object write ///Result returns the object's unified identifier after the write completes Result() (ID, error) @@ -314,21 +318,9 @@ When restoring from an old backup, Velero always creates BackupRepository CRs in When there are already backups or restores running during the upgrade, since after upgrade, the Velero server pods and VeleroNodeAgent daemonset pods are restarted, the existing backups/restores will fail immediately. ## Storage Configuration -The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. Therefore, we will add a structured storage configuration and preserved the existing repoIdentififer string for Restic, considering backward compatibility. -Moreover, the storage configuration structure is added into the BackupRepository CRs only. When a PodVolume controller processes a PodVolume CR, it could get the corresponding BackupRepository CR first and then get the storage configuration from the CR. By this there will not be redundant information. -The configuration in BackupRepository CRs are as below: -``` -spec: - backupStorageLocation: default - maintenanceFrequency: 168h0m0s - resticIdentifier: azure:container01:/restic/nginx-example - storageConfig: - provider: azure - param: - container: container01 - prefix: /restic/nginx-example - volumeNamespace: nginx-example -``` +The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. On the other hand, the parameters that are used to generate the repoIdentififer all come from the BackupStorageLocation. The latter has a map structure that could take parameters from any storage kind. +Therefore, for the new path, Velero uses the information in the BackupStorageLocation directly. That is, whenever Velero needs to initialize/connect to the Unified Repository, it accquires the storage configuration from the corresponding BackupStorageLocation. Then no more elements will be added in BackupRepository CRs, PodVolume Backup CRs or PodVolume Restore CRs. +The legacy path will be kept as is. That is, Velero still sets/gets the repoIdentififer in BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs and then passes to Restic CLI. ## Installation We will add a new flag "--pod-volume-backup-uploader" during installation. The flag has 3 meanings: From 7a38aa5e0fb8b9ffdb3597faf67e299d6a0d2527 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 20 Jul 2022 17:50:14 +0800 Subject: [PATCH 23/38] fix alignment Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 04d006553..d56f8ae77 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -102,12 +102,12 @@ type BackupRepoService interface { Init(ctx context.Context, repoOption RepoOptions, createNew bool) error ///Open an backup repository that has been created/connected - ///repoOption: options to open the backup repository and the underlying storage - Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) + ///repoOption: options to open the backup repository and the underlying storage + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) ///Periodically called to maintain the backup repository to eliminate redundant data and improve performance - ///repoOption: options to maintain the backup repository - Maintain(ctx context.Context, repoOption RepoOptions) error + ///repoOption: options to maintain the backup repository + Maintain(ctx context.Context, repoOption RepoOptions) error } ///BackupRepo provides the access to the backup repository @@ -155,11 +155,11 @@ type ObjectWriter interface { ///For some cases, i.e. block incremental, the object is not written sequentially io.Seeker - - // Periodically called to preserve the state of data written to the repo so far - // Return a unified identifier that represent the current state - // An empty ID could be returned on success if the backup repository doesn't support this - Checkpoint() (ID, error) + + // Periodically called to preserve the state of data written to the repo so far + // Return a unified identifier that represent the current state + // An empty ID could be returned on success if the backup repository doesn't support this + Checkpoint() (ID, error) ///Wait for the completion of the object write ///Result returns the object's unified identifier after the write completes From c845f0c5ea0dd4b4c7f171412e7a852e4a44d862 Mon Sep 17 00:00:00 2001 From: Lyndon-Li Date: Wed, 20 Jul 2022 18:05:28 +0800 Subject: [PATCH 24/38] fix typo Signed-off-by: Lyndon-Li --- .../unified-repo-and-kopia-integration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index d56f8ae77..122f85b43 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -319,7 +319,7 @@ When there are already backups or restores running during the upgrade, since aft ## Storage Configuration The backup repository needs some parameters to connect to various backup storage. For example, for a S3 compatible storage, the parameters may include bucket name, region, endpoint, etc. Different backup storage have totally different parameters. BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs save these parameters in their spec, as a string called repoIdentififer. The format of the string is for S3 storage only, it meets Restic CLI's requirements but is not enough for other backup repository. On the other hand, the parameters that are used to generate the repoIdentififer all come from the BackupStorageLocation. The latter has a map structure that could take parameters from any storage kind. -Therefore, for the new path, Velero uses the information in the BackupStorageLocation directly. That is, whenever Velero needs to initialize/connect to the Unified Repository, it accquires the storage configuration from the corresponding BackupStorageLocation. Then no more elements will be added in BackupRepository CRs, PodVolume Backup CRs or PodVolume Restore CRs. +Therefore, for the new path, Velero uses the information in the BackupStorageLocation directly. That is, whenever Velero needs to initialize/connect to the Unified Repository, it acquires the storage configuration from the corresponding BackupStorageLocation. Then no more elements will be added in BackupRepository CRs, PodVolume Backup CRs or PodVolume Restore CRs. The legacy path will be kept as is. That is, Velero still sets/gets the repoIdentififer in BackupRepository CRs, PodVolume Backup CRs and PodVolume Restore CRs and then passes to Restic CLI. ## Installation From f562a7ce2b811f1015b31094be767a399c790f0f Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 19 Jul 2022 16:06:40 +0800 Subject: [PATCH 25/38] Update the CRD for kopia integration This commit adds additional fields to podvolumebackup and podvolumerestore. The resticrepository will be renamed to backuprepository Signed-off-by: Daniel Jiang --- changelogs/unreleased/5135-reasonerjt | 1 + ...yaml => velero.io_backuprepositories.yaml} | 30 ++- .../v1/bases/velero.io_podvolumebackups.yaml | 20 +- .../v1/bases/velero.io_podvolumerestores.yaml | 16 +- config/crd/v1/crds/crds.go | 6 +- config/rbac/role.yaml | 40 ++-- .../unified-repo-and-kopia-integration.md | 18 +- ...ry_types.go => backup_repository_types.go} | 49 +++-- pkg/apis/velero/v1/pod_volume_backup_types.go | 12 +- pkg/apis/velero/v1/pod_volume_restore_type.go | 10 +- pkg/apis/velero/v1/register.go | 2 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 188 +++++++++--------- pkg/cmd/cli/backuplocation/delete.go | 6 +- pkg/cmd/cli/restic/repo/get.go | 8 +- pkg/cmd/server/server.go | 2 +- pkg/cmd/util/output/output.go | 8 +- pkg/cmd/util/output/restic_repo_printer.go | 6 +- .../restic_repository_controller.go | 50 ++--- .../restic_repository_controller_test.go | 36 ++-- pkg/controller/restore_controller.go | 4 + ...esticrepository.go => backuprepository.go} | 106 +++++----- .../velero/v1/fake/fake_backuprepository.go | 142 +++++++++++++ .../velero/v1/fake/fake_resticrepository.go | 142 ------------- .../velero/v1/fake/fake_velero_client.go | 8 +- .../typed/velero/v1/generated_expansion.go | 4 +- .../typed/velero/v1/velero_client.go | 10 +- .../informers/externalversions/generic.go | 4 +- ...esticrepository.go => backuprepository.go} | 38 ++-- .../externalversions/velero/v1/interface.go | 14 +- .../listers/velero/v1/backuprepository.go | 99 +++++++++ .../listers/velero/v1/expansion_generated.go | 16 +- .../listers/velero/v1/resticrepository.go | 99 --------- pkg/restic/backupper.go | 7 +- pkg/restic/mocks/repository_manager.go | 16 +- pkg/restic/repository_ensurer.go | 35 ++-- pkg/restic/repository_manager.go | 22 +- pkg/restic/restorer.go | 6 +- 37 files changed, 670 insertions(+), 610 deletions(-) create mode 100644 changelogs/unreleased/5135-reasonerjt rename config/crd/v1/bases/{velero.io_resticrepositories.yaml => velero.io_backuprepositories.yaml} (78%) rename pkg/apis/velero/v1/{restic_repository_types.go => backup_repository_types.go} (64%) rename pkg/generated/clientset/versioned/typed/velero/v1/{resticrepository.go => backuprepository.go} (53%) create 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_resticrepository.go rename pkg/generated/informers/externalversions/velero/v1/{resticrepository.go => backuprepository.go} (70%) create mode 100644 pkg/generated/listers/velero/v1/backuprepository.go delete mode 100644 pkg/generated/listers/velero/v1/resticrepository.go diff --git a/changelogs/unreleased/5135-reasonerjt b/changelogs/unreleased/5135-reasonerjt new file mode 100644 index 000000000..0505ab046 --- /dev/null +++ b/changelogs/unreleased/5135-reasonerjt @@ -0,0 +1 @@ +Update the CRD for kopia integration \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_resticrepositories.yaml b/config/crd/v1/bases/velero.io_backuprepositories.yaml similarity index 78% rename from config/crd/v1/bases/velero.io_resticrepositories.yaml rename to config/crd/v1/bases/velero.io_backuprepositories.yaml index 251766136..fa7e5596e 100644 --- a/config/crd/v1/bases/velero.io_resticrepositories.yaml +++ b/config/crd/v1/bases/velero.io_backuprepositories.yaml @@ -6,20 +6,23 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.7.0 creationTimestamp: null - name: resticrepositories.velero.io + name: backuprepositories.velero.io spec: group: velero.io names: - kind: ResticRepository - listKind: ResticRepositoryList - plural: resticrepositories - singular: resticrepository + kind: BackupRepository + listKind: BackupRepositoryList + plural: backuprepositories + singular: backuprepository scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date + - jsonPath: .spec.repositoryType + name: Repository Type + type: string name: v1 schema: openAPIV3Schema: @@ -37,7 +40,7 @@ spec: metadata: type: object spec: - description: ResticRepositorySpec is the specification for a ResticRepository. + description: BackupRepositorySpec is the specification for a BackupRepository. properties: backupStorageLocation: description: BackupStorageLocation is the name of the BackupStorageLocation @@ -47,12 +50,19 @@ spec: description: MaintenanceFrequency is how often maintenance should be run. type: string + 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 restic repository + description: VolumeNamespace is the namespace this backup repository contains pod volume backups for. type: string required: @@ -62,7 +72,7 @@ spec: - volumeNamespace type: object status: - description: ResticRepositoryStatus is the current status of a ResticRepository. + description: BackupRepositoryStatus is the current status of a BackupRepository. properties: lastMaintenanceTime: description: LastMaintenanceTime is the last time maintenance was @@ -72,10 +82,10 @@ spec: type: string message: description: Message is a message about the current status of the - ResticRepository. + BackupRepository. type: string phase: - description: Phase is the current state of the ResticRepository. + description: Phase is the current state of the BackupRepository. enum: - New - Ready diff --git a/config/crd/v1/bases/velero.io_podvolumebackups.yaml b/config/crd/v1/bases/velero.io_podvolumebackups.yaml index 69993f0d4..6af4ce294 100644 --- a/config/crd/v1/bases/velero.io_podvolumebackups.yaml +++ b/config/crd/v1/bases/velero.io_podvolumebackups.yaml @@ -37,9 +37,13 @@ spec: jsonPath: .spec.volume name: Volume type: string - - description: Restic repository identifier for this backup + - description: Backup repository identifier for this backup jsonPath: .spec.repoIdentifier - name: Restic Repo + name: Repository ID + type: string + - description: The type of the uploader to handle data transfer + jsonPath: .spec.uploaderType + name: Uploader Type type: string - description: Name of the Backup Storage Location where this backup should be stored @@ -70,7 +74,7 @@ spec: properties: backupStorageLocation: description: BackupStorageLocation is the name of the backup storage - location where the restic repository is stored. + location where the backup repository is stored. type: string node: description: Node is the name of the node that the Pod is running @@ -114,7 +118,7 @@ spec: type: string type: object repoIdentifier: - description: RepoIdentifier is the restic repository identifier. + description: RepoIdentifier is the backup repository identifier. type: string tags: additionalProperties: @@ -122,6 +126,14 @@ spec: description: Tags are a map of key-value pairs that should be applied to the volume backup as tags. type: object + uploaderType: + description: UploaderType is the type of the uploader to handle the + data transfer. + enum: + - kopia + - restic + - "" + type: string volume: description: Volume is the name of the volume within the Pod to be backed up. diff --git a/config/crd/v1/bases/velero.io_podvolumerestores.yaml b/config/crd/v1/bases/velero.io_podvolumerestores.yaml index 6f77cb67c..036f58a06 100644 --- a/config/crd/v1/bases/velero.io_podvolumerestores.yaml +++ b/config/crd/v1/bases/velero.io_podvolumerestores.yaml @@ -25,6 +25,10 @@ spec: jsonPath: .spec.pod.name name: Pod type: string + - description: The type of the uploader to handle data transfer + jsonPath: .spec.uploaderType + name: Uploader Type + type: string - description: Name of the volume to be restored jsonPath: .spec.volume name: Volume @@ -67,7 +71,7 @@ spec: properties: backupStorageLocation: description: BackupStorageLocation is the name of the backup storage - location where the restic repository is stored. + location where the backup repository is stored. type: string pod: description: Pod is a reference to the pod containing the volume to @@ -107,11 +111,19 @@ spec: type: string type: object repoIdentifier: - description: RepoIdentifier is the restic repository identifier. + description: RepoIdentifier is the backup repository identifier. type: string snapshotID: description: SnapshotID is the ID of the volume snapshot to be restored. type: string + uploaderType: + description: UploaderType is the type of the uploader to handle the + data transfer. + enum: + - kopia + - restic + - "" + type: string volume: description: Volume is the name of the volume within the Pod to be restored. diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 4b1197a47..2d7135ff6 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -29,13 +29,13 @@ import ( ) var rawCRDs = [][]byte{ + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WAo\xdc6\x13\xbd\xebW\f\xf2\x1dr\xf9\xa4M\xd0C\v\xddR\xb7\x05\x82&\x86a\a\xbe\x14=P\xe4\xec.c\x8ad\xc9\xe1\xa6ۢ\xff\xbd\x18R\xf2j%\xd9\x1b\a\xa8n\"\x87of\xde\xcc\x1bQU]ו\xf0\xfa\x1eC\xd4ζ \xbc\xc6?\t-\xbf\xc5\xe6\xe1\x87\xd8h\xb79\xbc\xad\x1e\xb4U-\\\xa5H\xae\xbf\xc5\xe8R\x90\xf8\x13n\xb5դ\x9d\xadz$\xa1\x04\x89\xb6\x02\x10\xd6:\x12\xbc\x1c\xf9\x15@:K\xc1\x19\x83\xa1ޡm\x1eR\x87]\xd2Fa\xc8\xe0\xa3\xebÛ\xe6\xfb\xe6M\x05 \x03\xe6\xe3\x9ft\x8f\x91D\xef[\xb0ɘ\n\xc0\x8a\x1e[\xe8\x84|H>\xa0wQ\x93\v\x1acs@\x83\xc15\xdaUѣd\xb7\xbb\xe0\x92o\xe1\xb4QN\x0f!\x95t~\xcc@\xb7#\xd01o\x19\x1d\xe9\xd7\xd5\xed\x0f:R6\xf1&\x05a\xd6\x02\xc9\xdbQ\xdb]2\",\f\xd8A\x94\xcec\v\xd7\x1c\x8b\x17\x12U\x050P\x90c\xabA(\x95I\x15\xe6&hK\x18\xae\x9cI\xfdHf\r\x9f\xa3\xb37\x82\xf6-4#\xed͂\xb2l;\x12\xf6n\x87\xc3;\x1dٹ\x12\x84K0f\xae9\xc5\xfa\xe9\xe8\xf1\f\xe5D\x04L\xf6\nb\xa4\xa0\xed\xae:\x19\x1f\xde\x16*\xe4\x1e{\xd1\x0e\xb6Σ}w\xf3\xfe\xfe\xbb\xbb\xb3e\x00\x1f\x9c\xc7@z,Oy&}9Y\x05P\x18eОr\u05fcf\xc0b\x05\x8a\x1b\x12#\xd0\x1eGNQ\r1\x80\xdb\x02\xedu\x84\x80>`D[Z\xf4\f\x18\xd8HXp\xddg\x94\xd4\xc0\x1d\x06\x86\x81\xb8w\xc9(\xee\xe3\x03\x06\x82\x80\xd2\xed\xac\xfe\xeb\x11;\x02\xb9\xec\xd4\b¡GNO\xae\xa1\x15\x06\x0e\xc2$\xfc?\b\xab\xa0\x17G\b\xc8^ \xd9\t^6\x89\r|t\x01AۭkaO\xe4c\xbb\xd9\xec4\x8dz\x94\xae\xef\x93\xd5t\xdcdi\xe9.\x91\vq\xa3\xf0\x80f\x13\xf5\xae\x16A\xee5\xa1\xa4\x14p#\xbc\xaes\xe86k\xb2\xe9\xd5\xff\u00a0\xe0\xf8\xfa,\xd6E-˓\xc5\xf2L\x05X-\xa0#\x88\xe1h\xc9\xe2D4/1;\xb7?\xdf}\x82\xd1u.Ɯ\xfd\xcc\xfb\xe9`<\x95\x80\t\xd3v\x8b\xa1\x14q\x1b\\\x9f1\xd1*ﴥ\xfc\"\x8dF;\xa7?\xa6\xae\xd7\xc4u\xff#a$\xaeU\x03WyHA\x87\x90<\xabA5\xf0\xde\u0095\xe8\xd1\\\x89\x88\xffy\x01\x98\xe9X3\xb1_W\x82\xe9|\x9d\x1b\x17\xd6&\x1b\xe3\b|\xa2^\xf3\xb1v\xe7Qr\xf9\x98A>\xaa\xb7Zfm\xc0\xd6\x05\x10\v\xfb\xe6\fz]\xba\xfc\x94\xe1wG.\x88\x1d~p\x05sn\xb4\x1a\xdb\xec\xcc\x18\x1cO\x96\"c\\7\\`\x03\xd0^\xd0D\xbf$\xb4}\x1c\x03\xab\xf90\x92-S\bhi\x80\xc97\x88o\x1e\x99FD\x9a\x8c\v\xbe\xcd]\xe8\x80\x0f\xcb\x13c`\f\x06\xc4\v\xd3\xf9\xf2E̿\xba\xb9hk\x93e\xebB/\xa8\\\x17k\x06ZX\xf0\xb5\\t\x06[\xa0\x90\x96\xdb\xcf\xcdQ\x8cQ\xec.e\xf7\xb1X\x95\xcb\xc5p\x04D\xe7\x12=A=\xed\x97Q\xc0\x85r\\\x88\xd4\xefE\xbc\x14\xe7\r۬5\xc4\xec{\xf5\\\bO\xcd\xcck\xfc\xb2\xb2z\x8bB-u\\õ\xa3\xf5\xad'3\\U\xc5b1\xf2=LM\xea\x1c\x8b\x90\xa7+\xa9{\xbcW\xb6\xf0\xf7?\xd5IXBJ\xf4\x84\xeaz\xfe\a6\xcc\xf7\xf1\x87*\xbfJg\xcb\x0fPl\xe1\xb7߫\xe2\n\xd5\xfd\xf8\x93ċ\xff\x06\x00\x00\xff\xff\xc8p\x98۸\x0e\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec}\xcdr\x1c9r\xf0\x9dO\x91\xc1\xef\xa0\xdd\bvs'\xbe\x83\x1d\xbci(M\xb8c\xc6\x1a\x86(\xcb\a\x87\x0f\xe8\xaa\xecn\fQ@-\x80j\xaa\xed\xf0\xbb;\x90\x00\xea\xafQUh\x0e\xb9;\xeb\x10.RW\x01\x89D\"\xff\x91\x05^\xadV\xab+V\xf3\xaf\xa8\rW\xf2\x0eX\xcd\xf1\x9bE\xe9~\x99\xf5\xd3?\x9b5W\xb7\xc7\x1f\xae\x9e\xb8,\xef\xe0\xbe1VU\x9fѨF\x17\xf8\x01w\\r˕\xbc\xaaв\x92Yvw\x05\xc0\xa4T\x96\xb9\xc7\xc6\xfd\x04(\x94\xb4Z\t\x81z\xb5G\xb9~j\xb6\xb8m\xb8(Q\x13\xf08\xf5\xf1/\xeb\u007fZ\xff\xe5\n\xa0\xd0Hÿ\xf0\n\x8deU}\a\xb2\x11\xe2\n@\xb2\n\xef`ˊ\xa7\xa66\xeb#\n\xd4j\xcdՕ\xa9\xb1ps\xed\xb5j\xea;\xe8^\xf8!\x01\x0f\xbf\x86\x1fi4=\x10\xdc؟{\x0f\u007f\xe1\xc6ҋZ4\x9a\x89v&zf\xb8\xdc7\x82\xe9\xf8\xf4\n\xc0\x14\xaa\xc6;\xf8䦨Y\x81\xe5\x15@X\x0eM\xb9\n\b\x1f\u007f\xf0\x10\x8a\x03V\xcc\xe3\x02\xa0j\x94\xef\x1f6_\xff\xff\xe3\xe01@\x89\xa6м\xb6D\x14\x8f\x18p\x03\f\xbeҲ@\a\xf2\x83=0\v\x1ak\x8d\x06\xa55`\x0f\b\x05\xabm\xa3\x11\xd4\x0e~n\xb6\xa8%Z4-h\x80B4Ƣ\x06c\x99E`\x16\x18ԊK\v\\\x82\xe5\x15\u009f\xde?l@m\u007f\xc3\xc2\x1a`\xb2\x04f\x8c*8\xb3X\xc2Q\x89\xa6B?\xf6\xcf\xeb\x16j\xadU\x8d\xda\xf2Hg\xdfz\\\xd5{:Z\xde;G\x01\xdf\vJ\xc7N\xe8\x97\x11\xa8\x88e \x9a[\x8f=p\xd3-\x978d\x00\x18\\'&\x03\xf2kxD\xed\xc0\x809\xa8F\x94\x8e\v\x8f\xa8\x1d\xc1\n\xb5\x97\xfc\xbfZ\xd8\x06\xac\xa2I\x05\xb3\x18\x18\xa0k\\ZԒ\t82\xd1\xe0\r\x91\xa4b'\xd0\xe8f\x81F\xf6\xe0Q\x17\xb3\x86\u007fU\x1a\x81˝\xba\x83\x83\xb5\xb5\xb9\xbb\xbd\xdds\x1b\xa5\xa9PU\xd5HnO\xb7$\x18|\xdbX\xa5\xcdm\x89G\x14\xb7\x86\xefWL\x17\an\xb1p\x1by\xcbj\xbe\"\xd4%IԺ*\xff_d\x00\xf3n\x80\xab=9f4Vs\xb9\xef\xbd \xae\x9f\xd9\x01'\x00\x9e\xbf\xfcP\xbf\x8a\x8e\xd0\ue463\xce珏_\xfa\xbc\xc7͘\xfaD\xf7\x1eCv[\xe0\b\xc6\xe5\x0e\xb5\xdfĝV\x15\xc1DYz\xee#\xd6\x15\x1c\xe5\x98\xfc\xa6\xd9Vܺ}\xffk\x83\xc61\xb9Z\xc3=\xa9\x18\xd8\"4u\xe98s\r\x1b\t\xf7\xacBq\xcf\f\xbe\xf9\x068J\x9b\x95#l\xde\x16\xf4\xb5㸳\xa7Z\xefE\xd4e\x13\xfb\xe5\x15\xc2c\x8d\xc5@`\xdc(\xbe\xe3\x05\x89\x05\xec\x94\xee\xf4\x85WW\xeb\x01ȴ\xc8\xfa\xc9v\xac\x11\xf6+\x89\xba\xf9\xa2>\xa3\xb1\xbc\x18\xf7\x1a!\xf5!9(\"\x85\x06\x9e\x0fh\x0f\xa8\x1d\xff\xd0\v\x12\xc93\x98@[j\xb0$\x89dO\b,`O\xa2-\x04\xd4*j!\x03\xdbSDv}\x06\xc9\xd3v\xab\x94@6\xd6\x12\xf8\xad\x10M\x89e\xab\xb6\xcfh0Z\xdddz\x01d\xd2\x18\x97Nj\x9c\x11q\xe8\xc9\xee\xadS̉\xc51\x8d\xe0\xf8\x96K\x0f\x8ft\xee\x01\x93\x1b\xe4\x1a\xb7X%p\x9bd3ߜ\xa9d[\x81w`u\x83\x13\x94aZ\xb3\xd3\x04]\xa2y\xcf%K\xdb?h\x11\xc1\v\xb2?\xad\xae \xcaxk\xc5\xf49F\xf0G&\xcaA\xa9\xa7%B\xfc\x8b\xeb\xd3\xe9=(\xc8K\x82-\x1eؑ+\x1d\x96\x1e\xcc\xd0\x16\x01\xbfa\xd1XL\xf1?\xb3P\xf2\xdd\x0e\xb5\x83S\x1f\x98A\xe3M\xdf4A\xa6E\xd95=\xbd\x99g\xeb\xe86\xd2q*\xad|\nu'\xd0c\xb9\x8a\xcd!ꌆs[dɏ\xbcl\x98\x00.\x8de\xb2\xf0\xeba-^\xe7끹M>\xc3٫È\xb9ۉ\x81jT\x12Ai\xa8\x9c=8\xef:\xb6`]\x9bZ\xf6\x969\xed\xa4<\x8b\xeaF\xa0\tS\x95\xa4s;\x1dp3\t\xba\xdd\x11\xefK\b\xb6E\x01\x06\x05\x16V\xe949\x966ٷ\x1c\xbd6Ań\x86\xebt\xb7[j\xb7\xb0\x19\x90\xe0\xd4\xf6\xf3\x81\x17\ao\xe6\x1d\a\x11\x1c(\x15\x1a\x92rV\xd7\xe24\xb5HX\xda\xf90ɜ\xa0wmA\xe4\xc7\xf0R\xc2ߵ\f\xddص\x05-9\xa4l\xcb\x0e`\xd5\xec\xb2\xffo\x126\xaa\xfd\x170\xed\xe6l\xe8\xeb2\xad#)w\xee\xfcf\aX\xd5\xf6t\x03\xdcƧK\x10\x9d\xb3\xd2\xcd\xff\x0f\xbc1\x97s\xfcf<\xf2U9~vW\x96 \xba]i\xa7\xff\a\xdc\x142\x16\x8f\xc1Vdo\xc8/\xfdQ7\xc0w톔7\xb0\xe3¢\x1e\xed\xcc\uf497\xd7 F\x8e\xbds\xadb\xb68|\xfc\xe6\xa5q\x03\f\x9e\xf0\xe4=\x16&\xc1m\x0es\x13MD:\xe7ġ\xdc\n1\xd9\x13\x9e\bLH\x96,\x8e\xcee\x05ߞ\xf0\x94\xd3mD@\x87\x137!\t\xe4(\xe9\x1e\x10!(\xb6\xce'\x1eP\xe2+\xea\xa2\xe5\xc5A\xbe\"\x89-\xd2\xfe\x05\xcbl\xb7\xad\x974\xa4\x8d}g\xfc\x169)8\xf0:s\xa1\xcéA\x92\x96\x98\xfa\xfa\xca\x04/ۉ<\xdfo\xe4\xb47\xbd\xf6\a=4\t+ȹX\x84[kU\xa01\xf3,\x92\xa1\xad\x17\x92\x84m\x82\x90\xf9\x00\xc6\x1f\x98\xcc'%c\xcbwH\x1d\x91.t\xe5?~\xebe/\x9d\xf0\xbb\xdfK\xccw)^@2[Ul|2\x98\x85\xe2\xbd\x1f\x19\xc5$\x00\xf2\xa1\x81\xde7$\xea\xf9\x1ed`\xa4?\x82\x99\xae\xb8\xdc\xd0\x04\xf0ë\x9b\xf5VI\xe2K\x1c\xf7\xfb8\xb6#z\xfb\x80\xa47\xd7#R\x94\xb9\xd78ع\xf3<\xb7s\x143AJe\xfb\xe9\x04\a\xb7V\xe5;\x03;\xae\x8d\xed#\x9a\xcb\x14͂\xf4w\xed\xd2\xc8I~\xd4\xfaE\x81ӯ~d/\x91uP\xcf\xf1|u\xf203\xd5\xe8P\b\x81\xef\x80[@Y\xa8FR\xfaʼn:M\xe1\xb7\xc0+\xe8l\x92\xe5)\b\xd7P6U\x1e\x01V\xc4u\\\xce\xe6i\xfa\xdd\u007fb\\\xbcŶY^\xa1jf\rg\xd7\x06\xdb\xf6ŏ\x1c\x1c\x94W\xec\x1b\xaf\x9a\nX\xe5H\x9f\x1b\xf6\xec|q\xcc`\xc7\xe1\x99qK\x96\xc3\xc1%3b\x95\x13\xaaZ\xa0͕\xc8-\xee\x94&y6\xbc\xc4\xd60\a.P\x12\x18\xec\x18\x17\x8d\xceԐ\x17\xd1\xf6\x92X#(\x8b\xd7\v\"\xf2&_\x11)2\x12\xb1\x99\xce⼶\xaeu\xbe\xab\xf8\xa01\xcf=[JJG\xf7\xac\xd6\xdc\xf1\x92zm\x0f-\xb0\x18\x93\xa7\xef.\xdaY\xfb\xee\xa2-\xb4\xef.\xdad\xfb\xee\xa2-\xb7\xef.Zh\xdf]\xb4ؾ\xbbh\xdf]\xb4\xb9ns\xdaz\t#_q?\xf1r\x11\x8b\x8c\xe3\xe99\x14g\xe0\x87j\x8a{_}\x9f[a\xb9I\x8fJ\xd4Ն\xb2\xfe\x15}\x91\x90\u202e\xe8\xa23%mɥ\x13\x90\xc8\u07be\x80x\xa1\b3\xab\x9c2]}\x9bS\xf0\xb3T\xe63\xac3m\xcblb\xa1\xa9\x8a\x93$\xe8\x10\xbflpno\xbf\x86dX\xafC~n\xc4\xf4\xef^\x83\x9aQ\x8a\xb3P\x803_\x98;G\xafQ\xe81$\x98\x1e\x14\x8c\xfea\xe8\xb5P%3]\x1b\x13N\x82в\xe3\x0f\xeb\xe1\x1b\xabB\xa5\f\x84\xbbX.\xe7Ï\xbcZ\x9a\x17W\xd0\f+d&T\xf4\xa5GF\xf9\x85\xc2\xf952\xf3E-\x97Tƌ\xeb^&\x81.\xd7\xc3\xe4D\x8e\v\xb5//\xa8xɬv\xfc\xdd\ac95-/\xaadY,\b̬_\x19V\xa6̃\xbc\xa0j%\x8b8\xcb\x15*\x17ץ\x84:\x90\xd9udW\xa3$\xeaLf\x01O֠\xccU\x97,d\xa5\xce+O\xf2kJfAS\xbd\xc9r%\xc9\xebՋ\xbe\x86\x0f<\xadj\x16\xabA\x16}\xe4y\xfc\x16\xeb=.\xa9\xf2X\xa4\xd8\v+:ڊ\x8d\x89y/\xad\xe3\x18\xd6iL\x00ͩޘ\xa8Θ\x808[\xb3\x91[\x931\x01{\xc1\xec\xcer\xc9\xcc\xcb\U00107430h\xdf\xc4ߊ\xa3^\xba0\xa5\a\xee⒇\xfe먻\xdb\xcb\xe85ͻ\x9f)ϓ\xdb\xc3\xe5\xeeg\xd5\b\xcbkA\xe9\xfc#/\x93A\xa3=\xe0\t\x9e\xb9\x10N\xad\xfe\xa6\xe83\xa7\xed\x89 \xfd\xfa\xb9e\xcf\xf5ȉf\x06\x9eQ\b`)\xe6:[y\xe1\xbf\xe5-\xd4\n\x9d\xcew\x02\x17>\xf9\f\x9f\xfc\xdex\x0e\xa6/\xb9R\x19O{\xc0\xcaA\x89ߎ^\x10~\xcc;\x88ޗ\xa5g\u007fmP\x9f@\x1dQw\x1e\xc3\xc2w\x04^\xd0L#\xba\u00ad\xa0?\xfc\x17\xe4#ǹ\x138x/\xbd\tK\x82\x1d\xe1Hp\x9c̋v\xaf\x9dzsq\xc0D\xd7t\xe2C\xb5\xa3\x13\xef\x97|\xcf\xdc\"\xfc\xb7\r\x1d.\x0f\x1e\x16\xcd\xf6\x9b\x04\x10/\x0f!f@\xe6\x16\xd5\xe7\x1d@-\x16ѿU(\xb1\x14Ld{QyE\xf2oQ\x1c\u007fAQ\xfc\x05A\xc5eaE6\x99r\x8a\xdf\xdf$\xb8x\xc3\xf0\xe2-\x02\x8c\x97\x85\x18\v GE\xed9\xe5\xeaY\x87\xab\xd9\xe7\v9\x87\xa3\xcbG\x00\xf3e\xe8\x19\xe5\xe7\x19\x87\x03K\x98f\x94\x99_V^\x9eA\xc37\n>\xde(\xfcx\x8b\x00\xe4mC\x90\xc5 d\x91sf_\xbf8\xbb\xact\x89z6\x19\x9f\xcbj\xb3L6\x8a\x17\x86s\x8e\xbe\xa8\x8dw\xa4\xb8^\x03\xd74\x95Rn\xbf\xfe,\xe0g.K\xbf\x1f\x8e\xa9zv\x9c\xee\x04\xa2\xfa\xf7֩\xe8\xfc\xb34\xd0ѡ\x82\xc1\x9ai\xba4j{\xf2\a\x93f\r\x1fYq\x18v\x84\x033\xb0S\xbaJ:L\xd7\xed\x89\xccm\x1c\xe5\x9e\\\xaf\x01~R\xed\xa1W\xffF\x05ëZ\x9c\\\x1c\x00\xd7\xc3!/c\x80$\xf3\x18\xc9jsP\U0007a6c5X\xefq\xd8;qx\x17/\xbb)\x84j\xca\x16\xfa\xc4\xe61y\x82\x87\xaf\xe4\x93\xd05!EweJ\xf0:b\xcc7\xbeQ\xe5\xc7\xd7?\xcc3Vi\xb6\xc7_\x94\xbfwh\x89\x12\xc3ރK\xa7\x82\xae\x88\x87\xeb\xf1ۋ\x94\r\r7 \x8d\x80u53A\x1a\xbasN\x87eJ\x89\xccȟ\xb5ba1_\xbe\xfc\xe2\x17`y\x85\xeb\x0f\x8d?8]\xd5L\x1btԌ\v\xf3\x83\xb6\xee\xbf\a\xf5\x9c\xcam\xa8\xb0\xe6\x1f\xc7xk\xa4\xba\x1c:\x9f\xbd\b{\u007fCRd\xbcH\xa2%F\xfd\x9a\x1e\xd5\v\xccz\x9b\xe4\xa5<\x19\x90O\xc1\xe9]$G)\v\xfa\xae\xe6u\xaf\xf9\x99\xd2\xdaSWmYf\x1b\xb3|\xd9\x16u\x8bW\xeb\x85\xea\xaeF\xd3\x1d=\x1e\x84\xbf\xd3\xe6E\xf7m\x85b\x94\xc1u\x87\xf3\xfbt\u007f>\x82.\xb5ӥG\x8d\xca`ڋ\xb3\x9e\x99i\v^\x92\x86\xb4\x03\xe7G\x92'\xeb\xa0a\txD\tJR}\v\xdd~\xe3/^\x1c\x8fI@\xedC\t\x054M-\x14+\xa3\x84G\x9b\x15.\xeb\xfbB\xfaK\x1fQ\xbf330\xe9r\xb0\x9d\xd2)\"\x9c+LoX\xee\xa0d\x16WI\xa0Y\xba/\xc9l\x85\xe1CF7\xef\xaduqA\xcaW\x1e\xee\xdf\xe3fjd\xb4\xbfVY&@6\xd5\xd6\x1bt\x16;\xa4\xf6\xefq3\x129\x13*\x9ef\xc4\xcb/\x8cK\x8b\xfb\xb3\x9c\xe2\xf9\xca\xee#\xff\\\xbc\xb2v\xe4\xd4\xcaLS\x14ḫ\x11\"\xe5ڷ\x9c\xfb\xfaˤZ\xbe\xc5;Ψ\x93W\x81T\b\x18/\xa2\xf3\x95\x80\x15\x1a\xc3\xf6\xf1r\xb3gg\x81\xf6(\x91\x1c\x9fT\xbe\xd1\a\x86]\xe5\xd8\xf0j/\x9f\xc1b\x85mX\x98 \x9e\xfc\xf7z\xbdK\xf9\x05B\xeda\xc7\x05u\r\xd70\x06\xd3|!M\xbe\xd5\\\xe7\x98\xf2\x8fmGG\x1bJ>\xd3Ftו\xa2\xe0{\xee\xec\xa0ۤ=\xd3[\xb6\xc7U\xa1\x84@*3?\xc7\xeb-\x855\xd4\xe7}Ff\x16\x97\xf6S\xbfo\xc8t\xf8\xdd\xf67c0\u007fA!\xdd^i\xb9\xc6\xee:\xd83\x84\x14M|\x91\xe9\xf6TH^\x9cz\x8ei\xbfo\x14\xb0\xa0W=\x9cx\x8f\xeaMp\x06\xd3\xd1l\xc5~S\xfa\x06*.\xdd?\xce\xe3\xa7TD\x1c|\x11\xfetg\xdd\x02\xde\x0f\xaeO[&\xdd3\xa4\x18\x05b\xcaUM\x97Ʈ\xe0\x13\x9e{V\xbe\xda\x15KJ\xbe\xa5n\x8bu]6\xf2A\xab\xbd\x8b\x87\x13/[\xe5\x95x\xf7\xc0\xb4\xe5L\x88\x93\x9fdr\xf6ċ\x0f\xe8\fפ\xf7\x92&k\xc0r\x89\xb2\xa1[\x17zs\xe99\x81\xeaT\xb7\xaa\xb1\x03Uҩ\xa2tڟ\x80\xadᓲ\x183\xba|\b\xd3)_4v\x85\xbb\x9d\xd2\xd6G\xfa\xab\x15\xf0]\xf0\x86\x12p\x9dLЉ\x94\xbf\xbc\x15\xb8\xed\x0e\xe5;\xee\xa5@G\x93\x10\xd2\rO\x15;\xf9\x9aEV\x14\xce\xd9\xc6[c\x99H\xe8\xb7\xdfU\x03En\xa7\xe3>,\xff-ᇝ\x11|\xd3\xef\xdf~8\xdeZ7\x02\xe7)G5\xe5^\xb7'-\x1dP\xa51Jx\xd6\xdcZ\xa7O\xfbGv`\x9d\x06\x15\x02\x8c\xd3)\x13\xd7\x04\xceivz\xefl\xeff:\x858\x8co\xda\xceS\xa6;,N\xb9m\xd9\x12\t&\x96\xe5\xbfY\xe2&\x8eu[Y\x1c\x98\xdc;\xa6Ҫ\xd9\x1f\"_NXƩ\f\\㐂Z4{\xc7\xea\xe1\xb8\xc46Z\xf6R0\xe1\x00\xa5\xec\xa1ˊ\xa7ILCJ8^ ~\x1b.\xfe[\xed\xb4\xaaVa/\xe8\x94\xe3&\xa4F4W\xce\xffw\x81\xfc\x04\xd0\xee\x86-b\x83\xbaF\t\xcc\x04|2>\xa8\x9a\xdfֹ<\x85e\xda\xe6F\x15\x8f\x83\xce\v\x01\x05AN\xe3\xfb\x18\x12?\xfeò\xfb\xf1U\xee7`\xb8\x8cw\x97\xfbĒg\x05\xe3\xe2\f\x8d\x14\xab'\x0f\xb0\xce\"\x84A<0D\xffo\x1b\n\x1c[\v\xf31ǧ\xfc:\xea>\xaa\xceuR\xdeA\f~`\x82\x1e\u007f\xe2;\u007f\xa6V8\xac\xff\xfcw\xaf\xba=f\xf9,\xeff\xdd\x15\xf2DZ\xbf\x03>`\xad\xb1`\xc9\xc0\x03\xe0A\xa0\xf3#\f\xe2\xd0\x13zw\x91\xcb{|Y\x10\xf7\x9a\x11\\\xbcV\xffu\xe2\x9a\xe3\xcbb\xb77\v\xdc^wu\xcfLK.\xf7K2\xf6\xef\xa1[\"r\v\x10\x12\xb1[b\x19m4\xb7\x18\xbb\xf5B\xb7\x88\xe3\xc4mףp\ue542\xb7\xa4\x1d8{H\n\xb4\xec\xc9v\x98)<\xe9\x12b\xac(б\xeb\xa7\xf1\x9fϸ\xbe\xa6\x1f\xf1/d\xd0\xcfBIon\xcd\x1d\xfc\xc7\u007f^Aȸ~\x8d\u007f\n\xc3=\xfc\xdf\x00\x00\x00\xff\xff\xa6\x16s\x9fjd\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKo#\xb9\x11\xbe\xebW\x14f\x0f\xbe\x8cZ\xb3\xc9!\x81.\x81F\x93\x00\x83x\xd6\xc6\xc8q\x0eI\x80\xa5Ȓ\xc45\x9b\xec\xf0!\xad\x12\xe4\xbf\aŇ\xba\xd5ݲ\xe4A\xb2ˋ->\x8aU_\xbdٓ\xe9t:a\x8d|F\xeb\xa4\xd1s`\x8dğ=j\xfa媗\u07fbJ\x9a\xd9\xfe\xfbɋ\xd4b\x0e\xcb༩\xbf\xa23\xc1r\xfc\x84\x1b\xa9\xa5\x97FOj\xf4L0\xcf\xe6\x13\x00\xa6\xb5\xf1\x8c\xa6\x1d\xfd\x04\xe0F{k\x94B;ݢ\xae^\xc2\x1a\xd7A*\x816\x12/W\xef?T\xbf\xab>L\x00\xb8\xc5x\xfcI\xd6\xe8<\xab\x9b9\xe8\xa0\xd4\x04@\xb3\x1a\xe7\xb0f\xfc%4\xce\x1b˶\xa8\fOwU{ThM%\xcd\xc45\xc8\xe9\xea\xad5\xa1\x99C\xbb\x90(d\xb6\x92H\x1f#\xb1U\"v\x9f\x89\xc5u%\x9d\xff\xf3\xe5=\xf7\xd2\xf9\xb8\xafQ\xc12u\x89\xad\xb8\xc5\xed\x8c\xf5?\xb4WOa\xedTZ\x91z\x1b\x14\xb3\x17\x8eO\x00\x1c7\r\xce!\x9en\x18G1\x01ȘEjS`BD-0\xf5h\xa5\xf6h\x97F\x85Z\x9f\xee\x12踕\x8d\x8f('Y \v\x03E\x1ap\x9e\xf9\xe0\xc0\x05\xbe\x03\xe6`\xb1gR\xb1\xb5\xc2\xd9_4+\xffGz\x00?9\xa3\x1f\x99\xdf͡J\xa7\xaaf\xc7\\YM:z\xec\xcc\xf8#\t༕z;\xc6\xd2=s\xfe\x99))NZ\a\xe9\xc0\xef\x10\x14s\x1e\x8c\xb6e,\x1e?\x97ț\x1c(\xfb[ƪ\x82E\xf6\\\xb3\x81\x0f \xa4\xa3\x02\xc0E\xa2C\xb0\xa8<\xa3\xf59x\x1b\xde$>7z#\xb7C\xa1\xbb5\xcd%\x8b\xb9B\xba\x87\xdc2\xdeD\xa1\x89\xac\xa3\xb1f/\x05\xda)\xf9\x87\xdcH\x9e9\t6e\xae\x8dD%\xdcP\xd2\v^\x16E\xb1(ȫ\x99\xba\xa2\xc3\xe5ic,\x8d\x99\xd4ɂ[\x021\xd8\xd8:\xa7T\xedQ\x8bS5rƍ\x89Qˡ\x80\x83\xf4\xbb\x14\x0e\u0558\xdf\xc1\xab\xbeG\xe3\x05\x8fc\xd3=ޟvH;S\x02Ep\xc8-\xfahm\xa8\xc8|Ȕ*\x80/\xc1ŀڏ\x13e\xc4B\xad\x9c~\xc1\xe3\x10h\xb8\xa6\xdc\\\xc2\\g\xf9\x8eJ\xe7°\xc5\rZ\xd4~4\xa8Sgb5z\x8cq]\x18\xee(\xa4sl\xbc\x9b\x99=ڽ\xc4\xc3\xec`\xec\x8b\xd4\xdb)\x01>\xcd\x1e4\x8bm\xc5\xec\xbb\xf8\xe7\x82\xc8O\x0f\x9f\x1e\xe6\xb0\x10\x02\x8cߡ%\xadm\x82*\x86֩o\xde\xc7\x1c\xfb\x1e\x82\x14\u007f\xb8\xfb\x16\\L\x93<\xe7\x06lV\xd1\xfa\x8fT\xa8E\xa6\b\xa2UҊ\xb1@\x99\x92\x94]gm\xa6X3f\x88c\x15fwP`\xa2\f2\x16Q_p\x18L_q\xb3\\\xec^\xf1\xb1RHK-$\xa7B\xec\xdc7J\x83!\xce\xea\xed\x11\xc1\xfa\x15\xf8\xa5\x880.x\x12 \xe7\xc3+\x1c?t\xf7\xb6mY\nO9\xc79\xf4T@9\xd0H9\x90\xd9!r1(p\xa35y\xa37\xc0N\xa1\xee\xce\xf5c\xfc\x1b#\xc4:\xf0\x17\x1c\x01~ \xcaǸ\xb1`\x9c\x8e\x11/\xc1a\f\xbe\xd7\u0600\xeb6\xce\xd9\x12\xed-\xbc,\x17\xb4\xf1\x94&\x19,\x17\xb0\x0eZ(,\x1c\x1dv\xa8\xa9C\x90\x9b\xe3\xf8]4\x9e\xeeW\x05\xd5Xa\xe4\x1a\xbf`;.C\x8a\xe1sX\x1fGj\x82\x1b\x84l,n\xe4\xcf7\b\xf9\x187\x16\xc0\x1b\xe6w \xb5\x93\x02\x81\x8d\xc0\x9f\x8a\xb5\v\x82\x9e\xf2\xffC\x8e\"ߠ\x9e\u05fc=\xb1\xf3\x16\x87/\x18_\xf1\x9fǼ\xed\x84B\xf9\x9d#\xffy-xɏG%ڟ\x1e\f\xfe\x94*,>\x92*Ϙy\x1e\x9ex\xa5R+\xcf\x16c\xceLu\x81\xb1\x16]c\xb4\xa0\xe6\xe9\xb6:\xade\xf9\u007fW\xad\x8d\xabuz\x1e\xe5zkE\v7\xb5*\xf1\x89\xe6\xcd\xcdJz\xb8\xea\xb6\x02f\xed\xa8Sl\xfb\x95\x9e\x8c\xbfH\x9b\xf2\xaeӧP?\xac!\xe8X\xa9Ō_\xc1\xdf5|\xa2ޖ\xb2\x93\x98\x13\xdfv\xcc\x00\xa4\x03m\x0et\xbcC/\x92\x00\xa3S\xbe\xa6n\x8di\x91\x9b\xe1\xb8t\x90JQƶX\x9b\xfdhƦBӢ:\x02sd:\xfb\xdfT\x1f\xaaw\xbfZ\x17\xa4\x98\xf3\xd4Ԡ\xf8\x8a{9|\xe5\x19\xa2{?8Q\x1c\xff\xe4\x0e\xf4\xe3\xc7\xd2,\xcfl\xde\xf6\xe3\b\x18\x1b\xa9\xa8\x16\x1c\x89\x13m\xc50|\x8f\xfc\xb8\xba\xbfs\xb1\x84G\xed\xc7ʾ\x03Z\x8c\x1d\x13\n\xaa\xe2M~\x97\bΣ\x1d1\x80\x93\xf6\xa2\xceA\x19\xbd\xed9N\x1a\xf9\x95\x82*\xb4dPƂ@O\xa9Io\x81\xef\x98\xdeb\xfb\n\x95\xf9\u007f\x9dS2\x9f\x9eʹ\x16\"\xf5%\xf3\xb8I\xa3Or\xacL\x1f\xbc\x00\xb7\x9b\xc7_\u007f\v\xf7E\xb3\x17ۜ+\xb8\x0f\xf6\x97,M\xa0N}\xfb\"\u070eooo\x87\xcf\xcd7 \xf1ַ\xf0W\xde5\xe0\xc0\\\xfb*\xfe\xeb\xe1PS\xb5z\xb5\x04\xfe\x92v\xa5\xe7\xc3|\x04\xd8\xda\x04\xff\x9agލ\x19t~\xee\u007f\v\x8f\xf1#Ƶ\"\x83\xf6\x14\x8d\xf0`\xa9\x95l_\xc5bP\x18\xcb-\xb7?/-z\xdfZ\xbak\xc3/17\xc85\x9ak\a\x93)_v\xf4\x9aA\xee΄\xf5\xe9\xa5x\x0e\xff\xfeϤMה\x13\x1b\x8f\xe2\x87\xfeǵw)d\x94/d\xf1'\xa7:&}\x1d\x84\xbf\xfdc\x92\xaeB\xf1\\>i\xd1\xe4\u007f\x03\x00\x00\xff\xff\x1d\r\x93\v\x97\x1c\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4\x96Ms\xe36\x0f\xc7\xef\xfa\x14\x98}\x0e{y$\xefN\x0f\xed\xe8\xd6\xcd\xee!\xd36\xe3I2\xb9tz\xa0I\xd8\xe2F\"Y\x00t\xeav\xfa\xdd;$%\xbf\xc8v6=\x947\x91 \xf0\xe7\x0f\x04Ī\xae\xebJ\x05\xfb\x84\xc4ֻ\x16T\xb0\xf8\x87\xa0K_\xdc<\xff\xc0\x8d\xf5\x8b\xed\xc7\xea\xd9:\xd3\xc2Md\xf1\xc3=\xb2\x8f\xa4\xf13\xae\xad\xb3b\xbd\xab\x06\x14e\x94\xa8\xb6\x02P\xceyQi\x9a\xd3'\x80\xf6N\xc8\xf7=R\xbdA\xd7<\xc7\x15\xae\xa2\xed\rRv>\x85\xde~h\xbeo>T\x00\x9a0o\u007f\xb4\x03\xb2\xa8!\xb4\xe0b\xdfW\x00N\r\u0602\xc1\x1e\x05WJ?\xc7@\xf8{D\x16n\xb6\xd8#\xf9\xc6\xfa\x8a\x03\xea\x14xC>\x86\x16\x0e\ve\xff(\xaa\x1c\xe8sv\xf5)\xbb\xba/\xae\xf2joY~\xbaf\xf1\xb3\x1d\xadB\x1fI\xf5\x97\x05e\x03\xb6n\x13{E\x17M*\x00\xd6>`\vwIVP\x1aM\x050\xf2\xc82kP\xc6dª_\x92u\x82t\xe3\xfb8Ldk0Țl\x90L\xf0\xb1\xc3|D\xf0k\x90\x0e\xa1\x84\x03\xf1\xb0\xc2Q\x81\xc9\xfb\x00\xbe\xb2wK%]\vM\xe2\xd5\x14\xd3$d4(\xa8?ͧe\x97\x04\xb3\x90u\x9bk\x12X\x94D\x9eD\xe4\xb8\xd6;\xa0#\xbe\xa7\x02\xb2}\x13:ŧ\xd1\x1f\xf2µ\xc8\xc5f\xfb\xb1\x90\xd6\x1d\x0e\xaa\x1dm}@\xf7\xe3\xf2\xf6黇\x93i8\xd5z!\xb5`\x19Ԥ4\x81+\xd4\xc0;\x04O0x\x9a\xa8r\xb3w\x1a\xc8\a$\xb1\xd3\xd5*㨪\x8efg\x12\xde'\x95\xc5\nL*'\xe4\fm\xbc\x04hƃ\x15\x98\x96\x810\x102\xbaR`'\x8e!\x19)\a~\xf5\x15\xb54\xf0\x80\x94\xdc\x00w>\xf6&U\xe1\x16I\x80P\xfb\x8d\xb3\u007f\xee}s:g\n\xda+9\xe4g\x1a\xf9\xd29\xd5\xc3V\xf5\x11\xff\x0f\xca\x19\x18\xd4\x0e\bS\x14\x88\xee\xc8_6\xe1\x06~I\x98\xac[\xfb\x16:\x91\xc0\xedb\xb1\xb12u\x13\xed\x87!:+\xbbEn\fv\x15\xc5\x13/\fn\xb1_\xb0\xddԊtg\x05\xb5D\u0085\n\xb6\xce\xd2]\xee(\xcd`\xfeGc\xff\xe1\xf7'Z\xcf.H\x19\xb9\xd0_\xc9@*\xf3\x92\xf6\xb2\xb5\x9c\xe2\x00:M%:\xf7_\x1e\x1ea\n\x9d\x931\xa7\x9f\xb9\x1f6\xf2!\x05\t\x98uk\xa4\x92\xc45\xf9!\xfbDg\x82\xb7N\xf2\x87\xee-\xba9~\x8e\xab\xc1\nOW2媁\x9b\xdcbSQ\xc7`\x94\xa0i\xe0\xd6\xc1\x8d\x1a\xb0\xbfQ\x8c\xffy\x02\x12i\xae\x13ط\xa5\xe0\xf8\xef07.Ԏ\x16\xa6\xf6}%_\x17\x8a\xf6!\xa0N\x19L\x10\xd3n\xbb\xb6:\x97\a\xac=\xc1Kgu7\x15\xed\x8c\xee\xbe\xc0\x9b\x93\x85\xcb\x05\x9dơM\xceW\xae\x1e\x1er\xee,\xe1\xec\x16\xd6p\xd6s_璛\xe1\xbf$S:\xf1\xc8FG\"trԟեMoe\x81D\x9e\xcefg\xa2\xbed\xa3\xfc\x04P\xd61(\xb7\x1b7\x82tJ\xe0\x05)\x95\x81\xf61\xf5\x194`\xe2\x19\xbf\x11\xcb\xf1\xbf$\x90\xd7\xc8ܜ\xd9Y\xc1ႦW\xb2\x93Fz^\xa8U\x8f-\bE\xbc\x92YE\xa4v\xb3\xb5\xfc\xcf\xfa\x06\x82e\xb2\xb9\x94\x83\xfd\u007f\xfa\x9bIȸ]\x1c\xce#\xd5p\x87/\x17foݒ\xfc\x86\x90\xe7W>-.\v\xbd\xfdc\xe0\r\x94.^ʳIN\xfd\xce\x1cQd\xf1\xa46\xc7\\9\xae\xf6\xfd\xbb\x85\xbf\xfe\xae\x0e\xf7Zi\x8dA\xd0\xdc\xcd_i\xefޝ<\xb7\xf2\xa7\xf6\xae\xbc\x8c\xb8\x85_\u007f\xabJ(4O\xd3\xeb)M\xfe\x13\x00\x00\xff\xff--\nM\xde\n\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn4\xfa\xf150\x9b\xcf\xe73\xa6\xc5#\x1a+\x94\\\x02\xd3\x02\xbf8\x94\xf4\xcb\x16O\u007f\xb5\x85P\x8b\xdd\xf7\xb3'!\xf9\x12\xae[\xebT\xf3\x80V\xb5\xa6\xc4\x1b\xdc\b)\x9cPr֠c\x9c9\xb6\x9c\x010)\x95c\xd4l\xe9'@\xa9\xa43\xaa\xae\xd1\xcc+\x94\xc5S\xbb\xc6u+j\x8e\xc6\vOK\xef\xbe+\xfeR|7\x03(\r\xfa\xe9\x1fE\x83ֱF/A\xb6u=\x03\x90\xac\xc1%h\xc5w\xaan\x1b\\\xb3\xf2\xa9ն\xd8a\x8dF\x15Bͬƒ\x16\xad\x8cj\xf5\x12\x8e\x1dan\x04\x146s\xaf\xf8\xa3\x17\xf3ދ\xf1=\xb5\xb0\ue7f9ޟ\x84u~\x84\xae[\xc3\xea)\b\xdfi\x85\xacښ\x99I\xf7\f\xc0\x96J\xe3\x12\xee\b\x86f%\xf2\x19@ܻ\x875\aƹ\xd7&\xab\uf350\x0e\xcd5IHZ\x9c\x03G[\x1a\xa1\x9d\xd7ֽ\xe2\x10\x00B@\b\xd61\xd7Z\xb0m\xb9\x05f\xe1\x0e\xf7\x8b[yoTe\xd0\x06x\x00\xbfX%\xef\x99\xdb.\xa1\b\xc3\v\xbde\x16coP\xef\xcaw\xc4&w \xd0\xd6\x19!\xab\x1c\f:#\xd8oQ\x82\xdb\n\va\xb7\xb0g\x96\xe0\x18\xe7w\x99_\xd8\xf7wG<@pM\x06\xd0M\r\x108s\x98\x03\xd0\xe9\x13\xd4\x06\xdc\x16I\xf3\xde☐BV\xbe)\x9c\x048\x05k\xf4\x10\x91C\xab3\xc84\x96\x85V\xbc\x90I\xe8\x00\xd6ݨ\xf5\x92nh\xfc\xff\x1a\xd5\x00н⯀\xf2\xa2u\xc3\xe0\xc1\xaa\x8f\xfd\xa6K\v?\xa0u\xa2\x04\x83ZY\xe1\x949\x80\xe0(\x9d\xd8\b4\xb0Q\xa6o6' \xd0\xdc\xdbn\xd2\x00J\x94\xfe\x80Z\xbdB\x11\xd1oVN\x19V!\xfc\xa4J\x1fvȜ\r\x0e\xec\xd9nU[sX\xa7]\x03X\xa7Lָ\tq\x98\x15\xe5&\xb1#\x1f\x1b\xaey\x1a}Ov\n\xb2\xc5$@\x0ed\xbf\xab0\xef9\xa1{\xf7}\bU\xe5\x16\x1b\xb6\x8c#\x95F\xf9\xee\xfe\xf6\xf1\x8f\xabA3\x806J\xa3q\"\x85\xce\xf0\xf52F\xaf\x15\x86\xaa\xbe\"\x81a\x14pJ\x15h\x83\xfd\x856\xe4\x11C8\x0ea\xc9H\fZ\x94\xae\xaf\x92\xf4\xa9\r0\tj\xfd\v\x96\xae\x80\x15\x1a\x12\x93\x0e\xa6Tr\x87Ɓ\xc1RUR\xfc\xa7\x93m\xc9\xcciњ9\x8c\x11\xfc\xf8\xf9 +Y\r;V\xb7\xf8\x16\x98\xe4а\x03\x18\xa4U\xa0\x95=y~\x88-\xe0ge\x10\x84ܨ%l\x9d\xd3v\xb9XT¥LY\xaa\xa6i\xa5p\x87\x85Ozb\xdd:e\xec\x82\xe3\x0e\xeb\x85\x15՜\x99r+\x1c\x96\xae5\xb8`Z\xcc=t\xe9\xb3e\xd1\xf0oḼ\xf6j\x80ub\x18\xe1\xf3\x89\xec\xcc\tP*\x03a\x81ũa\x17GE\xa7P\xf4\xf0\xb7\xd5GHK\xfb\xc3\x18k\xdf\xeb\xfd8\xd1\x1e\x8f\x80\x14&\xe4\x06\xa3+o\x8cj\xbcL\x94\\+!\x9d\xffQ\xd6\x02\xe5X\xfd\xb6]7\xc2ѹ\xff\xbbE\xeb\xe8\xac\n\xb8\xf6\xf4\x81BS\xab\xc9ry\x01\xb7\x12\xaeY\x83\xf55\xb3\xf8\xd5\x0f\x804m\xe7\xa4\xd8\xe7\x1dA\x9f\xf9\x8c\a\a\xad\xf5:\x12=9q^#α\xd2X\xd2\xe9\x91\x02i\xa6؈\x18\xa1(p\xb2\xf1\xf0b 8\xef\xb8\xf4e\xa3\xd3x\xd0\b\xd9\xfbܜ\x84M\xf6bj\n\x98a\xe4D(@=\x8e\xb2d\x8e\x93\x1cac\x80-&\x12N\x1c\x03}Rq\xbc\xb0\x8f;\xc51\a\x9b\xa6\x82۲`\xadĭ(\x1e\xb5RNW\xa1O\xc9\x17\x01ӊ_\xc0\x15Wd`p\x83\x06e\x89)p\x9d#\x0e\x19d\xfd\x94>\xc5x\xda(\xe0LT\xcf\"~w\u007f\x9b\"yRb\xc4\xee\xa6\xeb^\xd0\x0f}\x1b\x815\xf7\x89\xee\xf2\xdaW\xb7\x9b\xb0\x98\x8fiN\x01\x03-0P\xc0.I\x80\x90\xd6!\xe3\xa06Y\x89T\xa8\x009\xbe\xc18\xe3m\x88`1T\x1eS\v\xe9\x1e\x18\xc5N\xc1\xe1\x1f\xab\x0fw\x8b\xbf\xe7T\xdf\xed\x02XY\xa2\xf5\f\xd8a\x83ҽ\xedH9G+\fr\xa2\xd8X4L\x8a\rZW\xc45\xd0\xd8O?|\xcek\x0f\xe0Ge\x00\xbf\xb0F\xd7\xf8\x16D\xd0x\x17\x96\x93\xd1\b\x1b\xd4\xd1I\x84\xbdp[1N\xa6\x9d\x06ȼ\xe2\xb6\xf7~\xbb\x8e=!\xa8\xb8\xdd\x16\xa1\x16O\xb8\x847\x9e\xd6\x1ca\xfeJ\xbe\xf3ۛ\x13R\xff\x10\\\xfb\r\rz\x13\xc0uy\xb8\xeftG\x90\xc1\xf3\x8c\xa8*<\xb2\xaa\xf1\xe7\x93\n\x85\xeaoA\x19ҀT=\x11^0\x9d^\b\x94\xc8'\xa0?\xfd\xf0\xf9$⡾@H\x8e_\xe0\a\x10\xb1\xacъ\u007f[\xc0Go\x1d\a\xe9\xd8\x17Z\xa9\xdc*\x8b\xa74\xabd}\xa0=o\xd9\x0e\xc1**\x92\xb0\xae\xe7\x81\aqس\x03i!\x1d\x1c\xd9\x1b\x03͌;k\xad\x89\xfd|\xfcp\xf3a\x19\x90\x91AU>\x12S\xd6\xdc\bb3DcB.\xf6\xd68I\xe6\xe9\xb3m0\x1f\xa7\xa0\xdc2Ya\xd8/¦\xa5\xecX\\\xbdƏ\xa7\x94$}\x19j2\x0e\x1c\xff\xb7\xe4\xfe\xcc\xcdy\x06\xfd\x8c\xcd\xf5\xab\x8c\xb3\x9b{j\xd7h$:\xf4\xfb㪴\xb4\xb5\x12\xb5\xb3\v\xb5C\xb3\x13\xb8_\xec\x95y\x12\xb2\x9a\x93i\u0383\r\u0605/Q\x17\xdf\xf8?\xafދ\xaff\x9f\xbb\xa1A\x95\xfd5wE\xeb\xd8ū6\x958\xec\xf3\xf3\xd8\xd5*2\xab\xf1\\r\x8b\xfdV\x94\xdbT\x9c\xc4\x18{\u0099\x041a\x1eB3\x93\x87\xafnʤ\xd0\xd6\x10\xa2\xc3<^\xb0͙\xe4\xf4\xbf\x15\xd6Q\xfb\xab4؊g\xb9\xef\xbfno~\x1f\x03oū|\xf5\x04\x01\x0f6ҿO\xb8@\xcc\x1e\x06\x83\x13u\xcc0\xd6n̋\x98\xa1cU\x86\x8a\xf5/\x02\xcf\x11\xb6\xb3\x1a\x18^ӱ\xca\x023\b\f\x1a\xa6\xe9\xe4\x9e\xf00\x0f)^3A\xf9\x99Rpw\xcf\x01L\xebZdSqL䑄F\xbeO\x856\xab쩽g\xcf!H\xb8\xa0\xffxř\xa1\xec\x11@\xe07\x1dm\xf7\xb7Z9^|\x9a\x14\x9f\xd4\"ե\xc4ֆ\x10\xe7\xf9\x02j4\x86\n\x8aQ\x93V|Ԓ\xbd\xd9J\x9d\x83\x9b\xb7\xb3\xca\f\x17\xaa/\xa8+\xc3Eq\xd4i\x88\".]\x1f\x13\x85~meY*b\xa7ë\xfb\xf3\xc7{=\x9d\xe1/q\f\x0f\xe0\x9ch\xc8f{\xd7\xcaq\x8d\\i\b=qa\xa6\x8f\xdb$\r\xb9\xa7\x8e\xc4l7L\xd4\xc8!\xbd\x1d\x8c\xe7d\xa4\xf6\xa5\xacqCA\xaeյb<\x15d\x11^GϨ^\xf7\xb7#W\xf6\x8c\xcc\xd6\"\xf7\x95|F\tSʶQ\xa6a.\xdc\xe6ͳBe[\xd7l]\xe3\x12\x9ci\xa7\xddg\x82E\x83ֲ\xea\x92+\xfe\x1cF\x85:5N\x01\xb6V\xad\xeb\n\xd5AP\xb8\xb2Ѧ^V+gK\xc0\xa193\xa2\xe86Rպ\xf6s\xfa\x81\xe0\xf8\xe0\xe4Q\xad1\x9f\xea^\x13\x13\x00\xfc\x83\xc9%\x844&\xe7`]\xf4:\xeba\xf4\xa1l\x9b\xe9*s\xb8\xc3}\xa6u\xf2\xd0\xd3\xef\xbcN.\x93\xe9\xfb\xd1{Ë\xf6\x1f\x17\xba\xa4\x828\f\xb6\xaaNά\x1c\xabA\xb6\xcd\x1a\r\xe9a}ph\x87\xe1n\xe6r\xd0\n53\xe4\xe9\xfe&\xfcz\xfcT\xf3\x16\xac\xf0\xd7{ķ\x02\x01\vŷ\xa5\xe4D\xc4R\x19̄L\x98\xa6\x95A\x12\x19\xc2\xff=\xf3G\xd6N&\x8d\x1e9\xefɎW\xc4\xfd\x96v\xdd=\u007f,\xe1\xd7\xdffGb\xc3J*\x19\x90ߍ\x1f\xf1߄+\x9d\xf4*\xef\u007f\x96J\x06\xfel\x97\xf0\xe9\xf3,=\xd6=\xa6\xc7vj\xfco\x00\x00\x00\xff\xff\xbe\xf4?~\xf9 \x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Y_s\x1b\xb7\x11\u007f\xe7\xa7\xd8q\x1e\xd4̘\xc7\xc4\xed\xb4\x1d\xbe\xd9R\xd3Q\x9b\xc8\x1aSՋ\xc7\x0f\xe0ay\x87\xe8\x0e\xb8b\x01\xd2l&߽\xb3\xc0\x81\xbc\u007f\x14%M\x94܋-`\xb1\xf8\xedb\xffs6\x9f\xcfg\xa2Q\xf7hI\x19\xbd\x04\xd1(\xfc\xeaP\xf3_\x94=\xfc\x9d2e\x16\xdb\xefg\x0fJ\xcb%\\zr\xa6\xfe\x84d\xbc\xcd\xf1\n7J+\xa7\x8c\x9e\xd5\xe8\x84\x14N,g\x00Bk\xe3\x04/\x13\xff\t\x90\x1b\xed\xac\xa9*\xb4\xf3\x02u\xf6\xe0\u05f8\xf6\xaa\x92h\x03\xf3t\xf5\xf6\xbb\xeco\xd9w3\x80\xdcb8~\xa7j$'\xeaf\t\xdaW\xd5\f@\x8b\x1a\x97\xd0\x18\xb95\x95\xaf\xd1\"9c\x91\xb2-VhM\xa6̌\x1a\xcc\xf9\xd6\xc2\x1a\xdf,\xe1\xb8\x11\x0f\xb7\x88\xa24\xb7F\xde\a>\x9f\"\x9f\xb0U)r\xff\x9e\xdc\xfeQ\x91\v$M孨&p\x84]R\xba\xf0\x95\xb0\xe3\xfd\x19\x00\xe5\xa6\xc1%\xdc0\x94F\xe4(g\x00\xad\x02\x02\xb49\b)\x83JEuk\x95vh/\x99ER\xe5\x1c$RnU\xe3\x82\xca\x0e|\xc0l\xc0\x95\xc8W\x06u\v\xa5\x95.\xc2R\x84\x00\xce\xc0\x1a\xa1E\"\x033\x80\x9f\xc9\xe8[\xe1\xca%d\xac\xb8\xac12ӉgK\x13u~3Xu{\x96\x83\x9cU\xba8\x85\xec7\x06\xd5\xc3sk\xe4\v\x90<\xe7\xdaHۻ\xf4\xbe\xbbt\xee\xde[#\xdb\x03\xd0\x1a\x10\x90\x13\xce\x13\x90\xcfK\x10\x047\xb8[\\\xeb[k\n\x8bD\x130\x02y֔\x82\xfa8Va\xe3uql\x8c\xad\x85[\x82\xd2\xee\xaf\u007f9\x8d\xad=\x949\xe3D\xf5a\xef\x90zH\xef\x86\xcb\x11-\x1bv\x81\xf6\x8f\x83\xbbfHWF\xf7\xf5\xfaa\xb0:\x05\xb6\xc34\x05\xbdl\x14\xb0z\\\xdf\x17}~R\xb8\xb8\x10\xb7\xb7\xdfǰ\x91\x97X\x8beKi\x1a\xd4\xefo\xaf\xef\xff\xbc\xea-\x034\xd64h\x9dJ\x91,~\x9d\b\xdeY\x85\xbef/\x98a\xa4\x02ɡ\x1b):E\\C\xd9b\x88\u03a2\b,6\x16\tu\f\xe6=\xc6\xc0DB\x83Y\xff\x8c\xb9\xcb`\x85\x96\xd9\x00\x95\xc6W\xc1۷h\x1dX\xccM\xa1\xd5\xff\x0e\xbc\x89}\x8f/\xad\x84\xc36\x9c\x1e\xbf\x10ﴨ`+*\x8foAh\t\xb5\u0603E\xbe\x05\xbc\xee\xf0\v$\x94\xc1Ol!Jo\xcc\x12J\xe7\x1aZ.\x16\x85r)s妮\xbdVn\xbf\bIH\xad\xbd3\x96\x16\x12\xb7X-H\x15sa\xf3R9̝\xb7\xb8\x10\x8d\x9a\a\xe8:d\xaf\xac\x96\xdf\xd86\xd7\xd1E\x0f\xeb\xc8\xe9\xe2\x17\xf2\xca#/\xc0\x89\x05\x14\x81h\x8fF)\x8e\x8aN\xe1\xf1\xd3?Vw\x90\xae\x0e\x8f1\xd4~\xd0\xfb\xf1 \x1d\x9f\x80\x15\xa6\xf4\x06m|č5u\xe0\x89Z6Fi\x17\xfe\xc8+\x85z\xa8~\xf2\xebZ9~\xf7\xffz$\xc7o\x95\xc1eH\xe7\x1c/}Ö+3\xb8\xd6p)j\xac.\x05\xe1\xab?\x00k\x9a\xe6\xacا=A\xb7\x12\x19\x12G\xadu6R\xb5p⽆\x15\xc0\xaa\xc1\x9c\x9f\x8f5\xc8G\xd5F\xe5\xc178\xfc\x80\x18\xd1g=\xd6Ӯ\xcb\xdfZ\xe4\x0f\xbeY9cE\x81?\x9a\xc8sH4\xc0\xf6a\xeaL\x02\xa7;9/2\a\x8a\x94#\xa6\x00U:\xbc+\xd1b8éQ\xe5l^\x86\x943vόc\xb6\xccF\x1cN\xbf\xfb2\xad=\x80\x1f\x8c\x05\xfc*\xea\xa6·\xa0\xa2\xc6\x0f\xe1/ٌ\xa2\xa8\x8e\x03G\xd8)W\xaaa\xd2:h\x80\xad\xab\x15{\x17\xc4u\xe2\x01\xc1\xb4\xe2z\x84J=\xe0\x12ބJ\xf0\b\xf3\x17v\xac_ߜ\xe0\xfa\xa7\xe8@o\x98\xe8M\x04w\xc8w]\x8f<\x82t\xa5p\xe0\xac*\n<\x16\xa2\xc3/\x04o\x0e\x89߂\xb1\xac\x01m:,\x02c~\xbd\x18\x8fP\x8e@\u007f~\xf7\xe5$⾾@i\x89_\xe1\x1d(\x1du\xd3\x18\xf9m\x06w\xc1:\xf6ډ\xaf|S^\x1a\xc2S\x9a5\xbaڳ̥\xd8\"\x90\xa9\x11vXU\xf3XoH؉=k!=\x1cۛ\x80FX\xf7\xa8\xb5\xa6*\xe3\xee\xe3\xd5\xc7eD\xc6\x06U\x84x\xc7\xd9i\xa3\xb8j\xe0r!\xe6\xbc`\x8d\xa3\xa4\x99>\xf2\xd1|\x9c\x81\xbc\x14\xba\xc0(/\xc2\xc6s\x16\xca.^\xe2\xc7\xe3ԟ\xbe\x89\x12`\x188\xfe\xb0$\xfaD\xe1B\xa5\xfa\x04ẽ֣\xc2=\xf85Z\x8d\x0e\x83|\xd2\xe4Ģ\xe5\xd88Z\x98-ڭ\xc2\xddbg\xec\x83\xd2ŜMs\x1em\x80\x16\xa1=]|\x13\xfey\xb1,\xa1\x93}\xaa@\xbd\x06\xfb5\xa5\xe2{h\xf1\"\xa1R\xad\xf8\xf4\xc2\xf9t\xe70\xa0i\x8c\x1c\xac\xf4-a\xb0y|\x9a\xc1Fo \xd6\xc5;n\xab´\xe59\x8dU\x9c\U0003468d\xfe\xed\xd2܇\x8b\xdb\x17\xb7V\xb9\xe1±?M~\xfc\x95/\xc7'\xc2\x1c\xc3ʈΩ\x1aC\xbf\x12\x87S;A钩\x17\x85\x0e\xbfx4\xc4Tf\x872\x94u\\un\x84\xaaP\xc2a\x9e\rw\xdca\x86\x86\xfeb\xaa\x8aI\x8c<\xa1\f\xbd\xe7\x04\xe8\xf1\xb94#\xe36~\xce,F\x14\xdaW\x95XW\xb8\x04g\xfdx\xfb\x11\a\xaa\x91H\x14\xe7<\xe8\xa7H\x15;\xbe\xf6\b\x88\xb5\xf1\xee\xd0\xf2\xb5\xaeԪ\xe2\x82Z+x^\xdbY\n:\a\xe5\x96i\xa6,\xee\xe0ԏ\x9b\x1c\u007f\xa8}=\xbef\x0e7\xb8\x9bX\x1d\xcd,\xbb\x9b\x97Ʉ&\xf6~\b\xd6\xf1,\x05\xb4\x17\x9d\xd3AK\x06\xa5\xa9\x92u\x1b'*о^\xa3eE\x84Ai\xd2H\n\rS=t\xa8\xbd\x8f\x9a\xbc_=(#\x1b\xb8\x8a\x81m\xb7\xa1`\xa3\x17\xf4\x13m\x95Q\xac\xacYu\xc4(\x91\xb1Y\x01\xa01\x961\x89CZ\x02\bk\xd8[\xad\xc9W;2\xf5Cl\xa9\x8dJK\xf2\xd9\xf8\xe0\xfa\xf0\xae\xfe\xbe~\xb7\x02\x10\x9e\xf2\xf1Ϫ\xa3\xc0ع\x06L\xd4z\x05`\xb0\xa3\x06<\x05V\u0093\xb3A\xb1\xf5\x8aB} M\xde\xd6ʮ\x82#\x91\xdc\uef0d\xae\x81\xd3F9݇T\xd2\xd9dC\x9b\xc1\xd01oi\x15\xf8\xd7\xc5\xedO*pVq:z\xd4K\x81\xe4\xed\xa0\xcc.j\xf43\x85\xe4 \b먁\x9b\x14\x8bCAr\x05\xd0C\x90c\xab\x00\xa5̠\xa2\xbe\xf5\xca0\xf9+\xabc7\x80Y\xc1\x97`\xcd-\xf2\xbe\x81z\x80\xbd\x9eA\x96u\a\xc0>\xec\xa8_\xf319\x97\xc8EP\xb6\x0f\xefK\xd8bO\x1d6\xbd\xa6ud>\xdc~\xbc\xff\xee\xeeL\f\xe0\xbcu\xe4Y\rP\x96oTC#)\x80\xa4 \xbcr\x9c\x19~\x9b\f\x16-\x90\xa9x(\x00\xefiȟd\x1f\x03\xd8-\xf0^\x05\xf0\xe4<\x052\xa5\x9c\xce\fCRB\x03\xb6\xfdB\x82k\xb8#\x9f\xcc@\xd8ۨe\xaa\xb9\x03y\x06O\xc2\xee\x8c\xfa\xeb\xc9v\x00\xb6٩F\xa6\x9e\xcfӗ\xf16\xa8\xe1\x80:\xd2\xff\x01\x8d\x84\x0e\x8f\xe0)y\x81hF\xf6\xb2J\xa8\xe1\xdaz\x02e\xb6\xb6\x81=\xb3\v\xcdz\xbdS<\xf4\x8e\xb0]\x17\x8d\xe2\xe3:\xb7\x81j#[\x1f֒\x0e\xa4\xd7A\xed*\xf4b\xaf\x98\x04GOkt\xaaʡ\x9b\xdc?u'\xff\xe7\xfbn\vo\xcfb-L\x06\xf6\xca\xecF\x1b\xb9\xb0_` U6\xa8\x00\xd8\x1f-Y\x9c\x80N\xa2\x84\xce\xe6\xe7\xbb\xcf0\xb8\xcedL\xd1ϸ\x9f\x0e\x86\x13\x05\t0e\xb6\xe4\v\x89[o\xbbl\x93\x8ctV\x19\xce\v\xa1\x15\x99)\xfc!\xb6\x9d\xe2\xc4\xfb\x1f\x91\x02'\xaej\xb8\xca\x03\x05Z\x82\xe8R\xe5\xca\x1a>\x1a\xb8\u008e\xf4\x15\x06\xfa\xcf\tHH\x87*\x01\xfbu\x14\x8cg\xe1T\xb9\xa06\xda\x18\xc6\xd53|MGН#\x91\xe8K\b\xa6\xa3j\xabD\xee\r\xd8Z\x0f8ӯ\xcfL/\xb7n\xfaZ\x14\x0f\xd1ݱ\xf5\xb8\xa3O\xb6\u061c*Mb\xfbq\xe9\xcc\x10\\\x9a,\xa5\x8diYqf\x1b\x80\xf7ȣ\xfeeT\xe6i\f,\xe6\xf3\x02\t\x99\bL\xedl\xd0\b\xfa%W\x94\x11\xc7\v9]/\x1cI)\xed\xed#\xd8-\x93\x19\x1b\xedc]Ȥ%\xf0Ѽ*\xd8rU|\x94\xa9\xf0\xb6\x8a\xfc\x85@7\x13\xf5\x01\xf7mԺ\xb7U\t\xdb9d\xd5jZv\x99\xbeT6\xaaX9\x96\xde\xffv\xbc\x0f鞢\xa7\x9b\xedB\x06\xf7\xe7\xda\xe3\xc2)\x82>\x94\x94\n\xf8\xf3;\xfa\xfc\xebk%\x80\xb3\xb2\x0f\xa2/\xe8\x90\xf2{E\x0e\x89r\xe5i2A\xab\xe5\xf6\x98\xe8,U\xdbDe\xca\xf1d{\x82\xdfW\x8d\x0fF\x8e\xe15\x03$\x1f\x18\xc0\x16\xd1{2ܛ\xc97\xea7\x8f\x10\x8d\x81G\xed\x93^\"\x17*\xe0\xd3\xfc\xc4\x10X2\x06\x9c\x04\xe3~{\xc4\xe9-\x94I[괭\xf5\x1dry\xeaT\xc9\xd0L#=)\xb1\xd5\xd4\x00\xfb8\xdf~i\xaeP\b\xb8\xbb\x94\xddu\xd1*\x97m\u007f\x04\xb0\xb5\x91\x9f\x81\x9e\xf7\xf3(\xe0\x02\x1d\x17\"u{\f\x97\xe2\xbcM:K\x05\xf14\xbf/\x87@&vs7\x15\xdc\xd0\xe3\x82tC(\xe7}\\\xc1\x8d\xe5\xe5\xadg3\\슙0\xa4w\x89\x1c\xf1\x1cJ#\x8f%\xb1}zg5\xf0\xf7?\xabSc\xa1\x10\xe4\x98\xe4\xcd\xf4\xef\xe1͛\xb3\x9f\x81\xbc\x14֔\xc7{h\xe0\xb7\xdfW\xc5\x15\xc9\xfbၟ\x84\xff\x06\x00\x00\xff\xff\xe08S\ft\r\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4Z\xdds\x1b\xb7\x11\u007f\xe7_\xb1\xe3<\xa8\x991\x8f\x89\xdbi;|s\xa4\xa6\xa36\x915\x96\xad\x17\x8f\x1f\xc0\xc3\xf2\x0e\xd1\x1d\x80\x028\xcal&\xff{g\xf1q\xbc\x0f\x90\x944ur/6\xf1\xb1\xf8a\xbfw\xa1\xc5r\xb9\\0-\xee\xd1X\xa1\xe4\x1a\x98\x16\xf8š\xa4_\xb6x\xf8\xbb-\x84Z\xed\xbe_<\b\xc9\xd7p\xd9Y\xa7\xda\xf7hUgJ\xbc\u00ad\x90\xc2\t%\x17-:ƙc\xeb\x05\x00\x93R9FÖ~\x02\x94J:\xa3\x9a\x06ͲBY\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0b43e2f54..6b02727a5 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -24,6 +24,26 @@ rules: - pods verbs: - get +- apiGroups: + - velero.io + resources: + - backuprepositories + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - velero.io + resources: + - backuprepositories/status + verbs: + - get + - patch + - update - apiGroups: - velero.io resources: @@ -131,26 +151,6 @@ rules: - get - patch - update -- apiGroups: - - velero.io - resources: - - resticrepositories - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - velero.io - resources: - - resticrepositories/status - verbs: - - get - - patch - - update - apiGroups: - velero.io resources: diff --git a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md index 122f85b43..5b4a897e2 100644 --- a/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md +++ b/design/unified-repo-and-kopia-integration/unified-repo-and-kopia-integration.md @@ -297,11 +297,11 @@ Kopia’s debug logs will be written to the same log file as Velero server or Ve As mentioned above, There will be two paths. The related controllers need to identify the path during runtime and adjust its working mode. According to the requirements, path changing is fulfilled at the backup/restore level. In order to let the controllers know the path, we need to add some option values. Specifically, there will be option/mode values for path selection in two places: - Add the “uploader-type” option as a parameter of the Velero server. The parameters will be set by the installation. Currently the option has two values, either "restic" or "kopia" (in future, we may add other file system uploaders, then we will have more values). -- Add a "uploader-type" value in the PodVolume Backup/Restore CR and a "repository-type" value in the BackupRepository CR. "uploader-type" currently has two values , either "restic" or "kopia"; "repository-type" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repository-type is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploader-type=restic" and "repository-type=restic", so the legacy CRs are handled correctly by Restic. +- Add a "uploaderType" value in the PodVolume Backup/Restore CR and a "repositoryType" value in the BackupRepository CR. "uploaderType" currently has two values , either "restic" or "kopia"; "repositoryType" currently has two values, either "restic" or "kopia" (in future, the Unified Repository could opt among multiple backup repository/backup storage, so there may be more values. This is a good reason that repositoryType is a multivariate flag, however, in which way to opt among the backup repository/backup storage is not covered in this PR). If the values are missing in the CRs, it by default means "uploaderType=restic" and "repositoryType=restic", so the legacy CRs are handled correctly by Restic. The corresponding controllers handle the CRs by checking the CRs' path value. Some examples are as below: -- The PodVolume BR controller checks the "uploader-type" value from PodVolume CRs and decide its working path -- The BackupRepository controller checks the "repository-type" value from BackupRepository CRs and decide its working path +- The PodVolume BR controller checks the "uploaderType" value from PodVolume CRs and decide its working path +- The BackupRepository controller checks the "repositoryType" value from BackupRepository CRs and decide its working path - The Backup controller that runs in Velero server checks its “uploader-type” parameter to decide the path for the Backup it is going to create and then create the PodVolume Backup CR and BackupRepository CR - The Restore controller checks the Backup, from which it is going to restore, for the path and then create the PodVolume Restore CR and BackupRepository CR @@ -345,7 +345,7 @@ The BackupRepository CRs and PodVolume Backup/Restore CRs created in this case a spec: backupStorageLocation: default maintenanceFrequency: 168h0m0s - repository-type: restic + repositoryType: restic volumeNamespace: nginx-example ``` ``` @@ -359,7 +359,7 @@ spec: uid: 86aaec56-2b21-4736-9964-621047717133 tags: ... - uploader-type: restic + uploaderType: restic volume: nginx-log ``` ``` @@ -371,7 +371,7 @@ spec: namespace: nginx-example uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 snapshotID: 1741e5f1 - uploader-type: restic + uploaderType: restic volume: nginx-log ``` **"Kopia"**: it means Velero will use Kopia uploader to do the pod volume backup (so it will use Unified Repository as the backup target). Therefore, the Velero server deployment will be created as below: @@ -390,7 +390,7 @@ The BackupRepository CRs created in this case are hard set with "kopia" at prese spec: backupStorageLocation: default maintenanceFrequency: 168h0m0s - repository-type: kopia + repositoryType: kopia volumeNamespace: nginx-example ``` ``` @@ -404,7 +404,7 @@ spec: uid: 86aaec56-2b21-4736-9964-621047717133 tags: ... - uploader-type: kopia + uploaderType: kopia volume: nginx-log ``` ``` @@ -416,7 +416,7 @@ spec: namespace: nginx-example uid: e56d5872-3d94-4125-bfe8-8a222bf0fcf1 snapshotID: 1741e5f1 - uploader-type: kopia + uploaderType: kopia volume: nginx-log ``` We will add the flag for both CLI installation and Helm Chart Installation. Specifically: diff --git a/pkg/apis/velero/v1/restic_repository_types.go b/pkg/apis/velero/v1/backup_repository_types.go similarity index 64% rename from pkg/apis/velero/v1/restic_repository_types.go rename to pkg/apis/velero/v1/backup_repository_types.go index 8e315592f..300ecae9c 100644 --- a/pkg/apis/velero/v1/restic_repository_types.go +++ b/pkg/apis/velero/v1/backup_repository_types.go @@ -20,9 +20,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// ResticRepositorySpec is the specification for a ResticRepository. -type ResticRepositorySpec struct { - // VolumeNamespace is the namespace this restic repository contains +// BackupRepositorySpec is the specification for a BackupRepository. +type BackupRepositorySpec struct { + // VolumeNamespace is the namespace this backup repository contains // pod volume backups for. VolumeNamespace string `json:"volumeNamespace"` @@ -30,6 +30,11 @@ type ResticRepositorySpec struct { // that should contain this repository. BackupStorageLocation string `json:"backupStorageLocation"` + // RepositoryType indicates the type of the backend repository + // +kubebuilder:validation:Enum=kopia;restic;"" + // +optional + RepositoryType string `json:"repositoryType"` + // ResticIdentifier is the full restic-compatible string for identifying // this repository. ResticIdentifier string `json:"resticIdentifier"` @@ -38,23 +43,23 @@ type ResticRepositorySpec struct { MaintenanceFrequency metav1.Duration `json:"maintenanceFrequency"` } -// ResticRepositoryPhase represents the lifecycle phase of a ResticRepository. +// BackupRepositoryPhase represents the lifecycle phase of a BackupRepository. // +kubebuilder:validation:Enum=New;Ready;NotReady -type ResticRepositoryPhase string +type BackupRepositoryPhase string const ( - ResticRepositoryPhaseNew ResticRepositoryPhase = "New" - ResticRepositoryPhaseReady ResticRepositoryPhase = "Ready" - ResticRepositoryPhaseNotReady ResticRepositoryPhase = "NotReady" + BackupRepositoryPhaseNew BackupRepositoryPhase = "New" + BackupRepositoryPhaseReady BackupRepositoryPhase = "Ready" + BackupRepositoryPhaseNotReady BackupRepositoryPhase = "NotReady" ) -// ResticRepositoryStatus is the current status of a ResticRepository. -type ResticRepositoryStatus struct { - // Phase is the current state of the ResticRepository. +// BackupRepositoryStatus is the current status of a BackupRepository. +type BackupRepositoryStatus struct { + // Phase is the current state of the BackupRepository. // +optional - Phase ResticRepositoryPhase `json:"phase,omitempty"` + Phase BackupRepositoryPhase `json:"phase,omitempty"` - // Message is a message about the current status of the ResticRepository. + // Message is a message about the current status of the BackupRepository. // +optional Message string `json:"message,omitempty"` @@ -72,33 +77,35 @@ type ResticRepositoryStatus struct { // +kubebuilder:object:generate=true // +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="Repository Type",type="string",JSONPath=".spec.repositoryType" +// -type ResticRepository struct { +type BackupRepository struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ObjectMeta `json:"metadata,omitempty"` // +optional - Spec ResticRepositorySpec `json:"spec,omitempty"` + Spec BackupRepositorySpec `json:"spec,omitempty"` // +optional - Status ResticRepositoryStatus `json:"status,omitempty"` + Status BackupRepositoryStatus `json:"status,omitempty"` } // TODO(2.0) After converting all resources to use the runtime-controller client, // the k8s:deepcopy marker will no longer be needed and should be removed. // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:object:root=true -// +kubebuilder:rbac:groups=velero.io,resources=resticrepositories,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=velero.io,resources=resticrepositories/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=velero.io,resources=backuprepositories,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=velero.io,resources=backuprepositories/status,verbs=get;update;patch -// ResticRepositoryList is a list of ResticRepositories. -type ResticRepositoryList struct { +// BackupRepositoryList is a list of BackupRepositories. +type BackupRepositoryList struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ListMeta `json:"metadata,omitempty"` - Items []ResticRepository `json:"items"` + Items []BackupRepository `json:"items"` } diff --git a/pkg/apis/velero/v1/pod_volume_backup_types.go b/pkg/apis/velero/v1/pod_volume_backup_types.go index c532f096d..d34e09f6c 100644 --- a/pkg/apis/velero/v1/pod_volume_backup_types.go +++ b/pkg/apis/velero/v1/pod_volume_backup_types.go @@ -34,12 +34,17 @@ type PodVolumeBackupSpec struct { Volume string `json:"volume"` // BackupStorageLocation is the name of the backup storage location - // where the restic repository is stored. + // where the backup repository is stored. BackupStorageLocation string `json:"backupStorageLocation"` - // RepoIdentifier is the restic repository identifier. + // RepoIdentifier is the backup repository identifier. RepoIdentifier string `json:"repoIdentifier"` + // UploaderType is the type of the uploader to handle the data transfer. + // +kubebuilder:validation:Enum=kopia;restic;"" + // +optional + UploaderType string `json:"uploaderType"` + // Tags are a map of key-value pairs that should be applied to the // volume backup as tags. // +optional @@ -107,7 +112,8 @@ type PodVolumeBackupStatus struct { // +kubebuilder:printcolumn:name="Namespace",type="string",JSONPath=".spec.pod.namespace",description="Namespace of the pod containing the volume to be backed up" // +kubebuilder:printcolumn:name="Pod",type="string",JSONPath=".spec.pod.name",description="Name of the pod containing the volume to be backed up" // +kubebuilder:printcolumn:name="Volume",type="string",JSONPath=".spec.volume",description="Name of the volume to be backed up" -// +kubebuilder:printcolumn:name="Restic Repo",type="string",JSONPath=".spec.repoIdentifier",description="Restic repository identifier for this backup" +// +kubebuilder:printcolumn:name="Repository ID",type="string",JSONPath=".spec.repoIdentifier",description="Backup repository identifier for this backup" +// +kubebuilder:printcolumn:name="Uploader Type",type="string",JSONPath=".spec.uploaderType",description="The type of the uploader to handle data transfer" // +kubebuilder:printcolumn:name="Storage Location",type="string",JSONPath=".spec.backupStorageLocation",description="Name of the Backup Storage Location where this backup should be stored" // +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" // +kubebuilder:object:root=true diff --git a/pkg/apis/velero/v1/pod_volume_restore_type.go b/pkg/apis/velero/v1/pod_volume_restore_type.go index 45bca8e24..e0370da63 100644 --- a/pkg/apis/velero/v1/pod_volume_restore_type.go +++ b/pkg/apis/velero/v1/pod_volume_restore_type.go @@ -30,12 +30,17 @@ type PodVolumeRestoreSpec struct { Volume string `json:"volume"` // BackupStorageLocation is the name of the backup storage location - // where the restic repository is stored. + // where the backup repository is stored. BackupStorageLocation string `json:"backupStorageLocation"` - // RepoIdentifier is the restic repository identifier. + // RepoIdentifier is the backup repository identifier. RepoIdentifier string `json:"repoIdentifier"` + // UploaderType is the type of the uploader to handle the data transfer. + // +kubebuilder:validation:Enum=kopia;restic;"" + // +optional + UploaderType string `json:"uploaderType"` + // SnapshotID is the ID of the volume snapshot to be restored. SnapshotID string `json:"snapshotID"` } @@ -89,6 +94,7 @@ type PodVolumeRestoreStatus struct { // +kubebuilder:storageversion // +kubebuilder:printcolumn:name="Namespace",type="string",JSONPath=".spec.pod.namespace",description="Namespace of the pod containing the volume to be restored" // +kubebuilder:printcolumn:name="Pod",type="string",JSONPath=".spec.pod.name",description="Name of the pod containing the volume to be restored" +// +kubebuilder:printcolumn:name="Uploader Type",type="string",JSONPath=".spec.uploaderType",description="The type of the uploader to handle data transfer" // +kubebuilder:printcolumn:name="Volume",type="string",JSONPath=".spec.volume",description="Name of the volume to be restored" // +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase",description="Pod Volume Restore status such as New/InProgress" // +kubebuilder:printcolumn:name="TotalBytes",type="integer",format="int64",JSONPath=".status.progress.totalBytes",description="Pod Volume Restore status such as New/InProgress" diff --git a/pkg/apis/velero/v1/register.go b/pkg/apis/velero/v1/register.go index ea7df3b5d..13915293a 100644 --- a/pkg/apis/velero/v1/register.go +++ b/pkg/apis/velero/v1/register.go @@ -52,7 +52,7 @@ func CustomResources() map[string]typeInfo { "DeleteBackupRequest": newTypeInfo("deletebackuprequests", &DeleteBackupRequest{}, &DeleteBackupRequestList{}), "PodVolumeBackup": newTypeInfo("podvolumebackups", &PodVolumeBackup{}, &PodVolumeBackupList{}), "PodVolumeRestore": newTypeInfo("podvolumerestores", &PodVolumeRestore{}, &PodVolumeRestoreList{}), - "ResticRepository": newTypeInfo("resticrepositories", &ResticRepository{}, &ResticRepositoryList{}), + "BackupRepository": newTypeInfo("backuprepositories", &BackupRepository{}, &BackupRepositoryList{}), "BackupStorageLocation": newTypeInfo("backupstoragelocations", &BackupStorageLocation{}, &BackupStorageLocationList{}), "VolumeSnapshotLocation": newTypeInfo("volumesnapshotlocations", &VolumeSnapshotLocation{}, &VolumeSnapshotLocationList{}), "ServerStatusRequest": newTypeInfo("serverstatusrequests", &ServerStatusRequest{}, &ServerStatusRequestList{}), diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index c0a8f2c56..a35e5e1ef 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -107,6 +107,100 @@ func (in *BackupProgress) DeepCopy() *BackupProgress { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepository) DeepCopyInto(out *BackupRepository) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepository. +func (in *BackupRepository) DeepCopy() *BackupRepository { + if in == nil { + return nil + } + out := new(BackupRepository) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupRepository) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepositoryList) DeepCopyInto(out *BackupRepositoryList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackupRepository, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepositoryList. +func (in *BackupRepositoryList) DeepCopy() *BackupRepositoryList { + if in == nil { + return nil + } + out := new(BackupRepositoryList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupRepositoryList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepositorySpec) DeepCopyInto(out *BackupRepositorySpec) { + *out = *in + out.MaintenanceFrequency = in.MaintenanceFrequency +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepositorySpec. +func (in *BackupRepositorySpec) DeepCopy() *BackupRepositorySpec { + if in == nil { + return nil + } + out := new(BackupRepositorySpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupRepositoryStatus) DeepCopyInto(out *BackupRepositoryStatus) { + *out = *in + if in.LastMaintenanceTime != nil { + in, out := &in.LastMaintenanceTime, &out.LastMaintenanceTime + *out = (*in).DeepCopy() + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupRepositoryStatus. +func (in *BackupRepositoryStatus) DeepCopy() *BackupRepositoryStatus { + if in == nil { + return nil + } + out := new(BackupRepositoryStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupResourceHook) DeepCopyInto(out *BackupResourceHook) { *out = *in @@ -965,100 +1059,6 @@ func (in *PodVolumeRestoreStatus) DeepCopy() *PodVolumeRestoreStatus { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepository) DeepCopyInto(out *ResticRepository) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepository. -func (in *ResticRepository) DeepCopy() *ResticRepository { - if in == nil { - return nil - } - out := new(ResticRepository) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ResticRepository) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepositoryList) DeepCopyInto(out *ResticRepositoryList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ResticRepository, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositoryList. -func (in *ResticRepositoryList) DeepCopy() *ResticRepositoryList { - if in == nil { - return nil - } - out := new(ResticRepositoryList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ResticRepositoryList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepositorySpec) DeepCopyInto(out *ResticRepositorySpec) { - *out = *in - out.MaintenanceFrequency = in.MaintenanceFrequency -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositorySpec. -func (in *ResticRepositorySpec) DeepCopy() *ResticRepositorySpec { - if in == nil { - return nil - } - out := new(ResticRepositorySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResticRepositoryStatus) DeepCopyInto(out *ResticRepositoryStatus) { - *out = *in - if in.LastMaintenanceTime != nil { - in, out := &in.LastMaintenanceTime, &out.LastMaintenanceTime - *out = (*in).DeepCopy() - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResticRepositoryStatus. -func (in *ResticRepositoryStatus) DeepCopy() *ResticRepositoryStatus { - if in == nil { - return nil - } - out := new(ResticRepositoryStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Restore) DeepCopyInto(out *Restore) { *out = *in diff --git a/pkg/cmd/cli/backuplocation/delete.go b/pkg/cmd/cli/backuplocation/delete.go index daedf77cd..1222220f5 100644 --- a/pkg/cmd/cli/backuplocation/delete.go +++ b/pkg/cmd/cli/backuplocation/delete.go @@ -151,8 +151,8 @@ func findAssociatedBackups(client kbclient.Client, bslName, ns string) (velerov1 return backups, err } -func findAssociatedResticRepos(client kbclient.Client, bslName, ns string) (velerov1api.ResticRepositoryList, error) { - var repos velerov1api.ResticRepositoryList +func findAssociatedResticRepos(client kbclient.Client, bslName, ns string) (velerov1api.BackupRepositoryList, error) { + var repos velerov1api.BackupRepositoryList err := client.List(context.Background(), &repos, &kbclient.ListOptions{ Namespace: ns, Raw: &metav1.ListOptions{LabelSelector: bslLabelKey + "=" + bslName}, @@ -172,7 +172,7 @@ func deleteBackups(client kbclient.Client, backups velerov1api.BackupList) []err return errs } -func deleteResticRepos(client kbclient.Client, repos velerov1api.ResticRepositoryList) []error { +func deleteResticRepos(client kbclient.Client, repos velerov1api.BackupRepositoryList) []error { var errs []error for _, repo := range repos.Items { if err := client.Delete(context.Background(), &repo, &kbclient.DeleteOptions{}); err != nil { diff --git a/pkg/cmd/cli/restic/repo/get.go b/pkg/cmd/cli/restic/repo/get.go index 8fd848fd4..24692f355 100644 --- a/pkg/cmd/cli/restic/repo/get.go +++ b/pkg/cmd/cli/restic/repo/get.go @@ -41,16 +41,16 @@ func NewGetCommand(f client.Factory, use string) *cobra.Command { veleroClient, err := f.Client() cmd.CheckError(err) - var repos *api.ResticRepositoryList + var repos *api.BackupRepositoryList if len(args) > 0 { - repos = new(api.ResticRepositoryList) + repos = new(api.BackupRepositoryList) for _, name := range args { - repo, err := veleroClient.VeleroV1().ResticRepositories(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{}) + repo, err := veleroClient.VeleroV1().BackupRepositories(f.Namespace()).Get(context.TODO(), name, metav1.GetOptions{}) cmd.CheckError(err) repos.Items = append(repos.Items, *repo) } } else { - repos, err = veleroClient.VeleroV1().ResticRepositories(f.Namespace()).List(context.TODO(), listOptions) + repos, err = veleroClient.VeleroV1().BackupRepositories(f.Namespace()).List(context.TODO(), listOptions) cmd.CheckError(err) } diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index 3c83ba75a..f257c96e7 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -527,7 +527,7 @@ func (s *server) initRestic() error { s.ctx, s.namespace, s.veleroClient, - s.sharedInformerFactory.Velero().V1().ResticRepositories(), + s.sharedInformerFactory.Velero().V1().BackupRepositories(), s.veleroClient.VeleroV1(), s.mgr.GetClient(), s.kubeClient.CoreV1(), diff --git a/pkg/cmd/util/output/output.go b/pkg/cmd/util/output/output.go index 24188ed0d..ab3f7a95d 100644 --- a/pkg/cmd/util/output/output.go +++ b/pkg/cmd/util/output/output.go @@ -177,15 +177,15 @@ func printTable(cmd *cobra.Command, obj runtime.Object) (bool, error) { ColumnDefinitions: scheduleColumns, Rows: printScheduleList(obj.(*velerov1api.ScheduleList)), } - case *velerov1api.ResticRepository: + case *velerov1api.BackupRepository: table = &metav1.Table{ ColumnDefinitions: resticRepoColumns, - Rows: printResticRepo(obj.(*velerov1api.ResticRepository)), + Rows: printResticRepo(obj.(*velerov1api.BackupRepository)), } - case *velerov1api.ResticRepositoryList: + case *velerov1api.BackupRepositoryList: table = &metav1.Table{ ColumnDefinitions: resticRepoColumns, - Rows: printResticRepoList(obj.(*velerov1api.ResticRepositoryList)), + Rows: printResticRepoList(obj.(*velerov1api.BackupRepositoryList)), } case *velerov1api.BackupStorageLocation: table = &metav1.Table{ diff --git a/pkg/cmd/util/output/restic_repo_printer.go b/pkg/cmd/util/output/restic_repo_printer.go index 803a3486f..fd6766087 100644 --- a/pkg/cmd/util/output/restic_repo_printer.go +++ b/pkg/cmd/util/output/restic_repo_printer.go @@ -33,7 +33,7 @@ var ( } ) -func printResticRepoList(list *v1.ResticRepositoryList) []metav1.TableRow { +func printResticRepoList(list *v1.BackupRepositoryList) []metav1.TableRow { rows := make([]metav1.TableRow, 0, len(list.Items)) for i := range list.Items { @@ -42,14 +42,14 @@ func printResticRepoList(list *v1.ResticRepositoryList) []metav1.TableRow { return rows } -func printResticRepo(repo *v1.ResticRepository) []metav1.TableRow { +func printResticRepo(repo *v1.BackupRepository) []metav1.TableRow { row := metav1.TableRow{ Object: runtime.RawExtension{Object: repo}, } status := repo.Status.Phase if status == "" { - status = v1.ResticRepositoryPhaseNew + status = v1.BackupRepositoryPhaseNew } var lastMaintenance string diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index d4d0ef68d..36f0e76a8 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -68,16 +68,16 @@ func NewResticRepoReconciler(namespace string, logger logrus.FieldLogger, client } func (r *ResticRepoReconciler) SetupWithManager(mgr ctrl.Manager) error { - s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.ResticRepositoryList{}, repoSyncPeriod) + s := kube.NewPeriodicalEnqueueSource(r.logger, mgr.GetClient(), &velerov1api.BackupRepositoryList{}, repoSyncPeriod) return ctrl.NewControllerManagedBy(mgr). - For(&velerov1api.ResticRepository{}). + For(&velerov1api.BackupRepository{}). Watches(s, nil). Complete(r) } func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { log := r.logger.WithField("resticRepo", req.String()) - resticRepo := &velerov1api.ResticRepository{} + resticRepo := &velerov1api.BackupRepository{} if err := r.Get(ctx, req.NamespacedName, resticRepo); err != nil { if apierrors.IsNotFound(err) { log.Warnf("restic repository %s in namespace %s is not found", req.Name, req.Namespace) @@ -87,7 +87,7 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, err } - if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.ResticRepositoryPhaseNew { + if resticRepo.Status.Phase == "" || resticRepo.Status.Phase == velerov1api.BackupRepositoryPhaseNew { if err := r.initializeRepo(ctx, resticRepo, log); err != nil { log.WithError(err).Error("error initialize repository") return ctrl.Result{}, errors.WithStack(err) @@ -105,16 +105,16 @@ func (r *ResticRepoReconciler) Reconcile(ctx context.Context, req ctrl.Request) } switch resticRepo.Status.Phase { - case velerov1api.ResticRepositoryPhaseReady: + case velerov1api.BackupRepositoryPhaseReady: return ctrl.Result{}, r.runMaintenanceIfDue(ctx, resticRepo, log) - case velerov1api.ResticRepositoryPhaseNotReady: + case velerov1api.BackupRepositoryPhaseNotReady: return ctrl.Result{}, r.checkNotReadyRepo(ctx, resticRepo, log) } return ctrl.Result{}, nil } -func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { log.Info("Initializing restic repository") // confirm the repo's BackupStorageLocation is valid @@ -129,9 +129,9 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() - rr.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady + rr.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady if rr.Spec.MaintenanceFrequency.Duration <= 0 { rr.Spec.MaintenanceFrequency = metav1.Duration{Duration: r.defaultMaintenanceFrequency} @@ -140,7 +140,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 } // defaulting - if the patch fails, return an error so the item is returned to the queue - if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + if err := r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Spec.ResticIdentifier = repoIdentifier if rr.Spec.MaintenanceFrequency.Duration <= 0 { @@ -154,8 +154,8 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { - rr.Status.Phase = velerov1api.ResticRepositoryPhaseReady + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { + rr.Status.Phase = velerov1api.BackupRepositoryPhaseReady rr.Status.LastMaintenanceTime = &metav1.Time{Time: time.Now()} }) } @@ -163,7 +163,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 // ensureRepo checks to see if a repository exists, and attempts to initialize it if // it does not exist. An error is returned if the repository can't be connected to // or initialized. -func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.RepositoryManager) error { +func ensureRepo(repo *velerov1api.BackupRepository, repoManager restic.RepositoryManager) error { if err := repoManager.ConnectToRepo(repo); err != nil { // If the repository has not yet been initialized, the error message will always include // the following string. This is the only scenario where we should try to initialize it. @@ -179,7 +179,7 @@ func ensureRepo(repo *velerov1api.ResticRepository, repoManager restic.Repositor return nil } -func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { log.Debug("resticRepositoryController.runMaintenanceIfDue") now := r.clock.Now() @@ -196,21 +196,21 @@ func (r *ResticRepoReconciler) runMaintenanceIfDue(ctx context.Context, req *vel log.Debug("Pruning repo") if err := r.repositoryManager.PruneRepo(req); err != nil { log.WithError(err).Warn("error pruning repository") - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() }) } - return r.patchResticRepository(ctx, req, func(rr *velerov1api.ResticRepository) { + return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.LastMaintenanceTime = &metav1.Time{Time: now} }) } -func dueForMaintenance(req *velerov1api.ResticRepository, now time.Time) bool { +func dueForMaintenance(req *velerov1api.BackupRepository, now time.Time) bool { return req.Status.LastMaintenanceTime == nil || req.Status.LastMaintenanceTime.Add(req.Spec.MaintenanceFrequency.Duration).Before(now) } -func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.ResticRepository, log logrus.FieldLogger) error { +func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *velerov1api.BackupRepository, log logrus.FieldLogger) error { // no identifier: can't possibly be ready, so just return if req.Spec.ResticIdentifier == "" { return nil @@ -226,16 +226,16 @@ func (r *ResticRepoReconciler) checkNotReadyRepo(ctx context.Context, req *veler return r.patchResticRepository(ctx, req, repoReady()) } -func repoNotReady(msg string) func(*velerov1api.ResticRepository) { - return func(r *velerov1api.ResticRepository) { - r.Status.Phase = velerov1api.ResticRepositoryPhaseNotReady +func repoNotReady(msg string) func(*velerov1api.BackupRepository) { + return func(r *velerov1api.BackupRepository) { + r.Status.Phase = velerov1api.BackupRepositoryPhaseNotReady r.Status.Message = msg } } -func repoReady() func(*velerov1api.ResticRepository) { - return func(r *velerov1api.ResticRepository) { - r.Status.Phase = velerov1api.ResticRepositoryPhaseReady +func repoReady() func(*velerov1api.BackupRepository) { + return func(r *velerov1api.BackupRepository) { + r.Status.Phase = velerov1api.BackupRepositoryPhaseReady r.Status.Message = "" } } @@ -243,7 +243,7 @@ func repoReady() func(*velerov1api.ResticRepository) { // patchResticRepository mutates req with the provided mutate function, and patches it // through the Kube API. After executing this function, req will be updated with both // the mutation and the results of the Patch() API call. -func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.ResticRepository, mutate func(*velerov1api.ResticRepository)) error { +func (r *ResticRepoReconciler) patchResticRepository(ctx context.Context, req *velerov1api.BackupRepository, mutate func(*velerov1api.BackupRepository)) error { original := req.DeepCopy() mutate(req) if err := r.Patch(ctx, req, client.MergeFrom(original)); err != nil { diff --git a/pkg/controller/restic_repository_controller_test.go b/pkg/controller/restic_repository_controller_test.go index 2e6b4308a..28e899329 100644 --- a/pkg/controller/restic_repository_controller_test.go +++ b/pkg/controller/restic_repository_controller_test.go @@ -30,7 +30,7 @@ import ( const defaultMaintenanceFrequency = 10 * time.Minute -func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { +func mockResticRepoReconciler(t *testing.T, rr *velerov1api.BackupRepository, mockOn string, arg interface{}, ret interface{}) *ResticRepoReconciler { mgr := &resticmokes.RepositoryManager{} if mockOn != "" { mgr.On(mockOn, arg).Return(ret) @@ -44,13 +44,13 @@ func mockResticRepoReconciler(t *testing.T, rr *velerov1api.ResticRepository, mo ) } -func mockResticRepositoryCR() *velerov1api.ResticRepository { - return &velerov1api.ResticRepository{ +func mockResticRepositoryCR() *velerov1api.BackupRepository { + return &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "repo", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, } @@ -64,10 +64,10 @@ func TestPatchResticRepository(t *testing.T) { assert.NoError(t, err) err = reconciler.patchResticRepository(context.Background(), rr, repoReady()) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) err = reconciler.patchResticRepository(context.Background(), rr, repoNotReady("not ready")) assert.NoError(t, err) - assert.NotEqual(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.NotEqual(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestCheckNotReadyRepo(t *testing.T) { @@ -77,11 +77,11 @@ 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.ResticRepositoryPhase("")) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhase("")) rr.Spec.ResticIdentifier = "s3:test.amazonaws.com/bucket/restic" err = reconciler.checkNotReadyRepo(context.TODO(), rr, reconciler.logger) assert.NoError(t, err) - assert.Equal(t, rr.Status.Phase, velerov1api.ResticRepositoryPhaseReady) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestRunMaintenanceIfDue(t *testing.T) { @@ -121,23 +121,23 @@ 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.ResticRepositoryPhaseReady) + assert.Equal(t, rr.Status.Phase, velerov1api.BackupRepositoryPhaseReady) } func TestResticRepoReconcile(t *testing.T) { tests := []struct { name string - repo *velerov1api.ResticRepository + repo *velerov1api.BackupRepository expectNil bool }{ { name: "test on api server not found", - repo: &velerov1api.ResticRepository{ + repo: &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "unknown", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, }, @@ -145,12 +145,12 @@ func TestResticRepoReconcile(t *testing.T) { }, { name: "test on initialize repo", - repo: &velerov1api.ResticRepository{ + repo: &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "repo", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, }, @@ -158,16 +158,16 @@ func TestResticRepoReconcile(t *testing.T) { }, { name: "test on repo with new phase", - repo: &velerov1api.ResticRepository{ + repo: &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: velerov1api.DefaultNamespace, Name: "repo", }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ MaintenanceFrequency: metav1.Duration{defaultMaintenanceFrequency}, }, - Status: velerov1api.ResticRepositoryStatus{ - Phase: velerov1api.ResticRepositoryPhaseNew, + Status: velerov1api.BackupRepositoryStatus{ + Phase: velerov1api.BackupRepositoryPhaseNew, }, }, expectNil: true, diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 092b90002..3799259fa 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -71,10 +71,14 @@ var nonRestorableResources = []string{ // https://github.com/vmware-tanzu/velero/issues/622 "restores.velero.io", + // TODO: Remove this in v1.11 or v1.12 // Restic repositories are automatically managed by Velero and will be automatically // created as needed if they don't exist. // https://github.com/vmware-tanzu/velero/issues/1113 "resticrepositories.velero.io", + + // Backup repositories were renamed from Restic repositories + "backuprepositories.velero.io", } type restoreController struct { diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/resticrepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go similarity index 53% rename from pkg/generated/clientset/versioned/typed/velero/v1/resticrepository.go rename to pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go index 44d5c0760..7ecef6dcf 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/resticrepository.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/backuprepository.go @@ -30,46 +30,46 @@ import ( rest "k8s.io/client-go/rest" ) -// ResticRepositoriesGetter has a method to return a ResticRepositoryInterface. +// BackupRepositoriesGetter has a method to return a BackupRepositoryInterface. // A group's client should implement this interface. -type ResticRepositoriesGetter interface { - ResticRepositories(namespace string) ResticRepositoryInterface +type BackupRepositoriesGetter interface { + BackupRepositories(namespace string) BackupRepositoryInterface } -// ResticRepositoryInterface has methods to work with ResticRepository resources. -type ResticRepositoryInterface interface { - Create(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.CreateOptions) (*v1.ResticRepository, error) - Update(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (*v1.ResticRepository, error) - UpdateStatus(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (*v1.ResticRepository, error) +// 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.ResticRepository, error) - List(ctx context.Context, opts metav1.ListOptions) (*v1.ResticRepositoryList, 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.ResticRepository, err error) - ResticRepositoryExpansion + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.BackupRepository, err error) + BackupRepositoryExpansion } -// resticRepositories implements ResticRepositoryInterface -type resticRepositories struct { +// backupRepositories implements BackupRepositoryInterface +type backupRepositories struct { client rest.Interface ns string } -// newResticRepositories returns a ResticRepositories -func newResticRepositories(c *VeleroV1Client, namespace string) *resticRepositories { - return &resticRepositories{ +// newBackupRepositories returns a BackupRepositories +func newBackupRepositories(c *VeleroV1Client, namespace string) *backupRepositories { + return &backupRepositories{ client: c.RESTClient(), ns: namespace, } } -// Get takes name of the resticRepository, and returns the corresponding resticRepository object, and an error if there is any. -func (c *resticRepositories) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// 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("resticrepositories"). + Resource("backuprepositories"). Name(name). VersionedParams(&options, scheme.ParameterCodec). Do(ctx). @@ -77,16 +77,16 @@ func (c *resticRepositories) Get(ctx context.Context, name string, options metav return } -// List takes label and field selectors, and returns the list of ResticRepositories that match those selectors. -func (c *resticRepositories) List(ctx context.Context, opts metav1.ListOptions) (result *v1.ResticRepositoryList, err error) { +// 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.ResticRepositoryList{} + result = &v1.BackupRepositoryList{} err = c.client.Get(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Do(ctx). @@ -94,8 +94,8 @@ func (c *resticRepositories) List(ctx context.Context, opts metav1.ListOptions) return } -// Watch returns a watch.Interface that watches the requested resticRepositories. -func (c *resticRepositories) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { +// 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 @@ -103,34 +103,34 @@ func (c *resticRepositories) Watch(ctx context.Context, opts metav1.ListOptions) opts.Watch = true return c.client.Get(). Namespace(c.ns). - Resource("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&opts, scheme.ParameterCodec). Timeout(timeout). Watch(ctx) } -// Create takes the representation of a resticRepository and creates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *resticRepositories) Create(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.CreateOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// 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("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&opts, scheme.ParameterCodec). - Body(resticRepository). + Body(backupRepository). Do(ctx). Into(result) return } -// Update takes the representation of a resticRepository and updates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *resticRepositories) Update(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// 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("resticrepositories"). - Name(resticRepository.Name). + Resource("backuprepositories"). + Name(backupRepository.Name). VersionedParams(&opts, scheme.ParameterCodec). - Body(resticRepository). + Body(backupRepository). Do(ctx). Into(result) return @@ -138,25 +138,25 @@ func (c *resticRepositories) Update(ctx context.Context, resticRepository *v1.Re // UpdateStatus was generated because the type contains a Status member. // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *resticRepositories) UpdateStatus(ctx context.Context, resticRepository *v1.ResticRepository, opts metav1.UpdateOptions) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +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("resticrepositories"). - Name(resticRepository.Name). + Resource("backuprepositories"). + Name(backupRepository.Name). SubResource("status"). VersionedParams(&opts, scheme.ParameterCodec). - Body(resticRepository). + Body(backupRepository). Do(ctx). Into(result) return } -// Delete takes name of the resticRepository and deletes it. Returns an error if one occurs. -func (c *resticRepositories) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { +// 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("resticrepositories"). + Resource("backuprepositories"). Name(name). Body(&opts). Do(ctx). @@ -164,14 +164,14 @@ func (c *resticRepositories) Delete(ctx context.Context, name string, opts metav } // DeleteCollection deletes a collection of objects. -func (c *resticRepositories) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { +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("resticrepositories"). + Resource("backuprepositories"). VersionedParams(&listOpts, scheme.ParameterCodec). Timeout(timeout). Body(&opts). @@ -179,12 +179,12 @@ func (c *resticRepositories) DeleteCollection(ctx context.Context, opts metav1.D Error() } -// Patch applies the patch and returns the patched resticRepository. -func (c *resticRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.ResticRepository, err error) { - result = &v1.ResticRepository{} +// 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("resticrepositories"). + Resource("backuprepositories"). Name(name). SubResource(subresources...). VersionedParams(&opts, scheme.ParameterCodec). 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 new file mode 100644 index 000000000..ef9d6b41c --- /dev/null +++ b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_backuprepository.go @@ -0,0 +1,142 @@ +/* +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_resticrepository.go b/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_resticrepository.go deleted file mode 100644 index aeda0c9cb..000000000 --- a/pkg/generated/clientset/versioned/typed/velero/v1/fake/fake_resticrepository.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" -) - -// FakeResticRepositories implements ResticRepositoryInterface -type FakeResticRepositories struct { - Fake *FakeVeleroV1 - ns string -} - -var resticrepositoriesResource = schema.GroupVersionResource{Group: "velero.io", Version: "v1", Resource: "resticrepositories"} - -var resticrepositoriesKind = schema.GroupVersionKind{Group: "velero.io", Version: "v1", Kind: "ResticRepository"} - -// Get takes name of the resticRepository, and returns the corresponding resticRepository object, and an error if there is any. -func (c *FakeResticRepositories) Get(ctx context.Context, name string, options v1.GetOptions) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(resticrepositoriesResource, c.ns, name), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// List takes label and field selectors, and returns the list of ResticRepositories that match those selectors. -func (c *FakeResticRepositories) List(ctx context.Context, opts v1.ListOptions) (result *velerov1.ResticRepositoryList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(resticrepositoriesResource, resticrepositoriesKind, c.ns, opts), &velerov1.ResticRepositoryList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &velerov1.ResticRepositoryList{ListMeta: obj.(*velerov1.ResticRepositoryList).ListMeta} - for _, item := range obj.(*velerov1.ResticRepositoryList).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 resticRepositories. -func (c *FakeResticRepositories) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(resticrepositoriesResource, c.ns, opts)) - -} - -// Create takes the representation of a resticRepository and creates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *FakeResticRepositories) Create(ctx context.Context, resticRepository *velerov1.ResticRepository, opts v1.CreateOptions) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(resticrepositoriesResource, c.ns, resticRepository), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// Update takes the representation of a resticRepository and updates it. Returns the server's representation of the resticRepository, and an error, if there is any. -func (c *FakeResticRepositories) Update(ctx context.Context, resticRepository *velerov1.ResticRepository, opts v1.UpdateOptions) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(resticrepositoriesResource, c.ns, resticRepository), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), 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 *FakeResticRepositories) UpdateStatus(ctx context.Context, resticRepository *velerov1.ResticRepository, opts v1.UpdateOptions) (*velerov1.ResticRepository, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(resticrepositoriesResource, "status", c.ns, resticRepository), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), err -} - -// Delete takes name of the resticRepository and deletes it. Returns an error if one occurs. -func (c *FakeResticRepositories) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(resticrepositoriesResource, c.ns, name), &velerov1.ResticRepository{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeResticRepositories) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(resticrepositoriesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &velerov1.ResticRepositoryList{}) - return err -} - -// Patch applies the patch and returns the patched resticRepository. -func (c *FakeResticRepositories) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *velerov1.ResticRepository, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(resticrepositoriesResource, c.ns, name, pt, data, subresources...), &velerov1.ResticRepository{}) - - if obj == nil { - return nil, err - } - return obj.(*velerov1.ResticRepository), 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 index e90115813..444c1f89f 100644 --- 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 @@ -32,6 +32,10 @@ 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} } @@ -52,10 +56,6 @@ func (c *FakeVeleroV1) PodVolumeRestores(namespace string) v1.PodVolumeRestoreIn return &FakePodVolumeRestores{c, namespace} } -func (c *FakeVeleroV1) ResticRepositories(namespace string) v1.ResticRepositoryInterface { - return &FakeResticRepositories{c, namespace} -} - func (c *FakeVeleroV1) Restores(namespace string) v1.RestoreInterface { return &FakeRestores{c, namespace} } diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go index 5deaaa51a..5032fd6a4 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/generated_expansion.go @@ -20,6 +20,8 @@ package v1 type BackupExpansion interface{} +type BackupRepositoryExpansion interface{} + type BackupStorageLocationExpansion interface{} type DeleteBackupRequestExpansion interface{} @@ -30,8 +32,6 @@ type PodVolumeBackupExpansion interface{} type PodVolumeRestoreExpansion interface{} -type ResticRepositoryExpansion interface{} - type RestoreExpansion interface{} type ScheduleExpansion interface{} diff --git a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go index 5758967ef..39f85628c 100644 --- a/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go +++ b/pkg/generated/clientset/versioned/typed/velero/v1/velero_client.go @@ -27,12 +27,12 @@ import ( type VeleroV1Interface interface { RESTClient() rest.Interface BackupsGetter + BackupRepositoriesGetter BackupStorageLocationsGetter DeleteBackupRequestsGetter DownloadRequestsGetter PodVolumeBackupsGetter PodVolumeRestoresGetter - ResticRepositoriesGetter RestoresGetter SchedulesGetter ServerStatusRequestsGetter @@ -48,6 +48,10 @@ 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) } @@ -68,10 +72,6 @@ func (c *VeleroV1Client) PodVolumeRestores(namespace string) PodVolumeRestoreInt return newPodVolumeRestores(c, namespace) } -func (c *VeleroV1Client) ResticRepositories(namespace string) ResticRepositoryInterface { - return newResticRepositories(c, namespace) -} - func (c *VeleroV1Client) Restores(namespace string) RestoreInterface { return newRestores(c, namespace) } diff --git a/pkg/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go index 15770dceb..605887024 100644 --- a/pkg/generated/informers/externalversions/generic.go +++ b/pkg/generated/informers/externalversions/generic.go @@ -55,6 +55,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // 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"): @@ -65,8 +67,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource 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("resticrepositories"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().ResticRepositories().Informer()}, nil case v1.SchemeGroupVersion.WithResource("restores"): return &genericInformer{resource: resource.GroupResource(), informer: f.Velero().V1().Restores().Informer()}, nil case v1.SchemeGroupVersion.WithResource("schedules"): diff --git a/pkg/generated/informers/externalversions/velero/v1/resticrepository.go b/pkg/generated/informers/externalversions/velero/v1/backuprepository.go similarity index 70% rename from pkg/generated/informers/externalversions/velero/v1/resticrepository.go rename to pkg/generated/informers/externalversions/velero/v1/backuprepository.go index f92565554..59865c894 100644 --- a/pkg/generated/informers/externalversions/velero/v1/resticrepository.go +++ b/pkg/generated/informers/externalversions/velero/v1/backuprepository.go @@ -32,59 +32,59 @@ import ( cache "k8s.io/client-go/tools/cache" ) -// ResticRepositoryInformer provides access to a shared informer and lister for -// ResticRepositories. -type ResticRepositoryInformer interface { +// BackupRepositoryInformer provides access to a shared informer and lister for +// BackupRepositories. +type BackupRepositoryInformer interface { Informer() cache.SharedIndexInformer - Lister() v1.ResticRepositoryLister + Lister() v1.BackupRepositoryLister } -type resticRepositoryInformer struct { +type backupRepositoryInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc namespace string } -// NewResticRepositoryInformer constructs a new informer for ResticRepository type. +// 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 NewResticRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredResticRepositoryInformer(client, namespace, resyncPeriod, indexers, nil) +func NewBackupRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredBackupRepositoryInformer(client, namespace, resyncPeriod, indexers, nil) } -// NewFilteredResticRepositoryInformer constructs a new informer for ResticRepository type. +// 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 NewFilteredResticRepositoryInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +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().ResticRepositories(namespace).List(context.TODO(), 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().ResticRepositories(namespace).Watch(context.TODO(), options) + return client.VeleroV1().BackupRepositories(namespace).Watch(context.TODO(), options) }, }, - &velerov1.ResticRepository{}, + &velerov1.BackupRepository{}, resyncPeriod, indexers, ) } -func (f *resticRepositoryInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredResticRepositoryInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +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 *resticRepositoryInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&velerov1.ResticRepository{}, f.defaultInformer) +func (f *backupRepositoryInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&velerov1.BackupRepository{}, f.defaultInformer) } -func (f *resticRepositoryInformer) Lister() v1.ResticRepositoryLister { - return v1.NewResticRepositoryLister(f.Informer().GetIndexer()) +func (f *backupRepositoryInformer) Lister() v1.BackupRepositoryLister { + return v1.NewBackupRepositoryLister(f.Informer().GetIndexer()) } diff --git a/pkg/generated/informers/externalversions/velero/v1/interface.go b/pkg/generated/informers/externalversions/velero/v1/interface.go index 981470c40..087dd3356 100644 --- a/pkg/generated/informers/externalversions/velero/v1/interface.go +++ b/pkg/generated/informers/externalversions/velero/v1/interface.go @@ -26,6 +26,8 @@ import ( 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. @@ -36,8 +38,6 @@ type Interface interface { PodVolumeBackups() PodVolumeBackupInformer // PodVolumeRestores returns a PodVolumeRestoreInformer. PodVolumeRestores() PodVolumeRestoreInformer - // ResticRepositories returns a ResticRepositoryInformer. - ResticRepositories() ResticRepositoryInformer // Restores returns a RestoreInformer. Restores() RestoreInformer // Schedules returns a ScheduleInformer. @@ -64,6 +64,11 @@ 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} @@ -89,11 +94,6 @@ func (v *version) PodVolumeRestores() PodVolumeRestoreInformer { return &podVolumeRestoreInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } -// ResticRepositories returns a ResticRepositoryInformer. -func (v *version) ResticRepositories() ResticRepositoryInformer { - return &resticRepositoryInformer{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} diff --git a/pkg/generated/listers/velero/v1/backuprepository.go b/pkg/generated/listers/velero/v1/backuprepository.go new file mode 100644 index 000000000..ef619baf1 --- /dev/null +++ b/pkg/generated/listers/velero/v1/backuprepository.go @@ -0,0 +1,99 @@ +/* +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/expansion_generated.go b/pkg/generated/listers/velero/v1/expansion_generated.go index b57656650..c0cd57654 100644 --- a/pkg/generated/listers/velero/v1/expansion_generated.go +++ b/pkg/generated/listers/velero/v1/expansion_generated.go @@ -26,6 +26,14 @@ type BackupListerExpansion interface{} // 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{} @@ -66,14 +74,6 @@ type PodVolumeRestoreListerExpansion interface{} // PodVolumeRestoreNamespaceLister. type PodVolumeRestoreNamespaceListerExpansion interface{} -// ResticRepositoryListerExpansion allows custom methods to be added to -// ResticRepositoryLister. -type ResticRepositoryListerExpansion interface{} - -// ResticRepositoryNamespaceListerExpansion allows custom methods to be added to -// ResticRepositoryNamespaceLister. -type ResticRepositoryNamespaceListerExpansion interface{} - // RestoreListerExpansion allows custom methods to be added to // RestoreLister. type RestoreListerExpansion interface{} diff --git a/pkg/generated/listers/velero/v1/resticrepository.go b/pkg/generated/listers/velero/v1/resticrepository.go deleted file mode 100644 index 96bcfdc7c..000000000 --- a/pkg/generated/listers/velero/v1/resticrepository.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" -) - -// ResticRepositoryLister helps list ResticRepositories. -// All objects returned here must be treated as read-only. -type ResticRepositoryLister interface { - // List lists all ResticRepositories in the indexer. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.ResticRepository, err error) - // ResticRepositories returns an object that can list and get ResticRepositories. - ResticRepositories(namespace string) ResticRepositoryNamespaceLister - ResticRepositoryListerExpansion -} - -// resticRepositoryLister implements the ResticRepositoryLister interface. -type resticRepositoryLister struct { - indexer cache.Indexer -} - -// NewResticRepositoryLister returns a new ResticRepositoryLister. -func NewResticRepositoryLister(indexer cache.Indexer) ResticRepositoryLister { - return &resticRepositoryLister{indexer: indexer} -} - -// List lists all ResticRepositories in the indexer. -func (s *resticRepositoryLister) List(selector labels.Selector) (ret []*v1.ResticRepository, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ResticRepository)) - }) - return ret, err -} - -// ResticRepositories returns an object that can list and get ResticRepositories. -func (s *resticRepositoryLister) ResticRepositories(namespace string) ResticRepositoryNamespaceLister { - return resticRepositoryNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// ResticRepositoryNamespaceLister helps list and get ResticRepositories. -// All objects returned here must be treated as read-only. -type ResticRepositoryNamespaceLister interface { - // List lists all ResticRepositories in the indexer for a given namespace. - // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1.ResticRepository, err error) - // Get retrieves the ResticRepository from the indexer for a given namespace and name. - // Objects returned here must be treated as read-only. - Get(name string) (*v1.ResticRepository, error) - ResticRepositoryNamespaceListerExpansion -} - -// resticRepositoryNamespaceLister implements the ResticRepositoryNamespaceLister -// interface. -type resticRepositoryNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ResticRepositories in the indexer for a given namespace. -func (s resticRepositoryNamespaceLister) List(selector labels.Selector) (ret []*v1.ResticRepository, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1.ResticRepository)) - }) - return ret, err -} - -// Get retrieves the ResticRepository from the indexer for a given namespace and name. -func (s resticRepositoryNamespaceLister) Get(name string) (*v1.ResticRepository, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1.Resource("resticrepository"), name) - } - return obj.(*v1.ResticRepository), nil -} diff --git a/pkg/restic/backupper.go b/pkg/restic/backupper.go index fd366a36b..589b396f3 100644 --- a/pkg/restic/backupper.go +++ b/pkg/restic/backupper.go @@ -176,8 +176,8 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. log.Warnf("Volume %s is declared in pod %s/%s but not mounted by any container, skipping", volumeName, pod.Namespace, pod.Name) continue } - - volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, pvc) + // TODO: Remove the hard-coded uploader type before v1.10 FC + volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, "restic", pvc) if volumeBackup, err = b.repoManager.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { errs = append(errs, err) continue @@ -236,7 +236,7 @@ func isHostPathVolume(volume *corev1api.Volume, pvc *corev1api.PersistentVolumeC return pv.Spec.HostPath != nil, nil } -func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume corev1api.Volume, repoIdentifier string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeBackup { +func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume corev1api.Volume, repoIdentifier, uploaderType string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeBackup { pvb := &velerov1api.PodVolumeBackup{ ObjectMeta: metav1.ObjectMeta{ Namespace: backup.Namespace, @@ -274,6 +274,7 @@ func newPodVolumeBackup(backup *velerov1api.Backup, pod *corev1api.Pod, volume c }, BackupStorageLocation: backup.Spec.StorageLocation, RepoIdentifier: repoIdentifier, + UploaderType: uploaderType, }, } diff --git a/pkg/restic/mocks/repository_manager.go b/pkg/restic/mocks/repository_manager.go index b164cb3e0..de8770c37 100644 --- a/pkg/restic/mocks/repository_manager.go +++ b/pkg/restic/mocks/repository_manager.go @@ -31,11 +31,11 @@ type RepositoryManager struct { } // ConnectToRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) ConnectToRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) ConnectToRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(*v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) @@ -59,11 +59,11 @@ func (_m *RepositoryManager) Forget(_a0 context.Context, _a1 restic.SnapshotIden } // InitRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) InitRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) InitRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) @@ -119,11 +119,11 @@ func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) ( } // PruneRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) PruneRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) PruneRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) @@ -133,11 +133,11 @@ func (_m *RepositoryManager) PruneRepo(repo *v1.ResticRepository) error { } // UnlockRepo provides a mock function with given fields: repo -func (_m *RepositoryManager) UnlockRepo(repo *v1.ResticRepository) error { +func (_m *RepositoryManager) UnlockRepo(repo *v1.BackupRepository) error { ret := _m.Called(repo) var r0 error - if rf, ok := ret.Get(0).(func(*v1.ResticRepository) error); ok { + if rf, ok := ret.Get(0).(func(repository *v1.BackupRepository) error); ok { r0 = rf(repo) } else { r0 = ret.Error(0) diff --git a/pkg/restic/repository_ensurer.go b/pkg/restic/repository_ensurer.go index f1f4f168a..d764a49c7 100644 --- a/pkg/restic/repository_ensurer.go +++ b/pkg/restic/repository_ensurer.go @@ -38,11 +38,11 @@ import ( // repositoryEnsurer ensures that Velero restic repositories are created and ready. type repositoryEnsurer struct { log logrus.FieldLogger - repoLister velerov1listers.ResticRepositoryLister - repoClient velerov1client.ResticRepositoriesGetter + repoLister velerov1listers.BackupRepositoryLister + repoClient velerov1client.BackupRepositoriesGetter repoChansLock sync.Mutex - repoChans map[string]chan *velerov1api.ResticRepository + repoChans map[string]chan *velerov1api.BackupRepository // repoLocksMu synchronizes reads/writes to the repoLocks map itself // since maps are not threadsafe. @@ -55,20 +55,20 @@ type repoKey struct { backupLocation string } -func newRepositoryEnsurer(repoInformer velerov1informers.ResticRepositoryInformer, repoClient velerov1client.ResticRepositoriesGetter, log logrus.FieldLogger) *repositoryEnsurer { +func newRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *repositoryEnsurer { r := &repositoryEnsurer{ log: log, repoLister: repoInformer.Lister(), repoClient: repoClient, - repoChans: make(map[string]chan *velerov1api.ResticRepository), + repoChans: make(map[string]chan *velerov1api.BackupRepository), repoLocks: make(map[repoKey]*sync.Mutex), } repoInformer.Informer().AddEventHandler( cache.ResourceEventHandlerFuncs{ UpdateFunc: func(old, upd interface{}) { - oldObj := old.(*velerov1api.ResticRepository) - newObj := upd.(*velerov1api.ResticRepository) + oldObj := old.(*velerov1api.BackupRepository) + newObj := upd.(*velerov1api.BackupRepository) // we're only interested in phase-changing updates if oldObj.Status.Phase == newObj.Status.Phase { @@ -76,7 +76,7 @@ func newRepositoryEnsurer(repoInformer velerov1informers.ResticRepositoryInforme } // we're only interested in updates where the updated object is either Ready or NotReady - if newObj.Status.Phase != velerov1api.ResticRepositoryPhaseReady && newObj.Status.Phase != velerov1api.ResticRepositoryPhaseNotReady { + if newObj.Status.Phase != velerov1api.BackupRepositoryPhaseReady && newObj.Status.Phase != velerov1api.BackupRepositoryPhaseNotReady { return } @@ -105,7 +105,7 @@ func repoLabels(volumeNamespace, backupLocation string) labels.Set { } } -func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.ResticRepository, error) { +func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation) // It's only safe to have one instance of this method executing concurrently for a @@ -132,7 +132,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam selector := labels.SelectorFromSet(repoLabels(volumeNamespace, backupLocation)) - repos, err := r.repoLister.ResticRepositories(namespace).List(selector) + repos, err := r.repoLister.BackupRepositories(namespace).List(selector) if err != nil { return nil, errors.WithStack(err) } @@ -140,7 +140,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam return nil, errors.Errorf("more than one ResticRepository found for workload namespace %q, backup storage location %q", volumeNamespace, backupLocation) } if len(repos) == 1 { - if repos[0].Status.Phase != velerov1api.ResticRepositoryPhaseReady { + if repos[0].Status.Phase != velerov1api.BackupRepositoryPhaseReady { return nil, errors.Errorf("restic repository is not ready: %s", repos[0].Status.Message) } @@ -151,13 +151,13 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam log.Debug("No repository found, creating one") // no repo found: create one and wait for it to be ready - repo := &velerov1api.ResticRepository{ + repo := &velerov1api.BackupRepository{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, GenerateName: fmt.Sprintf("%s-%s-", volumeNamespace, backupLocation), Labels: repoLabels(volumeNamespace, backupLocation), }, - Spec: velerov1api.ResticRepositorySpec{ + Spec: velerov1api.BackupRepositorySpec{ VolumeNamespace: volumeNamespace, BackupStorageLocation: backupLocation, }, @@ -169,7 +169,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam close(repoChan) }() - if _, err := r.repoClient.ResticRepositories(namespace).Create(context.TODO(), repo, metav1.CreateOptions{}); err != nil { + if _, err := r.repoClient.BackupRepositories(namespace).Create(context.TODO(), repo, metav1.CreateOptions{}); err != nil { return nil, errors.Wrapf(err, "unable to create restic repository resource") } @@ -181,7 +181,8 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam case <-ctx.Done(): return nil, errors.New("timed out waiting for restic repository to become ready") case res := <-repoChan: - if res.Status.Phase == velerov1api.ResticRepositoryPhaseNotReady { + + if res.Status.Phase == velerov1api.BackupRepositoryPhaseNotReady { return nil, errors.Errorf("restic repository is not ready: %s", res.Status.Message) } @@ -189,11 +190,11 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam } } -func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.ResticRepository { +func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { r.repoChansLock.Lock() defer r.repoChansLock.Unlock() - r.repoChans[name] = make(chan *velerov1api.ResticRepository) + r.repoChans[name] = make(chan *velerov1api.BackupRepository) return r.repoChans[name] } diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index f3ff735f9..f0ab63386 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -43,19 +43,19 @@ import ( // RepositoryManager executes commands against restic repositories. type RepositoryManager interface { // InitRepo initializes a repo with the specified name and identifier. - InitRepo(repo *velerov1api.ResticRepository) error + InitRepo(repo *velerov1api.BackupRepository) error // ConnectToRepo runs the 'restic snapshots' command against the // specified repo, and returns an error if it fails. This is // intended to be used to ensure that the repo exists/can be // authenticated to. - ConnectToRepo(repo *velerov1api.ResticRepository) error + ConnectToRepo(repo *velerov1api.BackupRepository) error // PruneRepo deletes unused data from a repo. - PruneRepo(repo *velerov1api.ResticRepository) error + PruneRepo(repo *velerov1api.BackupRepository) error // UnlockRepo removes stale locks from a repo. - UnlockRepo(repo *velerov1api.ResticRepository) error + UnlockRepo(repo *velerov1api.BackupRepository) error // Forget removes a snapshot from the list of // available snapshots in a repo. @@ -83,7 +83,7 @@ type RestorerFactory interface { type repositoryManager struct { namespace string veleroClient clientset.Interface - repoLister velerov1listers.ResticRepositoryLister + repoLister velerov1listers.BackupRepositoryLister repoInformerSynced cache.InformerSynced kbClient kbclient.Client log logrus.FieldLogger @@ -111,8 +111,8 @@ func NewRepositoryManager( ctx context.Context, namespace string, veleroClient clientset.Interface, - repoInformer velerov1informers.ResticRepositoryInformer, - repoClient velerov1client.ResticRepositoriesGetter, + repoInformer velerov1informers.BackupRepositoryInformer, + repoClient velerov1client.BackupRepositoriesGetter, kbClient kbclient.Client, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, @@ -181,7 +181,7 @@ func (rm *repositoryManager) NewRestorer(ctx context.Context, restore *velerov1a return r, nil } -func (rm *repositoryManager) InitRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) InitRepo(repo *velerov1api.BackupRepository) error { // restic init requires an exclusive lock rm.repoLocker.LockExclusive(repo.Name) defer rm.repoLocker.UnlockExclusive(repo.Name) @@ -189,7 +189,7 @@ func (rm *repositoryManager) InitRepo(repo *velerov1api.ResticRepository) error return rm.exec(InitCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) } -func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.BackupRepository) error { // restic snapshots requires a non-exclusive lock rm.repoLocker.Lock(repo.Name) defer rm.repoLocker.Unlock(repo.Name) @@ -204,7 +204,7 @@ func (rm *repositoryManager) ConnectToRepo(repo *velerov1api.ResticRepository) e return rm.exec(snapshotsCmd, repo.Spec.BackupStorageLocation) } -func (rm *repositoryManager) PruneRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) PruneRepo(repo *velerov1api.BackupRepository) error { // restic prune requires an exclusive lock rm.repoLocker.LockExclusive(repo.Name) defer rm.repoLocker.UnlockExclusive(repo.Name) @@ -212,7 +212,7 @@ func (rm *repositoryManager) PruneRepo(repo *velerov1api.ResticRepository) error return rm.exec(PruneCommand(repo.Spec.ResticIdentifier), repo.Spec.BackupStorageLocation) } -func (rm *repositoryManager) UnlockRepo(repo *velerov1api.ResticRepository) error { +func (rm *repositoryManager) UnlockRepo(repo *velerov1api.BackupRepository) error { // restic unlock requires a non-exclusive lock rm.repoLocker.Lock(repo.Name) defer rm.repoLocker.Unlock(repo.Name) diff --git a/pkg/restic/restorer.go b/pkg/restic/restorer.go index 242cc717d..e747b0b6d 100644 --- a/pkg/restic/restorer.go +++ b/pkg/restic/restorer.go @@ -139,8 +139,8 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { } } } - - volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, pvc) + // TODO: Remove the hard-coded uploader type before v1.10 FC + volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, "restic", pvc) if err := errorOnly(r.repoManager.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { errs = append(errs, errors.WithStack(err)) @@ -169,7 +169,7 @@ ForEachVolume: return errs } -func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backupLocation, volume, snapshot, repoIdentifier string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeRestore { +func newPodVolumeRestore(restore *velerov1api.Restore, pod *corev1api.Pod, backupLocation, volume, snapshot, repoIdentifier, uploaderType string, pvc *corev1api.PersistentVolumeClaim) *velerov1api.PodVolumeRestore { pvr := &velerov1api.PodVolumeRestore{ ObjectMeta: metav1.ObjectMeta{ Namespace: restore.Namespace, From 82ac228a01eb99bf590f0b06ca16bbbf5f56cc7c Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 22 Jul 2022 16:19:47 +0800 Subject: [PATCH 26/38] Delay CA file deletion in PVB controller Fix #5140. Signed-off-by: Xun Jiang --- changelogs/unreleased/5145-jxun | 1 + pkg/controller/pod_volume_backup_controller.go | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 changelogs/unreleased/5145-jxun diff --git a/changelogs/unreleased/5145-jxun b/changelogs/unreleased/5145-jxun new file mode 100644 index 000000000..959ecf73d --- /dev/null +++ b/changelogs/unreleased/5145-jxun @@ -0,0 +1 @@ +Delay CA file deletion in PVB controller. \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 322592f80..9cb6a3ae2 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -124,7 +124,11 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ if err != nil { return r.updateStatusToFailed(ctx, &pvb, err, "building Restic command", log) } - defer os.Remove(resticDetails.credsFile) + + defer func() { + os.Remove(resticDetails.credsFile) + os.Remove(resticDetails.caCertFile) + }() backupLocation := &velerov1api.BackupStorageLocation{} if err := r.Client.Get(context.Background(), client.ObjectKey{ @@ -344,8 +348,6 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log if err != nil { log.WithError(err).Error("creating temporary caCert file") } - defer os.Remove(details.caCertFile) - } cmd.CACertFile = details.caCertFile From 396e68b8108f2b8369f59bc156be527997b9798d Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 22 Jul 2022 14:16:51 +0800 Subject: [PATCH 27/38] VolumeSnapshotLocation refactor with kubebuilder. 1. modify VSL CRD API file name. Add kubebuilder related marker. 2. Add Labels init code for VSL create command. Signed-off-by: Xun Jiang --- changelogs/unreleased/5148-jxun | 1 + .../v1/bases/velero.io_volumesnapshotlocations.yaml | 2 ++ config/crd/v1/crds/crds.go | 2 +- config/rbac/role.yaml | 12 ++++++++++++ ..._location.go => volume_snapshot_location_type.go} | 8 +++++++- pkg/cmd/cli/snapshotlocation/create.go | 1 + 6 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 changelogs/unreleased/5148-jxun rename pkg/apis/velero/v1/{volume_snapshot_location.go => volume_snapshot_location_type.go} (88%) diff --git a/changelogs/unreleased/5148-jxun b/changelogs/unreleased/5148-jxun new file mode 100644 index 000000000..e0e489b8d --- /dev/null +++ b/changelogs/unreleased/5148-jxun @@ -0,0 +1 @@ +VolumeSnapshotLocation refactor with kubebuilder. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml index 56ef139d2..b47713497 100644 --- a/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml +++ b/config/crd/v1/bases/velero.io_volumesnapshotlocations.yaml @@ -13,6 +13,8 @@ spec: kind: VolumeSnapshotLocation listKind: VolumeSnapshotLocationList plural: volumesnapshotlocations + shortNames: + - vsl singular: volumesnapshotlocation scope: Namespaced versions: diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 4b1197a47..db0c6d934 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -39,7 +39,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdb6\x10\xbd\xebW\f\xd2C.\x95\x9c\xa0\x87\x16\xba\x05n\x0fA\xd3`\x11\xa7\xbe\x14=\xd0\xe4Ȟ.E\xb2\x9c\xa1\xdb\xed\xaf/HQ돕\xb7Y\xa0э\xc3\xe1\x9b7o>\xec\xa6m\xdbF\x05\xdabd\xf2\xae\a\x15\b\xff\x16t\xf9\xc4\xdd\xfd\x0fܑ_\x1d\xdf6\xf7\xe4L\x0f\xeb\xc4\xe2\xc7O\xc8>E\x8d?\xe2@\x8e\x84\xbckF\x14e\x94\xa8\xbe\x01P\xceyQ\xd9\xcc\xf9\b\xa0\xbd\x93\xe8\xad\xc5\xd8\xee\xd1u\xf7i\x87\xbbD\xd6`,\xe0s\xe8\xe3\x9b\xee\xfb\xeeM\x03\xa0#\x96\xe7\x9fiD\x165\x86\x1e\\\xb2\xb6\x01pj\xc4\x1e\x8eަ\x11٩\xc0\a/\xd6\xeb)XwD\x8b\xd1w\xe4\x1b\x0e\xa8s\xec}\xf4)\xf4p\xba\x98 *\xaf)\xa7mA\xdbT\xb4\x0f\x15\xad8Xb\xf9\xf9\x19\xa7\x0f\xc4R\x1c\x83MQٛ̊\x0f\x93\xdb'\xab\xe2-\xaf\x06\x80\xb5\x0f\xd8\xc3\xc7L1(\x8d\xa6\x01\xa8\xf2\x14\xca\xed,\xc0\xdb\tQ\x1fpTS.\x00>\xa0{w\xf7~\xfb\xdd\xe6\xc2\f`\x90u\xa4 E\xe4\xe5D\x80\x18\x14\xccL\xe0\xaf\x03F\x84mQ\rX|D\xae\xa4\x1fA\x01f\xfe\xdc=\x1aC\xf4\x01\xa3\xd0,\xf0\xf4\x9d\xb5י\xf5\x8a\xd7\xebL}\xf2\x02\x93\xfb\n\x19\xe4\x80s\xfahj\xb6\xe0\a\x90\x031D\f\x11\x19\x9d\x9c\xcau\xfa\xfc\x00ʁ\xdf\xfd\x81Z:\xd8`\xcc0\xc0\a\x9f\xac\xc9\xedx\xc4(\x10Q\xfb\xbd\xa3\u007f\x1e\xb1\x19ė\xa0V\t\xd6ʞ>r\x82\xd1)\vGe\x13~\v\xca\x19\x18\xd5\x03D\xccQ \xb93\xbc\xe2\xc2\x1d\xfc\xe2#\x02\xb9\xc1\xf7p\x10\tܯV{\x92y\xac\xb4\x1f\xc7\xe4H\x1eVeBh\x97\xc4G^\x19<\xa2]1\xed[\x15\xf5\x81\x04\xb5\xa4\x88+\x15\xa8-\xd4\xdd\xd4\xed\xa3\xf9&\xd6A\xe4\xd7\x17\\\xe5!w\x11K$\xb7?\xbb(\xed\xfeL\x05r\xa7O\x8d0=\x9d\xb28\t\x9dMY\x9dO?m>\xc3\x1c\xba\x14\xe3Z\xfd\xa2\xfb\xe9!\x9fJ\x90\x05#7`\x9c\x8a8D?\x16Lt&xrR\x0e\xda\x12\xbak\xf99\xedF\x92\\\xf7?\x13\xb2\xe4Zu\xb0.\xbb\x06v\b)\x18%h:x\xef`\xadF\xb4k\xc5\xf8\xd5\v\x90\x95\xe66\v\xfbe%8_\x93\xd7Γj\xe7\x03V\x97؍z-O\xf2&\xa0\xbe\x18\xa0\x8cB\x03\xd5\xc9\x1e|\xbc\xd2U\xcds\xbe\x8c\xd7]\xb8/\x0f8L;~\xa0\xfd\xb5\x15@\x19S~!\x94\xbd\xbb\xf9\xf6\x19\xc1\x16\xf2^\x97H\xb9Q\a\x1f3\xa3#\x19\x8c\xed\x9cge\x92bM\x98\xd0\x1a\xee\x9e@\xdeм&Y \x9fҼ\xe0qW\xdd2\x93,\xf4\xfcl\xdaPX\x17fY\x9fj\x8f\xb7\x18,d\x9c;\x9c\"^\xcdj\xfb\x18\xe0\x8bzG\x94$~y\xf7\x94g\xd5sW;H\xa7\x18\xd1I\xc5\\ش\xffO\a\x85\x83b\xfc\x0f͗#\xdc\xe5\x97s\x19,\r\xa8\x1f\xb4\xc5\t\x10\xfc\xb0\xd0m/\xa2\x9c?ti|ʭ\x85wGEV\xed,.\xdc\xfd\xea\xd4\xcdۛ\xc5_\xac\xe7\x13#\xe7ujz\x90\x98&\xec\xdae\xd5r\xaa\xbe\xd2\x1a\x83\xa0\xf9x\xfd\xaf\xe7ի\x8b?.娽\x9b\x86\x95{\xf8\xed\xf7fBE\xb3\x9d\xff\x81d\xe3\xbf\x01\x00\x00\xff\xff\xbf\xca\xff\xa71\n\x00\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), } var CRDs = crds() diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 0b43e2f54..61eefdf1b 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -191,3 +191,15 @@ rules: - get - patch - update +- apiGroups: + - velero.io + resources: + - volumesnapshotlocations + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/pkg/apis/velero/v1/volume_snapshot_location.go b/pkg/apis/velero/v1/volume_snapshot_location_type.go similarity index 88% rename from pkg/apis/velero/v1/volume_snapshot_location.go rename to pkg/apis/velero/v1/volume_snapshot_location_type.go index a2ba652a6..505e1d994 100644 --- a/pkg/apis/velero/v1/volume_snapshot_location.go +++ b/pkg/apis/velero/v1/volume_snapshot_location_type.go @@ -1,5 +1,5 @@ /* -Copyright 2018 the Velero contributors. +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. @@ -20,6 +20,10 @@ import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:resource:shortName=vsl +// +kubebuilder:object:generate=true +// +kubebuilder:storageversion // VolumeSnapshotLocation is a location where Velero stores volume snapshots. type VolumeSnapshotLocation struct { @@ -36,6 +40,8 @@ type VolumeSnapshotLocation struct { } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:object:root=true +// +kubebuilder:rbac:groups=velero.io,resources=volumesnapshotlocations,verbs=get;list;watch;create;update;patch;delete // VolumeSnapshotLocationList is a list of VolumeSnapshotLocations. type VolumeSnapshotLocationList struct { diff --git a/pkg/cmd/cli/snapshotlocation/create.go b/pkg/cmd/cli/snapshotlocation/create.go index 824156eac..2de6b2827 100644 --- a/pkg/cmd/cli/snapshotlocation/create.go +++ b/pkg/cmd/cli/snapshotlocation/create.go @@ -63,6 +63,7 @@ type CreateOptions struct { func NewCreateOptions() *CreateOptions { return &CreateOptions{ Config: flag.NewMap(), + Labels: flag.NewMap(), } } From a109a11851608bb33bb901b2e0fac35dcd096c0b Mon Sep 17 00:00:00 2001 From: OrlinVasilev Date: Tue, 26 Jul 2022 14:46:42 +0300 Subject: [PATCH 28/38] Fix zoom link for new meeting Signed-off-by: OrlinVasilev --- site/content/community/_index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/community/_index.md b/site/content/community/_index.md index 8680073fe..b243ce1d9 100644 --- a/site/content/community/_index.md +++ b/site/content/community/_index.md @@ -14,7 +14,7 @@ You can follow the work we do, see our milestones, and our backlog on our [GitHu * Follow us on Twitter at [@projectvelero](https://twitter.com/projectvelero) * Join our Kubernetes Slack channel and talk to over 800 other community members: [#velero](https://kubernetes.slack.com/messages/velero) * Join our [Google Group](https://groups.google.com/forum/#!forum/projectvelero) to get updates on the project and invites to community meetings. -* Join the Velero community meetings - [Zoom link](https://vmware.zoom.us/j/551441444?pwd=dHJyMWZtdHFPWWFJaTh5TnFuYWMvZz09): +* Join the Velero community meetings - [Zoom link](https://VMware.zoom.us/j/94501971662?pwd=aUxVbWVEWHZSbDh4ZGdGU1cxYUFoZz09): * 1st and 3rd Tuesday at 12PM ET / 9AM PT ([Convert to your time zone](https://dateful.com/convert/est-edt-eastern-time?t=12pm)) * 2nd and 4th Wednesday at 8am China Standard Time / Tuesday 7pm EST (8pm EDT) / Tuesday 4pm PST (5pm PDT) ([Convert to your time zone](https://dateful.com/convert/beijing-china?t=8am)) * Read and comment on the [meeting notes](https://hackmd.io/Jq6F5zqZR7S80CeDWUklkA?view) From cd643bbac9c14d5ef8b64c16453808a89329eb35 Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 28 Jul 2022 17:43:34 +0800 Subject: [PATCH 29/38] Add labeled and unlabeled events for PR changelog check action. Signed-off-by: Xun Jiang --- .github/workflows/pr-changelog-check.yml | 6 +++++- changelogs/unreleased/5157-jxun | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5157-jxun diff --git a/.github/workflows/pr-changelog-check.yml b/.github/workflows/pr-changelog-check.yml index 647c9d89a..308e07d2d 100644 --- a/.github/workflows/pr-changelog-check.yml +++ b/.github/workflows/pr-changelog-check.yml @@ -1,5 +1,9 @@ name: Pull Request Changelog Check -on: [pull_request] +# by setting `on: [pull_request]`, that means action will be trigger when PR is opened, synchronize, reopened. +# Add labeled and unlabeled events too. +on: + pull_request: + types: [opened, synchronize, reopened, labeled, unlabeled] jobs: build: diff --git a/changelogs/unreleased/5157-jxun b/changelogs/unreleased/5157-jxun new file mode 100644 index 000000000..e42a0c93c --- /dev/null +++ b/changelogs/unreleased/5157-jxun @@ -0,0 +1 @@ +Add labeled and unlabeled events for PR changelog check action. \ No newline at end of file From 52fd18e9db3451e4ef35f6fff7cca6ca54b9b18a Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:06:18 +0800 Subject: [PATCH 30/38] Kopia Integration Change - Storage Configuration (#5142) * unified repo storge config Signed-off-by: Lyndon-Li * add UT Signed-off-by: Lyndon-Li --- changelogs/unreleased/5142-lyndon | 4 + .../restic_repository_controller.go | 3 +- pkg/repository/config/aws.go | 99 +++++ pkg/{restic => repository/config}/aws_test.go | 4 +- pkg/{restic => repository/config}/azure.go | 28 +- .../config}/azure_test.go | 2 +- pkg/{restic => repository/config}/config.go | 49 +-- .../config}/config_test.go | 2 +- pkg/{restic => repository/config}/gcp.go | 20 +- pkg/{restic => repository/config}/gcp_test.go | 4 +- pkg/repository/provider/provider.go | 56 +++ pkg/repository/provider/unified_repo.go | 292 ++++++++++++ pkg/repository/provider/unified_repo_test.go | 414 ++++++++++++++++++ pkg/repository/udmrepo/repo-option-consts.go | 58 +++ pkg/repository/udmrepo/repo.go | 171 ++++++++ pkg/restic/aws.go | 41 -- pkg/restic/common.go | 21 +- pkg/util/ownership/backup_owner.go | 42 ++ 18 files changed, 1208 insertions(+), 102 deletions(-) create mode 100644 changelogs/unreleased/5142-lyndon create mode 100644 pkg/repository/config/aws.go rename pkg/{restic => repository/config}/aws_test.go (96%) rename pkg/{restic => repository/config}/azure.go (88%) rename pkg/{restic => repository/config}/azure_test.go (99%) rename pkg/{restic => repository/config}/config.go (74%) rename pkg/{restic => repository/config}/config_test.go (99%) rename pkg/{restic => repository/config}/gcp.go (59%) rename pkg/{restic => repository/config}/gcp_test.go (95%) create mode 100644 pkg/repository/provider/provider.go create mode 100644 pkg/repository/provider/unified_repo.go create mode 100644 pkg/repository/provider/unified_repo_test.go create mode 100644 pkg/repository/udmrepo/repo-option-consts.go create mode 100644 pkg/repository/udmrepo/repo.go delete mode 100644 pkg/restic/aws.go create mode 100644 pkg/util/ownership/backup_owner.go diff --git a/changelogs/unreleased/5142-lyndon b/changelogs/unreleased/5142-lyndon new file mode 100644 index 000000000..10286cf0b --- /dev/null +++ b/changelogs/unreleased/5142-lyndon @@ -0,0 +1,4 @@ +Kopia Integration: Add the Unified Repository Interface definition. +Kopia Integration: Add the changes for Unified Repository storage config. + +Related Issues; #5076, #5080 \ No newline at end of file diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 36f0e76a8..3f27de36f 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -31,6 +31,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -127,7 +128,7 @@ func (r *ResticRepoReconciler) initializeRepo(ctx context.Context, req *velerov1 return r.patchResticRepository(ctx, req, repoNotReady(err.Error())) } - repoIdentifier, err := restic.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) + repoIdentifier, err := repoconfig.GetRepoIdentifier(loc, req.Spec.VolumeNamespace) if err != nil { return r.patchResticRepository(ctx, req, func(rr *velerov1api.BackupRepository) { rr.Status.Message = err.Error() diff --git a/pkg/repository/config/aws.go b/pkg/repository/config/aws.go new file mode 100644 index 000000000..0ff4ca218 --- /dev/null +++ b/pkg/repository/config/aws.go @@ -0,0 +1,99 @@ +/* +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 config + +import ( + "context" + "os" + + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/endpoints" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3/s3manager" + "github.com/pkg/errors" +) + +const ( + // AWS specific environment variable + awsProfileEnvVar = "AWS_PROFILE" + awsProfileKey = "profile" + awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE" +) + +// GetS3ResticEnvVars gets the environment variables that restic +// relies on (AWS_PROFILE) based on info in the provided object +// storage location config map. +func GetS3ResticEnvVars(config map[string]string) (map[string]string, error) { + result := make(map[string]string) + + if credentialsFile, ok := config[CredentialsFileKey]; ok { + result[awsCredentialsFileEnvVar] = credentialsFile + } + + if profile, ok := config[awsProfileKey]; ok { + result[awsProfileEnvVar] = profile + } + + return result, nil +} + +// GetS3Credentials gets the S3 credential values according to the information +// of the provided config or the system's environment variables +func GetS3Credentials(config map[string]string) (credentials.Value, error) { + credentialsFile := config[CredentialsFileKey] + if credentialsFile == "" { + credentialsFile = os.Getenv("AWS_SHARED_CREDENTIALS_FILE") + } + + if credentialsFile == "" { + return credentials.Value{}, errors.New("missing credential file") + } + + creds := credentials.NewSharedCredentials(credentialsFile, "") + credValue, err := creds.Get() + if err != nil { + return credValue, err + } + + return credValue, nil +} + +// GetAWSBucketRegion returns the AWS region that a bucket is in, or an error +// if the region cannot be determined. +func GetAWSBucketRegion(bucket string) (string, error) { + var region string + + sess, err := session.NewSession() + if err != nil { + return "", errors.WithStack(err) + } + + for _, partition := range endpoints.DefaultPartitions() { + for regionHint := range partition.Regions() { + region, _ = s3manager.GetBucketRegion(context.Background(), sess, bucket, regionHint) + + // we only need to try a single region hint per partition, so break after the first + break + } + + if region != "" { + return region, nil + } + } + + return "", errors.New("unable to determine bucket's region") +} diff --git a/pkg/restic/aws_test.go b/pkg/repository/config/aws_test.go similarity index 96% rename from pkg/restic/aws_test.go rename to pkg/repository/config/aws_test.go index 51f3ceb99..bdd3e4fa2 100644 --- a/pkg/restic/aws_test.go +++ b/pkg/repository/config/aws_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "testing" @@ -55,7 +55,7 @@ func TestGetS3ResticEnvVars(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual, err := getS3ResticEnvVars(tc.config) + actual, err := GetS3ResticEnvVars(tc.config) require.NoError(t, err) diff --git a/pkg/restic/azure.go b/pkg/repository/config/azure.go similarity index 88% rename from pkg/restic/azure.go rename to pkg/repository/config/azure.go index 20324b8e3..8c5871c52 100644 --- a/pkg/restic/azure.go +++ b/pkg/repository/config/azure.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "context" @@ -37,6 +37,7 @@ const ( storageAccountConfigKey = "storageAccount" storageAccountKeyEnvVarConfigKey = "storageAccountKeyEnvVar" subscriptionIDConfigKey = "subscriptionId" + storageDomainConfigKey = "storageDomain" ) // getSubscriptionID gets the subscription ID from the 'config' map if it contains @@ -131,10 +132,10 @@ func mapLookup(data map[string]string) func(string) string { } } -// getAzureResticEnvVars gets the environment variables that restic +// GetAzureResticEnvVars gets the environment variables that restic // relies on (AZURE_ACCOUNT_NAME and AZURE_ACCOUNT_KEY) based // on info in the provided object storage location config map. -func getAzureResticEnvVars(config map[string]string) (map[string]string, error) { +func GetAzureResticEnvVars(config map[string]string) (map[string]string, error) { storageAccountKey, _, err := getStorageAccountKey(config) if err != nil { return nil, err @@ -158,7 +159,7 @@ func credentialsFileFromEnv() string { // selectCredentialsFile selects the Azure credentials file to use, retrieving it // from the given config or falling back to retrieving it from the environment. func selectCredentialsFile(config map[string]string) string { - if credentialsFile, ok := config[credentialsFileKey]; ok { + if credentialsFile, ok := config[CredentialsFileKey]; ok { return credentialsFile } @@ -208,3 +209,22 @@ func getRequiredValues(getValue func(string) string, keys ...string) (map[string return results, nil } + +// GetAzureStorageDomain gets the Azure storage domain required by a Azure blob connection, +// if the provided config doean't have the value, get it from system's environment variables +func GetAzureStorageDomain(config map[string]string) string { + if domain, exist := config[storageDomainConfigKey]; exist { + return domain + } else { + return os.Getenv(cloudNameEnvVar) + } +} + +func GetAzureCredentials(config map[string]string) (string, string, error) { + storageAccountKey, _, err := getStorageAccountKey(config) + if err != nil { + return "", "", err + } + + return config[storageAccountConfigKey], storageAccountKey, nil +} diff --git a/pkg/restic/azure_test.go b/pkg/repository/config/azure_test.go similarity index 99% rename from pkg/restic/azure_test.go rename to pkg/repository/config/azure_test.go index acb2f2506..d20ac2e28 100644 --- a/pkg/restic/azure_test.go +++ b/pkg/repository/config/azure_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "os" diff --git a/pkg/restic/config.go b/pkg/repository/config/config.go similarity index 74% rename from pkg/restic/config.go rename to pkg/repository/config/config.go index 1600f39fa..24dc8d6b3 100644 --- a/pkg/restic/config.go +++ b/pkg/repository/config/config.go @@ -14,17 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( - "context" "fmt" "path" "strings" - "github.com/aws/aws-sdk-go/aws/endpoints" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3/s3manager" "github.com/pkg/errors" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -37,11 +33,18 @@ const ( AWSBackend BackendType = "velero.io/aws" AzureBackend BackendType = "velero.io/azure" GCPBackend BackendType = "velero.io/gcp" + FSBackend BackendType = "velero.io/fs" +) + +const ( + // CredentialsFileKey is the key within a BSL config that is checked to see if + // the BSL is using its own credentials, rather than those in the environment + CredentialsFileKey = "credentialsFile" ) // this func is assigned to a package-level variable so it can be // replaced when unit-testing -var getAWSBucketRegion = getBucketRegion +var getAWSBucketRegion = GetAWSBucketRegion // getRepoPrefix returns the prefix of the value of the --repo flag for // restic commands, i.e. everything except the "/". @@ -55,7 +58,7 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) prefix = layout.GetResticDir() } - backendType := getBackendType(location.Spec.Provider) + backendType := GetBackendType(location.Spec.Provider) if repoPrefix := location.Spec.Config["resticRepoPrefix"]; repoPrefix != "" { return repoPrefix, nil @@ -89,7 +92,7 @@ func getRepoPrefix(location *velerov1api.BackupStorageLocation) (string, error) return "", errors.New("restic repository prefix (resticRepoPrefix) not specified in backup storage location's config") } -func getBackendType(provider string) BackendType { +func GetBackendType(provider string) BackendType { if !strings.Contains(provider, "/") { provider = "velero.io/" + provider } @@ -97,6 +100,10 @@ func getBackendType(provider string) BackendType { return BackendType(provider) } +func IsBackendTypeValid(backendType BackendType) bool { + return (backendType == AWSBackend || backendType == AzureBackend || backendType == GCPBackend || backendType == FSBackend) +} + // GetRepoIdentifier returns the string to be used as the value of the --repo flag in // restic commands for the given repository. func GetRepoIdentifier(location *velerov1api.BackupStorageLocation, name string) (string, error) { @@ -107,29 +114,3 @@ func GetRepoIdentifier(location *velerov1api.BackupStorageLocation, name string) return fmt.Sprintf("%s/%s", strings.TrimSuffix(prefix, "/"), name), nil } - -// getBucketRegion returns the AWS region that a bucket is in, or an error -// if the region cannot be determined. -func getBucketRegion(bucket string) (string, error) { - var region string - - sess, err := session.NewSession() - if err != nil { - return "", errors.WithStack(err) - } - - for _, partition := range endpoints.DefaultPartitions() { - for regionHint := range partition.Regions() { - region, _ = s3manager.GetBucketRegion(context.Background(), sess, bucket, regionHint) - - // we only need to try a single region hint per partition, so break after the first - break - } - - if region != "" { - return region, nil - } - } - - return "", errors.New("unable to determine bucket's region") -} diff --git a/pkg/restic/config_test.go b/pkg/repository/config/config_test.go similarity index 99% rename from pkg/restic/config_test.go rename to pkg/repository/config/config_test.go index 8418d6808..2fa26a193 100644 --- a/pkg/restic/config_test.go +++ b/pkg/repository/config/config_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "testing" diff --git a/pkg/restic/gcp.go b/pkg/repository/config/gcp.go similarity index 59% rename from pkg/restic/gcp.go rename to pkg/repository/config/gcp.go index 96d1edfe6..ed9e3ec6a 100644 --- a/pkg/restic/gcp.go +++ b/pkg/repository/config/gcp.go @@ -14,21 +14,33 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config + +import "os" const ( // GCP specific environment variable gcpCredentialsFileEnvVar = "GOOGLE_APPLICATION_CREDENTIALS" ) -// getGCPResticEnvVars gets the environment variables that restic relies +// GetGCPResticEnvVars gets the environment variables that restic relies // on based on info in the provided object storage location config map. -func getGCPResticEnvVars(config map[string]string) (map[string]string, error) { +func GetGCPResticEnvVars(config map[string]string) (map[string]string, error) { result := make(map[string]string) - if credentialsFile, ok := config[credentialsFileKey]; ok { + if credentialsFile, ok := config[CredentialsFileKey]; ok { result[gcpCredentialsFileEnvVar] = credentialsFile } return result, nil } + +// GetGCPCredentials gets the credential file required by a GCP bucket connection, +// if the provided config doean't have the value, get it from system's environment variables +func GetGCPCredentials(config map[string]string) string { + if credentialsFile, ok := config[CredentialsFileKey]; ok { + return credentialsFile + } else { + return os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") + } +} diff --git a/pkg/restic/gcp_test.go b/pkg/repository/config/gcp_test.go similarity index 95% rename from pkg/restic/gcp_test.go rename to pkg/repository/config/gcp_test.go index 37f2bf2c7..cd4411e3b 100644 --- a/pkg/restic/gcp_test.go +++ b/pkg/repository/config/gcp_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package config import ( "testing" @@ -46,7 +46,7 @@ func TestGetGCPResticEnvVars(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual, err := getGCPResticEnvVars(tc.config) + actual, err := GetGCPResticEnvVars(tc.config) require.NoError(t, err) diff --git a/pkg/repository/provider/provider.go b/pkg/repository/provider/provider.go new file mode 100644 index 000000000..36d69a594 --- /dev/null +++ b/pkg/repository/provider/provider.go @@ -0,0 +1,56 @@ +/* +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 provider + +import ( + "context" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// RepoParam includes the parameters to manipulate a backup repository +// SubDir is used to generate the path in the backup storage +type RepoParam struct { + SubDir string + BackupLocation *velerov1api.BackupStorageLocation +} + +type Provider interface { + //InitRepo is to initialize a repository from a new storage place + InitRepo(ctx context.Context, param RepoParam) error + + //ConnectToRepo is to establish the connection to a + //storage place that a repository is already initialized + ConnectToRepo(ctx context.Context, param RepoParam) error + + //PrepareRepo is a combination of InitRepo and ConnectToRepo, + //it may do initializing + connecting, connecting only if the repository + //is already initialized, or do nothing if the repository is already connected + PrepareRepo(ctx context.Context, param RepoParam) error + + //PruneRepo does a full prune/maintenance of the repository + PruneRepo(ctx context.Context, param RepoParam) error + + //PruneRepoQuick does a quick prune/maintenance of the repository if available + PruneRepoQuick(ctx context.Context, param RepoParam) error + + //EnsureUnlockRepo esures to remove any stale file locks in the storage + EnsureUnlockRepo(ctx context.Context, param RepoParam) error + + //Forget is to delete a snapshot from the repository + Forget(ctx context.Context, snapshotID string, param RepoParam) error +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go new file mode 100644 index 000000000..49cef09ce --- /dev/null +++ b/pkg/repository/provider/unified_repo.go @@ -0,0 +1,292 @@ +/* +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 provider + +import ( + "context" + "fmt" + "path" + "strings" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + + "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + "github.com/vmware-tanzu/velero/pkg/util/ownership" +) + +type unifiedRepoProvider struct { + credentialsFileStore credentials.FileStore + workPath string + repoService udmrepo.BackupRepoService + log logrus.FieldLogger +} + +// this func is assigned to a package-level variable so it can be +// replaced when unit-testing +var getAzureCredentials = repoconfig.GetAzureCredentials +var getS3Credentials = repoconfig.GetS3Credentials +var getGCPCredentials = repoconfig.GetGCPCredentials +var getS3BucketRegion = repoconfig.GetAWSBucketRegion +var getAzureStorageDomain = repoconfig.GetAzureStorageDomain + +// NewUnifiedRepoProvider creates the service provider for Unified Repo +// workPath is the path for Unified Repo to store some local information +// workPath could be empty, if so, the default path will be used +func NewUnifiedRepoProvider( + credentialFileStore credentials.FileStore, + workPath string, + log logrus.FieldLogger, +) (Provider, error) { + repo := unifiedRepoProvider{ + credentialsFileStore: credentialFileStore, + workPath: workPath, + log: log, + } + + repo.repoService = createRepoService(log) + + log.Debug("Finished create unified repo service") + + return &repo, nil +} + +func (urp *unifiedRepoProvider) InitRepo(ctx context.Context, param RepoParam) error { + log := urp.log.WithFields(logrus.Fields{ + "BSL name": param.BackupLocation.Name, + "BSL UID": param.BackupLocation.UID, + }) + + log.Debug("Start to init repo") + + repoOption, err := urp.getRepoOption(param) + if err != nil { + return errors.Wrap(err, "error to get repo options") + } + + err = urp.repoService.Init(ctx, repoOption, true) + if err != nil { + return errors.Wrap(err, "error to init backup repo") + } + + log.Debug("Init repo complete") + + return nil +} + +func (urp *unifiedRepoProvider) ConnectToRepo(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) PrepareRepo(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) PruneRepo(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) PruneRepoQuick(ctx context.Context, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) EnsureUnlockRepo(ctx context.Context, param RepoParam) error { + return nil +} + +func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, param RepoParam) error { + ///TODO + return nil +} + +func (urp *unifiedRepoProvider) getRepoPassword(param RepoParam) (string, error) { + ///TODO: get repo password + + return "", nil +} + +func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOptions, error) { + repoOption := udmrepo.RepoOptions{ + StorageType: getStorageType(param.BackupLocation), + ConfigFilePath: getRepoConfigFile(urp.workPath, string(param.BackupLocation.UID)), + Ownership: udmrepo.OwnershipOptions{ + Username: ownership.GetRepositoryOwner().Username, + DomainName: ownership.GetRepositoryOwner().DomainName, + }, + StorageOptions: make(map[string]string), + GeneralOptions: make(map[string]string), + } + + repoPassword, err := urp.getRepoPassword(param) + if err != nil { + return repoOption, errors.Wrap(err, "error to get repo password") + } + + repoOption.RepoPassword = repoPassword + + storeVar, err := getStorageVariables(param.BackupLocation, param.SubDir) + if err != nil { + return repoOption, errors.Wrap(err, "error to get storage variables") + } + + for k, v := range storeVar { + repoOption.StorageOptions[k] = v + } + + storeCred, err := getStorageCredentials(param.BackupLocation, urp.credentialsFileStore) + if err != nil { + return repoOption, errors.Wrap(err, "error to get repo credential env") + } + + for k, v := range storeCred { + repoOption.StorageOptions[k] = v + } + + return repoOption, nil +} + +func getStorageType(backupLocation *velerov1api.BackupStorageLocation) string { + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + + switch backendType { + case repoconfig.AWSBackend: + return udmrepo.StorageTypeS3 + case repoconfig.AzureBackend: + return udmrepo.StorageTypeAzure + case repoconfig.GCPBackend: + return udmrepo.StorageTypeGcs + case repoconfig.FSBackend: + return udmrepo.StorageTypeFs + default: + return "" + } +} + +func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, credentialsFileStore credentials.FileStore) (map[string]string, error) { + result := make(map[string]string) + var err error + + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + if !repoconfig.IsBackendTypeValid(backendType) { + return map[string]string{}, errors.New("invalid storage provider") + } + + config := backupLocation.Spec.Config + if config == nil { + config = map[string]string{} + } + + if backupLocation.Spec.Credential != nil { + config[repoconfig.CredentialsFileKey], err = credentialsFileStore.Path(backupLocation.Spec.Credential) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get credential file in bsl") + } + } + + switch backendType { + case repoconfig.AWSBackend: + credValue, err := getS3Credentials(config) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get s3 credentials") + } + result[udmrepo.StoreOptionS3KeyId] = credValue.AccessKeyID + result[udmrepo.StoreOptionS3Provider] = credValue.ProviderName + result[udmrepo.StoreOptionS3SecretKey] = credValue.SecretAccessKey + result[udmrepo.StoreOptionS3Token] = credValue.SessionToken + + case repoconfig.AzureBackend: + storageAccount, accountKey, err := getAzureCredentials(config) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get azure credentials") + } + result[udmrepo.StoreOptionAzureStorageAccount] = storageAccount + result[udmrepo.StoreOptionAzureKey] = accountKey + + case repoconfig.GCPBackend: + result[udmrepo.StoreOptionCredentialFile] = getGCPCredentials(config) + } + + return result, nil +} + +func getStorageVariables(backupLocation *velerov1api.BackupStorageLocation, repoName string) (map[string]string, error) { + result := make(map[string]string) + + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) + if !repoconfig.IsBackendTypeValid(backendType) { + return map[string]string{}, errors.New("invalid storage provider") + } + + config := backupLocation.Spec.Config + if config == nil { + config = map[string]string{} + } + + bucket := strings.Trim(config["bucket"], "/") + prefix := strings.Trim(config["prefix"], "/") + if backupLocation.Spec.ObjectStorage != nil { + bucket = strings.Trim(backupLocation.Spec.ObjectStorage.Bucket, "/") + prefix = strings.Trim(backupLocation.Spec.ObjectStorage.Prefix, "/") + } + + prefix = path.Join(prefix, udmrepo.StoreOptionPrefixName, repoName) + "/" + + region := config["region"] + + if backendType == repoconfig.AWSBackend { + s3Url := config["s3Url"] + + var err error + if s3Url == "" { + region, err = getS3BucketRegion(bucket) + if err != nil { + return map[string]string{}, errors.Wrap(err, "error get s3 bucket region") + } + + s3Url = fmt.Sprintf("s3-%s.amazonaws.com", region) + } + + result[udmrepo.StoreOptionS3Endpoint] = strings.Trim(s3Url, "/") + result[udmrepo.StoreOptionS3DisableTlsVerify] = config["insecureSkipTLSVerify"] + } else if backendType == repoconfig.AzureBackend { + result[udmrepo.StoreOptionAzureDomain] = getAzureStorageDomain(config) + } + + result[udmrepo.StoreOptionOssBucket] = bucket + result[udmrepo.StoreOptionPrefix] = prefix + result[udmrepo.StoreOptionOssRegion] = strings.Trim(region, "/") + result[udmrepo.StoreOptionFsPath] = config["fspath"] + + return result, nil +} + +func getRepoConfigFile(workPath string, repoID string) string { + ///TODO: call udmrepo to get config file + return "" +} + +func createRepoService(log logrus.FieldLogger) udmrepo.BackupRepoService { + ///TODO: call udmrepo create repo service + return nil +} diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go new file mode 100644 index 000000000..ee78c7b5d --- /dev/null +++ b/pkg/repository/provider/unified_repo_test.go @@ -0,0 +1,414 @@ +/* +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 provider + +import ( + "errors" + "testing" + + awscredentials "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1api "k8s.io/api/core/v1" + + filecredentials "github.com/vmware-tanzu/velero/internal/credentials" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + velerotest "github.com/vmware-tanzu/velero/pkg/test" +) + +func TestGetStorageCredentials(t *testing.T) { + testCases := []struct { + name string + backupLocation velerov1api.BackupStorageLocation + credFileStore filecredentials.FileStore + getAzureCredentials func(map[string]string) (string, string, error) + getS3Credentials func(map[string]string) (awscredentials.Value, error) + getGCPCredentials func(map[string]string) string + expected map[string]string + expectedErr string + }{ + { + name: "invalid provider", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "invalid-provider", + }, + }, + expected: map[string]string{}, + expectedErr: "invalid storage provider", + }, + { + name: "credential section exists in BSL, file store fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "aws", + Credential: &corev1api.SecretKeySelector{}, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("", errors.New("fake error")), + expected: map[string]string{}, + expectedErr: "error get credential file in bsl: fake error", + }, + { + name: "aws, Credential section not exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { + return awscredentials.Value{ + AccessKeyID: "from: " + config["credentialsFile"], + }, nil + }, + + expected: map[string]string{ + "accessKeyID": "from: credentials-from-config-map", + "providerName": "", + "secretAccessKey": "", + "sessionToken": "", + }, + }, + { + name: "aws, Credential section exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + Credential: &corev1api.SecretKeySelector{}, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { + return awscredentials.Value{ + AccessKeyID: "from: " + config["credentialsFile"], + }, nil + }, + + expected: map[string]string{ + "accessKeyID": "from: credentials-from-credential-key", + "providerName": "", + "secretAccessKey": "", + "sessionToken": "", + }, + }, + { + name: "aws, get credentials fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("", nil), + getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { + return awscredentials.Value{}, errors.New("fake error") + }, + expected: map[string]string{}, + expectedErr: "error get s3 credentials: fake error", + }, + { + name: "azure, Credential section exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + Credential: &corev1api.SecretKeySelector{}, + }, + }, + credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + getAzureCredentials: func(config map[string]string) (string, string, error) { + return "storage account from: " + config["credentialsFile"], "", nil + }, + + expected: map[string]string{ + "storageAccount": "storage account from: credentials-from-credential-key", + "storageKey": "", + }, + }, + { + name: "azure, get azure credentials fails", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + getAzureCredentials: func(config map[string]string) (string, string, error) { + return "", "", errors.New("fake error") + }, + + expected: map[string]string{}, + expectedErr: "error get azure credentials: fake error", + }, + { + name: "gcp, Credential section not exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/gcp", + Config: map[string]string{ + "credentialsFile": "credentials-from-config-map", + }, + }, + }, + getGCPCredentials: func(config map[string]string) string { + return "credentials-from-config-map" + }, + + expected: map[string]string{ + "credFile": "credentials-from-config-map", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + getAzureCredentials = tc.getAzureCredentials + getS3Credentials = tc.getS3Credentials + getGCPCredentials = tc.getGCPCredentials + + actual, err := getStorageCredentials(&tc.backupLocation, tc.credFileStore) + + require.Equal(t, tc.expected, actual) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestGetStorageVariables(t *testing.T) { + testCases := []struct { + name string + backupLocation velerov1api.BackupStorageLocation + repoName string + getS3BucketRegion func(string) (string, error) + getAzureStorageDomain func(map[string]string) string + expected map[string]string + expectedErr string + }{ + { + name: "invalid provider", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "invalid-provider", + }, + }, + expected: map[string]string{}, + expectedErr: "invalid storage provider", + }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "region": "fake-region/", + "s3Url": "fake-url", + "insecureSkipTLSVerify": "true", + }, + }, + }, + expected: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix/unified-repo/", + "region": "fake-region", + "fspath": "", + "endpoint": "fake-url", + "skipTLSVerify": "true", + }, + }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url not exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "insecureSkipTLSVerify": "false", + }, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "region from bucket: " + bucket, nil + }, + expected: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix/unified-repo/", + "region": "region from bucket: fake-bucket", + "fspath": "", + "endpoint": "s3-region from bucket: fake-bucket.amazonaws.com", + "skipTLSVerify": "false", + }, + }, + { + name: "aws, ObjectStorage section not exists in BSL, s3Url not exist, get region fail", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{}, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "", errors.New("fake error") + }, + expected: map[string]string{}, + expectedErr: "error get s3 bucket region: fake error", + }, + { + name: "aws, ObjectStorage section exists in BSL, s3Url exist", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/aws", + Config: map[string]string{ + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "s3Url": "fake-url", + "insecureSkipTLSVerify": "false", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "fake-bucket-object-store", + Prefix: "fake-prefix-object-store", + }, + }, + }, + }, + getS3BucketRegion: func(bucket string) (string, error) { + return "region from bucket: " + bucket, nil + }, + expected: map[string]string{ + "bucket": "fake-bucket-object-store", + "prefix": "fake-prefix-object-store/unified-repo/", + "region": "fake-region", + "fspath": "", + "endpoint": "fake-url", + "skipTLSVerify": "false", + }, + }, + { + name: "azure, ObjectStorage section exists in BSL", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "bucket": "fake-bucket-config", + "prefix": "fake-prefix-config", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + StorageType: velerov1api.StorageType{ + ObjectStorage: &velerov1api.ObjectStorageLocation{ + Bucket: "fake-bucket-object-store", + Prefix: "fake-prefix-object-store", + }, + }, + }, + }, + getAzureStorageDomain: func(config map[string]string) string { + return config["storageDomain"] + }, + expected: map[string]string{ + "bucket": "fake-bucket-object-store", + "prefix": "fake-prefix-object-store/unified-repo/", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + }, + { + name: "azure, ObjectStorage section not exists in BSL, repo name exists", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/azure", + Config: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + }, + }, + repoName: "//fake-name//", + getAzureStorageDomain: func(config map[string]string) string { + return config["storageDomain"] + }, + expected: map[string]string{ + "bucket": "fake-bucket", + "prefix": "fake-prefix/unified-repo/fake-name/", + "region": "fake-region", + "fspath": "", + "storageDomain": "fake-domain", + }, + }, + { + name: "fs", + backupLocation: velerov1api.BackupStorageLocation{ + Spec: velerov1api.BackupStorageLocationSpec{ + Provider: "velero.io/fs", + Config: map[string]string{ + "fspath": "fake-path", + "prefix": "fake-prefix", + }, + }, + }, + expected: map[string]string{ + "fspath": "fake-path", + "bucket": "", + "prefix": "fake-prefix/unified-repo/", + "region": "", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + getS3BucketRegion = tc.getS3BucketRegion + getAzureStorageDomain = tc.getAzureStorageDomain + + actual, err := getStorageVariables(&tc.backupLocation, tc.repoName) + + require.Equal(t, tc.expected, actual) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/repository/udmrepo/repo-option-consts.go b/pkg/repository/udmrepo/repo-option-consts.go new file mode 100644 index 000000000..7cf55d017 --- /dev/null +++ b/pkg/repository/udmrepo/repo-option-consts.go @@ -0,0 +1,58 @@ +/* +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 udmrepo + +const ( + StorageTypeS3 = "s3" + StorageTypeAzure = "azure" + StorageTypeFs = "filesystem" + StorageTypeGcs = "gcs" + + GenOptionMaintainMode = "mode" + GenOptionMaintainFull = "full" + GenOptionMaintainQuick = "quick" + + StoreOptionS3KeyId = "accessKeyID" + StoreOptionS3Provider = "providerName" + StoreOptionS3SecretKey = "secretAccessKey" + StoreOptionS3Token = "sessionToken" + StoreOptionS3Endpoint = "endpoint" + StoreOptionS3DisableTls = "doNotUseTLS" + StoreOptionS3DisableTlsVerify = "skipTLSVerify" + + StoreOptionAzureKey = "storageKey" + StoreOptionAzureDomain = "storageDomain" + StoreOptionAzureStorageAccount = "storageAccount" + StoreOptionAzureToken = "sasToken" + + StoreOptionFsPath = "fspath" + + StoreOptionGcsReadonly = "readonly" + + StoreOptionOssBucket = "bucket" + StoreOptionOssRegion = "region" + + StoreOptionCredentialFile = "credFile" + StoreOptionPrefix = "prefix" + StoreOptionPrefixName = "unified-repo" + + ThrottleOptionReadOps = "readOPS" + ThrottleOptionWriteOps = "writeOPS" + ThrottleOptionListOps = "listOPS" + ThrottleOptionUploadBytes = "uploadBytes" + ThrottleOptionDownloadBytes = "downloadBytes" +) diff --git a/pkg/repository/udmrepo/repo.go b/pkg/repository/udmrepo/repo.go new file mode 100644 index 000000000..be18a6d17 --- /dev/null +++ b/pkg/repository/udmrepo/repo.go @@ -0,0 +1,171 @@ +/* +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 udmrepo + +import ( + "context" + "io" + "time" +) + +type ID string + +// ManifestEntryMetadata is the metadata describing one manifest data +type ManifestEntryMetadata struct { + ID ID // The ID of the manifest data + Length int32 // The data size of the manifest data + Labels map[string]string // Labels saved together with the manifest data + ModTime time.Time // Modified time of the manifest data +} + +type RepoManifest struct { + Payload interface{} // The user data of manifest + Metadata *ManifestEntryMetadata // The metadata data of manifest +} + +type ManifestFilter struct { + Labels map[string]string +} + +const ( + // Below consts descrbe the data type of one object. + // Metadata: This type describes how the data is organized. + // For a file system backup, the Metadata describes a Dir or File. + // For a block backup, the Metadata describes a Disk and its incremental link. + ObjectDataTypeUnknown int = 0 + ObjectDataTypeMetadata int = 1 + ObjectDataTypeData int = 2 + + // Below consts defines the access mode when creating an object for write + ObjectDataAccessModeUnknown int = 0 + ObjectDataAccessModeFile int = 1 + ObjectDataAccessModeBlock int = 2 + + ObjectDataBackupModeUnknown int = 0 + ObjectDataBackupModeFull int = 1 + ObjectDataBackupModeInc int = 2 +) + +// ObjectWriteOptions defines the options when creating an object for write +type ObjectWriteOptions struct { + FullPath string // Full logical path of the object + DataType int // OBJECT_DATA_TYPE_* + Description string // A description of the object, could be empty + Prefix ID // A prefix of the name used to save the object + AccessMode int // OBJECT_DATA_ACCESS_* + BackupMode int // OBJECT_DATA_BACKUP_* +} + +// OwnershipOptions is used to add some access control to the unified repository. +// For example, some privileged operations of the unified repository can be done by the +// repository owner only; the data of a backup may be manipulated by the backup owner +// who created it only. It is optional for a backup repository to support this ownership control. +type OwnershipOptions struct { + Username string + DomainName string + FullQualified string +} + +type RepoOptions struct { + // A repository specific string to identify a backup storage, i.e., "s3", "filesystem" + StorageType string + // Backup repository password, if any + RepoPassword string + // A custom path to save the repository's configuration, if any + ConfigFilePath string + // The ownership for the current repository operation + Ownership OwnershipOptions + // Other repository specific options + GeneralOptions map[string]string + // Storage specific options + StorageOptions map[string]string + + // Description of the backup repository + Description string +} + +// BackupRepoService is used to initialize, open or maintain a backup repository +type BackupRepoService interface { + // Create a backup repository or connect to an existing backup repository. + // repoOption: option to the backup repository and the underlying backup storage. + // createNew: indicates whether to create a new or connect to an existing backup repository. + Init(ctx context.Context, repoOption RepoOptions, createNew bool) error + + // Open an backup repository that has been created/connected. + // repoOption: options to open the backup repository and the underlying storage. + Open(ctx context.Context, repoOption RepoOptions) (BackupRepo, error) + + // Periodically called to maintain the backup repository to eliminate redundant data and improve performance. + // repoOption: options to maintain the backup repository. + Maintain(ctx context.Context, repoOption RepoOptions) error +} + +// BackupRepo provides the access to the backup repository +type BackupRepo interface { + // Open an existing object for read. + // id: the object's unified identifier. + OpenObject(ctx context.Context, id ID) (ObjectReader, error) + + // Get a manifest data. + GetManifest(ctx context.Context, id ID, mani *RepoManifest) error + + // Get one or more manifest data that match the given labels + FindManifests(ctx context.Context, filter ManifestFilter) ([]*ManifestEntryMetadata, error) + + // Create a new object and return the object's writer interface. + // return: A unified identifier of the object on success. + NewObjectWriter(ctx context.Context, opt ObjectWriteOptions) ObjectWriter + + // Save a manifest object + PutManifest(ctx context.Context, mani RepoManifest) (ID, error) + + // Delete a manifest object + DeleteManifest(ctx context.Context, id ID) error + + // Flush all the backup repository data + Flush(ctx context.Context) error + + // Get the local time of the backup repository. It may be different from the time of the caller + Time() time.Time + + // Close the backup repository + Close(ctx context.Context) error +} + +type ObjectReader interface { + io.ReadCloser + io.Seeker + + // Length returns the logical size of the object + Length() int64 +} + +type ObjectWriter interface { + io.WriteCloser + + // For some cases, i.e. block incremental, the object is not written sequentially + io.Seeker + + // Periodically called to preserve the state of data written to the repo so far. + // Return a unified identifier that represent the current state. + // An empty ID could be returned on success if the backup repository doesn't support this. + Checkpoint() (ID, error) + + // Wait for the completion of the object write. + // Result returns the object's unified identifier after the write completes. + Result() (ID, error) +} diff --git a/pkg/restic/aws.go b/pkg/restic/aws.go deleted file mode 100644 index d97c5f0b7..000000000 --- a/pkg/restic/aws.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. -*/ - -package restic - -const ( - // AWS specific environment variable - awsProfileEnvVar = "AWS_PROFILE" - awsProfileKey = "profile" - awsCredentialsFileEnvVar = "AWS_SHARED_CREDENTIALS_FILE" -) - -// getS3ResticEnvVars gets the environment variables that restic -// relies on (AWS_PROFILE) based on info in the provided object -// storage location config map. -func getS3ResticEnvVars(config map[string]string) (map[string]string, error) { - result := make(map[string]string) - - if credentialsFile, ok := config[credentialsFileKey]; ok { - result[awsCredentialsFileEnvVar] = credentialsFile - } - - if profile, ok := config[awsProfileKey]; ok { - result[awsProfileEnvVar] = profile - } - - return result, nil -} diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 23c09e558..580bd2708 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -32,6 +32,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/label" + repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -63,10 +64,6 @@ const ( // should be excluded from restic backup. VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" - // credentialsFileKey is the key within a BSL config that is checked to see if - // the BSL is using its own credentials, rather than those in the environment - credentialsFileKey = "credentialsFile" - // Deprecated. // // TODO(2.0): remove @@ -322,24 +319,24 @@ func CmdEnv(backupLocation *velerov1api.BackupStorageLocation, credentialFileSto if err != nil { return []string{}, errors.WithStack(err) } - config[credentialsFileKey] = credsFile + config[repoconfig.CredentialsFileKey] = credsFile } - backendType := getBackendType(backupLocation.Spec.Provider) + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) switch backendType { - case AWSBackend: - customEnv, err = getS3ResticEnvVars(config) + case repoconfig.AWSBackend: + customEnv, err = repoconfig.GetS3ResticEnvVars(config) if err != nil { return []string{}, err } - case AzureBackend: - customEnv, err = getAzureResticEnvVars(config) + case repoconfig.AzureBackend: + customEnv, err = repoconfig.GetAzureResticEnvVars(config) if err != nil { return []string{}, err } - case GCPBackend: - customEnv, err = getGCPResticEnvVars(config) + case repoconfig.GCPBackend: + customEnv, err = repoconfig.GetGCPResticEnvVars(config) if err != nil { return []string{}, err } diff --git a/pkg/util/ownership/backup_owner.go b/pkg/util/ownership/backup_owner.go new file mode 100644 index 000000000..078c799dd --- /dev/null +++ b/pkg/util/ownership/backup_owner.go @@ -0,0 +1,42 @@ +/* +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 ownership + +import "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" + +const ( + defaultOwnerUsername = "default" + defaultOwnerDomain = "default" +) + +// GetBackupOwner returns the owner used by uploaders when saving a snapshot or +// opening the unified repository. At present, use the default owner only +func GetBackupOwner() udmrepo.OwnershipOptions { + return udmrepo.OwnershipOptions{ + Username: defaultOwnerUsername, + DomainName: defaultOwnerDomain, + } +} + +// GetBackupOwner returns the owner used to create/connect the unified repository. +//At present, use the default owner only +func GetRepositoryOwner() udmrepo.OwnershipOptions { + return udmrepo.OwnershipOptions{ + Username: defaultOwnerUsername, + DomainName: defaultOwnerDomain, + } +} From 108c81d84cf6e10212a7f478c94d4691bf7b6a46 Mon Sep 17 00:00:00 2001 From: Daniel Jiang Date: Tue, 2 Aug 2022 15:47:57 +0800 Subject: [PATCH 31/38] Skip registering "crd-remap-version" plugin when feature flag (#5165) "EnableAPIGroupVersions" is set The crd-remap-version plugin will always backup v1b1 resource for some CRD. It impacts the feature flag `EnableAPIGroupVersions` which means to backup all versions, and make migration fail. In this commit the featureSet was removed from plugin server struct b/c it blocks the parm `--features` to be populated correctly. This change should not have negative impact b/c the attribute in server struct is never used. Fixes #5146 Signed-off-by: Daniel Jiang --- changelogs/unreleased/5165-reasonerjt | 1 + pkg/cmd/server/plugin/plugin.go | 17 ++++++++++------- pkg/plugin/framework/server.go | 6 +----- 3 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 changelogs/unreleased/5165-reasonerjt diff --git a/changelogs/unreleased/5165-reasonerjt b/changelogs/unreleased/5165-reasonerjt new file mode 100644 index 000000000..c33f179b3 --- /dev/null +++ b/changelogs/unreleased/5165-reasonerjt @@ -0,0 +1 @@ +Skip registering "crd-remap-version" plugin when feature flag "EnableAPIGroupVersions" is set \ No newline at end of file diff --git a/pkg/cmd/server/plugin/plugin.go b/pkg/cmd/server/plugin/plugin.go index 5c833d76a..5dec03856 100644 --- a/pkg/cmd/server/plugin/plugin.go +++ b/pkg/cmd/server/plugin/plugin.go @@ -19,9 +19,11 @@ package plugin import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" - apiextensions "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/features" + "github.com/vmware-tanzu/velero/pkg/backup" "github.com/vmware-tanzu/velero/pkg/client" velerodiscovery "github.com/vmware-tanzu/velero/pkg/discovery" @@ -36,11 +38,10 @@ func NewCommand(f client.Factory) *cobra.Command { Hidden: true, Short: "INTERNAL COMMAND ONLY - not intended to be run directly by users", Run: func(c *cobra.Command, args []string) { - pluginServer. + pluginServer = pluginServer. RegisterBackupItemAction("velero.io/pv", newPVBackupItemAction). RegisterBackupItemAction("velero.io/pod", newPodBackupItemAction). RegisterBackupItemAction("velero.io/service-account", newServiceAccountBackupItemAction(f)). - RegisterBackupItemAction("velero.io/crd-remap-version", newRemapCRDVersionAction(f)). RegisterRestoreItemAction("velero.io/job", newJobRestoreItemAction). RegisterRestoreItemAction("velero.io/pod", newPodRestoreItemAction). RegisterRestoreItemAction("velero.io/restic", newResticRestoreItemAction(f)). @@ -55,13 +56,15 @@ func NewCommand(f client.Factory) *cobra.Command { RegisterRestoreItemAction("velero.io/crd-preserve-fields", newCRDV1PreserveUnknownFieldsItemAction). RegisterRestoreItemAction("velero.io/change-pvc-node-selector", newChangePVCNodeSelectorItemAction(f)). RegisterRestoreItemAction("velero.io/apiservice", newAPIServiceRestoreItemAction). - RegisterRestoreItemAction("velero.io/admission-webhook-configuration", newAdmissionWebhookConfigurationAction). - Serve() + RegisterRestoreItemAction("velero.io/admission-webhook-configuration", newAdmissionWebhookConfigurationAction) + if !features.IsEnabled(velerov1api.APIGroupVersionsFeatureFlag) { + // Do not register crd-remap-version BIA if the API Group feature flag is enabled, so that the v1 CRD can be backed up + pluginServer = pluginServer.RegisterBackupItemAction("velero.io/crd-remap-version", newRemapCRDVersionAction(f)) + } + pluginServer.Serve() }, } - pluginServer.BindFlags(c.Flags()) - return c } diff --git a/pkg/plugin/framework/server.go b/pkg/plugin/framework/server.go index 066a44db6..b25ef341d 100644 --- a/pkg/plugin/framework/server.go +++ b/pkg/plugin/framework/server.go @@ -25,7 +25,6 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/pflag" - veleroflag "github.com/vmware-tanzu/velero/pkg/cmd/util/flag" "github.com/vmware-tanzu/velero/pkg/util/logging" ) @@ -78,6 +77,7 @@ type Server interface { // RegisterItemSnapshotters registers multiple Item Snapshotters RegisterItemSnapshotters(map[string]HandlerInitializer) Server + // Server runs the plugin server. Serve() } @@ -87,7 +87,6 @@ type server struct { log *logrus.Logger logLevelFlag *logging.LevelFlag flagSet *pflag.FlagSet - featureSet *veleroflag.StringArray backupItemAction *BackupItemActionPlugin volumeSnapshotter *VolumeSnapshotterPlugin objectStore *ObjectStorePlugin @@ -99,12 +98,10 @@ type server struct { // NewServer returns a new Server func NewServer() Server { log := newLogger() - features := veleroflag.NewStringArray() return &server{ log: log, logLevelFlag: logging.LogLevelFlag(log.Level), - featureSet: &features, backupItemAction: NewBackupItemActionPlugin(serverLogger(log)), volumeSnapshotter: NewVolumeSnapshotterPlugin(serverLogger(log)), objectStore: NewObjectStorePlugin(serverLogger(log)), @@ -116,7 +113,6 @@ func NewServer() Server { func (s *server) BindFlags(flags *pflag.FlagSet) Server { flags.Var(s.logLevelFlag, "log-level", fmt.Sprintf("The level at which to log. Valid values are %s.", strings.Join(s.logLevelFlag.AllowedValues(), ", "))) - flags.Var(s.featureSet, "features", "List of feature flags for this plugin") s.flagSet = flags s.flagSet.ParseErrorsWhitelist.UnknownFlags = true From 828e28aa43b08de948d565f902a7d5c41826f220 Mon Sep 17 00:00:00 2001 From: Ming Date: Tue, 2 Aug 2022 10:20:53 +0000 Subject: [PATCH 32/38] Fix restic backups to multiple backup storage locations bug Signed-off-by: Ming --- changelogs/unreleased/5172-qiuming-best | 1 + pkg/controller/pod_volume_backup_controller.go | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) create mode 100644 changelogs/unreleased/5172-qiuming-best diff --git a/changelogs/unreleased/5172-qiuming-best b/changelogs/unreleased/5172-qiuming-best new file mode 100644 index 000000000..fa57d3d30 --- /dev/null +++ b/changelogs/unreleased/5172-qiuming-best @@ -0,0 +1 @@ +Fix restic backups to multiple backup storage locations bug diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 9cb6a3ae2..8bce92ed7 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -241,7 +241,7 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l // Go through all the podvolumebackups for the PVC and look for the most // recent completed one to use as the parent. - var mostRecentPVB *velerov1api.PodVolumeBackup + var mostRecentPVB velerov1api.PodVolumeBackup for _, pvb := range pvbList.Items { if pvb.Status.Phase != velerov1api.PodVolumeBackupPhaseCompleted { continue @@ -258,12 +258,12 @@ func (r *PodVolumeBackupReconciler) getParentSnapshot(ctx context.Context, log l continue } - if mostRecentPVB == nil || pvb.Status.StartTimestamp.After(mostRecentPVB.Status.StartTimestamp.Time) { - mostRecentPVB = &pvb + if mostRecentPVB.Status == (velerov1api.PodVolumeBackupStatus{}) || pvb.Status.StartTimestamp.After(mostRecentPVB.Status.StartTimestamp.Time) { + mostRecentPVB = pvb } } - if mostRecentPVB == nil { + if mostRecentPVB.Status == (velerov1api.PodVolumeBackupStatus{}) { log.Info("No completed PodVolumeBackup found for PVC") return "" } From e8da5df57af1ad590eea10e31cd0c4c9b3c31bed Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Fri, 1 Jul 2022 07:18:58 +0000 Subject: [PATCH 33/38] Reduce CRD size. 1. Make the Restore hook.InitConatianer server side field pruing disable. 2. Remove restore patch in update-generate-crd-code.sh. 3. Modify related testcases. 4. Add Container fields validation in Restore Init hook. Signed-off-by: Xun Jiang --- changelogs/unreleased/5174-jxun | 1 + config/crd/v1/bases/velero.io_restores.yaml | 1421 +---------------- config/crd/v1/crds/crds.go | 2 +- hack/restore-crd-patch-v1.json | 3 - hack/restore-crd-patch-v1beta1.json | 3 - hack/update-generated-crd-code.sh | 11 +- internal/hook/item_hook_handler.go | 55 +- internal/hook/item_hook_handler_test.go | 207 ++- pkg/apis/velero/v1/restore.go | 5 +- pkg/apis/velero/v1/zz_generated.deepcopy.go | 2 +- pkg/builder/container_builder.go | 13 + pkg/controller/restore_controller.go | 17 + .../init_restorehook_pod_action_test.go | 10 +- 13 files changed, 207 insertions(+), 1543 deletions(-) create mode 100644 changelogs/unreleased/5174-jxun delete mode 100644 hack/restore-crd-patch-v1.json delete mode 100644 hack/restore-crd-patch-v1beta1.json diff --git a/changelogs/unreleased/5174-jxun b/changelogs/unreleased/5174-jxun new file mode 100644 index 000000000..4c3991b28 --- /dev/null +++ b/changelogs/unreleased/5174-jxun @@ -0,0 +1 @@ +Reduce CRD size. \ No newline at end of file diff --git a/config/crd/v1/bases/velero.io_restores.yaml b/config/crd/v1/bases/velero.io_restores.yaml index 4bc98b3a4..181a4e5fa 100644 --- a/config/crd/v1/bases/velero.io_restores.yaml +++ b/config/crd/v1/bases/velero.io_restores.yaml @@ -1,3 +1,5 @@ + +--- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: @@ -200,1426 +202,9 @@ spec: description: InitContainers is list of init containers to be added to a pod during its restore. items: - description: A single application container - that you want to run within a pod. - properties: - args: - description: 'Arguments to the entrypoint. - The docker image''s CMD is used if this - is not provided. Variable references $(VAR_NAME) - are expanded using the container''s environment. - If a variable cannot be resolved, the - reference in the input string will be - unchanged. Double $$ are reduced to a - single $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, - regardless of whether the variable exists - or not. Cannot be updated. More info: - https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - command: - description: 'Entrypoint array. Not executed - within a shell. The docker image''s ENTRYPOINT - is used if this is not provided. Variable - references $(VAR_NAME) are expanded using - the container''s environment. If a variable - cannot be resolved, the reference in the - input string will be unchanged. Double - $$ are reduced to a single $, which allows - for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string - literal "$(VAR_NAME)". Escaped references - will never be expanded, regardless of - whether the variable exists or not. Cannot - be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' - items: - type: string - type: array - env: - description: List of environment variables - to set in the container. Cannot be updated. - items: - description: EnvVar represents an environment - variable present in a Container. - properties: - name: - description: Name of the environment - variable. Must be a C_IDENTIFIER. - type: string - value: - description: 'Variable references - $(VAR_NAME) are expanded using the - previously defined environment variables - in the container and any service - environment variables. If a variable - cannot be resolved, the reference - in the input string will be unchanged. - Double $$ are reduced to a single - $, which allows for escaping the - $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" - will produce the string literal - "$(VAR_NAME)". Escaped references - will never be expanded, regardless - of whether the variable exists or - not. Defaults to "".' - type: string - valueFrom: - description: Source for the environment - variable's value. Cannot be used - if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of - a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - description: 'Name of the - referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the ConfigMap or its key - must be defined - type: boolean - required: - - key - type: object - fieldRef: - description: 'Selects a field - of the pod: supports metadata.name, - metadata.namespace, `metadata.labels['''']`, - `metadata.annotations['''']`, - spec.nodeName, spec.serviceAccountName, - status.hostIP, status.podIP, - status.podIPs.' - properties: - apiVersion: - description: Version of the - schema the FieldPath is - written in terms of, defaults - to "v1". - type: string - fieldPath: - description: Path of the field - to select in the specified - API version. - type: string - required: - - fieldPath - type: object - resourceFieldRef: - description: 'Selects a resource - of the container: only resources - limits and requests (limits.cpu, - limits.memory, limits.ephemeral-storage, - requests.cpu, requests.memory - and requests.ephemeral-storage) - are currently supported.' - properties: - containerName: - description: 'Container name: - required for volumes, optional - for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the - output format of the exposed - resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource - to select' - type: string - required: - - resource - type: object - secretKeyRef: - description: Selects a key of - a secret in the pod's namespace - properties: - key: - description: The key of the - secret to select from. Must - be a valid secret key. - type: string - name: - description: 'Name of the - referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether - the Secret or its key must - be defined - type: boolean - required: - - key - type: object - type: object - required: - - name - type: object - type: array - envFrom: - description: List of sources to populate - environment variables in the container. - The keys defined within a source must - be a C_IDENTIFIER. All invalid keys will - be reported as an event when the container - is starting. When a key exists in multiple - sources, the value associated with the - last source will take precedence. Values - defined by an Env with a duplicate key - will take precedence. Cannot be updated. - items: - description: EnvFromSource represents - the source of a set of ConfigMaps - properties: - configMapRef: - description: The ConfigMap to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - ConfigMap must be defined - type: boolean - type: object - prefix: - description: An optional identifier - to prepend to each key in the ConfigMap. - Must be a C_IDENTIFIER. - type: string - secretRef: - description: The Secret to select - from - properties: - name: - description: 'Name of the referent. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - TODO: Add other useful fields. - apiVersion, kind, uid?' - type: string - optional: - description: Specify whether the - Secret must be defined - type: boolean - type: object - type: object - type: array - image: - description: 'Docker image name. More info: - https://kubernetes.io/docs/concepts/containers/images - This field is optional to allow higher - level config management to default or - override container images in workload - controllers like Deployments and StatefulSets.' - type: string - imagePullPolicy: - description: 'Image pull policy. One of - Always, Never, IfNotPresent. Defaults - to Always if :latest tag is specified, - or IfNotPresent otherwise. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' - type: string - lifecycle: - description: Actions that the management - system should take in response to container - lifecycle events. Cannot be updated. - properties: - postStart: - description: 'PostStart is called immediately - after a container is created. If the - handler fails, the container is terminated - and restarted according to its restart - policy. Other management of the container - blocks until the hook completes. More - info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of - the following should be specified. - Exec specifies the action to take. - properties: - command: - description: Command is the - command line to execute inside - the container, the working - directory for the command is - root ('/') in the container's - filesystem. The command is - simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, - you need to explicitly call - out to that shell. Exit status - of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers - to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access - on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host - name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - preStop: - description: 'PreStop is called immediately - before a container is terminated due - to an API request or management event - such as liveness/startup probe failure, - preemption, resource contention, etc. - The handler is not called if the container - crashes or exits. The reason for termination - is passed to the handler. The Pod''s - termination grace period countdown - begins before the PreStop hooked is - executed. Regardless of the outcome - of the handler, the container will - eventually terminate within the Pod''s - termination grace period. Other management - of the container blocks until the - hook completes or until the termination - grace period is reached. More info: - https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' - properties: - exec: - description: One and only one of - the following should be specified. - Exec specifies the action to take. - properties: - command: - description: Command is the - command line to execute inside - the container, the working - directory for the command is - root ('/') in the container's - filesystem. The command is - simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, - you need to explicitly call - out to that shell. Exit status - of 0 is treated as live/healthy - and non-zero is unhealthy. - items: - type: string - type: array - type: object - httpGet: - description: HTTPGet specifies the - http request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. - You probably want to set "Host" - in httpHeaders instead. - type: string - httpHeaders: - description: Custom headers - to set in the request. HTTP - allows repeated headers. - items: - description: HTTPHeader describes - a custom header to be used - in HTTP probes - properties: - name: - description: The header - field name - type: string - value: - description: The header - field value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access - on the HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for - connecting to the host. Defaults - to HTTP. - type: string - required: - - port - type: object - tcpSocket: - description: 'TCPSocket specifies - an action involving a TCP port. - TCP hooks not yet supported TODO: - implement a realistic TCP lifecycle - hook' - properties: - host: - description: 'Optional: Host - name to connect to, defaults - to the pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name - of the port to access on the - container. Number must be - in the range 1 to 65535. Name - must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - type: object - type: object - livenessProbe: - description: 'Periodic probe of container - liveness. Container will be restarted - if the probe fails. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The command - is simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, you - need to explicitly call out to - that shell. Exit status of 0 is - treated as live/healthy and non-zero - is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) - to perform the probe. Default to 10 - seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an - action involving a TCP port. TCP hooks - not yet supported TODO: implement - a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time - when the processes are forcibly halted - with a kill signal. Set this value - longer than the expected cleanup time - for your process. If this value is - nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value - overrides the value provided by the - pod spec. Value must be non-negative - integer. The value zero indicates - stop immediately via the kill signal - (no opportunity to shut down). This - is a beta field and requires enabling - ProbeTerminationGracePeriod feature - gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - name: - description: Name of the container specified - as a DNS_LABEL. Each container in a pod - must have a unique name (DNS_LABEL). Cannot - be updated. - type: string - ports: - description: List of ports to expose from - the container. Exposing a port here gives - the system additional information about - the network connections a container uses, - but is primarily informational. Not specifying - a port here DOES NOT prevent that port - from being exposed. Any port which is - listening on the default "0.0.0.0" address - inside a container will be accessible - from the network. Cannot be updated. - items: - description: ContainerPort represents - a network port in a single container. - properties: - containerPort: - description: Number of port to expose - on the pod's IP address. This must - be a valid port number, 0 < x < - 65536. - format: int32 - type: integer - hostIP: - description: What host IP to bind - the external port to. - type: string - hostPort: - description: Number of port to expose - on the host. If specified, this - must be a valid port number, 0 < - x < 65536. If HostNetwork is specified, - this must match ContainerPort. Most - containers do not need this. - format: int32 - type: integer - name: - description: If specified, this must - be an IANA_SVC_NAME and unique within - the pod. Each named port in a pod - must have a unique name. Name for - the port that can be referred to - by services. - type: string - protocol: - default: TCP - description: Protocol for port. Must - be UDP, TCP, or SCTP. Defaults to - "TCP". - type: string - required: - - containerPort - - protocol - type: object - type: array - x-kubernetes-list-map-keys: - - containerPort - - protocol - x-kubernetes-list-type: map - readinessProbe: - description: 'Periodic probe of container - service readiness. Container will be removed - from service endpoints if the probe fails. - Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The command - is simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, you - need to explicitly call out to - that shell. Exit status of 0 is - treated as live/healthy and non-zero - is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) - to perform the probe. Default to 10 - seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an - action involving a TCP port. TCP hooks - not yet supported TODO: implement - a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time - when the processes are forcibly halted - with a kill signal. Set this value - longer than the expected cleanup time - for your process. If this value is - nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value - overrides the value provided by the - pod spec. Value must be non-negative - integer. The value zero indicates - stop immediately via the kill signal - (no opportunity to shut down). This - is a beta field and requires enabling - ProbeTerminationGracePeriod feature - gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - resources: - description: 'Compute Resources required - by this container. Cannot be updated. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum - amount of compute resources allowed. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the - minimum amount of compute resources - required. If Requests is omitted for - a container, it defaults to Limits - if that is explicitly specified, otherwise - to an implementation-defined value. - More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - securityContext: - description: 'SecurityContext defines the - security options the container should - be run with. If set, the fields of SecurityContext - override the equivalent fields of PodSecurityContext. - More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' - properties: - allowPrivilegeEscalation: - description: 'AllowPrivilegeEscalation - controls whether a process can gain - more privileges than its parent process. - This bool directly controls if the - no_new_privs flag will be set on the - container process. AllowPrivilegeEscalation - is true always when the container - is: 1) run as Privileged 2) has CAP_SYS_ADMIN' - type: boolean - capabilities: - description: The capabilities to add/drop - when running containers. Defaults - to the default set of capabilities - granted by the container runtime. - properties: - add: - description: Added capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - drop: - description: Removed capabilities - items: - description: Capability represent - POSIX capabilities type - type: string - type: array - type: object - privileged: - description: Run container in privileged - mode. Processes in privileged containers - are essentially equivalent to root - on the host. Defaults to false. - type: boolean - procMount: - description: procMount denotes the type - of proc mount to use for the containers. - The default is DefaultProcMount which - uses the container runtime defaults - for readonly paths and masked paths. - This requires the ProcMountType feature - flag to be enabled. - type: string - readOnlyRootFilesystem: - description: Whether this container - has a read-only root filesystem. Default - is false. - type: boolean - runAsGroup: - description: The GID to run the entrypoint - of the container process. Uses runtime - default if unset. May also be set - in PodSecurityContext. If set in - both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - runAsNonRoot: - description: Indicates that the container - must run as a non-root user. If true, - the Kubelet will validate the image - at runtime to ensure that it does - not run as UID 0 (root) and fail to - start the container if it does. If - unset or false, no such validation - will be performed. May also be set - in PodSecurityContext. If set in - both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: boolean - runAsUser: - description: The UID to run the entrypoint - of the container process. Defaults - to user specified in image metadata - if unspecified. May also be set in - PodSecurityContext. If set in both - SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - format: int64 - type: integer - seLinuxOptions: - description: The SELinux context to - be applied to the container. If unspecified, - the container runtime will allocate - a random SELinux context for each - container. May also be set in PodSecurityContext. If - set in both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - properties: - level: - description: Level is SELinux level - label that applies to the container. - type: string - role: - description: Role is a SELinux role - label that applies to the container. - type: string - type: - description: Type is a SELinux type - label that applies to the container. - type: string - user: - description: User is a SELinux user - label that applies to the container. - type: string - type: object - seccompProfile: - description: The seccomp options to - use by this container. If seccomp - options are provided at both the pod - & container level, the container options - override the pod options. - properties: - localhostProfile: - description: localhostProfile indicates - a profile defined in a file on - the node should be used. The profile - must be preconfigured on the node - to work. Must be a descending - path, relative to the kubelet's - configured seccomp profile location. - Must only be set if type is "Localhost". - type: string - type: - description: "type indicates which - kind of seccomp profile will be - applied. Valid options are: \n - Localhost - a profile defined - in a file on the node should be - used. RuntimeDefault - the container - runtime default profile should - be used. Unconfined - no profile - should be applied." - type: string - required: - - type - type: object - windowsOptions: - description: The Windows specific settings - applied to all containers. If unspecified, - the options from the PodSecurityContext - will be used. If set in both SecurityContext - and PodSecurityContext, the value - specified in SecurityContext takes - precedence. - properties: - gmsaCredentialSpec: - description: GMSACredentialSpec - is where the GMSA admission webhook - (https://github.com/kubernetes-sigs/windows-gmsa) - inlines the contents of the GMSA - credential spec named by the GMSACredentialSpecName - field. - type: string - gmsaCredentialSpecName: - description: GMSACredentialSpecName - is the name of the GMSA credential - spec to use. - type: string - hostProcess: - description: HostProcess determines - if a container should be run as - a 'Host Process' container. This - field is alpha-level and will - only be honored by components - that enable the WindowsHostProcessContainers - feature flag. Setting this field - without the feature flag will - result in errors when validating - the Pod. All of a Pod's containers - must have the same effective HostProcess - value (it is not allowed to have - a mix of HostProcess containers - and non-HostProcess containers). In - addition, if HostProcess is true - then HostNetwork must also be - set to true. - type: boolean - runAsUserName: - description: The UserName in Windows - to run the entrypoint of the container - process. Defaults to the user - specified in image metadata if - unspecified. May also be set in - PodSecurityContext. If set in - both SecurityContext and PodSecurityContext, - the value specified in SecurityContext - takes precedence. - type: string - type: object - type: object - startupProbe: - description: 'StartupProbe indicates that - the Pod has successfully initialized. - If specified, no other probes are executed - until this completes successfully. If - this probe fails, the Pod will be restarted, - just as if the livenessProbe failed. This - can be used to provide different probe - parameters at the beginning of a Pod''s - lifecycle, when it might take a long time - to load data or warm a cache, than during - steady-state operation. This cannot be - updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - properties: - exec: - description: One and only one of the - following should be specified. Exec - specifies the action to take. - properties: - command: - description: Command is the command - line to execute inside the container, - the working directory for the - command is root ('/') in the - container's filesystem. The command - is simply exec'd, it is not run - inside a shell, so traditional - shell instructions ('|', etc) - won't work. To use a shell, you - need to explicitly call out to - that shell. Exit status of 0 is - treated as live/healthy and non-zero - is unhealthy. - items: - type: string - type: array - type: object - failureThreshold: - description: Minimum consecutive failures - for the probe to be considered failed - after having succeeded. Defaults to - 3. Minimum value is 1. - format: int32 - type: integer - httpGet: - description: HTTPGet specifies the http - request to perform. - properties: - host: - description: Host name to connect - to, defaults to the pod IP. You - probably want to set "Host" in - httpHeaders instead. - type: string - httpHeaders: - description: Custom headers to set - in the request. HTTP allows repeated - headers. - items: - description: HTTPHeader describes - a custom header to be used in - HTTP probes - properties: - name: - description: The header field - name - type: string - value: - description: The header field - value - type: string - required: - - name - - value - type: object - type: array - path: - description: Path to access on the - HTTP server. - type: string - port: - anyOf: - - type: integer - - type: string - description: Name or number of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - scheme: - description: Scheme to use for connecting - to the host. Defaults to HTTP. - type: string - required: - - port - type: object - initialDelaySeconds: - description: 'Number of seconds after - the container has started before liveness - probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - periodSeconds: - description: How often (in seconds) - to perform the probe. Default to 10 - seconds. Minimum value is 1. - format: int32 - type: integer - successThreshold: - description: Minimum consecutive successes - for the probe to be considered successful - after having failed. Defaults to 1. - Must be 1 for liveness and startup. - Minimum value is 1. - format: int32 - type: integer - tcpSocket: - description: 'TCPSocket specifies an - action involving a TCP port. TCP hooks - not yet supported TODO: implement - a realistic TCP lifecycle hook' - properties: - host: - description: 'Optional: Host name - to connect to, defaults to the - pod IP.' - type: string - port: - anyOf: - - type: integer - - type: string - description: Number or name of the - port to access on the container. - Number must be in the range 1 - to 65535. Name must be an IANA_SVC_NAME. - x-kubernetes-int-or-string: true - required: - - port - type: object - terminationGracePeriodSeconds: - description: Optional duration in seconds - the pod needs to terminate gracefully - upon probe failure. The grace period - is the duration in seconds after the - processes running in the pod are sent - a termination signal and the time - when the processes are forcibly halted - with a kill signal. Set this value - longer than the expected cleanup time - for your process. If this value is - nil, the pod's terminationGracePeriodSeconds - will be used. Otherwise, this value - overrides the value provided by the - pod spec. Value must be non-negative - integer. The value zero indicates - stop immediately via the kill signal - (no opportunity to shut down). This - is a beta field and requires enabling - ProbeTerminationGracePeriod feature - gate. Minimum value is 1. spec.terminationGracePeriodSeconds - is used if unset. - format: int64 - type: integer - timeoutSeconds: - description: 'Number of seconds after - which the probe times out. Defaults - to 1 second. Minimum value is 1. More - info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' - format: int32 - type: integer - type: object - stdin: - description: Whether this container should - allocate a buffer for stdin in the container - runtime. If this is not set, reads from - stdin in the container will always result - in EOF. Default is false. - type: boolean - stdinOnce: - description: Whether the container runtime - should close the stdin channel after it - has been opened by a single attach. When - stdin is true the stdin stream will remain - open across multiple attach sessions. - If stdinOnce is set to true, stdin is - opened on container start, is empty until - the first client attaches to stdin, and - then remains open and accepts data until - the client disconnects, at which time - stdin is closed and remains closed until - the container is restarted. If this flag - is false, a container processes that reads - from stdin will never receive an EOF. - Default is false - type: boolean - terminationMessagePath: - description: 'Optional: Path at which the - file to which the container''s termination - message will be written is mounted into - the container''s filesystem. Message written - is intended to be brief final status, - such as an assertion failure message. - Will be truncated by the node if greater - than 4096 bytes. The total message length - across all containers will be limited - to 12kb. Defaults to /dev/termination-log. - Cannot be updated.' - type: string - terminationMessagePolicy: - description: Indicate how the termination - message should be populated. File will - use the contents of terminationMessagePath - to populate the container status message - on both success and failure. FallbackToLogsOnError - will use the last chunk of container log - output if the termination message file - is empty and the container exited with - an error. The log output is limited to - 2048 bytes or 80 lines, whichever is smaller. - Defaults to File. Cannot be updated. - type: string - tty: - description: Whether this container should - allocate a TTY for itself, also requires - 'stdin' to be true. Default is false. - type: boolean - volumeDevices: - description: volumeDevices is the list of - block devices to be used by the container. - items: - description: volumeDevice describes a - mapping of a raw block device within - a container. - properties: - devicePath: - description: devicePath is the path - inside of the container that the - device will be mapped to. - type: string - name: - description: name must match the name - of a persistentVolumeClaim in the - pod - type: string - required: - - devicePath - - name - type: object - type: array - volumeMounts: - description: Pod volumes to mount into the - container's filesystem. Cannot be updated. - items: - description: VolumeMount describes a mounting - of a Volume within a container. - properties: - mountPath: - description: Path within the container - at which the volume should be mounted. Must - not contain ':'. - type: string - mountPropagation: - description: mountPropagation determines - how mounts are propagated from the - host to container and the other - way around. When not set, MountPropagationNone - is used. This field is beta in 1.10. - type: string - name: - description: This must match the Name - of a Volume. - type: string - readOnly: - description: Mounted read-only if - true, read-write otherwise (false - or unspecified). Defaults to false. - type: boolean - subPath: - description: Path within the volume - from which the container's volume - should be mounted. Defaults to "" - (volume's root). - type: string - subPathExpr: - description: Expanded path within - the volume from which the container's - volume should be mounted. Behaves - similarly to SubPath but environment - variable references $(VAR_NAME) - are expanded using the container's - environment. Defaults to "" (volume's - root). SubPathExpr and SubPath are - mutually exclusive. - type: string - required: - - mountPath - - name - type: object - type: array - workingDir: - description: Container's working directory. - If not specified, the container runtime's - default will be used, which might be configured - in the container image. Cannot be updated. - type: string - required: - - name type: object type: array + x-kubernetes-preserve-unknown-fields: true timeout: description: Timeout defines the maximum amount of time Velero should wait for the initContainers diff --git a/config/crd/v1/crds/crds.go b/config/crd/v1/crds/crds.go index 2acede9be..fd9b62843 100644 --- a/config/crd/v1/crds/crds.go +++ b/config/crd/v1/crds/crds.go @@ -36,7 +36,7 @@ var rawCRDs = [][]byte{ []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4WM\x8f\xdb6\x10\xbd\xfbW\f\xd2CZ \x92\x13\xf4\xd0·v\x93âi\x10\xd8\xe9^\x8a\x1ehj,\xb1K\x91,g\xe8\xcd\xf6\xd7\x17CJ\xfe\x90\xe5\xdd͡\xbc\x89\x1c\x0e\x1f\x1f\xdf.\xbd\u007f[\xffT\xbf]\x00\xe8\x88y\xfa\x17\xd3#\xb1\xea\xc3\n\\\xb2v\x01\xe0T\x8f+h\xfc\x83\xb3^5\x11\xffIHL\xf5\x1e-F_\x1b\xbf\xa0\x80Z\x16m\xa3Oa\x05ǁ2w\x00T6\xf3~H\xb3.i\xf2\x885Ŀ͍~4CD\xb0)*{\t\"\x0f\x92qm\xb2*^\f/\x00H\xfb\x80+\xf8$0\x82\xd2\xd8,\x00\x86\xbdgXհ\xbb\xfd\xbb\x92Jwث\x82\x17\xc0\at\xbf|\xbe\xbd\xfbqs\xd6\r\xd0 \xe9h\x02g\x06'\x98\xc1\x10(\x18\x10\x00\xfb\x03(P\x0eTd\xb3S\x9aa\x17}\x0f[\xa5\xefS8d\x05\xf0ۿQ3\x10\xfb\xa8Z|\x03\x94t\aJ\xf2\x95P\xb0\xbe\x85\x9d\xb1X\x1f&\x85\xe8\x03F6#˥\x9d\x88\xeb\xa4w\x02\xfc\xb5\xec\xadDA#\xaaB\x02\xeep\xe4\a\x9b\x81\x0e\xf0;\xe0\xce\x10D\f\x11\t]\xd1\xd9Yb\x90 \xe5\x86\x1d\u0530\xc1(i\x80:\x9fl#b\xdccd\x88\xa8}\xeb̿\x87\xdc$\fɢV\xf1(\x87c3\x8e1:ea\xafl\xc27\xa0\\\x03\xbdz\x84\x88\x99\xa7\xe4N\xf2\xe5\x10\xaa\xe1w\x1f\x11\x8c\xdb\xf9\x15t́V\xcbekx,*\xed\xfb>9Ï\xcb\\\x1ff\x9b\xd8GZ6\xb8G\xbb$\xd3V*\xea\xce0jN\x11\x97*\x98*Cw\xb9\xb0\xea\xbe\xf9.\x0eeH\xafϰ\xf2\xa3Ȍ8\x1aמ\fd\xcd?q\x02\xa2\xfa\"\x982\xb5\xec\xe2H\xb4t\t;\xeb\x0f\x9b/0.\x9d\x0fc\xca~Q\xcea\"\x1d\x8f@\b3n\x87\xb1\x1cbV\x9e\xe4D\xd7\x04o\x1c\xe7\x0fm\r\xba)\xfd\x94\xb6\xbda\x1a\xc5,gU\xc3Mv\x1a\xd8\"\xa4\xd0(Ʀ\x86[\a7\xaaG{\xa3\b\xff\xf7\x03\x10\xa6\xa9\x12b_v\x04\xa7&9\r.\xac\x9d\f\x8cNv\xe5\xbc&\xa5\xbe\t\xa8\xe5\xf4\x84@\x99ivF\xe7Ҁ\x9d\x8f\xa0\x8e\x95?\x10X\x9fe\x9e\xaf\xdc\fN\xc5\x16y\xda;\xc1\xf2%\a\xc9\xf2\x0f\x9d:7\x9a\xef\xb1nk\xf1\n\x1a\x80\x14\xf7\xf8\xa1\xbe\xc8x\x1d\x03̪w\x16\xc9(b\xa1Ax\x15+\x10\x93:\xc5t\xb9\xb44t\xa9\x9f_\xa0\x82_3揾}r\xfc\xc6;\x16\xb9?\x19t\xe7m\xeaq\xe3T\xa0\xce?\x13{\xcbؿ,r\xbc\x90\x0f\x97\xd4e\xe0\x1a\xc5\xca\xf1\xfa&\x86\x805R\xb2W\x97\xbb\xd9\xdc~\xcb>\xae\x84?\xc9ԕ\xda\x19[\xbe#\x9f\x17\x82ܲ\xa3\x10dJ\xb98\x10\xe4\xed\x11\x1d2\xd2\xd1\xc3\x1e\fw\xb3\x19\x01\x1e:\xa3\xbb<1\xabH\xec\x91\xc8k\x93\xcd\xe6\xdb\xe1K\xf1\x99\x883J\xae\xb2\xc2g\xba\x05\xfcE\xf7\x15˸\xb6@5\x94\xf1\x8bl\x87\x15'\xfa\x06\xe3\xc9\xf1#\xd5:ň\x8e\x87,\xf9\"\x9eNx\xa9\xf3\x8c\xe5\xfa\xc7\xfa\xe33\xf6\xf3\xfe\x18\x99\x9f\x9aʸ\x82&D\xacȴ\xf2|\x9011\xa0l\f\x97d\x94v\xfe\x9c9'j\xf6D\xf1k01\xdb\xec3\x10?\x1c\x02\x8bK\xa2+7\xe0\xf4\xc1\x96\x13\"\xe5ׅV\xd3w\x8d\xb4-B\x83\x16\x19\x1b\xd8>\x16\xbb\u007f$\xc6\xfe\x12\xf7\xce\xc7^\xf1\n\xe4f\xac\xd8\xcc\xc8H\x1e\xd5jkq\x05\x1c\xd35\x95\xcdn\xf38\xfa\x01\xed\xe4\xfa\x8b\x99\xdb\x1e\xd1~[aޞ\xc3\xf4\xee\xfb\xe0@\xcb\x1a[\xb6\x8e+\x95F\xf9\xf6\xf6\xfa\xfe\xcfw\xa3a\x00m\x94F\xe3Dr\xe8\xe1\x1bı\xc1(\x8cY}A\x04\xc3*\xe0\x14\xc0\xd0\x06\xab\bc\xc8#\x86 \x0eaIu\rZ\x94nȒ\xf4\xa9-0\tj\xf3\v\x96\xae\x80;4D&\t\xa6Tr\x87Ɓ\xc1RUR\xfc\xb7\xa7mI\xd7\xe8І9\x8cq\xe5\xf0y\xd7/Y\x03;\xd6t\xf8\x1a\x98\xe4в=\x18\xa4S\xa0\x93\x03z~\x89-\xe0ge\x10\x84ܪ5\xd4\xcei\xbb^\xad*\xe1R\xfc.U\xdbvR\xb8\xfdʇb\xb1\xe9\x9c2v\xc5q\x87\xcdʊj\xc9LY\v\x87\xa5\xeb\f\xae\x98\x16K\x0f]\xfa\x18^\xb4\xfc\x1b\x13#\xbe\xbd\x18a\x9d)F\xf8|x=!\x01\n\xb0 ,\xb0\xb85\xdc\xe2\xc0\xe8\xe4 \xdf\xff\xe3\xee\x03\xa4\xa3\xbd0\xa6\xdc\xf7|?l\xb4\a\x11\x10Ä\xdcbt0[\xa3ZO\x13%\xd7JH\xe7\u007f\x94\x8d@9e\xbf\xed6\xadp$\xf7\xffth\x1dɪ\x80K\x9fԐ\xc3\xec4i./\xe0Z\xc2%k\xb1\xb9d\x16\xbf\xba\x00\x88\xd3vI\x8c}\x9a\b\x86\xf9\xd8tq\xe0\xda`\"%MG\xe45Ʉ\xee4\x96$=b \xed\x14[\x11=\x14\xb9s6]^\x8c\b\xe7\r\x97\xbe\xacw\x9a.\x82\\p\x99\xecI\xd8\xe4\xc0\xa7&\x87\x19VΈ\x024S/\xdb\xef\x19F.\x1b\x1dl1\xa3pD\f\xf4I\xc5\xf1\xcc=n\x14\xc7\x1cl\xda\n\xaefA[)\xe3#\u007f\xd4I9?\x85>%\x9f\x05L+~\x06W<\x91\x81\xc1-\x1a\x94%&\xc7u*\x9d\xc9 \x1b&\x1as\x8cǕ\x02Nx\xf5,ⷷ\xd7ɓ'&F\xecn~\xee\x19\xfeз\x15\xd8p\x1f\xe8Ο}q\xbd\r\x87y\x9f\xe6\x140\xd0\x02Cb\xda\a\t\x10\xd2:d\x1c\xd46K\x91\xca' \xc37\x18w\xbc\x0e\x1e,\xba\xcaCh!\xde\x03#\xdf)8\xfc\xeb\xee\xdd\xcd\xea\x9f9\xd6\xf7\xb7\x00V\x96h}^\xee\xb0E\xe9^\xf7\xa5\x02G+\frJ\xfc\xb1h\x99\x14[\xb4\xae\x88g\xa0\xb1\x9f\xde|\xces\x0f\xe0Ge\x00\xbf\xb0V7\xf8\x1aD\xe0x\uf593\xd2\b\x1b\xd8\xd1S\x84G\xe1j1\r\xa6=\aH\xbd\xe2\xb5\x1f\xfdu\x1d{@P\xf1\xba\x1dB#\x1ep\r\xaf|Zs\x80\xf9+\xd9\xceo\xaf\x8eP\xfdS0\xedW\xb4\xe8U\x00\xd7\xc7\xe1\xa1\xd1\x1d@\x06\xcb3\xa2\xaa\xf0\x90UM?\x1fT\xc8U\u007f\v\xca\x10\a\xa4\x1a\x90\xf0\x84Iz\xc1Q\"\x9f\x81\xfe\xf4\xe6\xf3Q\xc4c~\x81\x90\x1c\xbf\xc0\x1b\x10\xb1\xd8Ҋ\u007f[\xc0\a\xaf\x1d{\xe9\xd8\x17:\xa9\xac\x95\xc5c\x9cU\xb2ه\xf6\x881\tʄyp\xcdL\ueffa*\x13C;C\x88\xf6\xcb\xd8\xf6[2\xc9\xe9\xffVXG\xe3/\xe2`'\x9ed\xbe\x1f\xaf\xaf~\x1f\x05\xefċl\xf5H\x02\x1etd\xd8\xe58\x93\x98\xbd\x1f-N\xa9c&c\xed\xd7<+3t\xacʤb\xc3\xf6䩄\xed$\aƭ\x18VY`\x06\x81A\xcb4I\xee\x01\xf7\xcb\x10\xe25\x13\x14\x9f)\x04\xf7}\x0e`Z7\"\x1b\x8ac \x8fIh\xe4\x04\x15ڬ\xb2\xc7\ue795ð\xafsF\n\x1f\aK\x93\f\xcet\x96\\\x9d\xb3\xd4Q\xbfi\x8e\x16e\xd7Ρ,\xe1Ai\xc12\xe3\x06\xad\x13ef\xe2\xd5<\xd38!\xac\xc0\xcb3<\x88-\xe8L\xf1\x12E\x112\xbd\xbe\x80\xf1]\xc7\\\x85p\xbc<8\n\x91*t\xca[\xc7\x10\x97\xf9Rr\xb2\x86J\xabɐV|1ed\xa6\xf3\x98&G\x9d\xd1!\xd2y}\xed\x1b\xdeϨ\xb0C#?\xf24\xf8S\x97\xda\xfbTL\xbc\xb4\xc6.\x15\xe5\xe9㧕\xd3⽜\xef\xf0\xed,ã\xba\x8b\x96\xacw\xd0\xf6\x8fg\xe4\x8ad\x18\x90\v;}\x04#j\xc8}\x12M9\xfe\x96\x89\x069\xa4\xb7\x9d\xe9\x9e\f\xd5!\x95\rn\xc9\xdd\a\xd3K\xa5i\x84\xd7'\xaa5\x82\xf5}\xa2\v{\x82fg\x91\xfb\x9eF\x86\t\xf3\xe4u\xabL\xcb\\\xe8k.\xb3De\xd74l\xd3\xe0\x1a\x9c\xe9\xe6\xd3',\xb1EkYu\xce\x14\u007f\x0e\xabB\xc5\x1e\xb7\x00ۨ\xce\xf5%\xfb\xc8=^بS\xcf\xeb\x1ad\x8b\xe1\xb1:3*VlLڛ\xc6\xef\x19:\x82Ã\xa0G\xb5\xc1|\xd0\u007f\x89O\x00\xf0\x0fZ\xe7\x10Қ\x9c\x81\xf5\xde뤅\xc1\t\xa7|\x83\x8f\x99\xd1\xd9C\xdcp\xf22\x99Lf\xeeGo\rϺ\u007f<\xe8\x1c\v\xe22\xa8U\x93\x8cY9ր\xec\xda\r\x1a\xe2\xc3f\xefЎ\xddy\xae?\xe3\xeb\xba\x03\x1b\a\xfb\x93\xfc\x02\xa5X\xaa\x96L\xfa>*Y\x97S\xc0\x85\xd5\r\xdbg\b\xa7\x8b\xf8܍\x8c\x8b\\\xc0A\x9f\x93Qk4~\xea\xb9}%\x8f\xe9J\xc9#\x95F\xb2g!\xdd_\xffr\"\xd3\x13\xd2a5\t\x0eq\x9e\xd8\xf9\x03\x9d\xf2uN8\x91\xc4Xɴ\xad\x95\xbb\xbe:\xa3\x05w\xfd\xc2d\r\xb3\xe79\xec\xa9EUȉ\xaa\xf7-\xcf2\xd5\xf1\x13\xf09\xa8\xa3\xc5g\xa2P||\xceŠ;\xd4̐\xa5\xfb7\x81\xcb\xe9\xa3\xd5k\xb0\xc27:)\xf3\f\xa9hhCX\nN\x94Z)\x83\x19\x97\t\xf3\xb02\n\"c\xf8\xbfg\xfc\xc8\xea\xc9l\xd0#\xe7\x03ڱY>\x1c\xe96\xfdC\xd0\x1a~\xfdmqHlXI\xc5\x13\xf2\x9b\xe9\x1fYĔ3\xfdՄ\xffY*\x19*\t\xbb\x86O\x9f\x17\xe9\xd9\xf2>\xfd1\x04\r\xfe/\x00\x00\xff\xff\xb0\xddǼ\x99\"\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xc4YKs\x1b\xb9\x11\xbe\xf3Wti\x0f\xcaV\x99\xc3]'\x95\xa4x\xb3\xa5lJɮ\xac2e]\\>\x80\x83&\a\xab\x19`\x82Ɛf\xb6\xf6\xbf\xa7\x1a\x0fr^$%U\xb4\x9e\x8b-\xa0\xd1\xf8\xf0\xa1_hN\xa6\xd3\xe9D\xd4\xea\x01-)\xa3\xe7 j\x85_\x1dj\xfe\x8b\xb2ǿS\xa6\xccl\xf3\xe3\xe4Qi9\x87\xab\x86\x9c\xa9>\"\x99\xc6\xe6x\x8d+\xa5\x95SFO*tB\n'\xe6\x13\x00\xa1\xb5q\x82\x87\x89\xff\x04ȍv֔%\xda\xe9\x1au\xf6\xd8,q٨R\xa2\xf5\xca\xd3֛\x1f\xb2\xbfe?L\x00r\x8b~\xf9\xbd\xaa\x90\x9c\xa8\xea9\xe8\xa6,'\x00ZT8\x87\xdaȍ)\x9b\n-\x923\x16)\xdb`\x89\xd6d\xcaL\xa8Ɯw][\xd3\xd4s8L\x84\xc5\x11Q8͝\x91\x0f^\xcfǠ\xc7O\x95\x8aܿG\xa7\u007fV\xe4\xbcH]6V\x94#8\xfc,)\xbdnJa\x87\xf3\x13\x00\xcaM\x8ds\xb8e(\xb5\xc8QN\x00\"\x01\x1e\xda\x14\x84\x94\x9eRQ\xdeY\xa5\x1d\xda+V\x91\xa8\x9c\x82Dʭ\xaa\x9d\xa7l\xaf\a\xcc\n\\\x81\xbc\xa5\xa7[(\xad\xf4\xda\x0f\x05\b\xe0\f,\x11\"\x12\xe9\x95\x01\xfcJF\xdf\tW\xcc!c\xe2\xb2\xda\xc8L'\x9dQ&p~\xdb\x1bu;>\a9\xab\xf4\xfa\x18\xb2\xff3\xa8\x0e\x9e;#\x9f\x88\xe4\xbe@/\x93\xd04ui\x84D˛\x17B\xcb\x12\x81-\x17\x9c\x15\x9aVh\x8f\xa0H\xcb\xeewu\x17ɧ\xa4\xaf5\xf3\x1cv\x9eCE\x90\xedl\xff\xd0\x1e:\xb7\uf751q\x01D\xa3\x06r\xc25\x04\xd4\xe4\x05\b\x82[\xdc\xcen\xf4\x9d5k\x8bD#0\xbcxV\x17\x82\xba8\x16~\xe2uq\xac\x8c\xad\x84\x9b\x83\xd2\xee\xaf\u007f9\x8e-.ʜq\xa2|\xbfsH\x1d\xa4\xf7\xfdဖ\x9dm\x1d\xaf\xff\x9b\xc0]2\xa4k\xa3\xbb\xbc\xbe\uf34e\x81m)M\x818\x1b\x04ю\xd6w\xeb\xae>)\\\x18\bӛ\x1fC(\xcb\v\xac\xc4ם\xb8\x01Nv\xa0\bD\\\x1aNq :\x85\xec\x8f\xffX\xdcC\xda\xda_F\x9f}\xcf\xfba!\x1d\xae\x80\tSz\xc5A\x97/qeM\xe5u\xa2\x96\xb5Q\xda\xf9?\xf2R\xa1\xee\xd3OͲR\x8e\xef\xfd?\r\x92\xe3\xbb\xca\xe0ʗ\x18\x1c/\x9b\x9a-Wfp\xa3\xe1JTX^\t\xc2W\xbf\x00f\x9a\xa6L\xecӮ\xa0]\x1d\xf5\x85\x03k\xad\x89T\xc1\x1c\xb9\xaf~U\xb2\xa81\xe7\xebc\x06y\xa9Z\xa9\xdc\xfb\x06\x87\x1f\x10\x03\xf9\xac\xa3z\xdcu\xf9[\x8a\xfc\xb1\xa9\x17\xceX\xb1ƟM\xd0\xd9\x17\xeaa{?\xb6&\x81ӭ\x9c\x17\x94\x03\x05ɁR\x802-\xde\x16h\xb1\xbd\xc6bmH9cw\xac8d\xcbl\xa0\xe1\xc8E\xf8#\x1by\xe6\x18\x1c\xee\xbdCX\\\xa1E\x9dc\x8a\x10\xa7*\x99\x91S\xb4\x12\xfa\x10\xe2q\xea\xe1D\xf4\x1c\x05\xfc\xee\xee&E\xcc\xc4p\x84\xee\x86\xfb\x9e\xa1\x87\xbf\x95\xc2R\xfa\x84r~\xef˛U\xd8\xcc\xc7\x0eg@@\xad0T\xa4\xfb`\fJ\x93C!\xc1\xacF5\xf2\xa3\x01\xd8\xc1,\xc6\x15oB\xa4\x88!\xe9\x10\u0099z\x10\x1c\xa3\x94\x84\u007f->\xdc\xce\xfe9\xc6\xfc\xfe\x14 \xf2\x1c\x89|\xbe\xc6\n\xb5{\xb3\xcf\xd9\x12IY\x94\\\xb8`V\t\xadVH.\x8b{\xa0\xa5\xcfo\xbf\x8c\xb3\a\U00013c40_EU\x97\xf8\x06T`|\x1f\xfe\x92\xcd(\nt\xec5\xc2V\xb9B\xf5\x93֞\x01\xb6\xaex\xec\xad?\xae\x13\x8f\b&\x1e\xb7A(\xd5#\xce\xe1\xc2W\x82\a\x98\xbf\xb1c\xfd~qD럂\x03]\xb0\xd0E\x00\xb7\xcfwm\x8f<\x80t\x85p\xe0\xacZ\xaf\xf1P\x88\xf6?\x1f\xbc9$~\x0f\xc62\x03ڴTx\xc5|{!\x1e\xa1\x1c\x80\xfe\xfc\xf6\xcbQ\xc4]\xbe@i\x89_\xe1-(\x1d\xb8\xa9\x8d\xfc>\x83{o\x1d;\xed\xc4W\xde)/\f\xe11f\x8d.w\xa1\xda\xdf \x90\xa9\x10\xb6X\x96\xd3PoH؊\x1d\xb3\x90.\x8e\xedM@-\xac;i\xad\xa9ʸ\xffp\xfda\x1e\x90\xb1A\xad}\xbc\xe3\xec\xb4R\\5p\xb9\x10r\x9e\xb7\xc6A\xd2L\x1f5\xc1|\x9c\x81\xbc\x10z\x8d\xe1\xbc\b\xab\x86\xb3Pv\xf9\x12?\x1e\xa6\xfe\xf4\x8d\x94\x00\xfd\xc0\xf1͒\xe8\x13\x0f\xe7+\xd5'\x1c\xae\xfd\xd6:y\xb8\xc7f\x89V\xa3C\u007f>ir\xe2\xa3\xe5X;\x9a\x99\rڍ\xc2\xedlk\xec\xa3\xd2\xeb)\x9b\xe64\xd8\x00\xcd\xfc\x93y\xf6\x9d\xff\xe7\xc5g\xf1\xaf\xeb\xa7\x1e\xa8\xf3\xe8\u007f\xcdS\xf1>4{ѡR\xad\xf8\xf4\x87\xdf~\x9f\x1cҝȹ\xd6Ey\xdb\xff!8V#\xe9w]\xffgnt\xf8!\x96\xe6\xf0\xf9\xcb\x04b\xab\xf1!\xfdX˃\xff\v\x00\x00\xff\xff\xd7w>\xba>\x1f\x00\x00"), - []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xec\xbd}s#\xb7\x910\xfe\xbf?\x05j\xe3\xfaI{\x11)or\xc9\xefn+\xf5\xa4\x94]\xd9Q٫U\xad\x94\xf5\x93r|>p\xa6I\xe24\x03L\x00\f%\xde\xf9\xbe\xfbSh\x00\xf3B\x0e\xc9\x01\x86\xda\x17{pU\x17/5\xd3\x034\x1a\xfd\x8enZ\xb0\xf7 \x15\x13\xfc%\xa1\x05\x83G\r\xdc\xfcKM\xef\xffMM\x998_\xbd\xf8\xe2\x9e\xf1\xf4%yU*-\xf2w\xa0D)\x13x\rsƙf\x82\u007f\x91\x83\xa6)\xd5\xf4\xe5\x17\x84P΅\xa6\xe6ge\xfeIH\"\xb8\x96\"\xcb@N\x16\xc0\xa7\xf7\xe5\ff%\xcbR\x90\b\xdc\u007fz\xf5\xd5\xf4\xff\x9f~\xf5\x05!\x89\x04|\xfd\x8e\xe5\xa04͋\x97\x84\x97Y\xf6\x05!\x9c\xe6\xf0\x92HPZHP\xd3\x15d Ŕ\x89/T\x01\x89\xf9\xd8B\x8a\xb2xI\xea?\xd8w\xdcD\xec\"\xde\xd9\xd7\xf1\x97\x8c)\xfdm\xf3\xd7\xef\x98\xd2\xf8\x97\"+%\xcd\xea\x8fᏊ\xf1E\x99QY\xfd\xfc\x05!*\x11\x05\xbc$\xd7\xe63\x05M \xfd\x82\x10\xb7&\xfc\xec\xc4\xcdz\xf5\u0082H\x96\x90S;\x1fBD\x01\xfc\xe2\xe6\xea\xfd\xefo[?\x13\x92\x82J$+4b\xc6͍0E(y\x8fk3\x13\xc0M zI5\x91PHP\xc0\xb5\"z\t\x84\x16E\xc6\x12Db\x05\x91\x101\xaf\xdeRd.E^C\x9b\xd1\xe4\xbe,\x88\x16\x84\x12M\xe5\x024\xf9\xb6\x9c\x81\xe4\xa0A\x91$+\x95\x069\xad`\x15R\x14 5\U000c8d63AG\x8d_7\xd6rb\x96k\x9f\"\xa9! \xb0Sv(\x83\xd4a\xc8\xccV/\x99\xaa\x97\xb6\xb9\x1c\xb7$ʉ\x98\xfd\x17$zJnA\x1a0D-E\x99\xa5\x86\xeeV \rr\x12\xb1\xe0\xec\xbf+\xd8\xca,\xd4|4\xa3\x1a\xdc~׃q\r\x92ӌ\xachV\xc2\x19\xa1<%9]\x13\t\xe6+\xa4\xe4\rx\xf8\x88\x9a\x927\xb8=|.^\x92\xa5օzy~\xbe`ڟ\x9fD\xe4yə^\x9f\xe3Q`\xb3R\v\xa9\xceSXAv\xae\xd8bBe\xb2d\x1a\x12]J8\xa7\x05\x9b\xe0\xd49\x9e\xa1i\x9e\xfe\xa6ڶ\x93\xd6\\\xf5\xdaP\x9eҒ\xf1E\xe3\x0fH\xe6{v\xc0\x10\xbc\xa5%\xfb\xaa]E\x8dh\xf3\x93\xc1λ\xcbۻ&\x9d1\xb5\x89}\xc4{\x83\xf8\xea-0\bc|\x0e\xd2n\"R\x9b\x81\t<-\x04\xe3\x1a\xff\x91d\f\xf8&\xfaU9˙6\xfb\xfe\xcf\x12\x94!h1%\xaf\x90\xa9\x90\x19\x90\xb2H\xa9\x86tJ\xae8yEs\xc8^Q\x05O\xbe\x01\x06\xd3jb\x10\xdbo\v\x9a\xfcp\xf3a\x8b\xb5\xc6\x1f<\xf3ڱ_\xee\xf4\xdf\x16\x90\xb4N\x8cy\x8d\xcd\xdd1's![\xcc\xc1\xbc2m\x01\xed>\xb4f\xd8\xd3o8\xd8\xe6_6\xa6\xf2\x97\xeaAC?f\x12%g\xff,\x01Y\x9c=\xb1\xb0\xc5R\xb6@\x12??$\x8b\xe9\xd6\xdfw\xe0\xd4\fxL\xb22\x85\xb4\xe2\xb6[k٘\xf1\xe5\xd6\v(\x8e(\xe3\x86\xfe\r\xfb7\xd3\xe6\xf5_\r;\xed\x981\x95@\f\x052n\xe1\x11\xc6q\xb1\x9d\x986\x83i\xc8;&\xb7wu\x04\xe5\x1c\x9de\xf0\x92hY\xc2\x0e\xccP)\xe9z\ab\xbcl\ue2d7\xeay\xc7\x102\x96@SPX\xd4X!C\xe5\xf6\x8c\xc8'\x8e\x15\xa6\f;\xf3\xab\xbc\x11\x19K\xd6\aQ\xd3\xf5\x92?n\xee\xf0y\n\x9e\xc1\x92\xae\x98(eǚ̑4\xcf\xdeג\xb4\xe6\xa6\xc203\a%\x8d[q'\xb6\x96B\xdc\x1f\xda\xfc\xbf\x9agj\xb6M\x12T\xeb\xfcZ\xa4\xdbn'Eg@\xe0\x11\x92RwL\x93\x90\xb4D\t\"$)\x84һ7~7\xf3!\x96\x1f\xec\xa2Z\xb2\x8fj\xb6V\xe6x\xa5\xdf:\xb3\xd0\x16\xdf\x14\x1c\xcc\\s\xb3u\xf5\xb3R\x94\xf6\xd9M\x01\xd7\xc0x7FȌ*H\x89pd_f\xa0ܷR\xdc\xfe\x9a\xb1\x9c\xed\x04]-ު\x1a\x19\x9dAF\x14d\x90h!\xb71\xd9\a\x9fv\xf4a\x96;\xf0\xd8\xc16\xdb\xf4_/l\x0fHb\xc8\xfcaɒ\xa5\xd5\x02\fm\"\x1c\x92\nP\xc89\x8c\xa6\xba\u07b5Hrh\xef\xddG\xf6\xf1\x8ez\x1c8S\x9b\xf0\xba\xf8I=z\xf0\xdbz\x1c\xe0\xbc[\x9c\xc5\xfd\xde):\xeb\xf1\xcbD\xac\x17%\x11D{\xb5\xf5\xeaq\x89\x16\xad*\xa3\xed_\xcd\t\xe4\x85^\x9f\x11\xa6\xfd\xaf\x87 \xd2,k|\xff3ޘp\x8a\xbf\xda|\xf3\xa8\x14\xbfwW\x0eA4\xbbR}\xfe3\xdc\x14\x14\x16\xb7NV\xf4ސ\xef\x9ao\x9d\x116\xaf6$=#s\x96i\x90\x1b;3\xe8\xbc\x1c\x03\x19}\xe4\x9d\x199\xd5\xc9\xf2\xf2\xd1h6\xaa\xf6@\xf5\xc4\xcb\xe6\xcbV'\xf6FB[0\x1f\x80K\xd0~e\x12rk\x17\xdf!6\xeb_Р\xb8\xb8~\r\xe9>\xf4\x90~\x94\xb7\xb5\x90\x8b\x8d\xc96?\xed\x14\xfd\xbe\xcbp\xaaOe4Y\x8f\xc7\x19\xa1\xe4\x1e\xd6Vc\xa1\x9c\x98͡\x1a\xf5\xddN\xf3i\x1b9\xe8z\xb1\xea1\xac\x11\x8c\xf3\xa5\x1c|\xbb/)\xd8q\x0f\x1d\xfa~\xd7h!\xd0\xcc\xc9Y\xb8\x16\x93\xe6\aD\x04Z\xde\xfd\x91G\xd0/\xe6y\xd1\xe1ő\xfe\x8c\xc4\x0f\x8f\xfb\x88eV\xdb\xd6\xf0\x1f\xe2ƞ(\xbbE\xe6\x14,Y\xd1s\xa1\xe8>T\x80\xa7\xc5{\xc6\xdeӌ\xa5Շ,\xdd_\xf1\xdd\xdap{\\\v}\xc5ϬI\xa6\x90J^\vP\xd7B\xe3/O\x82N;\xf1\bd\xda\x17\xf1xq˶\r\x1e\x9a.\xb6\x1e\xc4mǕ\xf5\xa4T\xdb\xc3\x14\xb9\xe2\xc6pq\xf8@\x87\xa9\xfd\xdc~\xf9\xd0\x1ey\xa9Ї\xc6\x05\x9f\xa0\xa8\x9cv}\xc9\"\xbb'H![;\xb2=\xb5\xea\xa3\xf6\x83=\xc1\xde\x19Ib߷.\xe0\x8c&\x90zk\x13\x1d\x97TÂ%$\a\xb9\xd8'8\x9a\xa30\xfc\xbd\xdf\x14zr];\x02)\xac\x9fh\xf7ñ\xee\xf4\xf0d&\xe6\xe4\xf6x\xcao\xf6\xc1Gw\xf8+w?zxE(bQ\xff8\x88]\x9a\xa6\x18\\\xa2\xd9M\x00\xc7\x0f؋m\xd9o'f%dN\vs~\xffLj9$\xe8\xff%\x05e\xb2\xc7\x19\xbe\xc08Q\x06\xadw\x9dg\xac\xf9\x19\xf3\x05\xa6\x88\xd9\xdf\x15Ͷ=\xe1\x1d\x8b\x13\x86\xb7@f\x05\xb9\x98oi,g\xe4a)\x94\x95\xa9s\x06Y\x97˦=\x98\"\xcf\xeea\xfd\xecl\x8b\x0f<\xbb\xe2Ϭ\x80\x0ff7\x95\xb6 x\xb6&\xcf\xf0\xddgC\x94\xa0\x9e\x94\xd8\xeb1\xde\xe9\xe7\xaeG\x8b,\x9a\xbe\xee\xda\xc9\xed\xd4\xdc}\xb3\xeeE\x87\x85P\xfa\xaf\xdd\x0e\xbb\x1d\xf3\xb9\xf1o\xb4u\xd3\x0e\xbf\xd7A\x9d\xdd\xf9\xb0*\xa6j4\xb9\xb9\x06\xe9\x9cx\x96\xd1z\v`\xa0mt\xc8IW9\xe8h\xe5Y5\b>@\x156\xe6\xd1g\x8a!Z\xa3\xc1K\xa0\xbe}\xf9\xd8\xf01\x9a\x13j\xfe\xdd\\ȱ\xb5\xdaD\xe49\u074c\xf2\xf5\x9a\xea+\xfb\xa6\xa7i\a\xc8\xee\xbe\\\x94x.\xfb\xab{\x9e\x860\xbe\xf7\xc0\xf4\x92qB\xfd\xf1\a\xe9\b\x8a\x92B\x1c\xe6Dv,\xa9\"3\x00^\xf9\xc6?\x05y\x9d3~\x85\x1f /\x8e.\xdfI\x8d\xae\xa8\xed\xf4\xa8\xae6\xb4\xfa\x01%N_\xd5H\xa4\xe4a\t\x12ZT\xb1\xed\xf06\x1acO\x90\\\xe8\xa6_\xc1\xc0-Dz\xa2ȜI\xa5\x9b\x13\xedKp\xa5\xeaK\x0e\x81;lVw\xc7r\x10\xa5\x8e\u0603\xcb\xfa\xedV\x806\xa7\x8f,/sBsQ\xf6\x10\xeev\x18\xf9\xc2\xf2*\x8a\xeav\xe0\x812]œ\xd0â\x85٥\"\x03\xddw\x8bg07\xec(\x11\\\xb1\x14\xa4\x8f\xf2\u06dde\xc2\x1c\xdc9eY\xd9\x15\xbe\xe9\x1a\xa1f*\xbf\x942\xcaJ}k\xdflx\r\x97⡍\xa0\xde(X\xd2\x15\x106'L\x13\xe0\x89\xd9\x17\x90\x96e\xe3'\x1c2\x105\xbdɲ\x1f\x837\x03x\x99\xf7C\xc0\x04O6\xe3{\x9db\xcdǿ\xa6,{\x8am3\x94\x17\u007f4\xbe\xaf\xdf\xfe G\xa3b*\xfdE\xd8\f\xc8;\xa0\xe9ڟ\x0f\xaa\xb51U\x91\x06\x04\x91%or\xc4'8\x19!\xf6\x9d\x9b\xc51\r7\xc6Y\x8f\x8d\xdd\xf0\xe73\xdd\xd4v\f\x88'\xd5v\xcc\a*A\x17㚹j\x010\xa2\xd2+\xce8\xf7\x8aj\x024\x9f\x19\x18\x03\x15R\xeb\xf42\xe2\xd3\xe9\xd16wiG\x18\xbcsu!\xaaˆ\x9b\xd7\x19\x9a\x8d|\xbf\xe0#\xe0\x1c\xbckQ\x92\aʵ'\xfaJ\x99+DO\xaa\x0f\xddU;\xa8\\\x04<\xbd\x95L\xe8UV\x9f\xd1\a\\\xcb5f\x98\xf5\x9d\xb4\x1d\xc64MEroԑ\x9c.\xe0\xe4D\x91Wo^\x1bR1Z\x87\x11\x19\x01\x12\xc1\x0ef#\xb1\x85\x14+\x96\x1a\xd5\xe9=\x95\x8c\xce2c\x04\xcfA\x02O@\x91/O\xdf_\xbc\xfb\xe9\xfa\xe2\xcd\xe5\xf3 \xe0\xc6t\x86ǂrC\x83\xa5\xf2Ҽ\xda}\xb3\x00\xe0+&\x057\b\n\xc3\xc6՜P\xb2\xf2\xb3M\xaa\xe4;cje+\xa7\xcd\x05A\xacV\xec\x1d!\x8c\x17\xa5\xf6\xde\xd1\a\x96ed\x16\x06\xb1\xe4ɒ\xf2\x85\xc1\xebkQ\x9ay~\xf9%bEBZ&\xee`\x06At\x87\xe9\xcb3\x17\u03a2Y&\x1e\x14\xca\x16P\t-\x1c\x8e\x83`6\xb6\x97\xa85\xd7\xf4\xf1%aS\x98\x92g_6\xfe\xf4,\b&b\xab\x90\xc2,\xd3\xc6#,\x163\xa6AҌ\xa1\x13\xb5\x84,;\t\x98e\x90\xb8\xb0#\xd8\xdem\xbe\x16\x12`\btL\xd8\xd1\xe6\xe8\x97\x15\x03\xb7_\x9e\x92k\xa1\xf7e\xa0\xed\x1e\x95\bC\x1cO;y\xfc\xe5\xf5ݻ\xbf\u07fc\xbd\xba\xbe\ve\xedM\xb1\xb0\x9b\xd5\xc71ɖX\xe8`\xf5a\xbb\xbfO,\xb4Y}\x10\xdc\x1dba\x8bՇ!\xb6C,l\xb3\xfa0\x16\xbc-\x16v\xb0\xfa \xb0\x9bba'\xab\x0f\x82\xda\x16\v\xbbX}\x10\xc8n\xb1\xd0\xc1\xeaÅжXh\xb3\xfa0\x88\xbb\xc5\xc2\x06\xab\x0f\x02\xdb-\x16FV\xdf\xf1Z\x18\xab\a\xbe\x8af\xf3\xdf9\xf3\xab\xc1\x8a\xaa=\x0f\xa3C-0\xe3\x80\xf16\x9f\xeb\xd2\n\x9e\x16\xf3m\x97 _\xbd\xa7\xed\xb4\n\xde\\l\x10dR\x1f\a\x9f\xb1\x8db\xad\xb2h\xc3XL\x8c\x95fǡ\xc8Y\xf7؎\xa7\xb9\x8b\"\xf1\xf8 \r\x9cL\xc9\x1b\x97a@ɫ\x9f\xae^_^\xdf]}}u\xf9.\f)$\xfe\xec\x10\x9f42\x105'\x1d\xe6a\x04^\xf6k\x0e\xc1\x02َB\u008a\x89Re\xeb*\xbd}\xf8ѵc\xf3五\xb25Q W,\x89\x99m\xe7Ԇ\xa8:v\x1cTx\xe2W\u007f@\xed\x89\x00\xbc\xdb&v\xcaO\fi\x1d\xd32v \x9f\xc0>\xb6〕\x1c\x01\xf1\xb8\nTc\x96{ը\b\xa0\xfbml\xd2;q\xb19P\xfdz\rsZf\xd6\xdb\xf6\xec\xd94D\x97\xb1c(\x8b\xfdZ\x8a\x9e\x01\x94\xe6h\xb1\xd9[{\x01\xcbG\f\x8e#\x84N\\blK\xedP\x81\xf6\xaa\x1d\xcc\xe5Nz\x9b2(o\xae\x1e\xf1R\x9eؐ\xf4\x9c-\xde\xd0\xe2[X\xbf\x83y\f\x88M\xb4cάK/\r5\r\xea\x81Z\x8f\x9dZ\fW\x1c\x8a\x17\x12\x92Q\xdc5Z8\xb9s\xd9Ϩ\xc3\x1a\xf4\xc4-\x89\f;X~\xc4iw~\xb4U\x99\x86\x9a\x17\r\xb1\xf2\x87辆[\"x\x02\x85V\xe7bet\ax8\u007f\x10\xf2\xdeXb\x0fL/'6\x1e\xa6\xce\xf1\x1a\xce\xf9o\xf0\u007f\x06\xcc\xee\xee\xed\xeb\xb7/\xc9E\x9a\x12\x81\xac\xb6T0/3\x9bv\xd7;ӷk\xd4u\x14\xce\xf0.\xff\x19)Y\xfa\xe7pf\xeb\xc7\x11hC\x146\x13\xf3H\xf4q\x8b\x91\xfc\xb5\x97R\x03peXx\xc5\x11\x88\x90\x18~\xeb\x93\x06\xbb{\xf8\x84e\xa7\xe8\x0eD\xfbL\x88\f\xe8f݊~\xa3\u007fh\xb8k\xf4K\a\xee\x1a\x01\xe1㮁'\xe08R\xe3\xa4\x16\x1b\xfd\xd2Y\xbb\x8738\v\x91\xbe$\xaa,\n!\xb5\xaaj4L\r#\b\x8bgԣ\x05\x04\xef\xf6\x9d\x91\xff\xac~Ļ#ꇓ\x93?}{\xf9\xf7\xffsr\xf2\xe3\u007f\xc6~\xa7\x86\xd9(\xafs\f\xc0\xaa\x80d\xcaE\n\x86e\x9f\xd9\u007f:\xcb\xeb\"\xc1\x04\x99\xeb\x01\xe8Q\x9a\xeaRM\x97B髛3\xff\xcfB\xa4W7\x03A\"\f\x15\xa1\x82\x92\xa3(\x01\xbbj݄\x8c\x16\xa5\xfb\x9a8\x83\x85\xa6+\x9fc\xe8\xfdksdn\xa8^\xf6O\xb1\xeb\x1a\x0f\x92i\r\x1cmU\x90\xb9\"b~f\xb8#\x9a\x02Cx\xb7 \xcfV/\x02#\x94-\x00\xc3\x05\xdbܣ\xe8Hۈ\xd8v\xecf\b\xc7\"\u07b5i؟\xf7\x12Tٔ\x03\x80^\xdc\\\xf9ZK\x1f\x11\xf1C%[\xb5m\x1fC\xbe\xf9\x84\xf3\xaf\x9fD\xcey\xe8\xc3D]\x9dSl\xef`\xf4\xbbɻ{d\f\x8b2Q\x9eօ\x99N\xed\x8fӤ(c\x99\xb9\x83\x90C.\xe4\xfa\xcc\xff\x13\x8a%\xe4 i6QZH\xba\x88\x16?~\xaa8\xc5\xfa_\xf6s\xb1\x9c\xbf\x81\x82홆%\xf14\xa0J I)\x8d\xb5\x93\xad\xbd\x8e\x02\xe9G\x93o\x15\xfdtW\x85\xea;\xdaD^'\xab\x0f\xb35k\xfe\x81n\x9c\x95\xc8\xca\x1c\xd4Ye\xa5\f\x00\x8c.M\xbe\"+*\xd5G\xb5\xb8R\xb6b\xaao\xbatנ|\xfd6\x925\x11\xe4\xb1v\x11\x8ckX\f0\xd1&\xc7@F\xa7\xf9\xe8\xcaG\f\xd8lQ\xea\xa2\xc4\xe4\xe1\x9c\xea**\xf5X\x888ϝ\x1f\x8d\x82>i\xd3a\xfa\"ƍmGA\xb5\x06\xc9_\x92\xff8\xfd\xc7o\u007f\x9e<\xff\xf3\xe9\xe9\x0f_M\xfe\xfd\xc7ߞ\xfec\x8a\xff\xf1/\xcf\xff\xfc\xfcg\xff\x8f\xdf>\u007f~z\xfa÷o\xbe\xb9\xbb\xb9\xfc\x91=\xff\xf9\a^\xe6\xf7\xf6_?\x9f\xfe\x00\x97?\xf6\x04\xf2\xfc\xf9\x9f\xbf\x8c\x9e\xf2\xe3\xa4\xf6\xd0L\x18\xd7\x13!'\x96\b\x0e\x16{\xd87\xa57\x1e\x8c!\xff\x84f\x19\xa4\x84\xe59\xa4\x8cj\xc8\xc2\xfd\xe4\xbe\x02h\xb3ܣ\xed\xf0i\x8bIF\xe9\rK\xca\xd3\f$\xd6+t>\xc0\x16|\r2g\x9c\x86\x16\f!Uz\x17\xba,!%4I\x84L]-8_ً\xcap\x8d\xbf\xe2x\xa8+5$\xcff\xbe^0\xe4Y&\x92{EJ\xaeYV\x97\x87\xf4\xb5!]o\xca`\xa8Q,\xa6\xfa\xcfIu&&؏\xec\xfc7\xf5\x9f\xf0\x87P\xe5w\x88\xe5ӯ\x9e\xef\xf6بA\tH\x1a\x98L)\xc2Ŗ\x1f\x98\x17,\x8c\xfab\x88\xaa\xae\xafZ\t\x9a8#\x03\v\x10\xb7\xbb\xeaPd\x9bX\x17\x8d\xde\aV!\xb1ch\x8eBD-\xa0\xe6\xd8S\xb68:\xd6\xe8\v\xdbf\x8cC\xb3~1Ú\xa8\xf1!\xcc\xe6\t\xb6\xfc\xc8Y\xa8\xf1A`&\xb1\xc7ȺQ\xdb\xd2\xce}H2\xbf\x14B\x93ӓ\xf3\x93\xe7[A\xad\x93x\xa8s\x96\x81\x95\xae\xb6\xc8RRmV4H\xc5\xf2\"[\xe3\xfe\x9c\xa4\xd8\xd1\xc9]\x87\x95e\\\x8c\x98 S3\xbb\xec\vB\x9d\x11%\x88\x96\xd4w\x19\x88\x9f\xab\x81f\x80kY:]\xe5\xf4\xe4\xe7\x933\x02:\x89\xcd\a&\xe4A\xf0\x13\x8dd4%w\x82\x94\xaa\x9ex4̵(\t\a[\a\x00\x1e\x8b\x8c%Lgk\x14\xf3\xd10E\xa9m\xf1El\x90\x88\x85\xb6.\x1f\x99v\xf7t\xe2\xc1\xce\xc9Wxڭ\xaa@\xa81\x86Vp\xbe\x04\x9a\xe9e\xfc\xfd>C\x97\\\xf0\xc9\u007f\x83\x14XƋ;\x88\xb1ޝ\x88\xd8Ys\x1c!\xe3$ƍ\xb0\xf9vd\x12\x83Q\x10\xbe\x81`\x95\x93lu$\xbd\xbb\xbb\xf9\x06t[\x84E\xa1\xc3\xcc\xc8\xe7\xe7\xa3K\x1b\xe4\\Ȏ.Ç\xc7P\xf9\xb7\x14*\n3d\xbb_\xabҶ\xfb\x845Rx\x9c\xcf\xd9\x0e-\xdai\xc9.\xa3\x91\\\xdd\xc4'b\xfd]\x94\x06[3:\xcb\xd6U\x15Y\x05\x9a<3S\x8fO{f\x1c\xf7\xf3\xaf@S\xac\xdb˕\x06\x1a\xa9\"\x1d\xe1\xa85\xe6r\x1c\xa5\xc6\xf6\xdd]Z\x90\x03v\xb4Y\x02\xcb\xd1\xfe\x14\xcfT<\x9b\xb4\x15^$\x14\x96\xfd\xba9~$&\xb9\xc5+\xec.\xb8\xdfg\x83r\x13\xa9o\u007fl\x97\xe8j;G\x16\xef\xf0\x83q\x9c&\x1e\x8a\x01\xb3\x1b\x9e%L\x06g\xab\x92\xae\b\x98\xc5\xd5 \x98\xee\xeeexZ\xda\xe68\xca\xfd\x92\xe8\x12^\xcd\xf1\x94h\xc2\xe9}|<\rK\xb5$q\x89\x88\xedׇab\xe0\r\x052X\xdf\xc2\xcb<\xd1\u05cd\xb7/\x1bkAh\x92\xc4\x15\x8a\xb2\xc3u/G\x86\xa5@\xaeB\x13\x1c\xeb1\x98\xc4\n\x11\xee\xbf\xf4cЅ\xb7\xe3\\w;\xcae\xb7\x8ez\x89\x92\xf02\x9f\r\xe0$U\x01\f\xa9k\x82q\x1b?\xc0\x99R\x15ۼ\xc6\xe9\xf9\x10\xee\x10}\x0fU\x18\xca\x17@^\x98\x99\xfe\xf1\x0f\u007f\xf8\xfd\x1f\xa6\x88\x86h\xa8>\xb0L9\xb9\xba\xb8\xbe\xf8\xe9\xf6\xfd+,\xe2\x16K\xe5Or\xb3\r\xcb6D˟vd\x1eA\x19\xec\x95\n\v\x9d\r\xd9ack8\xff\xb7u.\xab\xd88[sh\x81\xec\xe6#\xf1\x99!Bl\x82\x87\xe8C\xdb\xd9:)nEr\u007f\x04K\xfb\xe4\xeeՍ\x05U\x1b\xdbQ\xbb@\xb9w13\xbe\x12\xd9\xca\xf6\v\xbc{u\x83\b\x8a\xdbY\xf36\xc6\a\xd0շ6s\xf47\xe1mjN\x14T\x96\x17\xaec&%\x12hƔf\t~\xab\nSD\xda\xf7\xe2>&\x8b\xe7\x93\xf1+\x9c\xbc\xf5\xe9@\xe8b\x88>\xcd\x1b\xae\x89\x96\x8ba\b\x8bh\xb8&b\xaft\x8d\x1a\xc9\xd15\x12+\xea\x85\x1c\xa6Ǐ\x1aɧ\xad\x91|n22\xfa\xd5B\u00ad\x16\xc5\xc0\xac\t\v\xe4H9\x13\xbe\x13ݮ\xa4\x06\x92Fl\xa9\xed\x1d}qsUy\xc7E+\x11\x01\x93W\x82\xa1\xaa2Y\xfa\xd8\f\a\xa5\xce1=\xa2,\xac\xe7\xcb7\x94\f\x8fX\x15\x12\xb0\v\x9f\xe0gUE\x02D\ap\xfb#\xe8$\xfc\xb4\xa0O\xc6厸x\xa2߮\xa1i\x18\x89\xa4j\tة\x02\x1e\x99V\xbe\xdb5U\x82\xdb\x10\xae\xdb>&\xc2\x03\x98L\x91\x82*e\x03w\xba^\x84\xfdȍHO\"\xa2\xb7\x8d\t\x91\x85\xa4\t\x90\x02$\x13)\xc1\xaa\u007f\xa9x\b\x9f\xe7\f\x16\x8c+O\xbff\xa2\xfe`\x18]\t\xa2\"\xc2u\xe7\xd9w\xad.Rخ\xbcԉ\x88\xe0\xc3\xeeu\x87\xc5\xcd\x04\xa2\u0ad38Ms|J\x9ae\xeb\xfa\xa0\xfa\x9b\x9e\xfa\xf8\x9b\xb4\x9dI\x14\x8b\x84zݛ\x99D\xe1\xd1\xc0V\xe6\x919\nuV\xd2\x10\xf2oQ'S\xe6T%\xcb\x01m\xbeHx\xfa\xf2\x98\xdatp\x8c\xa9M\xbdǘ\xda4\xa66\x8d\xa9McjӘ\xda\xd4\vĘ\xdaT\xcdhLm\xda3\xc6Ԧ1\xb5\xe9\xf0\x18S\x9b\xc6Ԧz\x8c\xa9M\xbdƘ\xda\xd4c\x8c\xa9Mcj\xd3\xce1\x06\x12\xc7Ԧ_c qLm\n{}Lm\n\x18cjӘ\xda\xe4ǘ\xda\x148F\x8ddLm\xfa5j$\x9f\x9b\x8c\x1cT],\xf05\x9f\xc7s#\xc5l@\x99\xb1\x1b\x8cճĥ\x01\x89ytm\x1d;\x9d)y\xd5J\xcfpM\xf8m\x95\x96 \x88.ѧNO\x1aZ\xaf'\xb8&\x93/\n\xa6\xce\va\xff_\x9dS\xd0H&\xb0\xfe\xb5\x10\xe1\x10+|c\xb2\b\x0ee\x10D\xf1\xba\xfd\xd9\x03\x98\t\x10\f\xf3\x98\x99\x03C\xb4\x9b\x01\x19\x03{\xb2\x05<\xd8(\xe6ڝ)\xb0\x11\xf1\x8fN\x05qY\x02\xdb\xd1\xfe!\t\x17\x98\n\xb7\x15鏄Xe\a\xec\x8a\xf2ǩ\xe4\xea\xf8\x11\xfe'\x88\xee\x1f?\xb2\xbf'\xaaO֢\x8c\x82\xb9#\xa2\xef\"\xf3\x91\xb4\xd9\x19\xcd\xf7Q\xf98\x98ݑ\xfcVD>\x96\x98\x06E\xf1\a\x04\xa7\x06*\xd7\xf1\x9e\xe4hM\xc9%\x1b\xdf-%\xa8\xa5Ȃym\x8bϾa\x9c\xe5en\u06042쑭\xaal\xe6p\x1a\xf1yNV\xeb\xb0a8\x03\x98\xa5\x80M,)\xcbbJ\xd5ai\xbd%E\xff\x84*\x93\x04 5r\xf2u\x1d\x02\x0f\x86\xf9\xfbi\xb5r\xdbU\x83)\xf2\"\x94\xf2lCE\xb4\xef~\xff\xbb\xa8ݏ\xb1\f#\x136\x0e'k \xe4`L\x0eO\xd4\x18\xa2n\xc4:R\x9e&9cOb\x06\xf9{\xa4hؓ\x94AX\x9c\x98=RB\xc6 \xce90\x11cO\x12\x86\xc3Q\xa4\x02\xb2\x9d\x80\xb1\x99H\x11\x87\xf2\xf8\xe4\x8b\x01\xb2\xed\xa9\x92.v'\\Ē$\x19\x9cl1<\xd1\xe2\x88\xdd\x0e\xeb́\xc1\xdd\xf1\a\xb9\xe8\x8e\xe09\x1c\x98T\xf1Th9F\n\xc1G\xec>\x1b\xbd\xabC\x92'\x06&N\fI\x9a\x88M\x98ؓ,1\xc4\xd3<0Qb\x10\xf9Ć#\xa2C\x11\xc3\xc3\x10\x83C\x10{\x12\"\x86\xf4\x84\xed\f=\xc4\xf6/\xf4\xa3\x1dv\xd8\b\x1fD\xaa\x85͐\xc3QC\aG\x0f\x1b\xc4'1\xecO`h$\"\xc4\xe2p;yaH\x12\xc2\x00\x8a\x8ee\xfeQA\x95h\xa6\xcd8ӌf\xaf!\xa3\xeb[H\x04O\x835\xa3\x8d\x86H\xd5yU\x16\x9c\xb5\xcc\xc33!Z\xb71\x97\xd4u΄\xd4_\xa8\xf5ѐp֊\xea#\xa1\x18\xa70\xab\xd7\xedۓ\x1f7nA>\x9a\xcb\xc0^)=\x06\x11\xfcU<\x101\xd7\xc0\xc9)\xe3\x9e\x0e\xc2\xfd\xa8\xb5\xb3\xa0\xf6\x17U\xc7\xda\xfc\xf5\xc5W\xe1\x9c\xcbN\xe6\xf3u\xec\xa0kK\xa9\xa7\xf3\xeb\xb9\x0f\x1c߱\xe7\x00\xcf\xcbp\x1f}˹g\x1d\x84m\xfe\x1e\xbcyu\x1b\xbe\x178o\xcfM\xd0K\xed\xca6D\xc0\xfcL\x89*:\xed\xec`\xca\x19\x89\xe8<\xb6/ݬN\x1d\v\x06\xbb#լN\x1b\v\x9f\xe8\xae4\xb3\xa8\x94\xb1\x8f\xee\xe1\xdcH\x13\x8b7?w\xa4\x889\xf5,R\x89\x8fN\x0f\x1b\xed\xb0\xc0\xb1'\rl\xb4\xc3>!;\xec\xf3\xb00\x1a\xb5N\xbe\x914\x81\x9b\xa3\xa9\x99\x9e]\x91\xb4\x94ԉ\f\xaf\xe0E\xd9\x1b\x86\xc9p\x80\xd4r\xaa\xaap\rV\\\x99\x97YD\xf1\xaa\xb2\x10\xbc]\xfd\xc9\xe6T4\x8b\xb8\x04\x03u\xd9.\x1d\xabv\x8aR\xcc\t-\xa4\xb0j\x1f\x91%\xe7F꺳d\x90bl%\x15'!\x9b5{\x14[\x98\xed2*\x16V\xc1a\x11\xf2\xe5a\t\xdck\x99n\xc2fvs!\x136\xcb\xd6dI\xb3\x98\xf0\xcb\x03\xd3KB\xc9=\xcb27\xcd)\xb9\x05M\xf4\x92\xa9Hgj&\xf8\x027\x83\xda\t\xc3c\x01\x89Q;\x92\f(/\x8b\xb8\xf5\x1beu-J\xe9\xd7\xef\xda\xc6\xf9Y\xc6$mp\x96\x9d\xf9\xad>Q\xfb\x0fl\x04bm\x82b\xa9\xc0\xd7iz`\nΆ`ַ\x19\xb5\xe7\xc0\xae\xbb\x90b\xc5RH\xc9l\x1dG\xff\"E\xaduJ\xde#<\xcf\xf7\xb9\xe0\x13\x0e\vjl\xa3\xf0\x93j\x85\xb8=\xf3v\x9e\xb6\x1e\x05OYBu\x84\x8d\xa5\xb0\xb0^]N\x8f\xac\x18E,4(7\x18\xe8)\x17D\xa0R\\r\xa6\xd7\x18\x1b]\x96\x9a\xa4\xe2\x81?\x9fb\xeb\xd9\x18&E\xc9\f4u\xf7Zm+A\x14X\x8a\x00\xa7\xb3,F9\xc1LܻN\x02%s\xa0\xba\x8c\xe8\uede0\x1a:\xfd\x01\x96\x1e\x8e{\x1c\x98r\x11\xd09)\xb9\x82\xe0\xfb3\r\xfb\xf0\x8f\xff\xfa\xe1\xecC\x96\x83(\xf5'\xe5 |X\xb2d\xd9\xf47\xb0\x1c\x14\x11\xe5\x90kkZ\x90\x17nZ\xdd\x14\xf1\xc4\xed#\u007fq^\xc5(\xad14\xc4\xde\x117ڬ\xe6W%N\a\xad\x9b\x1a\x1e\xf6\xfa\xfa\xf6\xa7\xef.\xfer\xf9ݔ\\\xd2d\xd9,C\xca\t5r#\b&ʕ%]\x01\xa1\xa4\xe4쟥\xed\xe0\x9e\xc4E\xa7&m].\xf0]O\x05a\xd6ِ\x8a\x02\xa1\x89έ\x10\xa0\xb1`&9-&\xf7\xb0\x0e\xd2y\xe3\xb1\x14\x85\xa3\xedI\xdb\xc5\xe7\xb4\xffM0\t4e\x9fP1\x05ǥ\xeayuWU\xc8\xc5*\xd0i\x84֞\x87\x0e<-\x04\xe3Zu\x95Z\b\x02\xbbm2~2)\x8bc\xa9\x85\x8e1\x96Z\xd89\xc6R\vc\xa9\x85\xb1\xd4\xc2Xj\xa1{\x8c\xa5\x16\xc6R\v\x9fU\xf2\xf4Xj\xa11\xc6R\v\x11c,\xb5\xb0k\x8c\xa5\x16z\x8d\xb1\xd4\xc2\xf6\x18K-t\x8e\xb1\xd4B\xc7\x18K-\xf4\x1dc\xa9\x85j\xfcr\xae\xf8\x8c\xa5\x16>\xd5+>c\xa9\x85>\xe3\xf3\xb8\b5\x96Z\x18K-x\xbc\x8c\xa5\x16\x82\xc6Xjac\x8c\xa5\x16>W\xa2\x1aK-\x8c\xa5\x16\xc6R\v\xdd\xdf\xfd\xb5\xdbac\xa9\x85O\xd5\x0e\xfb<,\x8c\xb1\xd4\xc2Xja,\xb50\x96Z\x18K-\x8c\xa5\x16\xc6R\vc\xa9\x85\xb1\xd4¯ȫ\x18\xa55JP\xa2\x94I\x98\x1d\xdc&\xb2W\"/J\r\xe4\x9d\aU)\xcbA\vGY\xc2T\xf3F\xff\x87\xedD\x98\b>g\v\xa7\xe8\x9d\xe7\x94\xd3\x05L*\xfcL\xea\xfbw\xe7\x1f\"7>c9\v+\xb2`F]\xb1\xe0f\x80\x87#Ҡ\x1ejN\x0f4\xa6\v\xaa5H\xfe\x92\xfc\xc7\xe9?~\xfb\xf3\xe4\xf9\x9fOO\u007f\xf8j\xf2\xef?\xfe\xf6\xf4\x1fS\xfc\x8f\u007fy\xfe\xe7\xe7?\xfb\u007f\xfc\xf6\xf9\xf3\xd3\xd3\x1f\xbe}\xf3\xcd\xdd\xcd\xe5\x8f\xec\xf9\xcf?\xf02\xbf\xb7\xff\xfa\xf9\xf4\a\xb8\xfc\xb1'\x90\xe7\xcf\xff\xfce\xf0T\x8fl\x9c\xb6\xcf\xe3wH9uJ\x11\xf2\xed\x9c>\x1a\x06\x1bN\n\xb9(\xb9\xb6\xb7l\xec1\xafN\x84M\xc3\n=\x94\xe4S9\x98d\x88\xa1\xed\xf2\xd1\xc6\xf3\x190\xc6\xf3i\xcf\xe7;G;\xed\x13\x1a<\xc7ܩL{Nh0L/\xb8\xd1Э\xe6\xc9\x14\x119\xd3Ɯ\x8e\xb9f\xdc(\xa4\x82WR\x9a.j˫µ\xbc\xb9\xbdM\xc1T\xf3\x82F\xe3R\xb8\xf0\xb6o\x8cZJy\x1d\xa7@\x9e3Ia\xce8\xa4V=\xfd\xf5\xf1\xbb\xa8\xd7\x14$\xa5dz\xfdJp\r\x8fA\x8e\xfd\xf6y\xb9m\x03\"v3\xc2\x0f\x8d\x9f\x10\x11\x85\xbdv\xb4Q!\fo\xfe\x85\xa9\xac@d\xc9џe\xeb\\\x80\xb6\xce\x1d4\xc3\xf1f\xcf\xc6\xe4\x83\xc0{\u05cb\xf5h\xfd\xb3d+\x9a\x01\xd7\r\xe87h\x1c7?\xf0\x14\x1a\xb2\xa6꾦J\x98\x18S\xa9\xc2۹G+\xfe\x04\x8f\xfa\x83hǨz\xdcH\xb6b\x19,\xe0R%4\xc3\xd32\xcc\\\xbe\xd8\x015\xf8\xc0\x1bTH\x91)\xf2\xb0\x04É\b\xf5.D,\xb2\xb0\xa0\x11Iٹ٫\xc2ONY_\xa7Q\xf4\n*\rUx\x1fe0`\xacE4\x13\"s7&\xb3u=\u007f\x16\x17\x82\xe2\xe2'\x0e\x0f?\x99\xd9*2\xcf\xe8\xa2rM*бi\xa2\xf5Q\xadܱG\xdb0\xa6P\xae\x13\x9a=е\xaa\x1d\xdfqW\xcb-ė\xe4\xc5s\xe4\x0fT\x91j\x8e)\xf9\xdds̰zuq\xf3\xd3\xed\xdfo\u007f\xbax\xfd\xe6\xea:\x8e\x8f\x9b=\x83\xc0\x98\u007fB\v:c\x19\x8bQ<\xb72Ǜ\xc0Pp\xa6\xe9y*E\xf8\x95%ķ\x8f\x85\xd4\xf2m\x98w\xa9Y\x11\x0e\xc9nޚp\xb8\xefRR\xae+\xa7w\x83\x1ce\xc95\xcb?\xe8\xa5n\x9a\x0e\xbf\xd0}\x91\xa6\x90\x0eC\xc9\xf1\xee¼\xf2\xd3X\xd7\x05颠\x12r\xf3\xf6\xf6\xea\xffn\xd0\xe6\xba\x18\x92\xa0\xff\x11\xee\xa2\x12b\x0e\xd2\xe0=~g\xebW\x8c\xbb\xbcw|~7\x8e+=`XN⻒\xb7\xcb\xd9\xd6p#\xf4\x93\x14\xa6䦊\x14\xb7\xa05\x98z8\xaf\x93@\fH\xae\x19ͲuS\x13\xd6\x02k2\x84\a5\xf9\x8e\xdc\xf59\xcdT0#\x8f\x97\xc6F\x91yc\xcc\xf7A\xbbXA!)p\xa1\x9d\xc7/\xea4\x889B#֧и,ВxQJf-\x8c\x99\xf28\xbf\xa9f\x8e\x11\xa6\xf0\x94\x0f\x05\x9bf\x9c\x13ƕ\x97!*\xe8/\x81\xa6XS\xa6\xa0zi\xf3Ts\xaa\xee!\xb5?D\xea\xd8UH\xd6̸Z\xfaݺ\x80\xe8x*\xea\xd66\xfb\x17\xe3\xbc\xe1\xde\xd8\x01\xe5\xbdh\xfa\x96g\xebwB诫2&\x83\b\xf9{g-\xb5\xe3@\xc1HYb\xd9n3\xbf\tn\"\x96miVZq\xd4\x17c%|`\x06!K~\xa1\xbe\x91\xa2\fV\x05\xb6\x94\xf5o\xae^#\xbf,]\x86\f\xd7r\x8d\xa5\xa9b\x98D\xfb\xccU\xf6\xd8\xdf\\NST\xb6M\xc5\x1e|\xb8\x9e\xbc\xa1kB3%\x9c\xe1\x18\x11\f\xee\xf2\x90\x10窉\xb9\x19=\x13z\xb9\xe9\xd3A\xf6\xb0\xfd\x9d\xf0\x02Fu\x82M\xe5\xc94K\x18\xe2BB\xb0\xf4\x1e\x14)$$\x90\x02O\x82\xa9\xf7\xe3\xa4A \xe5_\vn\xd8\xcb ڿ\xf2\xf9?\xd6e<̪\xc7L%g\xd3S\xccWB\xe6R*\x9069L\x96\x10\xb7\xf1ߖ3\xc8@[G\t\x16\xb9\xa5\xdaz\xfeXN\x17ᧉ\xeaJ\x14jA\x80\xabR\x82s\x9ak\x92\x8a\b3\xc0Ց2K\xff\xdb\xd5k\xf2\x1595k\u007f\x8e\xe4?\xa7,\x8b\xa9\xfa\x82\xb7?6\xb8\t\x9b\xfb)\x1a\x94\x86\xeb\x04\x1c\x8d}iY\xf5\x19Ⴈ2Yz\x9c\xc6x\x87\xbc\xf3\xcaݐ\xc2+l#k\xfa\x04X\xd3@\xc1\xfa7\x05r\xb0\\\xfd\xdb\a\x90\xabC\xdc`\x867\xb5w\r\x19\n\xc9AӔj\x1a\x13|+y\xa3:\xe2\xc6Q\x88\xa1\xdd\xfdG\x01I;\x18\xe6\xaf\xec(|\x1c)\xad\xe0;\xc6\xcbG{;`\xb8C\xf9\xf6\x12\xc1\x11\x17J\x8a\x91(3 \xb4(2f\xcb\xf7m\xb4\x88\xb9j\x91n\xdc\xdeo\x9b\x9a(\x1eh\x96\t\xa3fD\x84\xc7%\xe5\xa9ȷ\x16o\fQ\xa0\x11Vqc\xc1\x1d\x87s\xd7a\v\x97\xdd\xf5\xe1\xfc\xb5\x1d\xb6!\xae\xfb\fV\x10Q\xa5|\xb3\x89\x92\x81b\fRO5\b6\xca\xfb\x99\xd1\x19dV5\xb4'Gm\x9f\x9ch\xafh\xa4SU\x8alxɋw\"\x03\x9b\x1b\xef\x91d\xc0\xfebp\x84/\x0f\xc5\x11z\x9fZ8\x8a\xf6\xa2\u007f\x8a8*#4<\xb2\x89#\xa3&\xb6qd\xc0\xfeBp\x14\x1d\x82P\x90$\"/n\xa4\x98\xb3\xf0ú%\xfa\x1d\xb8:9'\\\xf4\x97\n\xba\xb2\xc8Q\x8fD\xe0\xe1\x1a\xb9\x9b\f\x95\x8dKOT[\x99\xe7nq\x05\x03\xfd\xff\x1a*\x04r\xed\xb3\r\xbd\xc2}5|\xb6\xcd|\xa1B\xa4\x1e\xd0\a\x95n\"\xa1\x19v$\x8a\xa3\v\xb2I\x1b\x9b\x00\a\xdc\xe7\"\xb6\xb1\x9d\x83\xe3s\xfa\xb0%\v\xfe\x12\xe1\x19 \xbe\xb9\x9fH\xa1Q;\xde^\xc0\xbb\xb3\xf7e\f\xec(\xc0\xfeZ\x9c\xd1S|\xf2U\xeacW\xe6\x8bq\xd3\x15\xaeT\xf6\x9b\xaa\xa7\x92A8\xf04\xb6\x12TA\xf5\xf2\x8cH\xc8\xf0\xe2\x9egh\xf7֡u\x12\xb7O\x8d\x05{\xce\xe07\x0e\xf5l&x\xdc%v\\5\x86\x05\xbcF{K\xccm_\xe2`\xff\xc1\x0e\xf7F\xed\xae\bw\xa6\xecqoXwE0ȏ\xe3\xdeX䊾\x92滚\xd1\xec\xb6\b\xef\xf0C6i\xf9\x9b7\xb7\x17m\x90q\x9c\x1d\xf3z\xa5U\x8f\rLBӜ)\xc5\x04'\x0f0[\nq\x1f\x05\xf7ԧ\xce/\x98^\x96\xb3i\"\xf2F\x16\xfdD\xb1\x85:w'{b\xb0\x13\xd7\xe4\x84\xf1\xcc\xdfz\xb0\x1eB\xae\x95\x8f\x18\x98\xc5\xc4iY\x15V\x91\x00]\xa7B\x97ຍ\xf6\xeb\xd8\"UxcძTۤx\x1dYP\xfc\x009F\xe3\xc5U\x97iT{\xb2\x84Y\xefK\x9c\xb85{iC?\x1f\xbe\x8d\x805\xd5\x12P\xc3\xdb\b\xfc\xb5\x86ER\xb0\xc5!\"\xed>6o5\xf4\xae\x15\x12\x1bю\xb4%O\xb0v\x9b\x9b\xe2I\xd3\xe9\x10Uǃ\xf8\xa3\x82ަ\xacX\xd2\t:\bP\xde\x18\x81\x16\x05\xd1\x1b;K\xc1\x85\xb4\xc7ۨ\xf4\x82G\xf4\xfc\xb6\x03\xfdW6\xdf\fi\xd6)\x1a\x8d\xedz\x15\x9f\xeei\x86K\x87\xc3\xf46\xac\rd\xd4\x16\xeb؉/S\xff\xc0\xf4\x12\xdb7-\xa1\xf5\x81x\xccJP\x98\xb0\xc4\tH)\xa4\xbb7\xe2\x13\rb\xeb*[\xfd\a/\xb7\x18\xa6@ͿNԐ\fZ\xd2j\x1fk>\xa0\fǁ\xf9\x1c\x124\xd9\x1b;\x17\x05܆hN\xeb~c\xeej\xb8aB曑\xc7+g\x8f\x06\x03M60\x10\v\xbe/V7\xc8\xe7SB\xae\xe2\fQ\u007f\xb1\xfb\xccp\x9a&tw\xb3(\x96\x14x\xab35n\xa2\v\xe7\xc5I\x06\xc0\xac^3\xa3x\xc9\x10\x93oA\x9a9\x17G\x11Ø{မ#\xe8\x98P\xacOl;\u007fc+\x1f#\xce9\xb6\x99\xc3\xe1\xfdc\xd11\x84=\xb9\x1c\x84\x85\x87q\x89͙:j>\aّ\xd3\x11\x9f\xddD\x9e:É<]\xb4\x99\x1c!\xe2L>J\x94'\xeeⷭ\xe8<\xb0\xcd\xefm\x03JãiT\x8f\xa0\xb5;qj\xab\xdaWU\xb1\xb3\xb5\xaf\xc6\xcf\xfe;4g\xbe\xdd~\x9e\v[l\xa0Y\xea\xde\xf55\rSSJ\xaeY\xe6\x83Wy\x91\x19\xe3\xb15\xe3\xe0lH\x84\xd5\xe87|V!\xa3no\xec\n\xfd\x87\x9d\x97\xffB1T\xb54\xf6\xf5\xbco\xaaO\xd9\xe8G\xa0\x06\xec\xda\xcfc\xc96-|\xbc\x8d\xa4l>\a\u007f\xc39P\xec\x15T\xd2\xdc\x18\x0e\x8a\xb8\xd4\xdf\x19,\x98\xbdfZ\xa9V\x81\x11\x8a\xaaHؙU\xf7\x98&9[,\xad\x97\x86P,E\x19^nR\v\x92\t\x9a\x12\xe4\xe2B\x92\a*sc\xb1\xd0d\x89\xf5\x1b)'i\x19|\xf0\xb1\x93\xdcz\xa24\xd5@D\x01\xb6\xa4\x84\xdd\x1b\x83o[]+\x8cL\xc7\xe6\xd3c\xf3\xe9\xdecl>=6\x9f\x1e\x9bO\x87\x8e\xb1\xf9\xf4\xd8|\xba\xf7\x18\x9bO\x8fͧ\xc7\xe6\xd3v\x8cͧ#\xc6\xd8|z\xd7\x18\x9bO\xf7\x1ac\xf3\xe9\xed16\x9f\xee\x1cc\xf3\xe9\x8e16\x9f\xee;\xc6\xe6\xd3\xd5\xf8\xe54=\x1b\x9bO\u007f\xaaM\xcf\xc6\xe6\xd3}\xc6\xe7\xd1\x1anl>=6\x9f\xf6x\x19\x9bO\a\x8d\xb1\xf9\xf4\xc6\x18\x9bO\u007f\xaeD56\x9f\x1e\x9bO\x8fͧ\xbb\xbf\xfbk\xb7\xc3\xc6\xe6ӟ\xaa\x1d\xf6yX\x18c\xf3\xe9\xb1\xf9\xf4\xd8|zl>=6\x9f\x1e\x9bO\x8fͧ\xc7\xe6\xd3c\xf3\xe9_\x91W1\xf2\xa6HʂZ\xb6\xf5\xe8\x16\x10S\xfb\xc4\xd7\xee4\x8c\xac\x9c\xcfA\xa2\xf0\xc5\xd9y\xe5(\xee\x12\x98\xef\fU\x89n\x97\x11\x8b\x8d\x02%\xd0\xd4ּ\b\xf3\vvN\xcb\x17!\xc5\xf6e\xf6^j`x\x80\\\xbe\xfd\xbav\xbaƴ:\x88\xbb\x1d\x88\xeby˓\xf8\xbbB5!tTg\ríM\xc0O2\xa1\xdc=YDv\xb2\xa4\x9cC\xe6\x94n\x16\x86\xd9%Ud\x06\xc0\x89(\x80[\xb5\x85\x12\xc5\xf8\"\x03B\xb5\xa6\xc9rjV\x10\xe6=sD\xe0\xba\xd6\xd53UZ\x02\xcd-1H\xc8C\xfb\f\x9a)\x12\x9aH\xa1\x14\xc9\xcbL\xb3\xa2\x9a$Q\x80\xa52\x02\x93\x96\xae\xe6\xf5\x06c\x92x}\x01\xf5\xacZE\xf0\x1cm\x19\xb4ƙ\xd7T\xea3l\a\x9b\x17zm\xefR\x85\t>\xec\xda)\x95&Iƀk\xb7j[\x9f\x11\xe7yFBs\xe3\xf1\xfa\xae\xdd\x05\xe5P\xcbS\xf4\x8a\x14Zٛ>q\x13uSL\x99r\xde7uF\xa8\xf6\x822\x98\xe8=-!\xd9{\x05\xce\xce\xda\xfd\x149ͺ\xa4\xbf\xaa\xaf\x9a\xd5\xccp\x9e\xd10\xc5\xd03\xa5\xb3V-\x87\xda>\xc4$wd\xabA`\xb1\xec\x90\xc5\x02\x1e\x1c\x0e+\xc3? \x01\xb6B\u007f\x90\xe1\x8cA\x107\xb9\xe8\x933ц\xee\xfa\x06\x94\xa2\v\xb8\tL\xb1\xd9\xe5 \xc6,\x9b\x9a\xb8\x02\r.\xacF\xa6EC\x87\xab/\xa0\xb4-\xd0 \xb0\xb9]ces>H\xa65 \x11c\xe7*\xcc<\f\xcc\x03ߚ\\\xf3z\xcc\x1b\xffA\xfb\xa1P\xaa5\xfa\x14O\xed\xa5\x8e\x19\x90\x99d0's\xc6i\xe6\xeea\x84]8\xc2~\x16T\x19ҤJ\x81D\x9f\x8bs;y܄\x11\xec\xf7\x0e\x91Z\x96<\xa1\x8d.\x97X\xfd\x8e\xcd\xc9\x02\xefz\x04\x1a\x13K\xcaɿ~\xf5\xef\u007f$\xb3\xb5т\xd18\xd6BӬ\xda\xc0\f\xf8\"\xb0\xb6\xbf\x13O\xed:d\x15%d,g\xa1n!c\f\xfc\xee~\xd6\x0e4\x9e\xa7\xb0:o\xd0\xe7$\x13\x8b0\x9c\xbe\xf2\xf7+\xab;\x93!\x8a|\x94k\xbf\x83\r\x88\x8c%\xebhF\xe0\x9b琥x\xb0\xae\xbc\x81'\xb6\xbe\xe2X\x88\xa2\xcclJ\xc6\u05fe\xb2d\x10\xc8R\xc1v5\xacN>\x18J\r~j\x9b\x1d\xc2\xed\x95)\xb7\x940\xa5\xc5\x15\x9es\xa1\xf1\xaag\x0e\xfa\x89\xbf\xa6Y6\xa3\xc9\xfd\x9d\xf8N,\xd4[~)e`\x9b}\xa4~\x8f\x8f\x8c\x1a-fY\xf2{ly[\xd7\x1a\x16a\xd2V\x94\xba(\xb5\xbf\xe4\xddt\xef\xfa\xcd\f\xae\aY)h\xde3\\\xcf\x0e\x1e\u0379E\xf7l\x18;p\xc5w,w\xc9Ģ\x9a\xb7\xf2\xcc \xf4F\xd0\xef\xbe\xfa\xd7\u007f\xb3,\x8b\bI\xfe\xed+\xbc2\xaaά\x10C\xdd\xc0(\xb29Ͳ\xd0`V\x93\xc1\x18\xa2\x9fv0\x89'\xe7\x11:\x9e\x1d<\x89\xc9}w\xf7w\xb4\xb7\x99V\x90\xcd\xcfl\xed\x11\xefA\f\x02z\x82J܉\x93\xb2X\xe4\xe6#\x18\xb4+\x91\x959\xbc\x86\x15K\xc2\"\xfc-T\xb7\xa0\xf8HPƔ&\"\xac\n\xc4,\x13\xc9=I\x1d\xa0\xc6\u074c\xcd>\xd6!\x98\x89\xb8\x85\xb2su\xf5\xfd\x13\x12ڌ(\xa7EQ\xd5r\x90\xf4\xa1\xb5X\xe4%\xc1\x17Phl\xa0:>\xab\xc3N7Ta\xf7\xef6\xb0Z\x03\xf2\x04S\x84J?;\xdc\xcd뭆T\xbe\x83^T\xae\x81\xdb\x13\xab\xa7\x99\x9dC\xce\x1c\x1e^\x1f\x90\xf4\x10w\xa7\xa7\x85c^\xe5\n\xe4T;\x9b&2\u007f\x06\xa9\xb6\x00\xa9\x982\n\xcc{<\x13\xaf2\xca\xf2\xf8\x1b\xfe1\r\t\x06\xb4\x80\x8d\xc9K\x984\xe84\xf0\xc5`D\x0f\xaa{\x14v\xb7Ų4l\xe9\x1b\xcf\xf5oD\xea\x00!\xab\xb6m\x98\x8d)\x1bL\x0e\xbb\n=\fR8\x86\xb2\xfd\xf75\x8e\x9a\\߮3\xfc8\xe3\x01\xb20\x1d\xb3\xff\x18\xec\x1b'\u007f\x04\xee\x8d|\xdb-chݹ\xa6\xc3\xc6\x11T\xc3\xf4r>\x92\xa9͍\x8d\x00o(\xc8M\x8f\x9c\xbc<\xf9\xa0<ܢ[\x8a\x82.\xd0\x1a\x19\x88\xf5Mp\xc3\n\xcd\x1a3\x19!V=c\x10.\xa4Um\xf3(\xa0\xf6B}-\x87\xbd\xf9\x84\x95\xc7\" >\xd05\xa1R\x94<\xb5\xb1\x87:(\xf5f\x03\x1dׂ\xc7L\xd9E\xbf]\xa5\xa9\xaa\xa6-\xa6\t0N^L_|\xf5\xb9\t~\\Ɇ\xe0\x8f,\xfc\xdc\xe0[\x1f\x14\v\xbee\xfb@L\xbcq.ֺ\xc3zT\xd9I\x1b\x03B \x0f\x92iG\xcd\x0fL\x019\r\xf5\x9a\xfb!d\xb3\x96\xe5\xf3\xb6K/\xaaw\xfb\xb0\xa2\xa7\xaa\x9c=\x81d\xb0\f=\x02=Ȅ\xba|\xf1*\x1ef\x87Xi\"\xfdYL\xa7\x8fS;\x9b\x13[\xf5\xea\xf9\a=$n\xcb.\x1f\x8b\x88\xceq\xadm\xbb|,(z\xfd\x8bz\xffb\xceI-\xc2w\xef_\x04\xdc\xddj\xc1_`IWQ\xf2O\xb1\x9ceTf\x98Zvk1If\xa5&\xc0WL\n\x1eu\xfb\x82\x90\x15\x95\f\xab\x8dK\xc0Z\x90\t(\xf2\xe5\xe9\xfb\x8bw\x98\xa1\x1dS\xb8˖\xe9t\xfbS*[^|(F\x1b\x8b\xdc<\x045IG\xc0\xb5\x87\xc0\xe3\xd3P&j\x00\x1e\xbf4\"U\x89\x90\xbc\xd4%Ͱ`[\x92\x95\x8a\xad>\xa4,\x8a\xb5\x1c+]\xfb\x17d8\xba\x92\x81\xafY\x10\xbf\xd9(\x8fX3\xf2\xad\n\x84\xc1\t\x1b\xa8\f\xd6\x15n;\xd3j\x02\xe9\xd87\xa8j&\t;\x87\xba\xab\x9ej\xaf\x10\xba\x9eo\xa1\xa9K\x1b\xd9\x069]|\x04\xd7z(M\aQe0=\x86Q\xa2\xcb\xfb\xec7\xf5\xb6Zl\xdft=\u05ec\xd71\xa7\x8f\x98PI\xf1\xb8\xf6\\\xa1\x98\xe3,\xc8{\xc8@\n/\x96\x1e(\xd3\xd5}Sƙ\x0e\xee,\x81\x86\x93\xad\xa7\u070f\x00\x82\xb6\xbe\xf7\xbe\xf4|\xf0\xf0\xb6\x1d\"\xb3\xbddup\x16\xfb\xbe\xbf\xe7eƓ\xacL\xe1UV*\r\xf2\x1d(Q\xca\xce\xe8\xc7Ft\xb9\xf3\xadƥ\xd2\a\x17pJ\xec#\x13\x95\x88\xa2\x93=\xc8\xfa\xe5J\x9fq\x93J}\xc1\t\xbc\x9f\\ݤ\xb1\x95\xbd\x94\x16\x12vT\xd6\xe6e\x96m\\j\xec\xec\x9b`\x9e3\xdaɎ\xbb]\xfb\xec\a?EcH\xaa\x82\xf6FY\xe3\x05\x9b\x80\xaf2\x96\xa0Þ\xfb?\xd8\xff2\xb3v\x1f\xe9X\xa1\xddK\x9b\x84\x8ayY\x18\x9d=\xc3\xe4\n^\u007f\xc1VP\xb0\x1f\xde^\xfeN\xa7\xe0ރ\xd4\vi]t\xe8'\x12Hd\xf5\xf3\x1b\b\xf3\x94\xd3\a_\xdbd\xd3\xc4XM\x83\xee\xb9\x19M\xee\xcb\xe2\xd3B\x1fv\xa0\xbe\x85\fu\x83\x03\xa8\xfb\xae\xf9\xacE[\x0e\x9a\xae^L\xdb\u007f1\xb65\xcb4f!w\xaaf\x0f6\x13\xd2`\xcd^8Kي\xa5%\xcdZ\x14\xd8\xc0Y\x8dZ\xbc\x90ʲ\xae\x04),\x89\xeb\xdeoḺ0\x18|V\xf7{\x81\xd1\xf1c\xd4o\x97\n\xdb͂\xdb\xeeōW,\x16]\x1c\u05f5\x03W\x1e\x8f\x8e\xb5\x1b\xfbag\x9a\xed\x9d+H\xe8\x9fÕ_\\\xbfޥ\xde\xecuٷ\xa6z\xb1g:\xee\xccT\x1b\xbe\xaf\v\x83S\xc4ܝ/uF(\xb9\x87\xf5\x99M~\xe5\xae\f\xbd\x03b\xbb\x06;\xb5\xe1\x1ev\xab*\xe6e\vo\x17b\xfa8\xf0\xefa\xaf鉶\x8e{XWawċ\xf9\xc1\a@kT\xb86\xee\xfbe\xff\xde(g/}\xc3c\xad\xf7\xf4+4K0\xc4gIŬ\xe1D\xb9V͂\xab%+\x0e%\xc7PL\xd9\x16s\x8f\xfd\xaay\xaf\x05o\xe9\uf29f\x91k\xa1\xcd\xff\\>2u\xe0B\x8e\xd9\xcb\xd7\x02Ե\xd0\xf8\xf4`\xe4ة\xf5F\x8d}\x1cI\x9a[\x1e\x89\x97\x94\xf0\x1b\xd52\xaf\x0e\xdf\u007f\xafP\xcc\x14\xb9\xe2\x86Q9\x1cT\x97\x15\x95\x03\u07fcc\x88\\m\xbf&j\xbf݂o\xd1j\xbe\xd1\xc4\\\xf3S\xfbQޚ\x86\x9d\x82\xf5hۿ`\x82v\x91\xd1\x04R\xd7g\xc2l\xbc\x96TÂ\xedo?\x90\x83\\`\xa2A\xb2ܷ\xaa\x1e\xa1Þ\x8a\xf71T\xe4ݬfR\xa1\xfd)Th'CP|\xee\xc0\x86\xef$F\xb3\x9b\x83\x1c\xed ƶe\x91\xfd\xb4\x13\xe6\xb40\x94\xff?\x86=#\x11\xfd/)(\x93jJ.\xdc\r\x95\x1d\xdfm\xbe\xe1t\x9d&p\x03\x97)bvaE3\xb0\x85\x8a)'\xb0\xb7\xfc\x8a\x98oI\xcb3\xf2\xb0\x14\n%C\x1dDzv\x0f\xebgg\xad\x13\xb2\x03\xa2y\xf8\x8a?;\xab\xe2e\xadCY\xc9)\fa<ÿ=\x9bn\t\xd8\x1d\xb0\x0f\x88ݽT\xb2珕\xd6\xfdƦ6m\xef|_\xfa\xd8K\x1b[\x15\x18\x9b\xdfl\x11GS9n\x99\x15]\x9f\xa4r\x01\xba\xcb\x04q\x1a3\xa62L\xc9\x05_o\xc1ŋq\x9d*\xb73\xe2*:+Z티\xa5%fH4@\xb9\xc4%\xd5m\b\x9b\a\xb7wmϦ\b\xd9\xd2w\x0fY\x1co7\x1e\xb7\xa9\xa8V\xe3ۯ?w\xa9\xceL/#\xf5g\u007f\x83\xab\x03ju\x89^/a]\xe1\xf3\xbf\x04\xe3ub\xe0\xdbw\xd5\xf9\x9an\x98\x02\x9d\rS\x1f \xcb\bU\xdb\xcbw\x8d\x86\x121\x01#\xb4\xccNzzp\xa5\xb5\xcf\xf0\fv\x19\xa8U[\x9d\xbc\xd9\x1f\xaa\xbf\x11\xb5_õ\xca8\xfe\xf6\xcf\x12\xe4\x1a\v\x0e\xd4*Oe\xd0u\x9fq\xcb)\xb0\x05\xa8\xe7]\x8e\x01\x1a~\xb3\xa5\xf9\xd7\x1c\x83\\p+\x83;\xc1n\xcc\x11\xe1\x80jZ;\x86?\x1bCfǣ\x9dP\xb9\xa8\xde\ue987\xbd\xa2\xa6\x9f\xe5\xf3ԶO\xb8\xf5sP\xefx\x12\v(\xde\x06\xda\x03\xd2\xc8\xc0\xc3VP\xdfD\xa6\x03\x96\xd0\xd3\xd9B\x87\xac\xa1\xdej`\x1f\x8b(\xd2&:\xb8\x80\xa7\xb0\x8a\xc2\xec\xa2\xdeh:l\x1b=\x91u\xf4\x84\xf6\xd1SXHq6\xd2\x01\x90\x95\x05\xd5\xd7J\xea\x99b\xd9;D\xd1'\nt8n\xb5\xcf^\xeaa1\xf5\n~\x1c\x9a\xe9A\xbb)\xccr\xea\x85\xc3'\xb2\x9e\x9e\xc8~z\n\v\xeaim\xa8\x83V\xd4A\xca\xd9\xfb\xe7h\x1f9\n\a\xb9\x82k\x91\u008d\x90]\xf9\xdc\xed\xfc\xac\xcd\xe7;\"X\r#Hd)ޫ\xc5G;\x16\x85\xba\xbc\xd3\xe3\xe3\x16\xd5\x1dlr߿y\u007fh=\xef\xaa\a\xf7/\x84b/=k\x9fu\xacüo\xef\xd6sZ\xa8\xa5\xd0\xe4\xd4\x17\xb1J2Q\xa6\xce\b\x91\x1d\xf9]\xc3Wy\x8b\x975\xfb-\xd4>\xdbZ+K\x96\x8dx\xceC\x95&\xe5\xa0w\x91\"\x16\xe1@@x\xd0PHbŵZ\xf5\xfe0a\nL\xe8\xd9\x1f!\xdb\xc2\xc4\xe5\xe6;\x1b\xcd\xc0j\\\xf8\xdb\xfc;\x0e\xac\xad\xa0 tE$5^\"\x02\x14\a\x99\xf4\x01D\x91\x83\x92\xa5G8q\vY\xdb!\xc5!\xc8\xea@T#\xb0ȴW\xabQ\b4Ì\x9f >\xf7\xf0c\x95,!-3\xe8n\xfe\xbf\xd5.\xc2?\xea펒\xb3\u007f\x96ЬM[e\x8a\xb8\xa7\xbb\xcedͫ\xaa\x10w#\xb3Ɉ\xb4\xbf \x9f\xf5_rHw\x90w\\Io\x82\xb4\xb2[(\x8d\xf5B\xb8n\x14?\xf7\xe1\xdfĵ\xc9t\x8fw\x96\xbb\xf1k\xd8\xe5\x96\xeaرn\xb5m⾺\x95\x99\xbecgT\a\x9b\xdc\xc3\"\x13Z\xe8R:2OJ\x89}\xb9\xebV\xa2\xd4c\xae\x8bM\xeefZ.?\x87\t~\xc7rP\x9a\xe6\xc5\x01\ny\xb5\xfd\x86\xd9\x00!SUU\x1cm\x86\xea\xeb\xe6\xd7]\x8e+Z\xb7\\O\xa7\r\xd8\x16\f\xea\xfd\x064\xa4\x04V\xc0\x89+\xb6\x01\x954\xe8:\x88wh\x18\xcb\x15\xe6\xe4y8x\xefv.$\xc1\xee\xf6\xd5Է)\u0097pK\xa9\x86IgE\x9f^\x12\xb2\xf3\xa0\xe3u\xf9C\x92\x11k\x108E7\xc1\xfbZf{\xb3̾\xed+\x00\xb8\x92;\x0f \x81,\x80\x1b\x14wj\x02\xce\\\xb2-\x97\rb\xdd\t\xae\x12)\xeel\x97꒺\x0fXIYyw\xbb<\x83H\xc9\xf8Hg\xb5\x93}\x85\xec\\\xe5\x85w@U\xd7\xfd\xa1\x16\"\xben>\xeb\xacb\x8b\x03\\zBm\xbf\xfa%\x10\xe0\x9aIا!\b\xfcr\xc0\x19'\xa4XRu\x88]ޘg\xaa\xd6؍CYq\xcaw;\xe6\x04\xbc̷\x81O\xc85]\x89\xbc\xaaX\xeae\x1f\xd9\xfc~\xe3\xf1\x8d\x84F#\xa5k\x88N\x9ev \xe7\x94ͭi\x91\x98Yo_\xa2\xf9\xc0\x89\x89\x0fTr\xc6\x17\x87\x16\xff\xbd{\xacC5q\x10:\x94\x93\x8eET\xeaJ\x90r\xe2'\xb9\xe3\xceM\xa5\xb0\fPO:\xcf\xd0֏H\xc8i\x03\xc9\xeeK\xee\x97Z\xad\xb7\xe5&]°\xc5\xed=\xe3\xe9K\u007f1\xaf\xc8JI3\xf7\xcfDp\xcb\x16\xd4K\xf2Ï_\xf8\x05\xbd\a\xa9\xaa\x1f\xff_\x00\x00\x00\xff\xff\xc8\aW} \xfd\x01\x00"), + []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xdc\x18\x93Z\x98\x92\x9a\x01fAW֬#D{zSI\xa4\xbd\x16ĺv\xacV\x95\x1f{h\xe0:\x14\x8fS\x04V\xcc`\x0e*\x88}%Є\xb5r\xc7\xfeV\xb1\\\x8d\x82n\x90\xf7\xae\x86`+\x14`P`f\x95\x1eR2\x85\x9e\xfeIQ\x96#t\x8c\xa8;\xfc\xb7\x88M\x80\x04\x12\xf3\xe7-϶\xde\v \xd9tp Wh\x9c\xe6 Ou?\x86$\xcc\xf1>,2\xa5;\xdag\xe6L\x1d\u008b\xe9\x93\xf6Iз\xed3\xa3y\a\x9a%\xbc\x8f\x9a\xce\xf6\xf9\xf7$lmJN\x10ڻ\xc1\xd4\xd7\x15Z\x17U\x91\xb7\u007f\xb7\x06,J\xbb\xbf\x02n\xeb\xb7s\x10\x99\x10\x9d\xf5\xffƌ9^\xe2\xef\x0eg\xbe\xaa\xc4Ore\x0e\"q\xa5Y\xfeo\xc8\x14g,\x1e\x83\xadHfȯ\xddYW\xc0\xd7\rC\xf2+XsaQ\x1fp\xe6E\xe7\xe55\x88\x91b\xef\xe8)\x98Ͷ\uffd0gc\xda\fT\"]\x0e'{\x9f\xb8\x0e\x12\xfa\x86y\x06.\xb8\xf8\x95k,|\\\xfc\xc9Q\xb3}\xe3\x02\x8aw\x1f~\xc4|\x8a<\x90&y\x03D\xde\x1dl\xb6\xbbtp\xf4S\xd1\b\xaeO\x134\xf9\x8c\xc7\x150x½\xf7X\x98\x04b\x0e\xb3\xceߍ\x86OC\xe2\xb8ԋw\x8fq\xef\xc0\x84\\\xca\xec\xecTQ\xf0\xcf\x13F\xfc\xfd\xd8\xd3# \xed)D\xb8\x9e\x92\xf4\xc2\x11\xc2E\xde\xe9\xc4\x03\x97\x17\xabu\xd1\xb7]o\x87@\x9c\xd5ۡ\x05\x1aCwJj\xe6\xae\a\x80Le\xed8\xbb\xbd7Rs\x84\xe7\xb3B\nP1\xf7I/2\x9f\xc1\x8f\xf6\xbdK#e\xf0(vǻ.I\x9c\xedNH\xcf\x12}Y\xb4\xed\n\v\x97\x14\xd4;\\T\xf2I\xaag\xb9p1\xa5\x99\xcd\xd67\x8b\x9f\xac8\xbe\xa6\xd2\xe8\x8bW\xba\b\xd4\xf6\xf7\fJ!\x99\xcdG\x05\xc6SR0\xa7\x86|\x1b\xebȟ\xb3\xbb\x98Z\u007fbr\xa89\xde\xfa\xfe\xd3\xd4ަ\xbb\xf8\xac\x8e\xff\xf0\xbcE\xbbE]7\xb6.\\\x0foL\xad\xb6\xa5\xc9\xd6\x15n\x9a\x9dH~jo\xca7\xe1\x1d\xb4?\xc5}eY\tqE\x82\xcd*a}+\xaa\xae\"B\x94\xd4\x03\xb4RJ ;l\x8bM)\xa2ϕ\xce\xfb\xfd`M\xe9\xban\bS\xf5\"\x11\f=/}\xd7g\xb7.ۯ\x81\xbb\xecO\xbdӿ\xbcU,\xa1\xbc=SԞn\xa0\x9b\xa2\xd7Pl\xba\x14ke0\x8c\v\x9d\x95\xdf\x14\xf9f\n\xd1\xe3\xe5\xe7\x90lE\xcbvo\x97\xfd\u007f\xac\n\xc5h\x97Y\x88\xa0\xf2\xbcm\xf2\x04\xce\xf2ʜ\xefx^1ѓ\xc0\x0e\xcdZ҂\xd2 \xb9\x88ա\x88\xe6\xf5\xfc\x1e\x8d\xe1c\xe9\xf3\xd1G\x9f\xd5iw'\xadf}r\xa5\xba_\x89\x1e\xd1\xe0Ǧf\xd3[\xf2\xd2k\xd1\xd3\xc5\xe3c*Ї\xf5\xe5Q\xa0\xf3u\xe7\x14Ou\xa6\xc6|Be9\xb1\xab\xe8\xc5\t\xe8\x94\xda\xf1I\x15\xe3\xd9ƛ\xc4:q\xbf\x02<\r\xf2\x88\xeap\x12q\xe6+\xc1G\xd7\u007fC\xbdu\x12\x8f\xe4\xaao\xa4\x9e;\tx\xb4\xd6;Uŝ&y\xa4\u009b^\xbb\x9d\x04\xed\xea\xba\xf3\x15\xdb\xd7\xeb\xcbz\r\x17y\\\xd5\xccV]_\xe4B'\xd4U\x8f\xa9\xa6\xceR\xec\xc4\xcaiS\x19\x1dY\xf7\xd8zi\xbf\x1e:\x024\xa5J:R\x05\x1d\x818Y\x1bM\xad}\x8e\xc0\x9e1\xbb\x93R2\xf1g\xe3u\xff\xc6ʒ\xcb͐\xf3\xa9\xf21)\x1b\x83\xd2iw͞pt\x9d\xe3^X\x11[\xd2\u007f\x90\x18\tA\xea\xa4\x13\x97V-\xe1\x9d\xdc\x0f\xe0\xbaf\xe8\xa8\xcb\xdd\xffb\x85\xb6\xf5̅\xe8~\x95\xe1\xc0vA\x85\x0f\x9cL<\x10\xa6\x81c\x1f3E\x99\xa2t\xcfߝ\x8b8>\x1e\f鈴\xa6\xfd\xe7\x98\xeb\xcc\xed\xf6D\xff\xb9\xa8\x84\xe5e\xf4\x10\x97Z\xed\xb8K\x8amq\xdf\xd0\xf3\x0f徇X\xed\x1d\xa4\x8f\x0f\xcd\xf9Z\x1e\x84\x02,v*\x9eQ\b`f\x88~\xe6\xbf\t\xcc\xd4\xc2}\xe6C\x9c\xac\xe5!|;x\xe5\xce`,@\x95\xf5\xd7j\x05\x81q\xdf\x15\x9aH\x02`ԺL{\xb8\xde\x19w\xef\xfe\xacP\xefA\xed\\\x154\xb8<3\r\xc7^S\x98J\xb4\x1d\x1eA\x01\xfa/Q\x0f<\xffVc\xc0;\xe9mp\x14\xec\xc1\x1e\x1d\x1cRZm\xb4C\xfa\x99\x02\x99\x91\xa1Q\xa8R5\xb3\xe3\xf20ijR\xbbu\xcf\x1b\xfb\x1c\x1f\xfd\xcc\xfa\x1dg\x89\x80N\x8f\x81&@\xa6vߦe\xecg\xbbm\xcf\x15\v\xcdEC\xc9n`Z7\xed9\xbah\x8f\xe8\x9e=\"*:..J&SJ\x97\xecY\xa2\xa33\xc6G爐N\x8b\x91f@\x1et\xbf\xa6\xf4\xb5&\x15\x99\x92K\x14)E\xa5\xf9\xba\xe6t\xbfjB\x9fjB\xf1cn\xa7\t\xfd\xa8\xc7\xf5\xa1&\xd0\xf0L\xd1ә\xe2\xa7sDP獡f\xa3\xa8Yə\xfc\xfb\xe4\x1cy]M\xfd\xa0r\xbcW\xda\xce9\xfc\xf7\x87\xe3#\x15\xacN\x10\xa4D\x0e\xb2\x1e\x1aA\xca\xf9\xf2\xc1\x8f?\r\xa9x\xb1)\xac\u007f\xffy\x0e\x9f\x87f\xe04\"\xe4\x92\xd6\xf1Y\x04\x0f\x9a\xefp1\x92\x95f\xab,|\xb7\xe3,\\(\xa2\xaa<\x04!\xfa\xfbs`\xf9h\x99\xad\x12\x11\xf5c{\xb8\xf2l۩\xe7G\x10\xb2\x91|\xbd}\xf8\u05fbN5\xf4䠚%H\v\x02\x1eX\xa8\xc1T\xcb\x0f\xdcN80H\\C\xe5\xa8Eap\x15(\x93\xd6 \x01\xb4\x81\x02\x8dԩL\x02E\xb9\xb3\xdd\xe92Ka\x83D\xdcuݡ0\xba@\xe3dX6\xbe\xb4\xd4D\xab\xb6\x87\xf1;\x9a\x94o\xe5\xa5\b-\vN\xb5\x180\xad\xe8\xe0e[\xda\x06\u007f&p\a0P#\xa1@o~\xc3ĭ\xe1\x0e\r\x81\tX'Z\xed\xd1\x10\x05\x12\xfd\xa8\xe4\xffְ-I\xaccArX\xad\xe5\xa6\xf0\xe2S\"\x83\xbd\xc8J\xbc\x00\xa1R\xc8\xc5\x01\f\xd2(P\xaa\x16\xf5\x99\xee-\tkX@\x04\x93j\x8b\xd5j\xdc\x1a\x9d3LTi\xa1\xa5r\xfc#\xc9$\xaa>\xf9m\xb9ɥ#\xbe\xff^\xa2uī5\\\xb3\xcd 9,\vZ=\xe9\x1an\x15\\\x8b\x1c\xb3ka\xf1\xd5\x19@\x94\xb6+\"l\x1c\v\xda\xe6\xae\xdf\xd8S\xad\xf5!\x98\xa6\x11~\x855~W`\xd2Y2\xd4One\xc2\v\x835_\xad\x02z\xdaϗ\xe1U\v\x95\xea\xa1\xe6\xfd\xfaIm\x13k\x13\x8e`B\xa5b\xd6G_F\xa8ɟ0/h\xb9Πx_5#\x14\x89Fi\xed\x82\x04c\x19ԛ\xae\xb4\x1a\x1c)\x15\x1en\x87D\xaf\xbdL+\xadqD\xcdi\x8azĶ\xa2\xcc\xdc\x03\xd9q\xb4\xf7\xfa\x1bZ'\x93\xa1\x96\xbdI|\x1c\xec\x18\xf8\x8d\x96(\xecvhhq\xf2\a\xd6w\x83p\x81\u05ccŔU\x9ex\"\x93\xb9\xf1\x14 ݙeP\xe8\x14\xf6~$\xd8\x1c\x02\xd2Ǽi\xf8\xb3\xd1:C1D5\xfc\x9ede\x8ai\xed\xe6\fҥ7ۛ\xa3N\xec\x10\n\xa9H\xca\xc8\xfd\"TU\xfdud\x9el\xaf\x84A E!\x95\x87\tһ%\x9b\x11\x81\xa3\"\x1d\xe6#xNJ\xa4/\xe4x\x8aM\x86W\xe0L9$\xeb\x01\x860F\x1c&h\x16\x9c\xe6%$\xab\xfbT\xea<\x93\t\x12\xb1j\xa5\xcdTcҌ\xcc\xef\x1f\x90`;\xad\x9fb\x88\xf4\x1fԮ1N\x90pl\x02\x1b܉\xbd\xd4\xc6\xf6=\x1c\xfc\x8eI\xe9pl\x1d\t\a\xa9\xdcn\xd1\x10,v\xa8k\xff{\x8aX\xd3*\x82\x8a\x99f\xfcѼ\x1a\xa6\x13\xf3\x98\x1acSaU<\n\x15\x18q\xd2\xd8e\x01R\xa5r/\xd3Rd \x95uB%~~\xa2\xc6ox~0'\x10G\xf8{\x05\x1cfA\\\xeaX6\xad\x90\xdc\xd1\\\x9ba\xe1\b\xe5\x18\xcc8\x196\x824\xa0\x1e3GM1\x14\xc5U\xa8\xa4lR\x1b\xbds\xd1p\xca;\x85\x99\xd8`\x06\x163L\x9c6\xe3\xe4\x89\x11\x02_b\xf5\xe7\be\a4ic3HPg\x95hS\x9c\x86\xe7\x9dLv\xde\u007f#)cX\x90j\xb4\xac1DQd\x87\xa9IC\x8cdT\x83\xcd)\x8d\xa6D\xa8\x8f>\xdc1EҔH\x1dܔ\x19mܥz-6oD\uf829~H\xd8o\x8f\xba\xbf\xbc\xb0\x13\xb9%\xc5w\xb7[\xc0\xbcp\x87\v\x90.\xd4\xc6@%\a\xab\xc1\xe3oƸ\xd3V\xcbm\xbf\xf7\x8b\xaf\x96\x17\xe1Z\x8d\xc6߄il\xac\xee*[\xb5\x88a\x9f\xda=/@nk\x86\xa5\x17\xb0\x95\x99C\xf6\xa5\xe6\x10m9:\xb3\x9c{I\x02\xc5\xda^*\xb9p\xc9\xee\xa6\x0ei#z\xf4h\xd5\a\xe0\xfd\xf2\x10\xc30\x0f\"@B\xedT\xf0.\x884\x98\xfbݕ{^\x1fM\r{\x80\x1f>\u007f\xc4t\x8ed\x10/\xa9G\x93\xfa\xd0\xf3t\xda(\xf0\x04\xa3@\xb6&\xc5nZ\x1d\xe3\xf9=\xb4\v\x10\xf0\x84\a\xefY\r\x06\x97C\x85X+j\x90\x06yC\x8f\xd5\xc8\x13\x1e\x18T\xb5C\x17\x05o\x89\xa8\xf8\xf2\x84\x87ئ=\xa2\x12~\xd5\x1e\x85\xa7.U\xf0,b\x96RSj\xa2Vk\a\x9c\x8e\x9b,,SJ\xa1\x04\x8a\x9f8\xed\x9aa\x9dm\xe9'<\xbc\xb3\x9e}\xb4jv\xb2X@\x01R\xd8`\x91WX؏}\x10\x99L\xeb\xc1x\x9d,\x80x\xab.\xe0\xb3v\xf4\xe7滴\x84\xa2J\xe1\xa3F\xfbY;\xaeyU\x12\xfbI\x9cH`ߙ\x97\xa5\xf2f\x81\xe8\xb2h\xfc\x06\a6\xa1$\xa25ۤ\x85[E\xf1\x99\xa7\xcf\x126\xed0 \xe7\xd1\xcaK\xcb;\xbaJ\xab\x15\x9b\xe90\xda\x02\xa0m\xbc*Vi\xd3\xe1\xd4\xc5B\x88\x83(V\xe8ݓ\xb5\xf2_\x8e\xf6§\x8a\xc1\"\x13\t\xa6\x90\x96\xbc\xdf\xce\x1b\xef\xc2\xe1\xa3L G\xf3\x88P\x90݈\x17\xaa\x05\x9aܗ\x13\xa40\u07b5\b\xa52\vi\x1cb+Z\xf5\x91-\x03\x9b\xa3\x9a\x8f\xec\xb2O7\x8f\x9b%\x9bw\xf6\x87\xa2\xa8\xdf>\xd2]fY\x16\xf2\xeb\xd8\a\xf1Hz\xf7#\x17\xbc\xd9\xfb\a\x99W\x16\xef?㬡\x90Ʈ\xe1\x03\x1fhg\xd8\xee\x1fv\t[CE\x81$L\xa4\x05\x92\x93\xbd\xc8\xc8} \xe5\xad\x003\xefL\xe8\xed\x91\a\x15\xa7b\x9ew\xdaz\x9b\xbf\x95\x98\xf1\xe9\xd6\xf9\x13\x1e\xce/\x8e\xb4\xd7\xf9\xad:\x8f\x83I:\xffHi\xd5^\x8bV\xd9\x01\xce\xf9\xdb9;fK\x96\xc8\t\xce\xdb\x02\xa9\x8en\xca\xc7\xcfKB\x01\x8a\xb5\x83\xd7B\x9d\xebCZr\xe1\xe7f\x11-Ӆ\xb6n\x11Z_\xb5u~\x03\xb0\xe3n\x0f\xec\x10\xc6D\u007fծ!\x88\xadC\x03\xd6i\x13\x0eDI\xed\xf66ȉ\xf3v\x9e\xf7\xc4\xeaz7\xd2\x03\xa6 \xf3\xbc\xd1\x10^\xa7\x9f\xfb\x93R\xfa\u007f\x1ef\xc2\xce\x12\xc3.\x8cN\xd0\xdayQ\x8a\xb4\x1c3\x1b\xb6\xf5f\xad\xf0\xc1\xdb6J5\xc7l%\x87\xb2\xcc\x15'Ҟ\x10\xd8\xdc|o\xed;\x93\x1a\xa2\xdf1\xa2|\n\x8e\xc0\x89Ny.\xfa\x87\xf3\xd1\xe8^\xfb\xdea\x01V\xc0|\xc0d\x1eKV*\xcb\xfc\xe6J$\xffj\x8eG.\xd5-\x0f\x04\xef_\xcdY\x81\xa0\xca\xf1\xd4P\xe6:\xf4o\x18RW\xc4Ư\x10\x8e\x9a5\x9f\xd5\x18\xecp\xf6\xf8$#\x9eS@δҮ\xbdYS\x8d\xf4\xce\xc2V\x1a\xeb\x1a\x84\x17@\x95\x96\x8f\x93_7\xc6T7Ɯ\x1cb~\xf1\xbd[ۊ;\xfd\\%F,\t\xac\x03\xf1wb\x8f \xb7 \x1d\xa0Jt\xa9xË\xd4\x05\r\xb3\x00\xa2g\xa27&\x916\xb3\xd5Y\x95y\x1f\xa8\xc5\xe7\xa5-\xcdF\xab\xf3\x87\xe6\xad\xca\x0f\xe4\xa0-\xbb 0\x9bo\x16\x834\xc4d\x99\r\xe7\x8f\xcd@]\x92[\x16\x1b\x83G\xe4\x91\xc5g\x8fő\a\xf8\xc6nl\xceX\xb4\xd7\x16\x9b\x1f\xf6:Ya\x91\xb9`\xad\f\xafY\x90'f\x80E\x13,.\xdb+:ǫ\x95\xb95O\xad\x89̮\xe1|\xadY\x90C\xf9\\1YZQ\xb8F\xe7f\xd5\x19W\xf3;\x89?\x94\x91\xf5\xf2\xb9\xdf/\xe9\xe7O\xe7WEeUE\xc5\x02\xf38G\xe5M-͖\x8a\xa2\xea\xd2̨:\xebib\xe0\xa8|\xa8\xe3\\\xa7\xa9\xa9\xccfA\x8dg8M\x81\x1d\xca}\x8a\xc8k\x9a\x00\xd9\xcexZ\xec\x06\xccJ\xd3L\x83\xe1[\xf5\xa1\xcc\xdb\xda\xec\xffC\x02\u007ft\xd2\xdat\\\xe0\x98\xa8\xe4K\xaf\v\xf1>x}Cn\xf5x\x8c\xe7\x9d\xed\x13\xdc\xea\x11\x90\xb7[\xc8\xcb\xcc\xc9\"k]\xafw;<\xc0\xb3\xcc2R\xe7\xbfi\xbez\xb990\xb4/\xdfj\x01\x1e\x03\xd9\r\x10\x84\x85g\xcc2\xfa{D\x85\xc4?\"\x91\xe8\x15\x92\xcd\x19\xdf\x06\xaf.\xc8W/P\\\xf8\\?\xbe\x97\xca\xf6,'H\xe1^\xfd\t\xe1״\xb3\xeb}t\xae\xfb\xbdDs\x00\xbdGS{5\xa3b\xd6\xdcW\xaa\x96\xa6-\xb3F\x95T:\xc9?e\xd2U-㫡^\xd0\xf0Ay3\xdbǕa\x91\x0ei\x82\xa3)\xd5I\xb1\xd0\x18\b\xa5k\b#\xfdc|\xe9%\x17x^#Tz\x89`)ʭx\x8d\x80\xe9\xb5B\xa6\xa5AӒ\xa3˨\v8\xaf\x11:-\t\x9e\x16y\x80\xf1\x17l^\xebb\xcd+\x04Q'\x87Q\x8bH\x17{qfq0\x151\xbf\x99\x8b2G\x1eW\x04\xc8\xd1\v2\xc3\x01U\x04ģ\x8b1\xb3!U\xcc:\xe8\a]?|\xcd%\xfa\x18\u007f\xd1YR\xec\x11|\xdc1\xcf\xfc\xf5\x95\xc8k+\x91\x87@1\xd8G^OY~-%\x92\xce'\x06[\x93CG^?Y\x14n\x9d\x18pMB\x9c\xban2\x1drMo\xa7\xf5\xaf\x99\x9c\xe0NDH\xd8l\x93\x1f>\x11\xd0&E3{\xb8\xb2D4g\x85\xb2\x17\x13u\xc7\xef\xbd:\x10\xdeĢV탛1\xee\xe8\xfa\x16|\x02\xbfJ\x95zސ\x10\xb6\xfc\v~h\x8f\xef\xc9Ԏϸ\x185\xdef\xef\xd0\xc8b!H\x8drPć\xdbv\r7\"\xd9\xd5\rG \xf2\xc8;aa\xabM.\x1c\x9cקq\x97\xa1'՜\xaf\x01~\xd1\xf5Ah땛\x11\xb8V\xe6Ev\xa0\xe8\aλ\x80~LtF\xc5\xcf*Q\u061d\x0e\x0f\xa2ED\xc0w\xdd\x1e\x03Ǿ\xe19\xb4$\xd3eZ\x8f0\xc1n\xa1\x0e\xf0\xf5\x81\xbd)~\x04*i\x1e˪|\xa5\x10\t\xf7\xde\xd2\x1a\x019\xf6\x06\xde\"\x92\x8d\x1f\x0e[\xa7\x8dx\xc4O\xda?\x0f\x18C\xb3n\x8f\xce\v\x91\x95\xae\n\xa9\"\xd5ݯQI\xf6s\xeb\x03l2Ȫ\xd5֜\xa5\x13\xb6cJlf\x9d;\x97EL\xee\xfe\xfe\x93\x9f\x90\x939\xae?\x96\xfe\xa0~U\bc\x91(\x1d&\xea;m\xc6\xed\xdcN?C\xa6+:\xfcܟ\x87A\xceX㜀\x93f\xe3_\xe2\v\xe2\x1bH\x17#\xf2\x0f\xc3=[\x81l\x8b\x89SG\xfbz;\nKX\xab\x13ɺ\x88\xb7\x848Q\xec\xf5\x9e\x8a\x9b\xb2(\x13*\xa3\xb4\xf8\xe5Y\xa1\xf9\x16\x16\xaa\xbdU\x9eS3\x8fG\xfe\xd7Q\xc7\xc0\xe0!\xf5A\xfa\xaf\xd7|\xc8\uea4a@\xd6?[\x1d\xf6\xb6\xa4\xad\x9f\xd3<&\xdd\xcc\xfa\x1f_\xfbþ\xebj\xf8\x05\xcbU\xfd\xa8\xe6Y\x04e\xfd\xb3\xc91\xef\x94\xfa\xb7\xa9\x13Q\xb8\xd2T\xe65)\r\xbf\x9bG@\xd0?+w\xdaK\xa5ͫ\xcd3\xbcl\xdeqn\xa2\xfd\xd9W\xa3\a\xf8W\xbfy:\xfa\b\xa8\xb7\xae\xfeU\xe7\x15\xc1?\x8d\x9d\x83\xeb\x80\xdf\x19\x9c\x99\xe9WjS'\xf9V\x84\xe6\x8e\xe1}»1ԇ\xb36W\xf0\x19\x9f\ajo\x14M\xe2\xf8Lͧfb\xca{\x04C\xaf4ONq_\xf7\xe2\xbc\xd8\x01m\xd1Us\xbd潄\x1b\x91e-\x88>\av\x88\xad\xff,\xb7~\x03'\xa19\xfd\xcbQ\x8bQ\xc55\xa9\xb4\xc6\x14\xd6\xe0\x92:\xaa\xb4h\xf6\x98\xb6\x84\xa4\xb2\xe1\xed\x9ar\xd3<\x17\t\u007f\xfcy֬J\x91$X\xb8*\xb1\xab\xfd\x9a\xfd\xf99\xff\b\x8f\xd5\xf3\xcfD+\xefh\xdb+\xf8\xef\xff9\x83\xca\x00?\x84\x17\xe9\xa9\xf2\xff\x02\x00\x00\xff\xff\x94\x01\x97\xcd\xfb_\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VO\x8f\xeb4\x10\xbf\xe7S\x8c\x1e\x87w!\xe9{\xe2\x00\xca\r\x15\x0e+`\xb5\xda>\xed\x05qp\x9di;\xacc\x9b\xf1\xb8K\xf9\xf4\xc8v\xb2m\x93\x94]\x90\xf0-\xf6\xfc\xf9\xcdo\xfed\xaa\xba\xae+\xe5\xe9\t9\x90\xb3-(O\xf8\xa7\xa0M_\xa1y\xfe.4\xe4V\xc7\xcf\xd53ٮ\x85u\f\xe2\xfaG\f.\xb2\xc6\x1fpG\x96\x84\x9c\xadz\x14\xd5)Qm\x05\xa0\xacu\xa2\xd2uH\x9f\x00\xdaYag\fr\xbdG\xdb<\xc7-n#\x99\x0e9\x1b\x1f]\x1f?5\xdf6\x9f*\x00͘տP\x8fAT\xef[\xb0ј\n\xc0\xaa\x1e[\b\xc8II\x94\xc4\xc0\xf8G\xc4 \xa19\xa2Av\r\xb9*x\xd4\xc9\xf1\x9e]\xf4-\x9c\x1f\x8a\xfe\x00\xaa\x04\xb4ɦ6\xd9\xd4c1\x95_\r\x05\xf9\xe9\x96\xc4\xcf4Hy\x13Y\x99e@Y \x1c\x1c\xcb\xfd\xd9i\r!py!\xbb\x8fF\xf1\xa2r\x05\x10\xb4\xf3\xd8B\xd6\xf5JcW\x01\fLe[\xf5\xc0\xc5\xf1s1\xa7\x0fث\xe2\x04\xc0y\xb4\xdf?\xdc=}\xb3\xb9\xba\x06\xe80h&/\x99\xef\x85Ȁ\x02(\x18P\x808PZc\b\xa0#3Z\x81\x82\x12\xc8\xee\x1c\xf79G\xaf\xa6\x01\xd4\xd6E\x019 d\x05\xd9*\x03Ge\"~\r\xcavЫ\x130&/\x10텽,\x12\x1a\xf8\xc51f2[8\x88\xf8ЮV{\x92\xb1\xeb\xb4\xeb\xfbhIN\xab\xdc@\xb4\x8d\xe28\xac:<\xa2Y\x05\xda\u05ca\xf5\x81\x04\xb5Dƕ\xf2Tg\xe86w^\xd3w_\xf1Ч\xe1\xe3\x15V9\xa5\xca\n\xc2d\xf7\x17\x0f\xb9!\xfe!\x03\xa9\x1dJ}\x14\xd5\x12ř\xe8t\x95\xd8y\xfcq\xf3\x05F\xd79\x19S\xf63\xefg\xc5pNA\"\x8c\xec\x0e\xb9$qǮ\xcf6\xd1vޑ-ե\r\xa1\x9d\xd2\x1f\xe2\xb6'\tc\xed\xa6\\5\xb0Σ\b\xb6\b\xd1wJ\xb0k\xe0\xce\xc2Z\xf5h\xd6*\xe0\xff\x9e\x80\xc4t\xa8\x13\xb1\xefK\xc1\xe5\x14\x9d\n\x17\xd6.\x1e\xc61w#_\vݽ\xf1\xa8S\x06\x13\x89I\x9bv\xa4s{\xc0\xce1\xa8%\x95\xe6]H\xb2ƿ\xc42L\x92\x82f2_R\u007f\xbe\x8dfy\x9c䗃\n8\xbd\x9c`zH2S\xff\x86v\xa8O\xda`1Q\xa6\t\xbe\r%\x1d\xb4\xb1\x9f\xfb\xac\xe1\x1e_\x16n\x1fإɚ\xe7\xfa\xf5\xb9Q\x1bP\xfe7{\xb2\xb3p\xa7\x91\x15\xa9\xfc\x0f\xbb\x1c\xd5\x17\x03z0\x04\x1c\xadM};\x9b\x90\x19\xc8t\x92\xcfdH\xb0_@\xb3\x88\xe7\xce\xee\\\xde\x04Tr\xac\xa4\xf4\x13\x0e\xc9\x1e\xfc\x14\\\v\x06o纜\xf9\xf0z\x17\xa1\xe5\xe4?\xe9\u007fSN\xe3\x86\x18\x17}\xd7\x19\xd5\xe2C\xf2\xb8\xc4\xf8r\u007f\r(\xa31jk\xb0\x05\xe18\xd7.\xba\x8aY\x9d\xa6U3\x96\xday\x9fz\xa3\x80f\n\xa9O^\x0ehou\x03\xbc\xa8锿\xf2\f\xdb\xd3-\xd5\xf5\xebr8o\xa9R\xba-\xa4\xd9]\v-p\xf6.R\x16\xb3WJzq\xf3\x98\x11\xb2\xb9\x94\x1dg\xc6Uk\x8c\x8b\xc8<\x86\x9b\x10\x16\x93=\xbb\xcc滋\xf0\x828V\xfb1\xe0\xf3\xe8M\x9b\x9a\x17\xec\xee\xa7+\xee\x87\x0fW\xbbj\xfe\xd4\xcevT6t\xf8\xf5\xb7\xaaX\xc5\xeei\\0\xd3\xe5\xdf\x01\x00\x00\xff\xff\xfb\xb1p\x12\x1b\f\x00\x00"), []byte("\x1f\x8b\b\x00\x00\x00\x00\x00\x00\xff\xb4VM\x8f\xdc6\x0f\xbe\xfbW\x10y\x0f\xb9\xbc\xf6$衅o\xc1\xb6\x87\xa0i\xb0Ȧ{)z\xd0H\xf4\f\xbb\xb2\xa4\x8aԴ\xdb__\xe8\xc3;\x1f\xeb\xd9&@\xeb\x9bi\xf2\xe1Ç\x1f3]\xdf\xf7\x9d\nt\x8f\x91ɻ\x11T \xfcS\xd0\xe57\x1e\x1e\xbe\xe3\x81\xfc\xe6\xf0\xb6{ gF\xb8I,~\xfe\x84\xecS\xd4\xf8=N\xe4HȻnFQF\x89\x1a;\x00\xe5\x9c\x17\x95͜_\x01\xb4w\x12\xbd\xb5\x18\xfb\x1d\xba\xe1!mq\x9b\xc8\x1a\x8c\x05|I}x3|;\xbc\xe9\x00t\xc4\x12\xfe\x99fdQs\x18\xc1%k;\x00\xa7f\x1c\xe1\xe0m\x9a\x91\x9d\n\xbc\xf7b\xbd\xaeɆ\x03Z\x8c~ \xdfq@\x9ds\xef\xa2Oa\x84\xe3\x87\n\xd1x՚\xee\v\xda]C\xfb\xd0Њ\x83%\x96\x1f_p\xfa@,\xc51\xd8\x14\x95\xbdʬ\xf8\xf0\xdeG\xf9x\xcc\xdeÁm\xfdBn\x97\xac\x8a\xd7\xe2;\x00\xd6>\xe0\b%<(\x8d\xa6\x03h\xc2\x15\xb8~\x91\xe6mE\xd4{\x9cU\xcd\x03\xe0\x03\xbaw\xb7\xef\ufff9;3\x03\x18d\x1d)H\x91\u007f\xbdD \x06\x05\v\x13\xf8c\x8f\x11\xe1\xbe\xe8\t,>\"7\xd2O\xa0\x00\v\u007f\x1e\x9e\x8c!\xfa\x80Qh)\xbe>'\x83wb\xbd\xe0\xf5:S\xaf^`\xf2\xc4!\x83\xecq)\x1fM\xab\x16\xfc\x04\xb2'\x86\x88!\"\xa3\x93c#\x8f\x8f\x9f@9\xf0\xdb\xdfP\xcb\x00w\x183L\xeeM\xb2&\x0f\xea\x01\xa3@D\xedw\x8e\xfez\xc2f\x10_\x92Z%\xd8z~|\xc8\tF\xa7,\x1c\x94M\xf8\u007fP\xce\xc0\xac\x1e!b\xce\x02ɝ\xe0\x15\x17\x1e\xe0'\x1f\x11\xc8M~\x84\xbdH\xe0q\xb3ّ,\v\xa7\xfd<'G\xf2\xb8)\xbbC\xdb$>\xf2\xc6\xe0\x01\xed\x86i\u05eb\xa8\xf7$\xa8%Eܨ@}\xa1\xee\xea\x1e\xcc\xe6\u007f\xb1\xad(\xbf>\xe3*\x8fy\x8aX\"\xb9\xddɇ\xb2\b/t \xef@\x1d\x84\x1aZ\xab8\n\x9dMY\x9dO?\xdc}\x86%uiƥ\xfaE\xf7c \x1f[\x90\x05#7a\xacM\x9c\xa2\x9f\v&:\x13<9)/\xda\x12\xbaK\xf99mg\x92\xdc\xf7\xdf\x13\xb2\xe4^\rpS\xae\x10l\x11R0J\xd0\f\xf0\xde\xc1\x8d\x9a\xd1\xde(\xc6\xff\xbc\x01Yi\uecf0_ւ\xd3\x03z\xe9\\U;]\xb0vޮ\xf4k}\x93\xef\x02\xea\xb3\x05\xca(4Q\xdb\xec\xc9\xc7\v]ղ\xe7\xebxÙ\xfb\xfa\x82C\xbd\xfe\x13\xed.\xad\x00ʘ\xf2ۡ\xec\xed\xd5\xd8\x17\x04[\xa9\xfb\xa6dʃ:\xf9\x98\x19\x1d\xc8`\xec\x97:\x1b\x93\x14[\xc1\x84\xd6\xf0\xf0\f\xf2\x8a\xe6\xad\xc8\x02\xf9\x9c\xe6\x19\x8f\xdb料d\xa1\x97\xb0z\xa1\xb0\x1d\xccr>\xd5\x0e\xaf1X\xa98O8E\xbc\xd8\xd5\xfe)\xc1\x17͎(I\xfc\xf5\xd3S\u009a\xe7\xb6M\x90N1\xa2\x93\x86\xb9ri\xff\x9d\t\n{\xc5\xf8\x0f\x9a\xafg\xb8͑K\x1b,M\xa8\x1f\xb5\xc5\n\b~Z\x99\xb6\xaf\xa2\x9c\x1fti~έ\x87w\aEVm-\xae|\xfb٩\xab_\xaf6\u007f\xb5\x9fό\x9cϩ\x19Ab\xaa\xd8mʚ\xe5\xd8}\xa55\x06A\xf3\xf1\xf2\xffЫWg\u007fiʫ\xf6\xae.+\x8f\xf0˯]EEs\xbf\xfc\x03\xc9ƿ\x03\x00\x00\xff\xffz{3\x1eK\n\x00\x00"), diff --git a/hack/restore-crd-patch-v1.json b/hack/restore-crd-patch-v1.json deleted file mode 100644 index b8b729d0d..000000000 --- a/hack/restore-crd-patch-v1.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - { "op": "replace", "path": "/spec/versions/0/schema/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] } -] diff --git a/hack/restore-crd-patch-v1beta1.json b/hack/restore-crd-patch-v1beta1.json deleted file mode 100644 index cbaa90a02..000000000 --- a/hack/restore-crd-patch-v1beta1.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - { "op": "replace", "path": "/spec/validation/openAPIV3Schema/properties/spec/properties/hooks/properties/resources/items/properties/postHooks/items/properties/init/properties/initContainers/items/properties/ports/items/required", "value": [ "containerPort", "protocol"] } -] diff --git a/hack/update-generated-crd-code.sh b/hack/update-generated-crd-code.sh index 70b9a942b..fb7be399e 100755 --- a/hack/update-generated-crd-code.sh +++ b/hack/update-generated-crd-code.sh @@ -47,7 +47,7 @@ ${GOPATH}/src/k8s.io/code-generator/generate-groups.sh \ # Generate apiextensions.k8s.io/v1 # Generate manifests e.g. CRD, RBAC etc. controller-gen \ - crd:crdVersions=v1\ + crd:crdVersions=v1 \ paths=./pkg/apis/velero/v1/... \ rbac:roleName=velero-perms \ paths=./pkg/controller/... \ @@ -55,13 +55,4 @@ controller-gen \ object \ paths=./pkg/apis/velero/v1/... -# this is a super hacky workaround for https://github.com/kubernetes/kubernetes/issues/91395 -# which a result of fixing the validation on CRD objects. The validation ensures the fields that are list map keys, are either marked -# as required or have default values to ensure merging of list map items work as expected. -# With "containerPort" and "protocol" being considered as x-kubernetes-list-map-keys in the container ports, and "protocol" was not -# a required field, the CRD would fail validation with errors similar to the one reported in https://github.com/kubernetes/kubernetes/issues/91395. -# once controller-gen (above) is able to generate CRDs with `protocol` as a required field, this hack can be removed. -kubectl patch -f config/crd/v1/bases/velero.io_restores.yaml -p "$(cat hack/restore-crd-patch-v1.json)" --type=json --local=true -o yaml > /tmp/velero.io_restores-yaml.patched -mv /tmp/velero.io_restores-yaml.patched config/crd/v1/bases/velero.io_restores.yaml - go generate ./config/crd/v1/crds diff --git a/internal/hook/item_hook_handler.go b/internal/hook/item_hook_handler.go index 96cb18bc6..83c756bd5 100644 --- a/internal/hook/item_hook_handler.go +++ b/internal/hook/item_hook_handler.go @@ -131,10 +131,10 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( pod.Spec.InitContainers = pod.Spec.InitContainers[1:] } - hooksFromAnnotations := getInitRestoreHookFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log) - if hooksFromAnnotations != nil { + initContainerFromAnnotations := getInitContainerFromAnnotation(kube.NamespaceAndName(pod), metadata.GetAnnotations(), log) + if initContainerFromAnnotations != nil { log.Infof("Handling InitRestoreHooks from pod annotations") - initContainers = append(initContainers, hooksFromAnnotations.InitContainers...) + initContainers = append(initContainers, *initContainerFromAnnotations) } else { log.Infof("Handling InitRestoreHooks from RestoreSpec") // pod did not have the annotations appropriate for restore hooks @@ -155,7 +155,22 @@ func (i *InitContainerRestoreHookHandler) HandleRestoreHooks( } for _, hook := range rh.RestoreHooks { if hook.Init != nil { - initContainers = append(initContainers, hook.Init.InitContainers...) + containers := make([]corev1api.Container, 0) + for _, raw := range hook.Init.InitContainers { + container := corev1api.Container{} + err := ValidateContainer(raw.Raw) + if err != nil { + log.Errorf("invalid Restore Init hook: %s", err.Error()) + return nil, err + } + err = json.Unmarshal(raw.Raw, &container) + if err != nil { + log.Errorf("fail to Unmarshal hook Init into container: %s", err.Error()) + return nil, errors.WithStack(err) + } + containers = append(containers, container) + } + initContainers = append(initContainers, containers...) } } } @@ -350,7 +365,7 @@ type ResourceRestoreHook struct { RestoreHooks []velerov1api.RestoreResourceHook } -func getInitRestoreHookFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *velerov1api.InitRestoreHook { +func getInitContainerFromAnnotation(podName string, annotations map[string]string, log logrus.FieldLogger) *corev1api.Container { containerImage := annotations[podRestoreHookInitContainerImageAnnotationKey] containerName := annotations[podRestoreHookInitContainerNameAnnotationKey] command := annotations[podRestoreHookInitContainerCommandAnnotationKey] @@ -373,15 +388,13 @@ func getInitRestoreHookFromAnnotation(podName string, annotations map[string]str log.Infof("Pod %s has no %s annotation, using generated name %s for initContainer", podName, podRestoreHookInitContainerNameAnnotationKey, containerName) } - return &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - { - Image: containerImage, - Name: containerName, - Command: parseStringToCommand(command), - }, - }, + initContainer := corev1api.Container{ + Image: containerImage, + Name: containerName, + Command: parseStringToCommand(command), } + + return &initContainer } // GetRestoreHooksFromSpec returns a list of ResourceRestoreHooks from the restore Spec. @@ -406,7 +419,7 @@ func GetRestoreHooksFromSpec(hooksSpec *velerov1api.RestoreHooks) ([]ResourceRes if rs.LabelSelector != nil { ls, err := metav1.LabelSelectorAsSelector(rs.LabelSelector) if err != nil { - return nil, errors.WithStack(err) + return []ResourceRestoreHook{}, errors.WithStack(err) } rh.Selector.LabelSelector = ls } @@ -526,3 +539,17 @@ func GroupRestoreExecHooks( return byContainer, nil } + +// ValidateContainer validate whether a map contains mandatory k8s Container fields. +// mandatory fields include name, image and commands. +func ValidateContainer(raw []byte) error { + container := corev1api.Container{} + err := json.Unmarshal(raw, &container) + if err != nil { + return err + } + if len(container.Command) <= 0 || len(container.Name) <= 0 || len(container.Image) <= 0 { + return fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field") + } + return nil +} diff --git a/internal/hook/item_hook_handler_test.go b/internal/hook/item_hook_handler_test.go index 267c413a1..9f8267808 100644 --- a/internal/hook/item_hook_handler_test.go +++ b/internal/hook/item_hook_handler_test.go @@ -1191,11 +1191,11 @@ func TestGroupRestoreExecHooks(t *testing.T) { } } -func TestGetInitRestoreHookFromAnnotations(t *testing.T) { +func TestGetInitContainerFromAnnotations(t *testing.T) { testCases := []struct { name string inputAnnotations map[string]string - expected velerov1api.InitRestoreHook + expected *corev1api.Container expectNil bool }{ { @@ -1223,12 +1223,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { podRestoreHookInitContainerNameAnnotationKey: "", podRestoreHookInitContainerCommandAnnotationKey: "/usr/bin/data-populator /user-data full", }, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), }, { name: "should generate container name when container name is missing", @@ -1237,22 +1233,14 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerCommandAnnotationKey: "/usr/bin/data-populator /user-data full", }, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), }, { name: "should return expected init container when all annotations are specified", expectNil: false, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"/usr/bin/data-populator /user-data full"}).Result(), inputAnnotations: map[string]string{ podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerNameAnnotationKey: "restore-init", @@ -1262,12 +1250,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { { name: "should return expected init container when all annotations are specified with command as a JSON array", expectNil: false, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"a", "b", "c"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"a", "b", "c"}).Result(), inputAnnotations: map[string]string{ podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerNameAnnotationKey: "restore-init", @@ -1277,12 +1261,8 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { { name: "should return expected init container when all annotations are specified with command as malformed a JSON array", expectNil: false, - expected: velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"[foobarbaz"}).Result(), - }, - }, + expected: builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"[foobarbaz"}).Result(), inputAnnotations: map[string]string{ podRestoreHookInitContainerImageAnnotationKey: "busy-box", podRestoreHookInitContainerNameAnnotationKey: "restore-init", @@ -1293,15 +1273,14 @@ func TestGetInitRestoreHookFromAnnotations(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - actual := getInitRestoreHookFromAnnotation("test/pod1", tc.inputAnnotations, velerotest.NewLogger()) + actualInitContainer := getInitContainerFromAnnotation("test/pod1", tc.inputAnnotations, velerotest.NewLogger()) if tc.expectNil { - assert.Nil(t, actual) + assert.Nil(t, actualInitContainer) return } - assert.NotEmpty(t, actual.InitContainers[0].Name) - assert.Equal(t, len(tc.expected.InitContainers), len(actual.InitContainers)) - assert.Equal(t, tc.expected.InitContainers[0].Image, actual.InitContainers[0].Image) - assert.Equal(t, tc.expected.InitContainers[0].Command, actual.InitContainers[0].Command) + assert.NotEmpty(t, actualInitContainer.Name) + assert.Equal(t, tc.expected.Image, actualInitContainer.Image) + assert.Equal(t, tc.expected.Command, actualInitContainer.Command) }) } } @@ -1347,11 +1326,11 @@ func TestGetRestoreHooksFromSpec(t *testing.T) { PostHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"foobarbaz"}).Result(), - *builder.ForContainer("restore-init2", "busy-box"). - Command([]string{"foobarbaz"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), + builder.ForContainer("restore-init2", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), }, }, }, @@ -1369,11 +1348,11 @@ func TestGetRestoreHooksFromSpec(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"foobarbaz"}).Result(), - *builder.ForContainer("restore-init2", "busy-box"). - Command([]string{"foobarbaz"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), + builder.ForContainer("restore-init2", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), }, }, }, @@ -1539,9 +1518,9 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("should-not exist", "does-not-matter"). - Command([]string{""}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("should-not exist", "does-not-matter"). + Command([]string{""}).ResultRawExtension(), }, }, }, @@ -1556,6 +1535,9 @@ func TestHandleRestoreHooks(t *testing.T) { Name: "app1", Namespace: "default", }, + Spec: corev1api.PodSpec{ + InitContainers: []corev1api.Container{}, + }, }, expectedError: nil, expectedPod: &corev1api.Pod{ @@ -1582,11 +1564,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1643,11 +1625,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1680,11 +1662,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1733,11 +1715,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1795,11 +1777,11 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), - *builder.ForContainer("restore-init-container-2", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), + builder.ForContainer("restore-init-container-2", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1868,9 +1850,9 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1911,9 +1893,9 @@ func TestHandleRestoreHooks(t *testing.T) { RestoreHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init-container-1", "nginx"). - Command([]string{"a", "b", "c"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + Command([]string{"a", "b", "c"}).ResultRawExtension(), }, }, }, @@ -1922,6 +1904,37 @@ func TestHandleRestoreHooks(t *testing.T) { }, namespaceMapping: map[string]string{"default": "new"}, }, + { + name: "Invalid InitContainer in Restore hook should return nil as pod, and error.", + podInput: corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "app1", + Namespace: "new", + }, + Spec: corev1api.PodSpec{}, + }, + expectedError: fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field"), + expectedPod: nil, + restoreHooks: []ResourceRestoreHook{ + { + Name: "hook1", + Selector: ResourceHookSelector{ + Namespaces: collections.NewIncludesExcludes().Includes("new"), + Resources: collections.NewIncludesExcludes().Includes(kuberesource.Pods.Resource), + }, + RestoreHooks: []velerov1api.RestoreResourceHook{ + { + Init: &velerov1api.InitRestoreHook{ + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init-container-1", "nginx"). + ResultRawExtension(), + }, + }, + }, + }, + }, + }, + }, } for _, tc := range testCases { @@ -1931,10 +1944,32 @@ func TestHandleRestoreHooks(t *testing.T) { assert.NoError(t, err) actual, err := handler.HandleRestoreHooks(velerotest.NewLogger(), kuberesource.Pods, &unstructured.Unstructured{Object: podMap}, tc.restoreHooks, tc.namespaceMapping) assert.Equal(t, tc.expectedError, err) - actualPod := new(corev1api.Pod) - err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod) - assert.NoError(t, err) - assert.Equal(t, tc.expectedPod, actualPod) + if actual != nil { + actualPod := new(corev1api.Pod) + err = runtime.DefaultUnstructuredConverter.FromUnstructured(actual.UnstructuredContent(), actualPod) + assert.NoError(t, err) + assert.Equal(t, tc.expectedPod, actualPod) + } }) } } + +func TestValidateContainer(t *testing.T) { + valid := `{"name": "test", "image": "busybox", "command": ["pwd"]}` + noName := `{"image": "busybox", "command": ["pwd"]}` + noImage := `{"name": "test", "command": ["pwd"]}` + noCommand := `{"name": "test", "image": "busybox"}` + expectedError := fmt.Errorf("invalid InitContainer in restore hook, it doesn't have Command, Name or Image field") + + // valid string should return nil as result. + assert.Equal(t, nil, ValidateContainer([]byte(valid))) + + // noName string should return expected error as result. + assert.Equal(t, expectedError, ValidateContainer([]byte(noName))) + + // noImage string should return expected error as result. + assert.Equal(t, expectedError, ValidateContainer([]byte(noImage))) + + // noCommand string should return expected error as result. + assert.Equal(t, expectedError, ValidateContainer([]byte(noCommand))) +} diff --git a/pkg/apis/velero/v1/restore.go b/pkg/apis/velero/v1/restore.go index 251f1a045..f36e7348c 100644 --- a/pkg/apis/velero/v1/restore.go +++ b/pkg/apis/velero/v1/restore.go @@ -17,8 +17,8 @@ limitations under the License. package v1 import ( - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ) // RestoreSpec defines the specification for a Velero restore. @@ -208,9 +208,10 @@ type ExecRestoreHook struct { // InitRestoreHook is a hook that adds an init container to a PodSpec to run commands before the // workload pod is able to start. type InitRestoreHook struct { + // +kubebuilder:pruning:PreserveUnknownFields // InitContainers is list of init containers to be added to a pod during its restore. // +optional - InitContainers []v1.Container `json:"initContainers"` + InitContainers []runtime.RawExtension `json:"initContainers"` // Timeout defines the maximum amount of time Velero should wait for the initContainers to complete. // +optional diff --git a/pkg/apis/velero/v1/zz_generated.deepcopy.go b/pkg/apis/velero/v1/zz_generated.deepcopy.go index a35e5e1ef..3d806d2bb 100644 --- a/pkg/apis/velero/v1/zz_generated.deepcopy.go +++ b/pkg/apis/velero/v1/zz_generated.deepcopy.go @@ -764,7 +764,7 @@ func (in *InitRestoreHook) DeepCopyInto(out *InitRestoreHook) { *out = *in if in.InitContainers != nil { in, out := &in.InitContainers, &out.InitContainers - *out = make([]corev1.Container, len(*in)) + *out = make([]runtime.RawExtension, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/pkg/builder/container_builder.go b/pkg/builder/container_builder.go index 80c99955d..da6215637 100644 --- a/pkg/builder/container_builder.go +++ b/pkg/builder/container_builder.go @@ -17,9 +17,11 @@ limitations under the License. package builder import ( + "encoding/json" "strings" corev1api "k8s.io/api/core/v1" + apimachineryRuntime "k8s.io/apimachinery/pkg/runtime" ) // ContainerBuilder builds Container objects @@ -89,6 +91,17 @@ func (b *ContainerBuilder) Result() *corev1api.Container { return b.object } +// ResultRawExtension returns the Container as runtime.RawExtension. +func (b *ContainerBuilder) ResultRawExtension() apimachineryRuntime.RawExtension { + result, err := json.Marshal(b.object) + if err != nil { + return apimachineryRuntime.RawExtension{} + } + return apimachineryRuntime.RawExtension{ + Raw: result, + } +} + // Args sets the container's Args. func (b *ContainerBuilder) Args(args ...string) *ContainerBuilder { b.object.Args = append(b.object.Args, args...) diff --git a/pkg/controller/restore_controller.go b/pkg/controller/restore_controller.go index 3799259fa..559611fe0 100644 --- a/pkg/controller/restore_controller.go +++ b/pkg/controller/restore_controller.go @@ -38,6 +38,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" + hook "github.com/vmware-tanzu/velero/internal/hook" api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" @@ -328,6 +329,22 @@ func (c *restoreController) validateAndComplete(restore *api.Restore, pluginMana return backupInfo{} } + // validate Restore Init Hook's InitContainers + restoreHooks, err := hook.GetRestoreHooksFromSpec(&restore.Spec.Hooks) + if err != nil { + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error()) + } + for _, resource := range restoreHooks { + for _, h := range resource.RestoreHooks { + for _, container := range h.Init.InitContainers { + err = hook.ValidateContainer(container.Raw) + if err != nil { + restore.Status.ValidationErrors = append(restore.Status.ValidationErrors, err.Error()) + } + } + } + } + // if ScheduleName is specified, fill in BackupName with the most recent successful backup from // the schedule if restore.Spec.ScheduleName != "" { diff --git a/pkg/restore/init_restorehook_pod_action_test.go b/pkg/restore/init_restorehook_pod_action_test.go index f4f8f4dfd..c69d3c23f 100644 --- a/pkg/restore/init_restorehook_pod_action_test.go +++ b/pkg/restore/init_restorehook_pod_action_test.go @@ -90,11 +90,11 @@ func TestInitContainerRestoreHookPodActionExecute(t *testing.T) { PostHooks: []velerov1api.RestoreResourceHook{ { Init: &velerov1api.InitRestoreHook{ - InitContainers: []corev1api.Container{ - *builder.ForContainer("restore-init1", "busy-box"). - Command([]string{"foobarbaz"}).Result(), - *builder.ForContainer("restore-init2", "busy-box"). - Command([]string{"foobarbaz"}).Result(), + InitContainers: []runtime.RawExtension{ + builder.ForContainer("restore-init1", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), + builder.ForContainer("restore-init2", "busy-box"). + Command([]string{"foobarbaz"}).ResultRawExtension(), }, }, }, From 088eb9b83cd00ba5e8cc3c5c59aa678973dd9f1f Mon Sep 17 00:00:00 2001 From: lyndon <98304688+Lyndon-Li@users.noreply.github.com> Date: Thu, 4 Aug 2022 15:20:02 +0800 Subject: [PATCH 34/38] repo credentials (#5167) Signed-off-by: Lyndon-Li --- changelogs/unreleased/5167-lyndon | 1 + internal/credentials/getter.go | 24 +++ internal/credentials/mocks/FileStore.go | 49 +++++ internal/credentials/mocks/SecretStore.go | 49 +++++ internal/credentials/secret_store.go | 56 ++++++ pkg/cmd/server/server.go | 3 +- .../pod_volume_backup_controller.go | 3 +- .../pod_volume_restore_controller.go | 3 +- pkg/repository/keys/keys.go | 75 ++++++++ pkg/repository/keys/keys_test.go | 30 +++ pkg/repository/provider/unified_repo.go | 79 +++++--- pkg/repository/provider/unified_repo_test.go | 179 ++++++++++++++++-- pkg/restic/repository_manager.go | 3 +- 13 files changed, 503 insertions(+), 51 deletions(-) create mode 100644 changelogs/unreleased/5167-lyndon create mode 100644 internal/credentials/getter.go create mode 100644 internal/credentials/mocks/FileStore.go create mode 100644 internal/credentials/mocks/SecretStore.go create mode 100644 internal/credentials/secret_store.go create mode 100644 pkg/repository/keys/keys.go create mode 100644 pkg/repository/keys/keys_test.go diff --git a/changelogs/unreleased/5167-lyndon b/changelogs/unreleased/5167-lyndon new file mode 100644 index 000000000..7bef82dcc --- /dev/null +++ b/changelogs/unreleased/5167-lyndon @@ -0,0 +1 @@ +Add changes for Kopia Integration: Unified Repository Provider - Repo Password \ No newline at end of file diff --git a/internal/credentials/getter.go b/internal/credentials/getter.go new file mode 100644 index 000000000..c890c0566 --- /dev/null +++ b/internal/credentials/getter.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 credentials + +// CredentialGetter is a collection of interfaces for interacting with credentials +// that are stored in different targets +type CredentialGetter struct { + FromFile FileStore + FromSecret SecretStore +} diff --git a/internal/credentials/mocks/FileStore.go b/internal/credentials/mocks/FileStore.go new file mode 100644 index 000000000..3fce9e843 --- /dev/null +++ b/internal/credentials/mocks/FileStore.go @@ -0,0 +1,49 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + v1 "k8s.io/api/core/v1" +) + +// FileStore is an autogenerated mock type for the FileStore type +type FileStore struct { + mock.Mock +} + +// Path provides a mock function with given fields: selector +func (_m *FileStore) Path(selector *v1.SecretKeySelector) (string, error) { + ret := _m.Called(selector) + + var r0 string + if rf, ok := ret.Get(0).(func(*v1.SecretKeySelector) string); ok { + r0 = rf(selector) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*v1.SecretKeySelector) error); ok { + r1 = rf(selector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewFileStore interface { + mock.TestingT + Cleanup(func()) +} + +// NewFileStore creates a new instance of FileStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewFileStore(t mockConstructorTestingTNewFileStore) *FileStore { + mock := &FileStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/credentials/mocks/SecretStore.go b/internal/credentials/mocks/SecretStore.go new file mode 100644 index 000000000..5494511c9 --- /dev/null +++ b/internal/credentials/mocks/SecretStore.go @@ -0,0 +1,49 @@ +// Code generated by mockery v2.14.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + v1 "k8s.io/api/core/v1" +) + +// SecretStore is an autogenerated mock type for the SecretStore type +type SecretStore struct { + mock.Mock +} + +// Get provides a mock function with given fields: selector +func (_m *SecretStore) Get(selector *v1.SecretKeySelector) (string, error) { + ret := _m.Called(selector) + + var r0 string + if rf, ok := ret.Get(0).(func(*v1.SecretKeySelector) string); ok { + r0 = rf(selector) + } else { + r0 = ret.Get(0).(string) + } + + var r1 error + if rf, ok := ret.Get(1).(func(*v1.SecretKeySelector) error); ok { + r1 = rf(selector) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +type mockConstructorTestingTNewSecretStore interface { + mock.TestingT + Cleanup(func()) +} + +// NewSecretStore creates a new instance of SecretStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +func NewSecretStore(t mockConstructorTestingTNewSecretStore) *SecretStore { + mock := &SecretStore{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/credentials/secret_store.go b/internal/credentials/secret_store.go new file mode 100644 index 000000000..f4d2111a5 --- /dev/null +++ b/internal/credentials/secret_store.go @@ -0,0 +1,56 @@ +/* +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 credentials + +import ( + "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" + kbclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/pkg/util/kube" +) + +// SecretStore defines operations for interacting with credentials +// that are stored in Secret. +type SecretStore interface { + // Get returns the secret key defined by the given selector + Get(selector *corev1api.SecretKeySelector) (string, error) +} + +type namespacedSecretStore struct { + client kbclient.Client + namespace string +} + +// NewNamespacedSecretStore returns a SecretStore which can interact with credentials +// for the given namespace. +func NewNamespacedSecretStore(client kbclient.Client, namespace string) (SecretStore, error) { + return &namespacedSecretStore{ + client: client, + namespace: namespace, + }, nil +} + +// Buffer returns the secret key defined by the given selector. +func (n *namespacedSecretStore) Get(selector *corev1api.SecretKeySelector) (string, error) { + creds, err := kube.GetSecretKey(n.client, n.namespace, selector) + if err != nil { + return "", errors.Wrap(err, "unable to get key for secret") + } + + return string(creds), nil +} diff --git a/pkg/cmd/server/server.go b/pkg/cmd/server/server.go index f257c96e7..642e1baf5 100644 --- a/pkg/cmd/server/server.go +++ b/pkg/cmd/server/server.go @@ -80,6 +80,7 @@ import ( "github.com/vmware-tanzu/velero/internal/storage" "github.com/vmware-tanzu/velero/internal/util/managercontroller" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" ) const ( @@ -519,7 +520,7 @@ func (s *server) initRestic() error { } // ensure the repo key secret is set up - if err := restic.EnsureCommonRepositoryKey(s.kubeClient.CoreV1(), s.namespace); err != nil { + if err := repokey.EnsureCommonRepositoryKey(s.kubeClient.CoreV1(), s.namespace); err != nil { return err } diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 8bce92ed7..d7fab3548 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/metrics" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/filesystem" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -324,7 +325,7 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log log.WithField("path", path).Debugf("Found path matching glob") // Temporary credentials. - details.credsFile, err = r.CredsFileStore.Path(restic.RepoKeySelector()) + details.credsFile, err = r.CredsFileStore.Path(repokey.RepoKeySelector()) if err != nil { return nil, errors.Wrap(err, "creating temporary Restic credentials file") } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3315dae3b..e52de6fdb 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -39,6 +39,7 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -241,7 +242,7 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve return errors.Wrap(err, "error identifying path of volume") } - credsFile, err := c.credentialsFileStore.Path(restic.RepoKeySelector()) + credsFile, err := c.credentialsFileStore.Path(repokey.RepoKeySelector()) if err != nil { return errors.Wrap(err, "error creating temp restic credentials file") } diff --git a/pkg/repository/keys/keys.go b/pkg/repository/keys/keys.go new file mode 100644 index 000000000..3da876e28 --- /dev/null +++ b/pkg/repository/keys/keys.go @@ -0,0 +1,75 @@ +/* +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 keys + +import ( + "context" + + "github.com/pkg/errors" + corev1api "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + + "github.com/vmware-tanzu/velero/pkg/builder" +) + +const ( + credentialsSecretName = "velero-restic-credentials" + credentialsKey = "repository-password" + + encryptionKey = "static-passw0rd" +) + +func EnsureCommonRepositoryKey(secretClient corev1client.SecretsGetter, namespace string) error { + _, err := secretClient.Secrets(namespace).Get(context.TODO(), credentialsSecretName, metav1.GetOptions{}) + if err != nil && !apierrors.IsNotFound(err) { + return errors.WithStack(err) + } + if err == nil { + return nil + } + + // if we got here, we got an IsNotFound error, so we need to create the key + + secret := &corev1api.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: credentialsSecretName, + }, + Type: corev1api.SecretTypeOpaque, + Data: map[string][]byte{ + credentialsKey: []byte(encryptionKey), + }, + } + + if _, err = secretClient.Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{}); err != nil { + return errors.Wrapf(err, "error creating %s secret", credentialsSecretName) + } + + return nil +} + +// RepoKeySelector returns the SecretKeySelector which can be used to fetch +// the restic repository key. +func RepoKeySelector() *corev1api.SecretKeySelector { + // For now, all restic repos share the same key so we don't need the repoName to fetch it. + // When we move to full-backup encryption, we'll likely have a separate key per restic repo + // (all within the Velero server's namespace) so RepoKeySelector will need to select the key + // for that repo. + return builder.ForSecretKeySelector(credentialsSecretName, credentialsKey).Result() +} diff --git a/pkg/repository/keys/keys_test.go b/pkg/repository/keys/keys_test.go new file mode 100644 index 000000000..3102b58c8 --- /dev/null +++ b/pkg/repository/keys/keys_test.go @@ -0,0 +1,30 @@ +/* +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 keys + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRepoKeySelector(t *testing.T) { + selector := RepoKeySelector() + + require.Equal(t, credentialsSecretName, selector.Name) + require.Equal(t, credentialsKey, selector.Key) +} diff --git a/pkg/repository/provider/unified_repo.go b/pkg/repository/provider/unified_repo.go index 49cef09ce..8ddff5799 100644 --- a/pkg/repository/provider/unified_repo.go +++ b/pkg/repository/provider/unified_repo.go @@ -28,15 +28,16 @@ import ( "github.com/vmware-tanzu/velero/internal/credentials" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" repoconfig "github.com/vmware-tanzu/velero/pkg/repository/config" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" "github.com/vmware-tanzu/velero/pkg/util/ownership" ) type unifiedRepoProvider struct { - credentialsFileStore credentials.FileStore - workPath string - repoService udmrepo.BackupRepoService - log logrus.FieldLogger + credentialGetter credentials.CredentialGetter + workPath string + repoService udmrepo.BackupRepoService + log logrus.FieldLogger } // this func is assigned to a package-level variable so it can be @@ -47,18 +48,30 @@ var getGCPCredentials = repoconfig.GetGCPCredentials var getS3BucketRegion = repoconfig.GetAWSBucketRegion var getAzureStorageDomain = repoconfig.GetAzureStorageDomain +type localFuncTable struct { + getRepoPassword func(credentials.SecretStore, RepoParam) (string, error) + getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) + getStorageCredentials func(*velerov1api.BackupStorageLocation, credentials.FileStore) (map[string]string, error) +} + +var funcTable = localFuncTable{ + getRepoPassword: getRepoPassword, + getStorageVariables: getStorageVariables, + getStorageCredentials: getStorageCredentials, +} + // NewUnifiedRepoProvider creates the service provider for Unified Repo // workPath is the path for Unified Repo to store some local information // workPath could be empty, if so, the default path will be used func NewUnifiedRepoProvider( - credentialFileStore credentials.FileStore, + credentialGetter credentials.CredentialGetter, workPath string, log logrus.FieldLogger, ) (Provider, error) { repo := unifiedRepoProvider{ - credentialsFileStore: credentialFileStore, - workPath: workPath, - log: log, + credentialGetter: credentialGetter, + workPath: workPath, + log: log, } repo.repoService = createRepoService(log) @@ -120,15 +133,38 @@ func (urp *unifiedRepoProvider) Forget(ctx context.Context, snapshotID string, p return nil } -func (urp *unifiedRepoProvider) getRepoPassword(param RepoParam) (string, error) { - ///TODO: get repo password +func getRepoPassword(secretStore credentials.SecretStore, param RepoParam) (string, error) { + if secretStore == nil { + return "", errors.New("invalid credentials interface") + } - return "", nil + buf, err := secretStore.Get(repokey.RepoKeySelector()) + if err != nil { + return "", errors.Wrap(err, "error to get password buffer") + } + + return strings.TrimSpace(string(buf)), nil } func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOptions, error) { + repoPassword, err := funcTable.getRepoPassword(urp.credentialGetter.FromSecret, param) + if err != nil { + return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo password") + } + + storeVar, err := funcTable.getStorageVariables(param.BackupLocation, param.SubDir) + if err != nil { + return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get storage variables") + } + + storeCred, err := funcTable.getStorageCredentials(param.BackupLocation, urp.credentialGetter.FromFile) + if err != nil { + return udmrepo.RepoOptions{}, errors.Wrap(err, "error to get repo credentials") + } + repoOption := udmrepo.RepoOptions{ StorageType: getStorageType(param.BackupLocation), + RepoPassword: repoPassword, ConfigFilePath: getRepoConfigFile(urp.workPath, string(param.BackupLocation.UID)), Ownership: udmrepo.OwnershipOptions{ Username: ownership.GetRepositoryOwner().Username, @@ -138,27 +174,10 @@ func (urp *unifiedRepoProvider) getRepoOption(param RepoParam) (udmrepo.RepoOpti GeneralOptions: make(map[string]string), } - repoPassword, err := urp.getRepoPassword(param) - if err != nil { - return repoOption, errors.Wrap(err, "error to get repo password") - } - - repoOption.RepoPassword = repoPassword - - storeVar, err := getStorageVariables(param.BackupLocation, param.SubDir) - if err != nil { - return repoOption, errors.Wrap(err, "error to get storage variables") - } - for k, v := range storeVar { repoOption.StorageOptions[k] = v } - storeCred, err := getStorageCredentials(param.BackupLocation, urp.credentialsFileStore) - if err != nil { - return repoOption, errors.Wrap(err, "error to get repo credential env") - } - for k, v := range storeCred { repoOption.StorageOptions[k] = v } @@ -187,6 +206,10 @@ func getStorageCredentials(backupLocation *velerov1api.BackupStorageLocation, cr result := make(map[string]string) var err error + if credentialsFileStore == nil { + return map[string]string{}, errors.New("invalid credentials interface") + } + backendType := repoconfig.GetBackendType(backupLocation.Spec.Provider) if !repoconfig.IsBackendTypeValid(backendType) { return map[string]string{}, errors.New("invalid storage provider") diff --git a/pkg/repository/provider/unified_repo_test.go b/pkg/repository/provider/unified_repo_test.go index ee78c7b5d..f2cccb8e5 100644 --- a/pkg/repository/provider/unified_repo_test.go +++ b/pkg/repository/provider/unified_repo_test.go @@ -22,25 +22,35 @@ import ( awscredentials "github.com/aws/aws-sdk-go/aws/credentials" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + corev1api "k8s.io/api/core/v1" - filecredentials "github.com/vmware-tanzu/velero/internal/credentials" + velerocredentials "github.com/vmware-tanzu/velero/internal/credentials" + credmock "github.com/vmware-tanzu/velero/internal/credentials/mocks" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/repository/udmrepo" ) func TestGetStorageCredentials(t *testing.T) { testCases := []struct { name string backupLocation velerov1api.BackupStorageLocation - credFileStore filecredentials.FileStore + credFileStore *credmock.FileStore + credStoreError error + credStorePath string getAzureCredentials func(map[string]string) (string, string, error) getS3Credentials func(map[string]string) (awscredentials.Value, error) getGCPCredentials func(map[string]string) string expected map[string]string expectedErr string }{ + { + name: "invalid credentials file store interface", + expected: map[string]string{}, + expectedErr: "invalid credentials interface", + }, { name: "invalid provider", backupLocation: velerov1api.BackupStorageLocation{ @@ -48,8 +58,9 @@ func TestGetStorageCredentials(t *testing.T) { Provider: "invalid-provider", }, }, - expected: map[string]string{}, - expectedErr: "invalid storage provider", + credFileStore: new(credmock.FileStore), + expected: map[string]string{}, + expectedErr: "invalid storage provider", }, { name: "credential section exists in BSL, file store fail", @@ -59,9 +70,10 @@ func TestGetStorageCredentials(t *testing.T) { Credential: &corev1api.SecretKeySelector{}, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("", errors.New("fake error")), - expected: map[string]string{}, - expectedErr: "error get credential file in bsl: fake error", + credFileStore: new(credmock.FileStore), + credStoreError: errors.New("fake error"), + expected: map[string]string{}, + expectedErr: "error get credential file in bsl: fake error", }, { name: "aws, Credential section not exists in BSL", @@ -78,7 +90,7 @@ func TestGetStorageCredentials(t *testing.T) { AccessKeyID: "from: " + config["credentialsFile"], }, nil }, - + credFileStore: new(credmock.FileStore), expected: map[string]string{ "accessKeyID": "from: credentials-from-config-map", "providerName": "", @@ -97,7 +109,8 @@ func TestGetStorageCredentials(t *testing.T) { Credential: &corev1api.SecretKeySelector{}, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + credFileStore: new(credmock.FileStore), + credStorePath: "credentials-from-credential-key", getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { return awscredentials.Value{ AccessKeyID: "from: " + config["credentialsFile"], @@ -121,12 +134,12 @@ func TestGetStorageCredentials(t *testing.T) { }, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("", nil), getS3Credentials: func(config map[string]string) (awscredentials.Value, error) { return awscredentials.Value{}, errors.New("fake error") }, - expected: map[string]string{}, - expectedErr: "error get s3 credentials: fake error", + credFileStore: new(credmock.FileStore), + expected: map[string]string{}, + expectedErr: "error get s3 credentials: fake error", }, { name: "azure, Credential section exists in BSL", @@ -139,7 +152,8 @@ func TestGetStorageCredentials(t *testing.T) { Credential: &corev1api.SecretKeySelector{}, }, }, - credFileStore: velerotest.NewFakeCredentialsFileStore("credentials-from-credential-key", nil), + credFileStore: new(credmock.FileStore), + credStorePath: "credentials-from-credential-key", getAzureCredentials: func(config map[string]string) (string, string, error) { return "storage account from: " + config["credentialsFile"], "", nil }, @@ -162,9 +176,9 @@ func TestGetStorageCredentials(t *testing.T) { getAzureCredentials: func(config map[string]string) (string, string, error) { return "", "", errors.New("fake error") }, - - expected: map[string]string{}, - expectedErr: "error get azure credentials: fake error", + credFileStore: new(credmock.FileStore), + expected: map[string]string{}, + expectedErr: "error get azure credentials: fake error", }, { name: "gcp, Credential section not exists in BSL", @@ -179,7 +193,7 @@ func TestGetStorageCredentials(t *testing.T) { getGCPCredentials: func(config map[string]string) string { return "credentials-from-config-map" }, - + credFileStore: new(credmock.FileStore), expected: map[string]string{ "credFile": "credentials-from-config-map", }, @@ -192,7 +206,13 @@ func TestGetStorageCredentials(t *testing.T) { getS3Credentials = tc.getS3Credentials getGCPCredentials = tc.getGCPCredentials - actual, err := getStorageCredentials(&tc.backupLocation, tc.credFileStore) + var fileStore velerocredentials.FileStore + if tc.credFileStore != nil { + tc.credFileStore.On("Path", mock.Anything, mock.Anything).Return(tc.credStorePath, tc.credStoreError) + fileStore = tc.credFileStore + } + + actual, err := getStorageCredentials(&tc.backupLocation, fileStore) require.Equal(t, tc.expected, actual) @@ -412,3 +432,124 @@ func TestGetStorageVariables(t *testing.T) { }) } } + +func TestGetRepoPassword(t *testing.T) { + testCases := []struct { + name string + getter *credmock.SecretStore + credStoreReturn string + credStoreError error + cached string + expected string + expectedErr string + }{ + { + name: "invalid secret interface", + expectedErr: "invalid credentials interface", + }, + { + name: "error from secret interface", + getter: new(credmock.SecretStore), + credStoreError: errors.New("fake error"), + expectedErr: "error to get password buffer: fake error", + }, + { + name: "secret with whitespace", + getter: new(credmock.SecretStore), + credStoreReturn: " fake-passwor d ", + expected: "fake-passwor d", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + var secretStore velerocredentials.SecretStore + if tc.getter != nil { + tc.getter.On("Get", mock.Anything, mock.Anything).Return(tc.credStoreReturn, tc.credStoreError) + secretStore = tc.getter + } + + urp := unifiedRepoProvider{ + credentialGetter: velerocredentials.CredentialGetter{ + FromSecret: secretStore, + }, + } + + password, err := getRepoPassword(urp.credentialGetter.FromSecret, RepoParam{}) + + require.Equal(t, tc.expected, password) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} + +func TestGetRepoOption(t *testing.T) { + testCases := []struct { + name string + funcTable localFuncTable + getRepoPassword func(velerocredentials.SecretStore, RepoParam) (string, error) + getStorageCredentials func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) + getStorageVariables func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) + expected udmrepo.RepoOptions + expectedErr string + }{ + { + name: "get repo password fail", + funcTable: localFuncTable{ + getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { + return "", errors.New("fake-error-1") + }, + }, + expectedErr: "error to get repo password: fake-error-1", + }, + { + name: "get storage variable fail", + funcTable: localFuncTable{ + getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { + return "fake-password", nil + }, + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, errors.New("fake-error-2") + }, + }, + expectedErr: "error to get storage variables: fake-error-2", + }, + { + name: "get storage credentials fail", + funcTable: localFuncTable{ + getRepoPassword: func(velerocredentials.SecretStore, RepoParam) (string, error) { + return "fake-password", nil + }, + getStorageVariables: func(*velerov1api.BackupStorageLocation, string) (map[string]string, error) { + return map[string]string{}, nil + }, + getStorageCredentials: func(*velerov1api.BackupStorageLocation, velerocredentials.FileStore) (map[string]string, error) { + return map[string]string{}, errors.New("fake-error-3") + }, + }, + expectedErr: "error to get repo credentials: fake-error-3", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + funcTable = tc.funcTable + urp := unifiedRepoProvider{} + + password, err := urp.getRepoOption(RepoParam{}) + + require.Equal(t, tc.expected, password) + + if tc.expectedErr == "" { + assert.NoError(t, err) + } else { + assert.EqualError(t, err, tc.expectedErr) + } + }) + } +} diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index f0ab63386..69927b851 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -36,6 +36,7 @@ import ( velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" + repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) @@ -242,7 +243,7 @@ func (rm *repositoryManager) Forget(ctx context.Context, snapshot SnapshotIdenti } func (rm *repositoryManager) exec(cmd *Command, backupLocation string) error { - file, err := rm.credentialsFileStore.Path(RepoKeySelector()) + file, err := rm.credentialsFileStore.Path(repokey.RepoKeySelector()) if err != nil { return err } From eb08bdeb62dc53611dfb008f87f9cf4791e3c4a6 Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Wed, 3 Aug 2022 20:00:49 +0800 Subject: [PATCH 35/38] fix issue#2413 by treating namespaces with exclude label as excludedNamespaces Signed-off-by: allenxu404 --- changelogs/unreleased/5178-allenxu404 | 2 ++ pkg/controller/backup_controller.go | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 changelogs/unreleased/5178-allenxu404 diff --git a/changelogs/unreleased/5178-allenxu404 b/changelogs/unreleased/5178-allenxu404 new file mode 100644 index 000000000..2c1b4b0e0 --- /dev/null +++ b/changelogs/unreleased/5178-allenxu404 @@ -0,0 +1,2 @@ +Treat namespaces with exclude label as excludedNamespaces +Related issue: #2413 diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 3f54e8f4f..387e922bd 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -70,6 +70,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/util/logging" "github.com/vmware-tanzu/velero/pkg/volume" + corev1api "k8s.io/api/core/v1" kbclient "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -257,7 +258,6 @@ func (c *backupController) processBackup(key string) error { log.Debug("Preparing backup request") request := c.prepareBackupRequest(original) - if len(request.Status.ValidationErrors) > 0 { request.Status.Phase = velerov1api.BackupPhaseFailedValidation } else { @@ -436,6 +436,15 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg request.Annotations[velerov1api.SourceClusterK8sMajorVersionAnnotation] = c.discoveryHelper.ServerVersion().Major request.Annotations[velerov1api.SourceClusterK8sMinorVersionAnnotation] = c.discoveryHelper.ServerVersion().Minor + // Add namespaces with label velero.io/exclude-from-backup=true into request.Spec.ExcludedNamespaces + // Essentially, adding the label velero.io/exclude-from-backup=true to a namespace would be equivalent to setting spec.ExcludedNamespaces + namespaces, excludeLabel := corev1api.NamespaceList{}, "velero.io/exclude-from-backup" + if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{excludeLabel: "true"}); err == nil { + for _, ns := range namespaces.Items { + request.Spec.ExcludedNamespaces = append(request.Spec.ExcludedNamespaces, ns.Name) + } + } + // validate the included/excluded resources for _, err := range collections.ValidateIncludesExcludes(request.Spec.IncludedResources, request.Spec.ExcludedResources) { request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("Invalid included/excluded resource lists: %v", err)) From 092fc01e8de1ef053459f24a5163487a355bd03b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wenkai=20Yin=28=E5=B0=B9=E6=96=87=E5=BC=80=29?= Date: Fri, 5 Aug 2022 17:15:38 +0800 Subject: [PATCH 36/38] Splic pkg/restic package (#5143) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit splits the pkg/restic package into several packages to support Kopia integration works Fixes #5055 Signed-off-by: Wenkai Yin(尹文开) --- Tiltfile | 2 +- changelogs/unreleased/5143-ywk253100 | 1 + pkg/backup/backup.go | 8 +- pkg/backup/backup_test.go | 4 +- pkg/backup/item_backupper.go | 6 +- .../restic_repository_controller.go | 1 - pkg/{restic => podvolume}/backupper.go | 38 +- pkg/podvolume/backupper_factory.go | 88 +++ pkg/{restic => podvolume}/backupper_test.go | 2 +- pkg/{restic => podvolume}/mocks/restorer.go | 10 +- pkg/{restic => podvolume}/restorer.go | 33 +- pkg/podvolume/restorer_factory.go | 85 +++ pkg/podvolume/util.go | 198 ++++++ pkg/podvolume/util_test.go | 563 ++++++++++++++++ pkg/repository/config/config.go | 2 - .../ensurer.go} | 16 +- .../repo_locker.go => repository/locker.go} | 21 +- pkg/restic/common.go | 172 ----- pkg/restic/common_test.go | 610 ------------------ pkg/restic/mocks/repository_manager.go | 17 +- pkg/restic/repository_manager.go | 79 +-- pkg/restore/restic_restore_action.go | 3 +- pkg/restore/restore.go | 16 +- pkg/restore/restore_test.go | 12 +- 24 files changed, 1051 insertions(+), 936 deletions(-) create mode 100644 changelogs/unreleased/5143-ywk253100 rename pkg/{restic => podvolume}/backupper.go (90%) create mode 100644 pkg/podvolume/backupper_factory.go rename pkg/{restic => podvolume}/backupper_test.go (99%) rename pkg/{restic => podvolume}/mocks/restorer.go (60%) rename pkg/{restic => podvolume}/restorer.go (87%) create mode 100644 pkg/podvolume/restorer_factory.go create mode 100644 pkg/podvolume/util.go create mode 100644 pkg/podvolume/util_test.go rename pkg/{restic/repository_ensurer.go => repository/ensurer.go} (93%) rename pkg/{restic/repo_locker.go => repository/locker.go} (79%) diff --git a/Tiltfile b/Tiltfile index 02da1df56..0d9a64263 100644 --- a/Tiltfile +++ b/Tiltfile @@ -7,7 +7,7 @@ k8s_yaml([ 'config/crd/v1/bases/velero.io_downloadrequests.yaml', 'config/crd/v1/bases/velero.io_podvolumebackups.yaml', 'config/crd/v1/bases/velero.io_podvolumerestores.yaml', - 'config/crd/v1/bases/velero.io_resticrepositories.yaml', + 'config/crd/v1/bases/velero.io_backuprepositories.yaml', 'config/crd/v1/bases/velero.io_restores.yaml', 'config/crd/v1/bases/velero.io_schedules.yaml', 'config/crd/v1/bases/velero.io_serverstatusrequests.yaml', diff --git a/changelogs/unreleased/5143-ywk253100 b/changelogs/unreleased/5143-ywk253100 new file mode 100644 index 000000000..cf5213645 --- /dev/null +++ b/changelogs/unreleased/5143-ywk253100 @@ -0,0 +1 @@ +This commit splits the pkg/restic package into several packages to support Kopia integration works \ No newline at end of file diff --git a/pkg/backup/backup.go b/pkg/backup/backup.go index 697be8500..d026b09cf 100644 --- a/pkg/backup/backup.go +++ b/pkg/backup/backup.go @@ -46,7 +46,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/collections" ) @@ -74,7 +74,7 @@ type kubernetesBackupper struct { dynamicFactory client.DynamicFactory discoveryHelper discovery.Helper podCommandExecutor podexec.PodCommandExecutor - resticBackupperFactory restic.BackupperFactory + resticBackupperFactory podvolume.BackupperFactory resticTimeout time.Duration defaultVolumesToRestic bool clientPageSize int @@ -100,7 +100,7 @@ func NewKubernetesBackupper( discoveryHelper discovery.Helper, dynamicFactory client.DynamicFactory, podCommandExecutor podexec.PodCommandExecutor, - resticBackupperFactory restic.BackupperFactory, + resticBackupperFactory podvolume.BackupperFactory, resticTimeout time.Duration, defaultVolumesToRestic bool, clientPageSize int, @@ -234,7 +234,7 @@ func (kb *kubernetesBackupper) BackupWithResolvers(log logrus.FieldLogger, ctx, cancelFunc := context.WithTimeout(context.Background(), podVolumeTimeout) defer cancelFunc() - var resticBackupper restic.Backupper + var resticBackupper podvolume.Backupper if kb.resticBackupperFactory != nil { resticBackupper, err = kb.resticBackupperFactory.NewBackupper(ctx, backupRequest.Backup) if err != nil { diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 53554e8b3..cf6a4269f 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -47,7 +47,7 @@ import ( "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/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" kubeutil "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -2595,7 +2595,7 @@ func TestBackupWithHooks(t *testing.T) { type fakeResticBackupperFactory struct{} -func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup) (restic.Backupper, error) { +func (f *fakeResticBackupperFactory) NewBackupper(context.Context, *velerov1.Backup) (podvolume.Backupper, error) { return &fakeResticBackupper{}, nil } diff --git a/pkg/backup/item_backupper.go b/pkg/backup/item_backupper.go index bd40c3bdb..df5d48cd2 100644 --- a/pkg/backup/item_backupper.go +++ b/pkg/backup/item_backupper.go @@ -42,7 +42,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/features" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/volume" ) @@ -53,7 +53,7 @@ type itemBackupper struct { tarWriter tarWriter dynamicFactory client.DynamicFactory discoveryHelper discovery.Helper - resticBackupper restic.Backupper + resticBackupper podvolume.Backupper resticSnapshotTracker *pvcSnapshotTracker volumeSnapshotterGetter VolumeSnapshotterGetter @@ -149,7 +149,7 @@ func (ib *itemBackupper) backupItem(logger logrus.FieldLogger, obj runtime.Unstr // Get the list of volumes to back up using restic from the pod's annotations. Remove from this list // any volumes that use a PVC that we've already backed up (this would be in a read-write-many scenario, // where it's been backed up from another pod), since we don't need >1 backup per PVC. - for _, volume := range restic.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { + for _, volume := range podvolume.GetPodVolumesUsingRestic(pod, boolptr.IsSetToTrue(ib.backupRequest.Spec.DefaultVolumesToRestic)) { if found, pvcName := ib.resticSnapshotTracker.HasPVCForPodVolume(pod, volume); found { log.WithFields(map[string]interface{}{ "podVolume": volume, diff --git a/pkg/controller/restic_repository_controller.go b/pkg/controller/restic_repository_controller.go index 3f27de36f..c3ca1505a 100644 --- a/pkg/controller/restic_repository_controller.go +++ b/pkg/controller/restic_repository_controller.go @@ -26,7 +26,6 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/clock" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" diff --git a/pkg/restic/backupper.go b/pkg/podvolume/backupper.go similarity index 90% rename from pkg/restic/backupper.go rename to pkg/podvolume/backupper.go index 589b396f3..116a5c4e7 100644 --- a/pkg/restic/backupper.go +++ b/pkg/podvolume/backupper.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package podvolume import ( "context" @@ -30,7 +30,9 @@ import ( "k8s.io/client-go/tools/cache" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/label" + "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -41,11 +43,12 @@ type Backupper interface { } type backupper struct { - ctx context.Context - repoManager *repositoryManager - repoEnsurer *repositoryEnsurer - pvcClient corev1client.PersistentVolumeClaimsGetter - pvClient corev1client.PersistentVolumesGetter + ctx context.Context + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter + pvClient corev1client.PersistentVolumesGetter results map[string]chan *velerov1api.PodVolumeBackup resultsLock sync.Mutex @@ -53,19 +56,21 @@ type backupper struct { func newBackupper( ctx context.Context, - repoManager *repositoryManager, - repoEnsurer *repositoryEnsurer, + repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, podVolumeBackupInformer cache.SharedIndexInformer, + veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, pvClient corev1client.PersistentVolumesGetter, log logrus.FieldLogger, ) *backupper { b := &backupper{ - ctx: ctx, - repoManager: repoManager, - repoEnsurer: repoEnsurer, - pvcClient: pvcClient, - pvClient: pvClient, + ctx: ctx, + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, + pvClient: pvClient, results: make(map[string]chan *velerov1api.PodVolumeBackup), } @@ -109,8 +114,8 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. // get a single non-exclusive lock since we'll wait for all individual // backups to be complete before releasing it. - b.repoManager.repoLocker.Lock(repo.Name) - defer b.repoManager.repoLocker.Unlock(repo.Name) + b.repoLocker.Lock(repo.Name) + defer b.repoLocker.Unlock(repo.Name) resultsChan := make(chan *velerov1api.PodVolumeBackup) @@ -176,9 +181,10 @@ func (b *backupper) BackupPodVolumes(backup *velerov1api.Backup, pod *corev1api. log.Warnf("Volume %s is declared in pod %s/%s but not mounted by any container, skipping", volumeName, pod.Namespace, pod.Name) continue } + // TODO: Remove the hard-coded uploader type before v1.10 FC volumeBackup := newPodVolumeBackup(backup, pod, volume, repo.Spec.ResticIdentifier, "restic", pvc) - if volumeBackup, err = b.repoManager.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { + if volumeBackup, err = b.veleroClient.VeleroV1().PodVolumeBackups(volumeBackup.Namespace).Create(context.TODO(), volumeBackup, metav1.CreateOptions{}); err != nil { errs = append(errs, err) continue } diff --git a/pkg/podvolume/backupper_factory.go b/pkg/podvolume/backupper_factory.go new file mode 100644 index 000000000..aaaa5e2ac --- /dev/null +++ b/pkg/podvolume/backupper_factory.go @@ -0,0 +1,88 @@ +/* +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 podvolume + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" +) + +// BackupperFactory can construct pod volumes backuppers. +type BackupperFactory interface { + // NewBackupper returns a pod volumes backupper for use during a single Velero backup. + NewBackupper(context.Context, *velerov1api.Backup) (Backupper, error) +} + +func NewBackupperFactory(repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, + veleroClient clientset.Interface, + pvcClient corev1client.PersistentVolumeClaimsGetter, + pvClient corev1client.PersistentVolumesGetter, + repoInformerSynced cache.InformerSynced, + log logrus.FieldLogger) BackupperFactory { + return &backupperFactory{ + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, + pvClient: pvClient, + repoInformerSynced: repoInformerSynced, + log: log, + } +} + +type backupperFactory struct { + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter + pvClient corev1client.PersistentVolumesGetter + repoInformerSynced cache.InformerSynced + log logrus.FieldLogger +} + +func (bf *backupperFactory) NewBackupper(ctx context.Context, backup *velerov1api.Backup) (Backupper, error) { + informer := velerov1informers.NewFilteredPodVolumeBackupInformer( + bf.veleroClient, + backup.Namespace, + 0, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, + func(opts *metav1.ListOptions) { + opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.BackupUIDLabel, backup.UID) + }, + ) + + b := newBackupper(ctx, bf.repoLocker, bf.repoEnsurer, informer, bf.veleroClient, bf.pvcClient, bf.pvClient, bf.log) + + go informer.Run(ctx.Done()) + if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, bf.repoInformerSynced) { + return nil, errors.New("timed out waiting for caches to sync") + } + + return b, nil +} diff --git a/pkg/restic/backupper_test.go b/pkg/podvolume/backupper_test.go similarity index 99% rename from pkg/restic/backupper_test.go rename to pkg/podvolume/backupper_test.go index 8969f6efa..fb0cacd1a 100644 --- a/pkg/restic/backupper_test.go +++ b/pkg/podvolume/backupper_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package podvolume import ( "context" diff --git a/pkg/restic/mocks/restorer.go b/pkg/podvolume/mocks/restorer.go similarity index 60% rename from pkg/restic/mocks/restorer.go rename to pkg/podvolume/mocks/restorer.go index 7f4f5c1d8..fd210aa00 100644 --- a/pkg/restic/mocks/restorer.go +++ b/pkg/podvolume/mocks/restorer.go @@ -2,8 +2,10 @@ package mocks -import mock "github.com/stretchr/testify/mock" -import restic "github.com/vmware-tanzu/velero/pkg/restic" +import ( + mock "github.com/stretchr/testify/mock" + "github.com/vmware-tanzu/velero/pkg/podvolume" +) // Restorer is an autogenerated mock type for the Restorer type type Restorer struct { @@ -11,11 +13,11 @@ type Restorer struct { } // RestorePodVolumes provides a mock function with given fields: _a0 -func (_m *Restorer) RestorePodVolumes(_a0 restic.RestoreData) []error { +func (_m *Restorer) RestorePodVolumes(_a0 podvolume.RestoreData) []error { ret := _m.Called(_a0) var r0 []error - if rf, ok := ret.Get(0).(func(restic.RestoreData) []error); ok { + if rf, ok := ret.Get(0).(func(podvolume.RestoreData) []error); ok { r0 = rf(_a0) } else { if ret.Get(0) != nil { diff --git a/pkg/restic/restorer.go b/pkg/podvolume/restorer.go similarity index 87% rename from pkg/restic/restorer.go rename to pkg/podvolume/restorer.go index e747b0b6d..daa3a630d 100644 --- a/pkg/restic/restorer.go +++ b/pkg/podvolume/restorer.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package podvolume import ( "context" @@ -28,7 +28,9 @@ import ( "k8s.io/client-go/tools/cache" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" "github.com/vmware-tanzu/velero/pkg/label" + "github.com/vmware-tanzu/velero/pkg/repository" "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) @@ -46,10 +48,11 @@ type Restorer interface { } type restorer struct { - ctx context.Context - repoManager *repositoryManager - repoEnsurer *repositoryEnsurer - pvcClient corev1client.PersistentVolumeClaimsGetter + ctx context.Context + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter resultsLock sync.Mutex results map[string]chan *velerov1api.PodVolumeRestore @@ -57,17 +60,19 @@ type restorer struct { func newRestorer( ctx context.Context, - rm *repositoryManager, - repoEnsurer *repositoryEnsurer, + repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, podVolumeRestoreInformer cache.SharedIndexInformer, + veleroClient clientset.Interface, pvcClient corev1client.PersistentVolumeClaimsGetter, log logrus.FieldLogger, ) *restorer { r := &restorer{ - ctx: ctx, - repoManager: rm, - repoEnsurer: repoEnsurer, - pvcClient: pvcClient, + ctx: ctx, + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, results: make(map[string]chan *velerov1api.PodVolumeRestore), } @@ -108,8 +113,8 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { // get a single non-exclusive lock since we'll wait for all individual // restores to be complete before releasing it. - r.repoManager.repoLocker.Lock(repo.Name) - defer r.repoManager.repoLocker.Unlock(repo.Name) + r.repoLocker.Lock(repo.Name) + defer r.repoLocker.Unlock(repo.Name) resultsChan := make(chan *velerov1api.PodVolumeRestore) @@ -142,7 +147,7 @@ func (r *restorer) RestorePodVolumes(data RestoreData) []error { // TODO: Remove the hard-coded uploader type before v1.10 FC volumeRestore := newPodVolumeRestore(data.Restore, data.Pod, data.BackupLocation, volume, snapshot, repo.Spec.ResticIdentifier, "restic", pvc) - if err := errorOnly(r.repoManager.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { + if err := errorOnly(r.veleroClient.VeleroV1().PodVolumeRestores(volumeRestore.Namespace).Create(context.TODO(), volumeRestore, metav1.CreateOptions{})); err != nil { errs = append(errs, errors.WithStack(err)) continue } diff --git a/pkg/podvolume/restorer_factory.go b/pkg/podvolume/restorer_factory.go new file mode 100644 index 000000000..3432f4be3 --- /dev/null +++ b/pkg/podvolume/restorer_factory.go @@ -0,0 +1,85 @@ +/* +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 podvolume + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1client "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/cache" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + clientset "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned" + velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" + "github.com/vmware-tanzu/velero/pkg/repository" +) + +// RestorerFactory can construct pod volumes restorers. +type RestorerFactory interface { + // NewRestorer returns a pod volumes restorer for use during a single Velero restore. + NewRestorer(context.Context, *velerov1api.Restore) (Restorer, error) +} + +func NewRestorerFactory(repoLocker *repository.RepoLocker, + repoEnsurer *repository.RepositoryEnsurer, + veleroClient clientset.Interface, + pvcClient corev1client.PersistentVolumeClaimsGetter, + repoInformerSynced cache.InformerSynced, + log logrus.FieldLogger) RestorerFactory { + return &restorerFactory{ + repoLocker: repoLocker, + repoEnsurer: repoEnsurer, + veleroClient: veleroClient, + pvcClient: pvcClient, + repoInformerSynced: repoInformerSynced, + log: log, + } +} + +type restorerFactory struct { + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer + veleroClient clientset.Interface + pvcClient corev1client.PersistentVolumeClaimsGetter + repoInformerSynced cache.InformerSynced + log logrus.FieldLogger +} + +func (rf *restorerFactory) NewRestorer(ctx context.Context, restore *velerov1api.Restore) (Restorer, error) { + informer := velerov1informers.NewFilteredPodVolumeRestoreInformer( + rf.veleroClient, + restore.Namespace, + 0, + cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, + func(opts *metav1.ListOptions) { + opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.RestoreUIDLabel, restore.UID) + }, + ) + + r := newRestorer(ctx, rf.repoLocker, rf.repoEnsurer, informer, rf.veleroClient, rf.pvcClient, rf.log) + + go informer.Run(ctx.Done()) + if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, rf.repoInformerSynced) { + return nil, errors.New("timed out waiting for cache to sync") + } + + return r, nil +} diff --git a/pkg/podvolume/util.go b/pkg/podvolume/util.go new file mode 100644 index 000000000..57baacc10 --- /dev/null +++ b/pkg/podvolume/util.go @@ -0,0 +1,198 @@ +/* +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 podvolume + +import ( + "strings" + + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +const ( + // PVCNameAnnotation is the key for the annotation added to + // pod volume backups when they're for a PVC. + PVCNameAnnotation = "velero.io/pvc-name" + + // Deprecated. + // + // TODO(2.0): remove + podAnnotationPrefix = "snapshot.velero.io/" + + // VolumesToBackupAnnotation is the annotation on a pod whose mounted volumes + // need to be backed up using restic. + VolumesToBackupAnnotation = "backup.velero.io/backup-volumes" + + // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes + // should be excluded from restic backup. + VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" +) + +// GetVolumeBackupsForPod returns a map, of volume name -> snapshot id, +// of the PodVolumeBackups that exist for the provided pod. +func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]string { + volumes := make(map[string]string) + + for _, pvb := range podVolumeBackups { + if !isPVBMatchPod(pvb, pod.GetName(), sourcePodNs) { + continue + } + + // skip PVBs without a snapshot ID since there's nothing + // to restore (they could be failed, or for empty volumes). + if pvb.Status.SnapshotID == "" { + continue + } + + // If the volume came from a projected or DownwardAPI source, skip its restore. + // This allows backups affected by https://github.com/vmware-tanzu/velero/issues/3863 + // or https://github.com/vmware-tanzu/velero/issues/4053 to be restored successfully. + if volumeHasNonRestorableSource(pvb.Spec.Volume, pod.Spec.Volumes) { + continue + } + + volumes[pvb.Spec.Volume] = pvb.Status.SnapshotID + } + + if len(volumes) > 0 { + return volumes + } + + return getPodSnapshotAnnotations(pod) +} + +func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace string) bool { + return podName == pvb.Spec.Pod.Name && namespace == pvb.Spec.Pod.Namespace +} + +// volumeHasNonRestorableSource checks if the given volume exists in the list of podVolumes +// and returns true if the volume's source is not restorable. This is true for volumes with +// a Projected or DownwardAPI source. +func volumeHasNonRestorableSource(volumeName string, podVolumes []corev1api.Volume) bool { + var volume corev1api.Volume + for _, v := range podVolumes { + if v.Name == volumeName { + volume = v + break + } + } + return volume.Projected != nil || volume.DownwardAPI != nil +} + +// getPodSnapshotAnnotations returns a map, of volume name -> snapshot id, +// of all snapshots for this pod. +// TODO(2.0) to remove +// Deprecated: we will stop using pod annotations to record restic snapshot IDs after they're taken, +// therefore we won't need to check if these annotations exist. +func getPodSnapshotAnnotations(obj metav1.Object) map[string]string { + var res map[string]string + + insertSafe := func(k, v string) { + if res == nil { + res = make(map[string]string) + } + res[k] = v + } + + for k, v := range obj.GetAnnotations() { + if strings.HasPrefix(k, podAnnotationPrefix) { + insertSafe(k[len(podAnnotationPrefix):], v) + } + } + + return res +} + +// GetVolumesToBackup returns a list of volume names to backup for +// the provided pod. +// Deprecated: Use GetPodVolumesUsingRestic instead. +func GetVolumesToBackup(obj metav1.Object) []string { + annotations := obj.GetAnnotations() + if annotations == nil { + return nil + } + + backupsValue := annotations[VolumesToBackupAnnotation] + if backupsValue == "" { + return nil + } + + return strings.Split(backupsValue, ",") +} + +func getVolumesToExclude(obj metav1.Object) []string { + annotations := obj.GetAnnotations() + if annotations == nil { + return nil + } + + return strings.Split(annotations[VolumesToExcludeAnnotation], ",") +} + +func contains(list []string, k string) bool { + for _, i := range list { + if i == k { + return true + } + } + return false +} + +// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. +func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { + if !defaultVolumesToRestic { + return GetVolumesToBackup(pod) + } + + volsToExclude := getVolumesToExclude(pod) + podVolumes := []string{} + for _, pv := range pod.Spec.Volumes { + // cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods + // and therefore not accessible to the restic daemon set. + if pv.HostPath != nil { + continue + } + // don't backup volumes mounting secrets. Secrets will be backed up separately. + if pv.Secret != nil { + continue + } + // don't backup volumes mounting config maps. Config maps will be backed up separately. + if pv.ConfigMap != nil { + continue + } + // don't backup volumes mounted as projected volumes, all data in those come from kube state. + if pv.Projected != nil { + continue + } + // don't backup DownwardAPI volumes, all data in those come from kube state. + if pv.DownwardAPI != nil { + continue + } + // don't backup volumes that are included in the exclude list. + if contains(volsToExclude, pv.Name) { + continue + } + // don't include volumes that mount the default service account token. + if strings.HasPrefix(pv.Name, "default-token") { + continue + } + podVolumes = append(podVolumes, pv.Name) + } + return podVolumes +} diff --git a/pkg/podvolume/util_test.go b/pkg/podvolume/util_test.go new file mode 100644 index 000000000..88b574668 --- /dev/null +++ b/pkg/podvolume/util_test.go @@ -0,0 +1,563 @@ +/* +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 podvolume + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" + corev1api "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/builder" +) + +func TestGetVolumeBackupsForPod(t *testing.T) { + tests := []struct { + name string + podVolumeBackups []*velerov1api.PodVolumeBackup + podVolumes []corev1api.Volume + podAnnotations map[string]string + podName string + sourcePodNs string + expected map[string]string + }{ + { + name: "nil annotations results in no volume backups returned", + podAnnotations: nil, + expected: nil, + }, + { + name: "empty annotations results in no volume backups returned", + podAnnotations: make(map[string]string), + expected: nil, + }, + { + name: "pod annotations with no snapshot annotation prefix results in no volume backups returned", + podAnnotations: map[string]string{"foo": "bar"}, + expected: nil, + }, + { + name: "pod annotation with only snapshot annotation prefix, results in volume backup with empty volume key", + podAnnotations: map[string]string{podAnnotationPrefix: "snapshotID"}, + expected: map[string]string{"": "snapshotID"}, + }, + { + name: "pod annotation with snapshot annotation prefix results in volume backup with volume name and snapshot ID", + podAnnotations: map[string]string{podAnnotationPrefix + "volume": "snapshotID"}, + expected: map[string]string{"volume": "snapshotID"}, + }, + { + name: "only pod annotations with snapshot annotation prefix are considered", + podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "volume1": "snapshot1", podAnnotationPrefix + "volume2": "snapshot2"}, + expected: map[string]string{"volume1": "snapshot1", "volume2": "snapshot2"}, + }, + { + name: "pod annotations are not considered if PVBs are provided", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"}, + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "volume backups are returned even if no pod annotations are present", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "only volumes from PVBs with snapshot IDs are returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest3-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-4").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest4-abc").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "only volumes from PVBs for the given pod are returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestAnotherPod").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, + }, + { + name: "only volumes from PVBs which match the pod name and source pod namespace are returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), + builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestAnotherPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), + builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestAnotherNS").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvbtest1-foo": "snapshot1"}, + }, + { + name: "volumes from PVBs that correspond to a pod volume from a projected source are not returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-projected").Result(), + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-projected").Result(), + }, + podVolumes: []corev1api.Volume{ + { + Name: "pvb-non-projected", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "pvb-projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvb-non-projected": "snapshot1"}, + }, + { + name: "volumes from PVBs that correspond to a pod volume from a DownwardAPI source are not returned", + podVolumeBackups: []*velerov1api.PodVolumeBackup{ + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-downwardapi").Result(), + builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-downwardapi").Result(), + }, + podVolumes: []corev1api.Volume{ + { + Name: "pvb-non-downwardapi", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "pvb-downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + podName: "TestPod", + sourcePodNs: "TestNS", + expected: map[string]string{"pvb-non-downwardapi": "snapshot1"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := &corev1api.Pod{} + pod.Annotations = test.podAnnotations + pod.Name = test.podName + pod.Spec.Volumes = test.podVolumes + + res := GetVolumeBackupsForPod(test.podVolumeBackups, pod, test.sourcePodNs) + assert.Equal(t, test.expected, res) + }) + } +} + +func TestVolumeHasNonRestorableSource(t *testing.T) { + testCases := []struct { + name string + volumeName string + podVolumes []corev1api.Volume + expected bool + }{ + { + name: "volume name not in list of volumes", + volumeName: "missing-volume", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: false, + }, + { + name: "volume name in list of volumes but not projected or DownwardAPI", + volumeName: "restorable", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: false, + }, + { + name: "volume name in list of volumes and projected", + volumeName: "projected", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: true, + }, + { + name: "volume name in list of volumes and is a DownwardAPI volume", + volumeName: "downwardapi", + podVolumes: []corev1api.Volume{ + { + Name: "restorable", + VolumeSource: corev1api.VolumeSource{ + PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, + }, + }, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{}, + }, + }, + { + Name: "downwardapi", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, + }, + }, + }, + expected: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := volumeHasNonRestorableSource(tc.volumeName, tc.podVolumes) + assert.Equal(t, tc.expected, actual) + }) + + } +} + +func TestGetVolumesToBackup(t *testing.T) { + tests := []struct { + name string + annotations map[string]string + expected []string + }{ + { + name: "nil annotations", + annotations: nil, + expected: nil, + }, + { + name: "no volumes to backup", + annotations: map[string]string{"foo": "bar"}, + expected: nil, + }, + { + name: "one volume to backup", + annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1"}, + expected: []string{"volume-1"}, + }, + { + name: "multiple volumes to backup", + annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1,volume-2,volume-3"}, + expected: []string{"volume-1", "volume-2", "volume-3"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pod := &corev1api.Pod{} + pod.Annotations = test.annotations + + res := GetVolumesToBackup(pod) + + // sort to ensure good compare of slices + sort.Strings(test.expected) + sort.Strings(res) + + assert.Equal(t, test.expected, res) + }) + } +} + +func TestGetPodVolumesUsingRestic(t *testing.T) { + testCases := []struct { + name string + pod *corev1api.Pod + expected []string + defaultVolumesToRestic bool + }{ + { + name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", + defaultVolumesToRestic: false, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3", + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude default service account token from restic backup", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic because colume mounting default service account token + {Name: "default-token-5xq45"}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude host path volumes from restic backups", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude volumes mounting secrets", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "superSecret", VolumeSource: corev1api.VolumeSource{Secret: &corev1api.SecretVolumeSource{SecretName: "super-secret"}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude volumes mounting config maps", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + // Restic Volumes + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + /// Excluded from restic through annotation + {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, + // Excluded from restic because hostpath + {Name: "appCOnfig", VolumeSource: corev1api.VolumeSource{ConfigMap: &corev1api.ConfigMapVolumeSource{LocalObjectReference: corev1api.LocalObjectReference{Name: "app-config"}}}}, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude projected volumes", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + { + Name: "projected", + VolumeSource: corev1api.VolumeSource{ + Projected: &corev1api.ProjectedVolumeSource{ + Sources: []corev1api.VolumeProjection{{ + Secret: &corev1api.SecretProjection{ + LocalObjectReference: corev1api.LocalObjectReference{}, + Items: nil, + Optional: nil, + }, + DownwardAPI: nil, + ConfigMap: nil, + ServiceAccountToken: nil, + }}, + DefaultMode: nil, + }, + }, + }, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + { + name: "should exclude DownwardAPI volumes", + defaultVolumesToRestic: true, + pod: &corev1api.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{ + VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", + }, + }, + Spec: corev1api.PodSpec{ + Volumes: []corev1api.Volume{ + {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, + { + Name: "downwardAPI", + VolumeSource: corev1api.VolumeSource{ + DownwardAPI: &corev1api.DownwardAPIVolumeSource{ + Items: []corev1api.DownwardAPIVolumeFile{ + { + Path: "labels", + FieldRef: &corev1api.ObjectFieldSelector{ + APIVersion: "v1", + FieldPath: "metadata.labels", + }, + }, + }, + }, + }, + }, + }, + }, + }, + expected: []string{"resticPV1", "resticPV2", "resticPV3"}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) + + sort.Strings(tc.expected) + sort.Strings(actual) + assert.Equal(t, tc.expected, actual) + }) + } +} diff --git a/pkg/repository/config/config.go b/pkg/repository/config/config.go index 24dc8d6b3..d7ed99b69 100644 --- a/pkg/repository/config/config.go +++ b/pkg/repository/config/config.go @@ -34,9 +34,7 @@ const ( AzureBackend BackendType = "velero.io/azure" GCPBackend BackendType = "velero.io/gcp" FSBackend BackendType = "velero.io/fs" -) -const ( // CredentialsFileKey is the key within a BSL config that is checked to see if // the BSL is using its own credentials, rather than those in the environment CredentialsFileKey = "credentialsFile" diff --git a/pkg/restic/repository_ensurer.go b/pkg/repository/ensurer.go similarity index 93% rename from pkg/restic/repository_ensurer.go rename to pkg/repository/ensurer.go index d764a49c7..15aa10701 100644 --- a/pkg/restic/repository_ensurer.go +++ b/pkg/repository/ensurer.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package restic +package repository import ( "context" @@ -35,8 +35,8 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" ) -// repositoryEnsurer ensures that Velero restic repositories are created and ready. -type repositoryEnsurer struct { +// RepositoryEnsurer ensures that backup repositories are created and ready. +type RepositoryEnsurer struct { log logrus.FieldLogger repoLister velerov1listers.BackupRepositoryLister repoClient velerov1client.BackupRepositoriesGetter @@ -55,8 +55,8 @@ type repoKey struct { backupLocation string } -func newRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *repositoryEnsurer { - r := &repositoryEnsurer{ +func NewRepositoryEnsurer(repoInformer velerov1informers.BackupRepositoryInformer, repoClient velerov1client.BackupRepositoriesGetter, log logrus.FieldLogger) *RepositoryEnsurer { + r := &RepositoryEnsurer{ log: log, repoLister: repoInformer.Lister(), repoClient: repoClient, @@ -105,7 +105,7 @@ func repoLabels(volumeNamespace, backupLocation string) labels.Set { } } -func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { +func (r *RepositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNamespace, backupLocation string) (*velerov1api.BackupRepository, error) { log := r.log.WithField("volumeNamespace", volumeNamespace).WithField("backupLocation", backupLocation) // It's only safe to have one instance of this method executing concurrently for a @@ -190,7 +190,7 @@ func (r *repositoryEnsurer) EnsureRepo(ctx context.Context, namespace, volumeNam } } -func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { +func (r *RepositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRepository { r.repoChansLock.Lock() defer r.repoChansLock.Unlock() @@ -198,7 +198,7 @@ func (r *repositoryEnsurer) getRepoChan(name string) chan *velerov1api.BackupRep return r.repoChans[name] } -func (r *repositoryEnsurer) repoLock(volumeNamespace, backupLocation string) *sync.Mutex { +func (r *RepositoryEnsurer) repoLock(volumeNamespace, backupLocation string) *sync.Mutex { r.repoLocksMu.Lock() defer r.repoLocksMu.Unlock() diff --git a/pkg/restic/repo_locker.go b/pkg/repository/locker.go similarity index 79% rename from pkg/restic/repo_locker.go rename to pkg/repository/locker.go index 29434753e..20eea9635 100644 --- a/pkg/restic/repo_locker.go +++ b/pkg/repository/locker.go @@ -13,23 +13,24 @@ 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 restic + +package repository import "sync" -// repoLocker manages exclusive/non-exclusive locks for +// RepoLocker manages exclusive/non-exclusive locks for // operations against restic repositories. The semantics // of exclusive/non-exclusive locks are the same as for // a sync.RWMutex, where a non-exclusive lock is equivalent // to a read lock, and an exclusive lock is equivalent to // a write lock. -type repoLocker struct { +type RepoLocker struct { mu sync.Mutex locks map[string]*sync.RWMutex } -func newRepoLocker() *repoLocker { - return &repoLocker{ +func NewRepoLocker() *RepoLocker { + return &RepoLocker{ locks: make(map[string]*sync.RWMutex), } } @@ -37,28 +38,28 @@ func newRepoLocker() *repoLocker { // LockExclusive acquires an exclusive lock for the specified // repository. This function blocks until no other locks exist // for the repo. -func (rl *repoLocker) LockExclusive(name string) { +func (rl *RepoLocker) LockExclusive(name string) { rl.ensureLock(name).Lock() } // Lock acquires a non-exclusive lock for the specified // repository. This function blocks until no exclusive // locks exist for the repo. -func (rl *repoLocker) Lock(name string) { +func (rl *RepoLocker) Lock(name string) { rl.ensureLock(name).RLock() } // UnlockExclusive releases an exclusive lock for the repo. -func (rl *repoLocker) UnlockExclusive(name string) { +func (rl *RepoLocker) UnlockExclusive(name string) { rl.ensureLock(name).Unlock() } // Unlock releases a non-exclusive lock for the repo. -func (rl *repoLocker) Unlock(name string) { +func (rl *RepoLocker) Unlock(name string) { rl.ensureLock(name).RUnlock() } -func (rl *repoLocker) ensureLock(name string) *sync.RWMutex { +func (rl *RepoLocker) ensureLock(name string) *sync.RWMutex { rl.mu.Lock() defer rl.mu.Unlock() diff --git a/pkg/restic/common.go b/pkg/restic/common.go index 580bd2708..6e2671625 100644 --- a/pkg/restic/common.go +++ b/pkg/restic/common.go @@ -20,11 +20,9 @@ import ( "context" "fmt" "os" - "strings" "time" "github.com/pkg/errors" - corev1api "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "sigs.k8s.io/controller-runtime/pkg/client" @@ -51,178 +49,8 @@ const ( // DefaultVolumesToRestic specifies whether restic should be used, by default, to // take backup of all pod volumes. DefaultVolumesToRestic = false - - // PVCNameAnnotation is the key for the annotation added to - // pod volume backups when they're for a PVC. - PVCNameAnnotation = "velero.io/pvc-name" - - // VolumesToBackupAnnotation is the annotation on a pod whose mounted volumes - // need to be backed up using restic. - VolumesToBackupAnnotation = "backup.velero.io/backup-volumes" - - // VolumesToExcludeAnnotation is the annotation on a pod whose mounted volumes - // should be excluded from restic backup. - VolumesToExcludeAnnotation = "backup.velero.io/backup-volumes-excludes" - - // Deprecated. - // - // TODO(2.0): remove - podAnnotationPrefix = "snapshot.velero.io/" ) -// getPodSnapshotAnnotations returns a map, of volume name -> snapshot id, -// of all restic snapshots for this pod. -// TODO(2.0) to remove -// Deprecated: we will stop using pod annotations to record restic snapshot IDs after they're taken, -// therefore we won't need to check if these annotations exist. -func getPodSnapshotAnnotations(obj metav1.Object) map[string]string { - var res map[string]string - - insertSafe := func(k, v string) { - if res == nil { - res = make(map[string]string) - } - res[k] = v - } - - for k, v := range obj.GetAnnotations() { - if strings.HasPrefix(k, podAnnotationPrefix) { - insertSafe(k[len(podAnnotationPrefix):], v) - } - } - - return res -} - -func isPVBMatchPod(pvb *velerov1api.PodVolumeBackup, podName string, namespace string) bool { - return podName == pvb.Spec.Pod.Name && namespace == pvb.Spec.Pod.Namespace -} - -// volumeHasNonRestorableSource checks if the given volume exists in the list of podVolumes -// and returns true if the volume's source is not restorable. This is true for volumes with -// a Projected or DownwardAPI source. -func volumeHasNonRestorableSource(volumeName string, podVolumes []corev1api.Volume) bool { - var volume corev1api.Volume - for _, v := range podVolumes { - if v.Name == volumeName { - volume = v - break - } - } - return volume.Projected != nil || volume.DownwardAPI != nil -} - -// GetVolumeBackupsForPod returns a map, of volume name -> snapshot id, -// of the PodVolumeBackups that exist for the provided pod. -func GetVolumeBackupsForPod(podVolumeBackups []*velerov1api.PodVolumeBackup, pod *corev1api.Pod, sourcePodNs string) map[string]string { - volumes := make(map[string]string) - - for _, pvb := range podVolumeBackups { - if !isPVBMatchPod(pvb, pod.GetName(), sourcePodNs) { - continue - } - - // skip PVBs without a snapshot ID since there's nothing - // to restore (they could be failed, or for empty volumes). - if pvb.Status.SnapshotID == "" { - continue - } - - // If the volume came from a projected or DownwardAPI source, skip its restore. - // This allows backups affected by https://github.com/vmware-tanzu/velero/issues/3863 - // or https://github.com/vmware-tanzu/velero/issues/4053 to be restored successfully. - if volumeHasNonRestorableSource(pvb.Spec.Volume, pod.Spec.Volumes) { - continue - } - - volumes[pvb.Spec.Volume] = pvb.Status.SnapshotID - } - - if len(volumes) > 0 { - return volumes - } - - return getPodSnapshotAnnotations(pod) -} - -// GetVolumesToBackup returns a list of volume names to backup for -// the provided pod. -// Deprecated: Use GetPodVolumesUsingRestic instead. -func GetVolumesToBackup(obj metav1.Object) []string { - annotations := obj.GetAnnotations() - if annotations == nil { - return nil - } - - backupsValue := annotations[VolumesToBackupAnnotation] - if backupsValue == "" { - return nil - } - - return strings.Split(backupsValue, ",") -} - -func getVolumesToExclude(obj metav1.Object) []string { - annotations := obj.GetAnnotations() - if annotations == nil { - return nil - } - - return strings.Split(annotations[VolumesToExcludeAnnotation], ",") -} - -func contains(list []string, k string) bool { - for _, i := range list { - if i == k { - return true - } - } - return false -} - -// GetPodVolumesUsingRestic returns a list of volume names to backup for the provided pod. -func GetPodVolumesUsingRestic(pod *corev1api.Pod, defaultVolumesToRestic bool) []string { - if !defaultVolumesToRestic { - return GetVolumesToBackup(pod) - } - - volsToExclude := getVolumesToExclude(pod) - podVolumes := []string{} - for _, pv := range pod.Spec.Volumes { - // cannot backup hostpath volumes as they are not mounted into /var/lib/kubelet/pods - // and therefore not accessible to the restic daemon set. - if pv.HostPath != nil { - continue - } - // don't backup volumes mounting secrets. Secrets will be backed up separately. - if pv.Secret != nil { - continue - } - // don't backup volumes mounting config maps. Config maps will be backed up separately. - if pv.ConfigMap != nil { - continue - } - // don't backup volumes mounted as projected volumes, all data in those come from kube state. - if pv.Projected != nil { - continue - } - // don't backup DownwardAPI volumes, all data in those come from kube state. - if pv.DownwardAPI != nil { - continue - } - // don't backup volumes that are included in the exclude list. - if contains(volsToExclude, pv.Name) { - continue - } - // don't include volumes that mount the default service account token. - if strings.HasPrefix(pv.Name, "default-token") { - continue - } - podVolumes = append(podVolumes, pv.Name) - } - return podVolumes -} - // SnapshotIdentifier uniquely identifies a restic snapshot // taken by Velero. type SnapshotIdentifier struct { diff --git a/pkg/restic/common_test.go b/pkg/restic/common_test.go index 7f3e0c503..fac82f901 100644 --- a/pkg/restic/common_test.go +++ b/pkg/restic/common_test.go @@ -28,212 +28,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" - "github.com/vmware-tanzu/velero/pkg/builder" velerotest "github.com/vmware-tanzu/velero/pkg/test" ) -func TestGetVolumeBackupsForPod(t *testing.T) { - tests := []struct { - name string - podVolumeBackups []*velerov1api.PodVolumeBackup - podVolumes []corev1api.Volume - podAnnotations map[string]string - podName string - sourcePodNs string - expected map[string]string - }{ - { - name: "nil annotations results in no volume backups returned", - podAnnotations: nil, - expected: nil, - }, - { - name: "empty annotations results in no volume backups returned", - podAnnotations: make(map[string]string), - expected: nil, - }, - { - name: "pod annotations with no snapshot annotation prefix results in no volume backups returned", - podAnnotations: map[string]string{"foo": "bar"}, - expected: nil, - }, - { - name: "pod annotation with only snapshot annotation prefix, results in volume backup with empty volume key", - podAnnotations: map[string]string{podAnnotationPrefix: "snapshotID"}, - expected: map[string]string{"": "snapshotID"}, - }, - { - name: "pod annotation with snapshot annotation prefix results in volume backup with volume name and snapshot ID", - podAnnotations: map[string]string{podAnnotationPrefix + "volume": "snapshotID"}, - expected: map[string]string{"volume": "snapshotID"}, - }, - { - name: "only pod annotations with snapshot annotation prefix are considered", - podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "volume1": "snapshot1", podAnnotationPrefix + "volume2": "snapshot2"}, - expected: map[string]string{"volume1": "snapshot1", "volume2": "snapshot2"}, - }, - { - name: "pod annotations are not considered if PVBs are provided", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - podAnnotations: map[string]string{"x": "y", podAnnotationPrefix + "foo": "bar", podAnnotationPrefix + "abc": "123"}, - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "volume backups are returned even if no pod annotations are present", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "only volumes from PVBs with snapshot IDs are returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest3-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-4").PodName("TestPod").PodNamespace("TestNS").Volume("pvbtest4-abc").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "only volumes from PVBs for the given pod are returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestAnotherPod").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1", "pvbtest2-abc": "snapshot2"}, - }, - { - name: "only volumes from PVBs which match the pod name and source pod namespace are returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvbtest1-foo").Result(), - builder.ForPodVolumeBackup("velero", "pvb-2").PodName("TestAnotherPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvbtest2-abc").Result(), - builder.ForPodVolumeBackup("velero", "pvb-3").PodName("TestPod").PodNamespace("TestAnotherNS").SnapshotID("snapshot3").Volume("pvbtest3-xyz").Result(), - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvbtest1-foo": "snapshot1"}, - }, - { - name: "volumes from PVBs that correspond to a pod volume from a projected source are not returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-projected").Result(), - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-projected").Result(), - }, - podVolumes: []corev1api.Volume{ - { - Name: "pvb-non-projected", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "pvb-projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvb-non-projected": "snapshot1"}, - }, - { - name: "volumes from PVBs that correspond to a pod volume from a DownwardAPI source are not returned", - podVolumeBackups: []*velerov1api.PodVolumeBackup{ - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot1").Volume("pvb-non-downwardapi").Result(), - builder.ForPodVolumeBackup("velero", "pvb-1").PodName("TestPod").PodNamespace("TestNS").SnapshotID("snapshot2").Volume("pvb-downwardapi").Result(), - }, - podVolumes: []corev1api.Volume{ - { - Name: "pvb-non-downwardapi", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "pvb-downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - podName: "TestPod", - sourcePodNs: "TestNS", - expected: map[string]string{"pvb-non-downwardapi": "snapshot1"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pod := &corev1api.Pod{} - pod.Annotations = test.podAnnotations - pod.Name = test.podName - pod.Spec.Volumes = test.podVolumes - - res := GetVolumeBackupsForPod(test.podVolumeBackups, pod, test.sourcePodNs) - assert.Equal(t, test.expected, res) - }) - } -} - -func TestGetVolumesToBackup(t *testing.T) { - tests := []struct { - name string - annotations map[string]string - expected []string - }{ - { - name: "nil annotations", - annotations: nil, - expected: nil, - }, - { - name: "no volumes to backup", - annotations: map[string]string{"foo": "bar"}, - expected: nil, - }, - { - name: "one volume to backup", - annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1"}, - expected: []string{"volume-1"}, - }, - { - name: "multiple volumes to backup", - annotations: map[string]string{"foo": "bar", VolumesToBackupAnnotation: "volume-1,volume-2,volume-3"}, - expected: []string{"volume-1", "volume-2", "volume-3"}, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - pod := &corev1api.Pod{} - pod.Annotations = test.annotations - - res := GetVolumesToBackup(pod) - - // sort to ensure good compare of slices - sort.Strings(test.expected) - sort.Strings(res) - - assert.Equal(t, test.expected, res) - }) - } -} - func TestGetSnapshotsInBackup(t *testing.T) { tests := []struct { name string @@ -419,410 +216,3 @@ func TestTempCACertFile(t *testing.T) { os.Remove(fileName) } - -func TestGetPodVolumesUsingRestic(t *testing.T) { - testCases := []struct { - name string - pod *corev1api.Pod - expected []string - defaultVolumesToRestic bool - }{ - { - name: "should get PVs from VolumesToBackupAnnotation when defaultVolumesToRestic is false", - defaultVolumesToRestic: false, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToBackupAnnotation: "resticPV1,resticPV2,resticPV3", - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should get all pod volumes when defaultVolumesToRestic is true and no PVs are excluded", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should get all pod volumes except ones excluded when defaultVolumesToRestic is true", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude default service account token from restic backup", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic because colume mounting default service account token - {Name: "default-token-5xq45"}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude host path volumes from restic backups", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath - {Name: "hostPath1", VolumeSource: corev1api.VolumeSource{HostPath: &corev1api.HostPathVolumeSource{Path: "/hostpathVol"}}}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude volumes mounting secrets", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath - {Name: "superSecret", VolumeSource: corev1api.VolumeSource{Secret: &corev1api.SecretVolumeSource{SecretName: "super-secret"}}}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude volumes mounting config maps", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - // Restic Volumes - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - /// Excluded from restic through annotation - {Name: "nonResticPV1"}, {Name: "nonResticPV2"}, {Name: "nonResticPV3"}, - // Excluded from restic because hostpath - {Name: "appCOnfig", VolumeSource: corev1api.VolumeSource{ConfigMap: &corev1api.ConfigMapVolumeSource{LocalObjectReference: corev1api.LocalObjectReference{Name: "app-config"}}}}, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude projected volumes", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{ - Sources: []corev1api.VolumeProjection{{ - Secret: &corev1api.SecretProjection{ - LocalObjectReference: corev1api.LocalObjectReference{}, - Items: nil, - Optional: nil, - }, - DownwardAPI: nil, - ConfigMap: nil, - ServiceAccountToken: nil, - }}, - DefaultMode: nil, - }, - }, - }, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - { - name: "should exclude DownwardAPI volumes", - defaultVolumesToRestic: true, - pod: &corev1api.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - VolumesToExcludeAnnotation: "nonResticPV1,nonResticPV2,nonResticPV3", - }, - }, - Spec: corev1api.PodSpec{ - Volumes: []corev1api.Volume{ - {Name: "resticPV1"}, {Name: "resticPV2"}, {Name: "resticPV3"}, - { - Name: "downwardAPI", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{ - Items: []corev1api.DownwardAPIVolumeFile{ - { - Path: "labels", - FieldRef: &corev1api.ObjectFieldSelector{ - APIVersion: "v1", - FieldPath: "metadata.labels", - }, - }, - }, - }, - }, - }, - }, - }, - }, - expected: []string{"resticPV1", "resticPV2", "resticPV3"}, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := GetPodVolumesUsingRestic(tc.pod, tc.defaultVolumesToRestic) - - sort.Strings(tc.expected) - sort.Strings(actual) - assert.Equal(t, tc.expected, actual) - }) - } -} - -func TestIsPVBMatchPod(t *testing.T) { - testCases := []struct { - name string - pvb velerov1api.PodVolumeBackup - podName string - sourcePodNs string - expected bool - }{ - { - name: "should match PVB and pod", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "matching-pod", - sourcePodNs: "matching-namespace", - expected: true, - }, - { - name: "should not match PVB and pod, pod name mismatch", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "not-matching-pod", - sourcePodNs: "matching-namespace", - expected: false, - }, - { - name: "should not match PVB and pod, pod namespace mismatch", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "matching-pod", - sourcePodNs: "not-matching-namespace", - expected: false, - }, - { - name: "should not match PVB and pod, pod name and namespace mismatch", - pvb: velerov1api.PodVolumeBackup{ - Spec: velerov1api.PodVolumeBackupSpec{ - Pod: corev1api.ObjectReference{ - Name: "matching-pod", - Namespace: "matching-namespace", - }, - }, - }, - podName: "not-matching-pod", - sourcePodNs: "not-matching-namespace", - expected: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := isPVBMatchPod(&tc.pvb, tc.podName, tc.sourcePodNs) - assert.Equal(t, tc.expected, actual) - }) - - } -} - -func TestVolumeHasNonRestorableSource(t *testing.T) { - testCases := []struct { - name string - volumeName string - podVolumes []corev1api.Volume - expected bool - }{ - { - name: "volume name not in list of volumes", - volumeName: "missing-volume", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: false, - }, - { - name: "volume name in list of volumes but not projected or DownwardAPI", - volumeName: "restorable", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: false, - }, - { - name: "volume name in list of volumes and projected", - volumeName: "projected", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: true, - }, - { - name: "volume name in list of volumes and is a DownwardAPI volume", - volumeName: "downwardapi", - podVolumes: []corev1api.Volume{ - { - Name: "restorable", - VolumeSource: corev1api.VolumeSource{ - PersistentVolumeClaim: &corev1api.PersistentVolumeClaimVolumeSource{}, - }, - }, - { - Name: "projected", - VolumeSource: corev1api.VolumeSource{ - Projected: &corev1api.ProjectedVolumeSource{}, - }, - }, - { - Name: "downwardapi", - VolumeSource: corev1api.VolumeSource{ - DownwardAPI: &corev1api.DownwardAPIVolumeSource{}, - }, - }, - }, - expected: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := volumeHasNonRestorableSource(tc.volumeName, tc.podVolumes) - assert.Equal(t, tc.expected, actual) - }) - - } -} diff --git a/pkg/restic/mocks/repository_manager.go b/pkg/restic/mocks/repository_manager.go index de8770c37..553370674 100644 --- a/pkg/restic/mocks/repository_manager.go +++ b/pkg/restic/mocks/repository_manager.go @@ -23,6 +23,7 @@ import ( restic "github.com/vmware-tanzu/velero/pkg/restic" v1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" + "github.com/vmware-tanzu/velero/pkg/podvolume" ) // RepositoryManager is an autogenerated mock type for the RepositoryManager type @@ -73,15 +74,15 @@ func (_m *RepositoryManager) InitRepo(repo *v1.BackupRepository) error { } // NewBackupper provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) (restic.Backupper, error) { +func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) (podvolume.Backupper, error) { ret := _m.Called(_a0, _a1) - var r0 restic.Backupper - if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup) restic.Backupper); ok { + var r0 podvolume.Backupper + if rf, ok := ret.Get(0).(func(context.Context, *v1.Backup) podvolume.Backupper); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(restic.Backupper) + r0 = ret.Get(0).(podvolume.Backupper) } } @@ -96,15 +97,15 @@ func (_m *RepositoryManager) NewBackupper(_a0 context.Context, _a1 *v1.Backup) ( } // NewRestorer provides a mock function with given fields: _a0, _a1 -func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) (restic.Restorer, error) { +func (_m *RepositoryManager) NewRestorer(_a0 context.Context, _a1 *v1.Restore) (podvolume.Restorer, error) { ret := _m.Called(_a0, _a1) - var r0 restic.Restorer - if rf, ok := ret.Get(0).(func(context.Context, *v1.Restore) restic.Restorer); ok { + var r0 podvolume.Restorer + if rf, ok := ret.Get(0).(func(context.Context, *v1.Restore) podvolume.Restorer); ok { r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { - r0 = ret.Get(0).(restic.Restorer) + r0 = ret.Get(0).(podvolume.Restorer) } } diff --git a/pkg/restic/repository_manager.go b/pkg/restic/repository_manager.go index 69927b851..39961fc02 100644 --- a/pkg/restic/repository_manager.go +++ b/pkg/restic/repository_manager.go @@ -18,16 +18,13 @@ package restic import ( "context" - "fmt" "os" "strconv" "github.com/pkg/errors" "github.com/sirupsen/logrus" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "k8s.io/client-go/tools/cache" - kbclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/vmware-tanzu/velero/internal/credentials" @@ -36,6 +33,8 @@ import ( velerov1client "github.com/vmware-tanzu/velero/pkg/generated/clientset/versioned/typed/velero/v1" velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions/velero/v1" velerov1listers "github.com/vmware-tanzu/velero/pkg/generated/listers/velero/v1" + "github.com/vmware-tanzu/velero/pkg/podvolume" + "github.com/vmware-tanzu/velero/pkg/repository" repokey "github.com/vmware-tanzu/velero/pkg/repository/keys" veleroexec "github.com/vmware-tanzu/velero/pkg/util/exec" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -62,23 +61,9 @@ type RepositoryManager interface { // available snapshots in a repo. Forget(context.Context, SnapshotIdentifier) error - BackupperFactory + podvolume.BackupperFactory - RestorerFactory -} - -// BackupperFactory can construct restic backuppers. -type BackupperFactory interface { - // NewBackupper returns a restic backupper for use during a single - // Velero backup. - NewBackupper(context.Context, *velerov1api.Backup) (Backupper, error) -} - -// RestorerFactory can construct restic restorers. -type RestorerFactory interface { - // NewRestorer returns a restic restorer for use during a single - // Velero restore. - NewRestorer(context.Context, *velerov1api.Restore) (Restorer, error) + podvolume.RestorerFactory } type repositoryManager struct { @@ -88,13 +73,15 @@ type repositoryManager struct { repoInformerSynced cache.InformerSynced kbClient kbclient.Client log logrus.FieldLogger - repoLocker *repoLocker - repoEnsurer *repositoryEnsurer + repoLocker *repository.RepoLocker + repoEnsurer *repository.RepositoryEnsurer fileSystem filesystem.Interface ctx context.Context pvcClient corev1client.PersistentVolumeClaimsGetter pvClient corev1client.PersistentVolumesGetter credentialsFileStore credentials.FileStore + podvolume.BackupperFactory + podvolume.RestorerFactory } const ( @@ -132,56 +119,18 @@ func NewRepositoryManager( log: log, ctx: ctx, - repoLocker: newRepoLocker(), - repoEnsurer: newRepositoryEnsurer(repoInformer, repoClient, log), + repoLocker: repository.NewRepoLocker(), + repoEnsurer: repository.NewRepositoryEnsurer(repoInformer, repoClient, log), fileSystem: filesystem.NewFileSystem(), } + rm.BackupperFactory = podvolume.NewBackupperFactory(rm.repoLocker, rm.repoEnsurer, rm.veleroClient, rm.pvcClient, + rm.pvClient, rm.repoInformerSynced, rm.log) + rm.RestorerFactory = podvolume.NewRestorerFactory(rm.repoLocker, rm.repoEnsurer, rm.veleroClient, rm.pvcClient, + rm.repoInformerSynced, rm.log) return rm, nil } -func (rm *repositoryManager) NewBackupper(ctx context.Context, backup *velerov1api.Backup) (Backupper, error) { - informer := velerov1informers.NewFilteredPodVolumeBackupInformer( - rm.veleroClient, - backup.Namespace, - 0, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(opts *metav1.ListOptions) { - opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.BackupUIDLabel, backup.UID) - }, - ) - - b := newBackupper(ctx, rm, rm.repoEnsurer, informer, rm.pvcClient, rm.pvClient, rm.log) - - go informer.Run(ctx.Done()) - if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, rm.repoInformerSynced) { - return nil, errors.New("timed out waiting for caches to sync") - } - - return b, nil -} - -func (rm *repositoryManager) NewRestorer(ctx context.Context, restore *velerov1api.Restore) (Restorer, error) { - informer := velerov1informers.NewFilteredPodVolumeRestoreInformer( - rm.veleroClient, - restore.Namespace, - 0, - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(opts *metav1.ListOptions) { - opts.LabelSelector = fmt.Sprintf("%s=%s", velerov1api.RestoreUIDLabel, restore.UID) - }, - ) - - r := newRestorer(ctx, rm, rm.repoEnsurer, informer, rm.pvcClient, rm.log) - - go informer.Run(ctx.Done()) - if !cache.WaitForCacheSync(ctx.Done(), informer.HasSynced, rm.repoInformerSynced) { - return nil, errors.New("timed out waiting for cache to sync") - } - - return r, nil -} - func (rm *repositoryManager) InitRepo(repo *velerov1api.BackupRepository) error { // restic init requires an exclusive lock rm.repoLocker.LockExclusive(repo.Name) diff --git a/pkg/restore/restic_restore_action.go b/pkg/restore/restic_restore_action.go index 91b4a6761..ba9f9cb0a 100644 --- a/pkg/restore/restic_restore_action.go +++ b/pkg/restore/restic_restore_action.go @@ -36,6 +36,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/label" "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/restic" "github.com/vmware-tanzu/velero/pkg/util/kube" ) @@ -96,7 +97,7 @@ func (a *ResticRestoreAction) Execute(input *velero.RestoreItemActionExecuteInpu for i := range podVolumeBackupList.Items { podVolumeBackups = append(podVolumeBackups, &podVolumeBackupList.Items[i]) } - volumeSnapshots := restic.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) + volumeSnapshots := podvolume.GetVolumeBackupsForPod(podVolumeBackups, &pod, podFromBackup.Namespace) if len(volumeSnapshots) == 0 { log.Debug("No restic backups found for pod") return velero.NewRestoreItemActionExecuteOutput(input.Item), nil diff --git a/pkg/restore/restore.go b/pkg/restore/restore.go index 691027c7c..11008b55e 100644 --- a/pkg/restore/restore.go +++ b/pkg/restore/restore.go @@ -59,7 +59,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/plugin/framework" "github.com/vmware-tanzu/velero/pkg/plugin/velero" "github.com/vmware-tanzu/velero/pkg/podexec" - "github.com/vmware-tanzu/velero/pkg/restic" + "github.com/vmware-tanzu/velero/pkg/podvolume" "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/collections" "github.com/vmware-tanzu/velero/pkg/util/filesystem" @@ -104,7 +104,7 @@ type kubernetesRestorer struct { discoveryHelper discovery.Helper dynamicFactory client.DynamicFactory namespaceClient corev1.NamespaceInterface - resticRestorerFactory restic.RestorerFactory + resticRestorerFactory podvolume.RestorerFactory resticTimeout time.Duration resourceTerminatingTimeout time.Duration resourcePriorities []string @@ -122,7 +122,7 @@ func NewKubernetesRestorer( dynamicFactory client.DynamicFactory, resourcePriorities []string, namespaceClient corev1.NamespaceInterface, - resticRestorerFactory restic.RestorerFactory, + resticRestorerFactory podvolume.RestorerFactory, resticTimeout time.Duration, resourceTerminatingTimeout time.Duration, logger logrus.FieldLogger, @@ -248,7 +248,7 @@ func (kr *kubernetesRestorer) RestoreWithResolvers( ctx, cancelFunc := go_context.WithTimeout(go_context.Background(), podVolumeTimeout) defer cancelFunc() - var resticRestorer restic.Restorer + var resticRestorer podvolume.Restorer if kr.resticRestorerFactory != nil { resticRestorer, err = kr.resticRestorerFactory.NewRestorer(ctx, req.Restore) if err != nil { @@ -338,7 +338,7 @@ type restoreContext struct { restoreItemActions []framework.RestoreItemResolvedAction itemSnapshotterActions []framework.ItemSnapshotterResolvedAction volumeSnapshotterGetter VolumeSnapshotterGetter - resticRestorer restic.Restorer + resticRestorer podvolume.Restorer resticWaitGroup sync.WaitGroup resticErrs chan error pvsToProvision sets.String @@ -1394,7 +1394,7 @@ func (ctx *restoreContext) restoreItem(obj *unstructured.Unstructured, groupReso // Do not create podvolumerestore when current restore excludes pv/pvc if ctx.resourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumeClaims.String()) && ctx.resourceIncludesExcludes.ShouldInclude(kuberesource.PersistentVolumes.String()) && - len(restic.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 { + len(podvolume.GetVolumeBackupsForPod(ctx.podVolumeBackups, pod, originalNamespace)) > 0 { restorePodVolumeBackups(ctx, createdObj, originalNamespace) } } @@ -1549,7 +1549,7 @@ func restorePodVolumeBackups(ctx *restoreContext, createdObj *unstructured.Unstr return } - data := restic.RestoreData{ + data := podvolume.RestoreData{ Restore: ctx.restore, Pod: pod, PodVolumeBackups: ctx.podVolumeBackups, @@ -1631,7 +1631,7 @@ func hasResticBackup(unstructuredPV *unstructured.Unstructured, ctx *restoreCont var found bool for _, pvb := range ctx.podVolumeBackups { - if pvb.Spec.Pod.Namespace == pv.Spec.ClaimRef.Namespace && pvb.GetAnnotations()[restic.PVCNameAnnotation] == pv.Spec.ClaimRef.Name { + if pvb.Spec.Pod.Namespace == pv.Spec.ClaimRef.Namespace && pvb.GetAnnotations()[podvolume.PVCNameAnnotation] == pv.Spec.ClaimRef.Name { found = true break } diff --git a/pkg/restore/restore_test.go b/pkg/restore/restore_test.go index 7655253d1..404d45e1a 100644 --- a/pkg/restore/restore_test.go +++ b/pkg/restore/restore_test.go @@ -48,8 +48,8 @@ import ( velerov1informers "github.com/vmware-tanzu/velero/pkg/generated/informers/externalversions" "github.com/vmware-tanzu/velero/pkg/kuberesource" "github.com/vmware-tanzu/velero/pkg/plugin/velero" - "github.com/vmware-tanzu/velero/pkg/restic" - resticmocks "github.com/vmware-tanzu/velero/pkg/restic/mocks" + "github.com/vmware-tanzu/velero/pkg/podvolume" + uploadermocks "github.com/vmware-tanzu/velero/pkg/podvolume/mocks" "github.com/vmware-tanzu/velero/pkg/test" testutil "github.com/vmware-tanzu/velero/pkg/test" "github.com/vmware-tanzu/velero/pkg/util/kube" @@ -2681,10 +2681,10 @@ func TestRestorePersistentVolumes(t *testing.T) { } type fakeResticRestorerFactory struct { - restorer *resticmocks.Restorer + restorer *uploadermocks.Restorer } -func (f *fakeResticRestorerFactory) NewRestorer(context.Context, *velerov1api.Restore) (restic.Restorer, error) { +func (f *fakeResticRestorerFactory) NewRestorer(context.Context, *velerov1api.Restore) (podvolume.Restorer, error) { return f.restorer, nil } @@ -2749,7 +2749,7 @@ func TestRestoreWithRestic(t *testing.T) { for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { h := newHarness(t) - restorer := new(resticmocks.Restorer) + restorer := new(uploadermocks.Restorer) defer restorer.AssertExpectations(t) h.restorer.resticRestorerFactory = &fakeResticRestorerFactory{ restorer: restorer, @@ -2773,7 +2773,7 @@ func TestRestoreWithRestic(t *testing.T) { // the restore process adds these labels before restoring, so we must add them here too otherwise they won't match pod.Labels = map[string]string{"velero.io/backup-name": tc.backup.Name, "velero.io/restore-name": tc.restore.Name} - expectedArgs := restic.RestoreData{ + expectedArgs := podvolume.RestoreData{ Restore: tc.restore, Pod: pod, PodVolumeBackups: tc.podVolumeBackups, From 7af1e23614c97dc1d0e78e9be086d0d69818871e Mon Sep 17 00:00:00 2001 From: Xun Jiang Date: Thu, 4 Aug 2022 17:19:31 +0800 Subject: [PATCH 37/38] Add annotation "pv.kubernetes.io/migrated-to" for CSI checking. 1. Also checking annotation "pv.kubernetes.io/migrated-to" to find out whether volume is provisioned by CSI. 2. Add UT cases. Signed-off-by: Xun Jiang --- changelogs/unreleased/5181-jxun | 1 + .../pod_volume_backup_controller.go | 15 +------- .../pod_volume_restore_controller.go | 17 ++------- pkg/util/kube/utils.go | 36 +++++++++++++++---- pkg/util/kube/utils_test.go | 17 +++++++++ 5 files changed, 52 insertions(+), 34 deletions(-) create mode 100644 changelogs/unreleased/5181-jxun diff --git a/changelogs/unreleased/5181-jxun b/changelogs/unreleased/5181-jxun new file mode 100644 index 000000000..4333691d2 --- /dev/null +++ b/changelogs/unreleased/5181-jxun @@ -0,0 +1 @@ +Add annotation "pv.kubernetes.io/migrated-to" for CSI checking. \ No newline at end of file diff --git a/pkg/controller/pod_volume_backup_controller.go b/pkg/controller/pod_volume_backup_controller.go index 8bce92ed7..420e3ca9c 100644 --- a/pkg/controller/pod_volume_backup_controller.go +++ b/pkg/controller/pod_volume_backup_controller.go @@ -208,19 +208,6 @@ func (r *PodVolumeBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -func (r *PodVolumeBackupReconciler) singlePathMatch(path string) (string, error) { - matches, err := r.FileSystem.Glob(path) - if err != nil { - return "", errors.WithStack(err) - } - - if len(matches) != 1 { - return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) - } - - return matches[0], nil -} - // getParentSnapshot finds the most recent completed PodVolumeBackup for the // specified PVC and returns its Restic snapshot ID. Any errors encountered are // logged but not returned since they do not prevent a backup from proceeding. @@ -317,7 +304,7 @@ func (r *PodVolumeBackupReconciler) buildResticCommand(ctx context.Context, log pathGlob := fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(pvb.Spec.Pod.UID), volDir) log.WithField("pathGlob", pathGlob).Debug("Looking for path matching glob") - path, err := r.singlePathMatch(pathGlob) + path, err := kube.SinglePathMatch(pathGlob, r.FileSystem, log) if err != nil { return nil, errors.Wrap(err, "identifying unique volume path on host") } diff --git a/pkg/controller/pod_volume_restore_controller.go b/pkg/controller/pod_volume_restore_controller.go index 3315dae3b..14671834e 100644 --- a/pkg/controller/pod_volume_restore_controller.go +++ b/pkg/controller/pod_volume_restore_controller.go @@ -215,19 +215,6 @@ func getResticInitContainerIndex(pod *corev1api.Pod) int { return -1 } -func singlePathMatch(path string) (string, error) { - matches, err := filepath.Glob(path) - if err != nil { - return "", errors.WithStack(err) - } - - if len(matches) != 1 { - return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) - } - - return matches[0], nil -} - func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *velerov1api.PodVolumeRestore, pod *corev1api.Pod, log logrus.FieldLogger) error { volumeDir, err := kube.GetVolumeDirectory(ctx, log, pod, req.Spec.Volume, c.Client) if err != nil { @@ -236,7 +223,9 @@ func (c *PodVolumeRestoreReconciler) processRestore(ctx context.Context, req *ve // Get the full path of the new volume's directory as mounted in the daemonset pod, which // will look like: /host_pods//volumes// - volumePath, err := singlePathMatch(fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir)) + volumePath, err := kube.SinglePathMatch( + fmt.Sprintf("/host_pods/%s/volumes/*/%s", string(req.Spec.Pod.UID), volumeDir), + c.fileSystem, log) if err != nil { return errors.Wrap(err, "error identifying path of volume") } diff --git a/pkg/util/kube/utils.go b/pkg/util/kube/utils.go index 24b2ef6c7..bf7ac0011 100644 --- a/pkg/util/kube/utils.go +++ b/pkg/util/kube/utils.go @@ -34,15 +34,20 @@ import ( "k8s.io/apimachinery/pkg/util/wait" corev1client "k8s.io/client-go/kubernetes/typed/core/v1" "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/vmware-tanzu/velero/pkg/util/filesystem" ) // These annotations are taken from the Kubernetes persistent volume/persistent volume claim controller. // They cannot be directly importing because they are part of the kubernetes/kubernetes package, and importing that package is unsupported. // Their values are well-known and slow changing. They're duplicated here as constants to provide compile-time checking. // Originals can be found in kubernetes/kubernetes/pkg/controller/volume/persistentvolume/util/util.go. -const KubeAnnBindCompleted = "pv.kubernetes.io/bind-completed" -const KubeAnnBoundByController = "pv.kubernetes.io/bound-by-controller" -const KubeAnnDynamicallyProvisioned = "pv.kubernetes.io/provisioned-by" +const ( + KubeAnnBindCompleted = "pv.kubernetes.io/bind-completed" + KubeAnnBoundByController = "pv.kubernetes.io/bound-by-controller" + KubeAnnDynamicallyProvisioned = "pv.kubernetes.io/provisioned-by" + KubeAnnMigratedTo = "pv.kubernetes.io/migrated-to" +) // NamespaceAndName returns a string in the format / func NamespaceAndName(objMeta metav1.Object) string { @@ -163,6 +168,9 @@ func GetVolumeDirectory(ctx context.Context, log logrus.FieldLogger, pod *corev1 return pvc.Spec.VolumeName, nil } +// isProvisionedByCSI function checks whether this is a CSI PV by annotation. +// Either "pv.kubernetes.io/provisioned-by" or "pv.kubernetes.io/migrated-to" indicates +// PV is provisioned by CSI. func isProvisionedByCSI(log logrus.FieldLogger, pv *corev1api.PersistentVolume, kbClient client.Client) (bool, error) { if pv.Spec.CSI != nil { return true, nil @@ -171,14 +179,15 @@ func isProvisionedByCSI(log logrus.FieldLogger, pv *corev1api.PersistentVolume, // Refer to https://github.com/vmware-tanzu/velero/issues/4496 for more details if pv.Annotations != nil { driverName := pv.Annotations[KubeAnnDynamicallyProvisioned] - if len(driverName) > 0 { + migratedDriver := pv.Annotations[KubeAnnMigratedTo] + if len(driverName) > 0 || len(migratedDriver) > 0 { list := &storagev1api.CSIDriverList{} if err := kbClient.List(context.TODO(), list); err != nil { return false, err } for _, driver := range list.Items { - if driverName == driver.Name { - log.Debugf("the annotation %s=%s indicates the volume is provisioned by a CSI driver", KubeAnnDynamicallyProvisioned, driverName) + if driverName == driver.Name || migratedDriver == driver.Name { + log.Debugf("the annotation %s or %s equals to %s indicates the volume is provisioned by a CSI driver", KubeAnnDynamicallyProvisioned, KubeAnnMigratedTo, driverName) return true, nil } } @@ -187,6 +196,21 @@ func isProvisionedByCSI(log logrus.FieldLogger, pv *corev1api.PersistentVolume, return false, nil } +// SinglePathMatch function will be called by PVB and PVR controller to check whether pass-in volume path is valid. +// Check whether there is only one match by the path's pattern (/host_pods/%s/volumes/*/volume_name/[mount|]). +func SinglePathMatch(path string, fs filesystem.Interface, log logrus.FieldLogger) (string, error) { + matches, err := fs.Glob(path) + if err != nil { + return "", errors.WithStack(err) + } + if len(matches) != 1 { + return "", errors.Errorf("expected one matching path: %s, got %d", path, len(matches)) + } + + log.Debugf("This is a valid volume path: %s.", matches[0]) + return matches[0], nil +} + // IsV1CRDReady checks a v1 CRD to see if it's ready, with both the Established and NamesAccepted conditions. func IsV1CRDReady(crd *apiextv1.CustomResourceDefinition) bool { var isEstablished, namesAccepted bool diff --git a/pkg/util/kube/utils_test.go b/pkg/util/kube/utils_test.go index 4a6db6069..178fa425f 100644 --- a/pkg/util/kube/utils_test.go +++ b/pkg/util/kube/utils_test.go @@ -197,6 +197,13 @@ func TestGetVolumeDirectorySuccess(t *testing.T) { pv: builder.ForPersistentVolume("a-pv").ObjectMeta(builder.WithAnnotations(KubeAnnDynamicallyProvisioned, "csi.test.com")).Result(), want: "a-pv/mount", }, + { + name: "Volume with CSI annotation 'pv.kubernetes.io/migrated-to' appends '/mount' to the volume name", + pod: builder.ForPod("ns-1", "my-pod").Volumes(builder.ForVolume("my-vol").PersistentVolumeClaimSource("my-pvc").Result()).Result(), + pvc: builder.ForPersistentVolumeClaim("ns-1", "my-pvc").VolumeName("a-pv").Result(), + pv: builder.ForPersistentVolume("a-pv").ObjectMeta(builder.WithAnnotations(KubeAnnMigratedTo, "csi.test.com")).Result(), + want: "a-pv/mount", + }, } csiDriver := storagev1api.CSIDriver{ @@ -425,3 +432,13 @@ func TestIsCRDReady(t *testing.T) { _, err = IsCRDReady(obj) assert.NotNil(t, err) } + +func TestSinglePathMatch(t *testing.T) { + fakeFS := velerotest.NewFakeFileSystem() + fakeFS.MkdirAll("testDir1/subpath", 0755) + fakeFS.MkdirAll("testDir2/subpath", 0755) + + _, err := SinglePathMatch("./*/subpath", fakeFS, logrus.StandardLogger()) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "expected one matching path") +} From e5d828a2a4e77e28301454638f312883659dcfcf Mon Sep 17 00:00:00 2001 From: allenxu404 Date: Mon, 8 Aug 2022 12:05:08 +0800 Subject: [PATCH 38/38] modify variables defination and expose err Signed-off-by: allenxu404 --- pkg/controller/backup_controller.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/controller/backup_controller.go b/pkg/controller/backup_controller.go index 387e922bd..5363e2baf 100644 --- a/pkg/controller/backup_controller.go +++ b/pkg/controller/backup_controller.go @@ -438,11 +438,13 @@ func (c *backupController) prepareBackupRequest(backup *velerov1api.Backup) *pkg // Add namespaces with label velero.io/exclude-from-backup=true into request.Spec.ExcludedNamespaces // Essentially, adding the label velero.io/exclude-from-backup=true to a namespace would be equivalent to setting spec.ExcludedNamespaces - namespaces, excludeLabel := corev1api.NamespaceList{}, "velero.io/exclude-from-backup" - if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{excludeLabel: "true"}); err == nil { + namespaces := corev1api.NamespaceList{} + if err := c.kbClient.List(context.Background(), &namespaces, kbclient.MatchingLabels{"velero.io/exclude-from-backup": "true"}); err == nil { for _, ns := range namespaces.Items { request.Spec.ExcludedNamespaces = append(request.Spec.ExcludedNamespaces, ns.Name) } + } else { + request.Status.ValidationErrors = append(request.Status.ValidationErrors, fmt.Sprintf("error getting namespace list: %v", err)) } // validate the included/excluded resources